Getting Linux System Notifications under Openbox
I've been a fan of Linux's lightweight window managers, particularly Openbox, for many years.
But admittedly, there are some things they don't generally handle. One of those is system notifications.
Mostly, I've been happy to go without notifications. Firefox is forever asking me whether I want to turn them on for particular websites (which wouldn't work, but Firefox doesn't know that), usually websites I'm visiting for the first time and probably don't ever want to visit again, let alone let them spam me with ads as notifications outside my browser.
But every now and then it would be handy.
Yesterday afternoon, I
thought of something I needed to do this morning and I wanted to set
up something that would remind me. On my phone, the only way to set that
up would be to set it as an event in my calendar, where it would then
live forever after. On Linux, for a reminder the same day, I use my
eggtimer
script, which has a lot of nice features like being able to add and
subtract time from a running timer. But eggtimer doesn't persist across
reboots, and for a reminder tomorrow morning,
although the at
command
would let me set up a reminder for tomorrow, there's no easy way
for the at job to pop up an alert, because the at job tomorrow won't be
running as a child of tomorrow's X11 session.
The best it could do is email me.
I had other things going on and didn't have time to investigate, so I scrawled a note on the back of an envelope and wedged it in the dashboard of the car so I'd see it before I left to go to the flying field. That old school method worked fine. But when I came back I decided I wanted to figure out notifications.
Note: There's actually a much easier way to do this than notifications. Jump to A Much Easier Way if you just want a quick solution.
First, Choose a Notification Server
There are many notification servers to choose from on Linux. Many of the pages I found when I first started searching pointed to notify-osd as a basic, no-frills option, which sounded ideal at least for a first try. But after spending an hour in web searches and manual reading, I was never able to figure out how one actually runs notify-osd, and I finally gave up on it.
During the search, I saw several positive comments about dunst, so I tried that next. It has the same problem: it has documentation, but the doc only covers the contents of the configuration file, and never touches on how to actually run dunst.
Web searching found a lot of pages recommending commands like
systemctl start --user dunst.service
, but that didn't work for me with Debian's dunst package.
I got a variety of errors (systemctl start
doesn't tell
you the errors, you have to read them separately using
systemctl --user status dunst.service
),
from "Can't find service" to
"Unable to open display" (the cure for that turned out to be
systemctl --user import-environment
)
to "start operation timed out" (I still have no clue why it was timing out,
it never gave me any hints).
How to Actually Start dunst
But it turns out you don't need to use systemd at all. When I ran
dunst &from the commandline, it ran and worked fine, and it worked just as well when I put that in my ~/.config/openbox/autostart file.
How to Generate a Notification
The easy way to generate a notification to see if your notification server
is working — or to pop up a notification in your at or cron job —
is to apt install libnotify-bin
,
which installs libnotify and also gives you the notify-send program.
Then you can run something like
notify-send 'Hello world!' 'This is an example notification.' -u normal
-u is the urgency, which can be low
, normal
or critical
.
You can configure things like the color dunst will use for each urgency
and how long each one will stay on your screen (you can dismiss a dunst
notification manually by clicking on it).
The notification text can also have simple HTML formatting, e.g.
notify-send 'Formatting!' 'This is <b>bold</b> and this is <i>italic</i>.' -u normal
What if you want a notification on a system that doesn't have libnotify? That's a little harder, but still doable:
gdbus call --session \ --dest=org.freedesktop.Notifications \ --object-path=/org/freedesktop/Notifications \ --method=org.freedesktop.Notifications.Notify \ "" 0 "" 'Hello world!' 'This is an example notification.' \ '[]' '{"urgency": <1>}' 5000
The 5000 is number of milliseconds the alert should stay up.
The <1> is supposedly urgency, but in practice it's actually ignored:
<0>, <1>, and <2>
all give the same result and none of them shows up in red as a
critical alert. I never did find out how to really
specify urgency via gdbus call
.
You can also do this low-level notification from Python:
import dbus name = "org.freedesktop.Notifications" notify_intf = dbus.Interface(dbus.SessionBus().get_object( name, "/" + name.replace(".", "/")), name) notify_intf.Notify("", 0, "", "Hello world!", "This is the notification body.", [], {"urgency": 2}, 3000)
As with the command-line version, no matter what you set "urgency" to, 2, '2', 2000, 'critical', it all gives the same result and doesn't send a critical notification.
Sending a Notification via at
So let's try an actual notification using at, for a few minutes from now.
$ at 9:15 warning: commands will be executed using /bin/sh at Sun Aug 20 09:15:00 2023 at> notify-send 'Notification' 'Sent via at' -u critical at>job 28 at Sun Aug 20 09:15:00 2023
Wait for 9:15 and ... nothing happens. Why?
It turns out that these notifications don't actually work from anywhere
other than the X session where you're running the notification server.
To test that, first you need a new shell that doesn't have any information
about your X session. Either su - yourusername
or
ssh localhost
will work.
Now, from that shell, try a notification:
$ notify-send 'Title' 'This is an example notification.' Cannot autolaunch D-Bus without X11 $DISPLAY
But not needing to know the X session details was the whole point of wanting to use notifications in the first place!
Fiddling around a bit, I saw suggestions to let systemd and dbus
know my environment variables under X using
systemctl --user import-environment
and
dbus-update-activation-environment
, but neither one made
any difference.
It turns out the variable the system needs is
$DBUS_SESSION_BUS_ADDRESS
. Unfortunately, I don't know of
any way of getting the $DBUS_SESSION_BUS_ADDRESS for the running X session.
One thing you can do is add this to .config/openbox/autostart,
right before or after you run dunst:
echo $DBUS_SESSION_BUS_ADDRESS > $HOME/.config/dunst/dbus-session-address(I just picked that location out of a hat. Put it wherever you want, since there's no standard place for this.)
Then, when you want to send a notification, use this:
DBUS_SESSION_BUS_ADDRESS=$(cat $HOME/.config/dunst/dbus-session-address) notify-send 'Title' 'This is an example notification.'
This works, but it seems like a ridiculously byzantine set of hoops to jump through just to pop up an alert.
And in fact there's a much easier way that doesn't need the system's notification framework at all.
A Much Easier Way: Forget About Notifications
If you set XAUTHORITY to point to ~/.Xauthority, then set your display to whatever display you're using, you can run any X client. For instance, you can use zenity to pop up a dialog box:
XAUTHORITY=~/.Xauthority DISPLAY=:0 zenity --title "Hello" --info --text="Hello world"
That works without needing any setup, special files, services or anything else. It works through at, too:
$ at 13:23 ~ warning: commands will be executed using /bin/sh at Sun Aug 20 13:23:00 2023 at> XAUTHORITY=~/.Xauthority DISPLAY=:0 zenity --title "Hello" --info --text="Hello world" at>... and, sure enough, the dialog pops up at the indicated time.job 30 at Sun Aug 20 13:23:00 2023
So, having wasted way too much time figuring out how to send and receive notifications, I'm going to forget all that and just use the simpler approach.
But customizing zenity (so that it's more attention-getting) turned out to be trickier than expected. So I'll save those details for a separate article.
[ 14:03 Aug 21, 2023 More linux | permalink to this entry | ]