#+titl the final code here.
¶The final piece
Desktop notifications are pretty common in applications, it’s good to support them in our desktop environment.
It’s also a useful tool for our own Emacs extensions and scripts!
Password sync script (View on GitHub)
We’ll use a program called Dunst to accomplish this.
¶Dunst
- Minimal
- Easy to customize
- Keyboard controlled
- Fits in well with Polybar
Homepage: https://dunst-project.org/
On Ubuntu you can install via apt
:
sudo apt install dunst
It’s pretty commonly available in other Linux distributions as well!
¶Launching Dunst
It’s just as simple as running dunst
! We can run it and see its output with async-shell-command
(M-&
)
Now to try it out with notify-send
:
notify-send "Test!" notify-send -u critical "Your computer asplode" notify-send -i "emblem-synchronizing" "Passwords synced!"
We can also call up previous notifications using the global C-`
keybinding! Useful if you saw a notification but didn’t have a chance to read it.
We can add it to our efs/exwm-init-hook
function:
(efs/run-in-background "dunst")
¶Customizing Dunst
Much of this is boilerplate, you can read more about how to configure Dunst here:
https://dunst-project.org/documentation/
We’ll add this to our Desktop.org
file:
:tangle ~/.config/dunst/dunstrc :mkdirp yes
[global] ### Display ### monitor = 0 # The geometry of the window: # [{width}]x{height}[+/-{x}+/-{y}] geometry = "500x10-10+50" # Show how many messages are currently hidden (because of geometry). indicate_hidden = yes # Shrink window if it's smaller than the width. Will be ignored if # width is 0. shrink = no # The transparency of the window. Range: [0; 100]. transparency = 10 # The height of the entire notification. If the height is smaller # than the font height and padding combined, it will be raised # to the font height and padding. notification_height = 0 # Draw a line of "separator_height" pixel height between two # notifications. # Set to 0 to disable. separator_height = 1 separator_color = frame # Padding between text and separator. padding = 8 # Horizontal padding. horizontal_padding = 8 # Defines width in pixels of frame around the notification window. # Set to 0 to disable. frame_width = 2 # Defines color of the frame around the notification window. frame_color = "#89AAEB" # Sort messages by urgency. sort = yes # Don't remove messages, if the user is idle (no mouse or keyboard input) # for longer than idle_threshold seconds. idle_threshold = 120 ### Text ### font = Cantarell 20 # The spacing between lines. If the height is smaller than the # font height, it will get raised to the font height. line_height = 0 markup = full # The format of the message. Possible variables are: # %a appname # %s summary # %b body # %i iconname (including its path) # %I iconname (without its path) # %p progress value if set ([ 0%] to [100%]) or nothing # %n progress value if set without any extra characters # %% Literal % # Markup is allowed format = "<b>%s</b>\n%b" # Alignment of message text. # Possible values are "left", "center" and "right". alignment = left # Show age of message if message is older than show_age_threshold # seconds. # Set to -1 to disable. show_age_threshold = 60 # Split notifications into multiple lines if they don't fit into # geometry. word_wrap = yes # When word_wrap is set to no, specify where to make an ellipsis in long lines. # Possible values are "start", "middle" and "end". ellipsize = middle # Ignore newlines '\n' in notifications. ignore_newline = no # Stack together notifications with the same content stack_duplicates = true # Hide the count of stacked notifications with the same content hide_duplicate_count = false # Display indicators for URLs (U) and actions (A). show_indicators = yes ### Icons ### # Align icons left/right/off icon_position = left # Scale larger icons down to this size, set to 0 to disable max_icon_size = 88 # Paths to default icons. icon_path = /home/daviwil/.guix-extra-profiles/desktop/desktop/share/icons/gnome/256x256/status/:/home/daviwil/.guix-extra-profiles/desktop/desktop/share/icons/gnome/256x256/devices/:/home/daviwil/.guix-extra-profiles/desktop/desktop/share/icons/gnome/256x256/emblems/ ### History ### # Should a notification popped up from history be sticky or timeout # as if it would normally do. sticky_history = no # Maximum amount of notifications kept in history history_length = 20 ### Misc/Advanced ### # Browser for opening urls in context menu. browser = qutebrowser # Always run rule-defined scripts, even if the notification is suppressed always_run_script = true # Define the title of the windows spawned by dunst title = Dunst # Define the class of the windows spawned by dunst class = Dunst startup_notification = false verbosity = mesg # Define the corner radius of the notification window # in pixel size. If the radius is 0, you have no rounded # corners. # The radius will be automatically lowered if it exceeds half of the # notification height to avoid clipping text and/or icons. corner_radius = 4 mouse_left_click = close_current mouse_middle_click = do_action mouse_right_click = close_all # Experimental features that may or may not work correctly. Do not expect them # to have a consistent behaviour across releases. [experimental] # Calculate the dpi to use on a per-monitor basis. # If this setting is enabled the Xft.dpi value will be ignored and instead # dunst will attempt to calculate an appropriate dpi value for each monitor # using the resolution and physical size. This might be useful in setups # where there are multiple screens with very different dpi values. per_monitor_dpi = false [shortcuts] # Shortcuts are specified as [modifier+][modifier+]...key # Available modifiers are "ctrl", "mod1" (the alt-key), "mod2", # "mod3" and "mod4" (windows-key). # Xev might be helpful to find names for keys. # Close notification. #close = ctrl+space # Close all notifications. #close_all = ctrl+shift+space # Redisplay last message(s). # On the US keyboard layout "grave" is normally above TAB and left # of "1". Make sure this key actually exists on your keyboard layout, # e.g. check output of 'xmodmap -pke' history = ctrl+grave # Context menu. context = ctrl+shift+period [urgency_low] # IMPORTANT: colors have to be defined in quotation marks. # Otherwise the "#" and following would be interpreted as a comment. background = "#222222" foreground = "#888888" timeout = 10 # Icon for notifications with low urgency, uncomment to enable #icon = /path/to/icon [urgency_normal] background = "#1c1f26" foreground = "#ffffff" timeout = 10 # Icon for notifications with normal urgency, uncomment to enable #icon = /path/to/icon [urgency_critical] background = "#900000" foreground = "#ffffff" frame_color = "#ff0000" timeout = 0 # Icon for notifications with critical urgency, uncomment to enable #icon = /path/to/icon
To refresh the configuration you’ll need to kill and restart Dunst:
pkill dunst && dunst &
Some things you’ll want to consider setting:
format
- Customize how notification text contents are displayedgeometry
- Where the notification appears and how large it should be by defaultmax_icon_size
- Constrain icon display since some icons will be larger than othersicon_path
- Important if your icons are not in a common location (like when using GNU Guix)idle_threshold
- Wait for user to become active for this long before hiding notificationsmouse_left/right/middle_click
- Action to take when clicking a notification- Any of the key bindings in the
shortcuts
section (though these are deprecated in 1.5.0, usedunstctl
)
¶Control Dunst with dunstctl
Starting with Dunst 1.5.0 there is a new command line tool called dunstctl
which enables you to set up key bindings in your desktop environment (Emacs!) which launch dunstctl
to control the running Dunst instance:
λ dunstctl --help Usage: dunstctl <command> [parameters] Commands: action Perform the default action, or open the context menu of the notification at the given position close Close the last notification close-all Close the all notifications context Open context menu history-pop Pop one notification from history is-paused Check if dunst is running or paused set-paused [true|false|toggle] Set the pause status debug Print debugging information help Show this help
(defun efs/dunstctl (command) (start-process-shell-command "dunstctl" nil (concat "dunstctl " command))) (exwm-input-set-key (kbd "s-n") (lambda () (interactive) (efs/dunstctl "history-pop"))) (exwm-input-set-key (kbd "s-N") (lambda () (interactive) (efs/dunstctl "close-all")))
¶Enabling and disabling notifications
You can use either of the following commands to disable desktop notifications temporarily:
notify-send "DUNST_COMMAND_PAUSE" killall -SIGUSR1 dunst dunstctl set-paused true # Only available form 1.5.0 onward
You can resume notifications (and see all the notifications that occurred while disabled) by running any of these commands:
notify-send "DUNST_COMMAND_RESUME" killall -SIGUSR2 dunst dunstctl set-paused false # Only available from 1.5.0 onward
It can be useful to create an interactive function to enable/disable notifications so that you can use it in your configuration!
(defun efs/disable-desktop-notifications () (interactive) (start-process-shell-command "notify-send" nil "notify-send \"DUNST_COMMAND_PAUSE\"")) (defun efs/enable-desktop-notifications () (interactive) (start-process-shell-command "notify-send" nil "notify-send \"DUNST_COMMAND_RESUME\"")) (defun efs/toggle-desktop-notifications () (interactive) (start-process-shell-command "notify-send" nil "notify-send \"DUNST_COMMAND_TOGGLE\"")) (start-process-shell-command "notify-send" nil "notify-send \"This is from Emacs!\"")
¶Sending notifications from Emacs
Now that we can display notifications, we can use them in our Emacs configuration too:
¶Built-in functions
Emacs has a built-in function for this:
(notifications-notify :title "Test!" :body "This is just a test!")
Manual: https://www.gnu.org/software/emacs/manual/html_node/elisp/Desktop-Notifications.html
¶alert.el
An alternative is alert.el: https://github.com/jwiegley/alert
(alert "This is just a test!" :title "Test!")
However, this library is usually only preferrable if you are writing a package that needs to show notifications that the user might want to customize.