mu4e: implement optional desktop notifications
This commit is contained in:
@ -46,6 +46,7 @@ mu4e_srcs=[
|
|||||||
'mu4e-mark.el',
|
'mu4e-mark.el',
|
||||||
'mu4e-message.el',
|
'mu4e-message.el',
|
||||||
'mu4e-modeline.el',
|
'mu4e-modeline.el',
|
||||||
|
'mu4e-notification.el',
|
||||||
'mu4e-obsolete.el',
|
'mu4e-obsolete.el',
|
||||||
'mu4e-org.el',
|
'mu4e-org.el',
|
||||||
'mu4e-query-items.el',
|
'mu4e-query-items.el',
|
||||||
|
|||||||
90
mu4e/mu4e-notification.el
Normal file
90
mu4e/mu4e-notification.el
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
;;; mu4e-notification.el --- -*- coding: utf-8; lexical-binding: t -*-
|
||||||
|
;;
|
||||||
|
;; Copyright (C) 1996-2023 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
;;; Generic support for showing new-mail notifications.
|
||||||
|
|
||||||
|
;;; Code:
|
||||||
|
|
||||||
|
(require 'mu4e-query-items)
|
||||||
|
(require 'mu4e-bookmarks)
|
||||||
|
|
||||||
|
;; for emacs' built-in desktop notifications to work, we need
|
||||||
|
;; dbus
|
||||||
|
(when (featurep 'dbus)
|
||||||
|
(require 'notifications))
|
||||||
|
|
||||||
|
(defcustom mu4e-notification-filter #'mu4e--default-notification-filter
|
||||||
|
"Function for determining if a notification is to be emitted.
|
||||||
|
|
||||||
|
If this is the case, the function should return non-nil.
|
||||||
|
|
||||||
|
The function must accept an optional single parameter, unused for
|
||||||
|
now."
|
||||||
|
:type 'function
|
||||||
|
:group 'mu4e-notification)
|
||||||
|
|
||||||
|
(defcustom mu4e-notification-function
|
||||||
|
#'mu4e--default-notification-function
|
||||||
|
"Function to emit a notification.
|
||||||
|
|
||||||
|
The function is invoked when we need to emit a new-mail
|
||||||
|
notification in some system-specific way. The function is invoked
|
||||||
|
when the query-items have been updated and
|
||||||
|
`mu4e-notification-filter' returns t.
|
||||||
|
|
||||||
|
The function must accept an optional single parameter, unused for
|
||||||
|
now."
|
||||||
|
:type 'function
|
||||||
|
:group 'mu4e-notification)
|
||||||
|
|
||||||
|
(defun mu4e--default-notification-filter (&optional _)
|
||||||
|
"Return t if a notification should be shown.
|
||||||
|
|
||||||
|
This default implementation does so when there are more unread
|
||||||
|
messages since baseline for the favorite bookmark."
|
||||||
|
(when-let ((fav (mu4e-bookmark-favorite)))
|
||||||
|
(> (or (plist-get fav :delta-unread) 0) 0)))
|
||||||
|
|
||||||
|
(defvar mu4e--notification-id nil
|
||||||
|
"The last notification id, so we can replace it.")
|
||||||
|
|
||||||
|
(defun mu4e--default-notification-function (&optional _)
|
||||||
|
"Default function for handling notifications.
|
||||||
|
The default implementation uses emacs' built-in dbus-notification
|
||||||
|
support."
|
||||||
|
(when-let ((fav (mu4e-bookmark-favorite)))
|
||||||
|
(let* ((title "Mu4e found new mail")
|
||||||
|
(delta-unread (or (plist-get fav :delta-unread) 0))
|
||||||
|
(body (format "There %s %d new message%s for your favorite bookmark"
|
||||||
|
(if (= delta-unread 1) "is" "are")
|
||||||
|
delta-unread
|
||||||
|
(if (= delta-unread 1) "" "s"))))
|
||||||
|
(cond
|
||||||
|
((fboundp 'notifications-notify)
|
||||||
|
;; notifactions available
|
||||||
|
(setq mu4e--notification-id
|
||||||
|
(notifications-notify
|
||||||
|
:title title
|
||||||
|
:body body
|
||||||
|
:app-name "mu4e@emacs"
|
||||||
|
:replaces-id mu4e--notification-id
|
||||||
|
;; a custom mu4e icon would be nice...
|
||||||
|
;; :app-icon (ignore-errors
|
||||||
|
;; (image-search-load-path
|
||||||
|
;; "gnus/gnus.png"))
|
||||||
|
:actions '("Show" "Favorite bookmark")
|
||||||
|
:on-action (lambda (_ _) (mu4e-jump-to-favorite)))))
|
||||||
|
;; ... TBI: other notifications ...
|
||||||
|
(t ;; last resort
|
||||||
|
(mu4e-message "%s: %s" title body))))))
|
||||||
|
|
||||||
|
(defun mu4e--notification ()
|
||||||
|
"Function called when the query items have been updated."
|
||||||
|
(when (and (funcall mu4e-notification-filter)
|
||||||
|
(functionp mu4e-notification-function))
|
||||||
|
(funcall mu4e-notification-function)))
|
||||||
|
|
||||||
|
(provide 'mu4e-notification)
|
||||||
|
;;; mu4e-notification.el ends here
|
||||||
30
mu4e/mu4e.el
30
mu4e/mu4e.el
@ -39,6 +39,7 @@
|
|||||||
(require 'mu4e-bookmarks)
|
(require 'mu4e-bookmarks)
|
||||||
(require 'mu4e-update)
|
(require 'mu4e-update)
|
||||||
(require 'mu4e-main)
|
(require 'mu4e-main)
|
||||||
|
(require 'mu4e-notification)
|
||||||
(require 'mu4e-server) ;; communication with backend
|
(require 'mu4e-server) ;; communication with backend
|
||||||
|
|
||||||
|
|
||||||
@ -49,7 +50,12 @@
|
|||||||
:group 'mu4e)
|
:group 'mu4e)
|
||||||
|
|
||||||
(defcustom mu4e-modeline-support t
|
(defcustom mu4e-modeline-support t
|
||||||
"Support for shoiwing information in the modeline."
|
"Support for showing information in the modeline."
|
||||||
|
:type 'boolean
|
||||||
|
:group 'mu4e)
|
||||||
|
|
||||||
|
(defcustom mu4e-notification-support nil
|
||||||
|
"Support for new-message notifications."
|
||||||
:type 'boolean
|
:type 'boolean
|
||||||
:group 'mu4e)
|
:group 'mu4e)
|
||||||
|
|
||||||
@ -82,8 +88,8 @@ is non-nil."
|
|||||||
(interactive "P")
|
(interactive "P")
|
||||||
;; start mu4e, then show the main view
|
;; start mu4e, then show the main view
|
||||||
(mu4e--init-handlers)
|
(mu4e--init-handlers)
|
||||||
(mu4e--start (unless background #'mu4e--main-view))
|
(mu4e--query-items-refresh)
|
||||||
(mu4e--query-items-refresh))
|
(mu4e--start (unless background #'mu4e--main-view)))
|
||||||
|
|
||||||
(defun mu4e-quit()
|
(defun mu4e-quit()
|
||||||
"Quit the mu4e session."
|
"Quit the mu4e session."
|
||||||
@ -134,13 +140,6 @@ Invoke FUNC if non-nil."
|
|||||||
(lambda () (mu4e-update-mail-and-index
|
(lambda () (mu4e-update-mail-and-index
|
||||||
mu4e-index-update-in-background)))))))
|
mu4e-index-update-in-background)))))))
|
||||||
|
|
||||||
;; ;; 2. update the main view, if any
|
|
||||||
;; (when-let ((mainbuf (get-buffer mu4e-main-buffer-name)))
|
|
||||||
;; (when (buffer-live-p mainbuf)
|
|
||||||
;; (mu4e--main-redraw-buffer)))
|
|
||||||
;; ;; 3. modeline.
|
|
||||||
;; (mu4e--modeline-update)
|
|
||||||
|
|
||||||
(defun mu4e--start (&optional func)
|
(defun mu4e--start (&optional func)
|
||||||
"Start mu4e.
|
"Start mu4e.
|
||||||
If `mu4e-contexts' have been defined, but we don't have a context
|
If `mu4e-contexts' have been defined, but we don't have a context
|
||||||
@ -153,12 +152,16 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
|
|||||||
(mu4e--context-autoswitch nil mu4e-context-policy))
|
(mu4e--context-autoswitch nil mu4e-context-policy))
|
||||||
(setq mu4e-pong-func
|
(setq mu4e-pong-func
|
||||||
(lambda (info) (mu4e--pong-handler info func)))
|
(lambda (info) (mu4e--pong-handler info func)))
|
||||||
(mu4e--modeline-register #'mu4e--bookmarks-modeline-item 'global)
|
;; modeline support
|
||||||
(when mu4e-modeline-support
|
(when mu4e-modeline-support
|
||||||
|
(mu4e--modeline-register #'mu4e--bookmarks-modeline-item 'global)
|
||||||
(mu4e-modeline-mode)
|
(mu4e-modeline-mode)
|
||||||
(add-hook 'mu4e-query-items-updated-hook
|
(add-hook 'mu4e-query-items-updated-hook
|
||||||
#'mu4e--modeline-update))
|
#'mu4e--modeline-update))
|
||||||
(mu4e-modeline-mode (if mu4e-modeline-support 1 -1))
|
(mu4e-modeline-mode (if mu4e-modeline-support 1 -1))
|
||||||
|
(when mu4e-notification-support
|
||||||
|
(add-hook 'mu4e-query-items-updated-hook
|
||||||
|
#'mu4e--notification))
|
||||||
(mu4e--server-ping)
|
(mu4e--server-ping)
|
||||||
;; maybe request the list of contacts, automatically refreshed after
|
;; maybe request the list of contacts, automatically refreshed after
|
||||||
;; reindexing
|
;; reindexing
|
||||||
@ -172,6 +175,8 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
|
|||||||
(mu4e-clear-caches)
|
(mu4e-clear-caches)
|
||||||
(remove-hook 'mu4e-query-items-updated-hook
|
(remove-hook 'mu4e-query-items-updated-hook
|
||||||
#'mu4e--modeline-update)
|
#'mu4e--modeline-update)
|
||||||
|
(remove-hook 'mu4e-query-items-updated-hook
|
||||||
|
#'mu4e--notification)
|
||||||
(mu4e-kill-update-mail)
|
(mu4e-kill-update-mail)
|
||||||
(mu4e-modeline-mode -1)
|
(mu4e-modeline-mode -1)
|
||||||
(mu4e--server-kill)
|
(mu4e--server-kill)
|
||||||
@ -271,6 +276,7 @@ chance."
|
|||||||
mu4e-maildir-list nil
|
mu4e-maildir-list nil
|
||||||
mu4e--contacts-set nil
|
mu4e--contacts-set nil
|
||||||
mu4e--contacts-tstamp "0"))
|
mu4e--contacts-tstamp "0"))
|
||||||
;;; _
|
|
||||||
|
;;;
|
||||||
(provide 'mu4e)
|
(provide 'mu4e)
|
||||||
;;; mu4e.el ends here
|
;;; mu4e.el ends here
|
||||||
|
|||||||
@ -96,6 +96,7 @@ with answers to frequently asked questions, @ref{FAQ}.
|
|||||||
* Dynamic folders:: Folders that change based on circumstances
|
* Dynamic folders:: Folders that change based on circumstances
|
||||||
* Actions:: Defining and using custom actions
|
* Actions:: Defining and using custom actions
|
||||||
* Extending mu4e:: Writing code for @t{mu4e}
|
* Extending mu4e:: Writing code for @t{mu4e}
|
||||||
|
* Integration:: Integrating @t{mu4e} with Emacs facilities
|
||||||
|
|
||||||
Appendices
|
Appendices
|
||||||
* Other tools:: mu4e and the rest of the world
|
* Other tools:: mu4e and the rest of the world
|
||||||
@ -3059,23 +3060,46 @@ see @code{mu4e-toggle-logging}.
|
|||||||
@code{user-error} and @code{error} functions.
|
@code{user-error} and @code{error} functions.
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
@node Integrating with Emacs
|
@node Integration
|
||||||
@chapter Integrating with Emacs
|
@chapter Integrating @t{mu4e} with Emacs facilities
|
||||||
|
|
||||||
In this chapter, we discuss how you can integrate @t{mu4e} with Emacs in various
|
In this chapter, we discuss how you can integrate @t{mu4e} with Emacs in various
|
||||||
ways. Here we focus on Emacs built-ins; for dealing with external tools,
|
ways. Here we focus on Emacs built-ins; for dealing with external tools,
|
||||||
@xref{Other tools}.
|
@xref{Other tools}.
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* Modeline::Showing mu4e's status in the modeline
|
|
||||||
* Default email client::Making mu4e the default emacs e-mail program
|
* Default email client::Making mu4e the default emacs e-mail program
|
||||||
* Using bookmarks::Using Emacs' bookmark system
|
* Modeline::Showing mu4e's status in the modeline
|
||||||
|
* Desktop notifications::Get desktop notifications for new mail
|
||||||
|
* Emacs bookmarks::Using Emacs' bookmark system
|
||||||
* Org-mode links::Adding mu4e to your organized life
|
* Org-mode links::Adding mu4e to your organized life
|
||||||
* iCalendar::Enabling iCalendar invite processing
|
* iCalendar::Enabling iCalendar invite processing
|
||||||
* Speedbar::A special frame with your folders
|
* Speedbar::A special frame with your folders
|
||||||
* Dired:: Attaching files using @t{dired}
|
* Dired:: Attaching files using @t{dired}
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
|
|
||||||
|
@node Default email client
|
||||||
|
@section Default email client
|
||||||
|
|
||||||
|
Emacs allows you to select an e-mail program as the default program it uses when
|
||||||
|
you press @key{C-x m} (@code{compose-mail}), call @code{report-emacs-bug} and so
|
||||||
|
on. If you want to use @t{mu4e} for this, you can do so by adding the following
|
||||||
|
to your configuration:
|
||||||
|
|
||||||
|
@lisp
|
||||||
|
(setq mail-user-agent 'mu4e-user-agent)
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
Similarly, to specify @t{mu4e} as your preferred method for reading
|
||||||
|
mail, customize the variable @code{read-mail-command}.
|
||||||
|
|
||||||
|
@lisp
|
||||||
|
(set-variable 'read-mail-command 'mu4e)
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
(@pxref{Top,,Emacs,Sending Mail, Mail Methods})
|
||||||
|
|
||||||
@node Modeline
|
@node Modeline
|
||||||
@section Modeline
|
@section Modeline
|
||||||
@cindex modeline
|
@cindex modeline
|
||||||
@ -3130,29 +3154,28 @@ Due to the way queries work, the modeline is @emph{not} immediately updated when
|
|||||||
you read messages; but going back to the main view (with @kbd{M-x mu4e} restores
|
you read messages; but going back to the main view (with @kbd{M-x mu4e} restores
|
||||||
things.
|
things.
|
||||||
|
|
||||||
@node Default email client
|
@node Desktop notifications
|
||||||
@section Default email client
|
@section Desktop notifications
|
||||||
|
|
||||||
Emacs allows you to select an e-mail program as the default program it uses when
|
Depending on your desktop environment, it is possible to get notification when
|
||||||
you press @key{C-x m} (@code{compose-mail}), call @code{report-emacs-bug} and so
|
there is new mail.
|
||||||
on. If you want to use @t{mu4e} for this, you can do so by adding the following
|
|
||||||
to your configuration:
|
|
||||||
|
|
||||||
@lisp
|
The default implementation (which you can override) depends on the same system
|
||||||
(setq mail-user-agent 'mu4e-user-agent)
|
used for the @xref{Bookmarks and Maildirs} in the main view and the
|
||||||
@end lisp
|
@xref{Modeline}, and thus gives updates when there new messages compared to some
|
||||||
|
``baseline'', as discussed earlier.
|
||||||
|
|
||||||
Similarly, to specify @t{mu4e} as your preferred method for reading
|
For now, notifications are implemented for desktop environments that support
|
||||||
mail, customize the variable @code{read-mail-command}.
|
DBus-based notifications, as per Emacs' notification sub-system
|
||||||
|
@xref{Desktop Notifications,,,elisp,GNU Emacs Lisp Reference Manual}.
|
||||||
|
|
||||||
@lisp
|
You can enable mu4e's desktop notifications (provided that you are on a
|
||||||
(set-variable 'read-mail-command 'mu4e)
|
supported system) by setting @code{mu4e-notification-support} to @t{t}. If you
|
||||||
@end lisp
|
want tweak the details, have a look at @code{mu4e-notification-filter} and
|
||||||
|
@code{mu4e-notification-function}.
|
||||||
|
|
||||||
(@pxref{Top,,Emacs,Sending Mail, Mail Methods})
|
@node Emacs bookmarks
|
||||||
|
@section Emacs bookmarks
|
||||||
@node Using bookmarks
|
|
||||||
@section Using bookmarks
|
|
||||||
|
|
||||||
Note, emacs bookmarks are not to be confused with mu4e's bookmarks; the former
|
Note, emacs bookmarks are not to be confused with mu4e's bookmarks; the former
|
||||||
are a generic linking system, while the latter are remember stored queries.
|
are a generic linking system, while the latter are remember stored queries.
|
||||||
|
|||||||
Reference in New Issue
Block a user