mu4e: implement optional desktop notifications

This commit is contained in:
Dirk-Jan C. Binnema 2023-01-15 12:19:23 +02:00
parent b37cf7341b
commit 08cb28da8c
4 changed files with 155 additions and 35 deletions

View File

@ -46,6 +46,7 @@ mu4e_srcs=[
'mu4e-mark.el',
'mu4e-message.el',
'mu4e-modeline.el',
'mu4e-notification.el',
'mu4e-obsolete.el',
'mu4e-org.el',
'mu4e-query-items.el',

90
mu4e/mu4e-notification.el Normal file
View 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

View File

@ -39,6 +39,7 @@
(require 'mu4e-bookmarks)
(require 'mu4e-update)
(require 'mu4e-main)
(require 'mu4e-notification)
(require 'mu4e-server) ;; communication with backend
@ -49,7 +50,12 @@
:group 'mu4e)
(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
:group 'mu4e)
@ -82,8 +88,8 @@ is non-nil."
(interactive "P")
;; start mu4e, then show the main view
(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()
"Quit the mu4e session."
@ -134,13 +140,6 @@ Invoke FUNC if non-nil."
(lambda () (mu4e-update-mail-and-index
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)
"Start mu4e.
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))
(setq mu4e-pong-func
(lambda (info) (mu4e--pong-handler info func)))
(mu4e--modeline-register #'mu4e--bookmarks-modeline-item 'global)
;; modeline support
(when mu4e-modeline-support
(mu4e--modeline-register #'mu4e--bookmarks-modeline-item 'global)
(mu4e-modeline-mode)
(add-hook 'mu4e-query-items-updated-hook
#'mu4e--modeline-update))
(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)
;; maybe request the list of contacts, automatically refreshed after
;; reindexing
@ -172,6 +175,8 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
(mu4e-clear-caches)
(remove-hook 'mu4e-query-items-updated-hook
#'mu4e--modeline-update)
(remove-hook 'mu4e-query-items-updated-hook
#'mu4e--notification)
(mu4e-kill-update-mail)
(mu4e-modeline-mode -1)
(mu4e--server-kill)
@ -271,6 +276,7 @@ chance."
mu4e-maildir-list nil
mu4e--contacts-set nil
mu4e--contacts-tstamp "0"))
;;; _
;;;
(provide 'mu4e)
;;; mu4e.el ends here

View File

@ -96,6 +96,7 @@ with answers to frequently asked questions, @ref{FAQ}.
* Dynamic folders:: Folders that change based on circumstances
* Actions:: Defining and using custom actions
* Extending mu4e:: Writing code for @t{mu4e}
* Integration:: Integrating @t{mu4e} with Emacs facilities
Appendices
* Other tools:: mu4e and the rest of the world
@ -2479,7 +2480,7 @@ contexts very often, and e.g. manually changes contexts with @kbd{M-x
mu4e-context-switch}.
@end itemize
You can easily switch contexts manually using the @kbd{;} key from
You can easily switch contexts manually using the @kbd{;} key from
the main screen, or by pressing @kbd{C-c ;} when drafting a message
using the editor view.
@ -3059,23 +3060,46 @@ see @code{mu4e-toggle-logging}.
@code{user-error} and @code{error} functions.
@end itemize
@node Integrating with Emacs
@chapter Integrating with Emacs
@node Integration
@chapter Integrating @t{mu4e} with Emacs facilities
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,
@xref{Other tools}.
@menu
* Modeline::Showing mu4e's status in the modeline
* 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
* iCalendar::Enabling iCalendar invite processing
* Speedbar::A special frame with your folders
* Dired:: Attaching files using @t{dired}
@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
@section 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
things.
@node Default email client
@section Default email client
@node Desktop notifications
@section Desktop notifications
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:
Depending on your desktop environment, it is possible to get notification when
there is new mail.
@lisp
(setq mail-user-agent 'mu4e-user-agent)
@end lisp
The default implementation (which you can override) depends on the same system
used for the @xref{Bookmarks and Maildirs} in the main view and the
@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
mail, customize the variable @code{read-mail-command}.
For now, notifications are implemented for desktop environments that support
DBus-based notifications, as per Emacs' notification sub-system
@xref{Desktop Notifications,,,elisp,GNU Emacs Lisp Reference Manual}.
@lisp
(set-variable 'read-mail-command 'mu4e)
@end lisp
You can enable mu4e's desktop notifications (provided that you are on a
supported system) by setting @code{mu4e-notification-support} to @t{t}. If you
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 Using bookmarks
@section Using bookmarks
@node Emacs bookmarks
@section Emacs bookmarks
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.