Everyone who uses OSS desktop these days probably seen libnotify magic in action - small popup windows that
appear at some corner of the screen, announcing events from other apps.
libnotify itself, however, is just a convenience lib for dispatching these
notifications over dbus, so the latter can pass it app listening on this
interface or even start it beforehand.
Standard app for rendering such messages is notification-daemon, which is
developed alongside with libnotify, but there are drop-in replacements like
. In dbus rpc mechanism
call signatures are clearly defined and visible, so it's pretty easy to
implement replacement for aforementioned daemons, plus vanilla
notification-daemon has introspection calls and dbus itself can be easily
monitored (via dbus-monitor utility) which make it's implementation even more
Now, polling every window for updates manually is quite inefficient - new
mail, xmpp messages, IRC chat lines, system events etc sometimes arrive every
few seconds, and going over all the windows (and by that I mean workspaces
where they're stacked) just to check them is a huge waste of time, especially
when some (or even most, in case of IRC) of these are not really important.
Either response time or focus (and, in extreme case, sanity) has to be
sacrificed in such approach. Luckily, there's another way to monitor this
stuff - small pop-up notifications allow to see what's happening right away,
w/o much attention-switching or work required from an end-user.
But that's the theory.
In practice, I've found that enabling notifications in IRC or jabber is pretty
much pointless, since you'll be swarmed by these as soon as any real activity
starts there. And w/o them it's a stupid wasteful poll practice, mentioned
Notification-daemon has no tricks to remedy the situation, but since the whole
thing is so abstract and transparent I've had no problem making my own fix.
Solution I came up with is to batch the notification messages into a digests as
soon as there are too many of them, displaying such digest pop-ups with some
time interval, so I can keep a grip on what's going on just by glancing at these
as they arrive, switching my activities if something there is important enough.
Having played with schedulers and network shaping/policing before, not much
imagination was required to devise a way to control the message flow rate.
I chose token-bucket algorithm
at first, but since prolonged flood of I-don't-care-about activity have
gradually decreasing value, I didn't want to receive digests of it every N
seconds, so I batched it with a gradual digest interval increase and
digests won't get too fat over these intervals.
Well, the result exceeded my expectations, and now I can use libnotify freely
even to indicate that some rsync just finished in a terminal on another
workspace. Wonder why such stuff isn't built into existing notification
Then, there was another, even more annoying issue: notifications during
fullscreen apps! WTF!?
Wonder if everyone got used to this ugly flickering in fullscreen mplayer,
huge lags in GL games like SpringRTS or I'm just re-inventing the wheel here,
since it's done in gnome or kde (knotify, huh?), but since I'm not gonna use
either one I just added fullscreen-app check
output, queueing them to digest if that is the case.
Ok, a few words about implementation.
Token bucket itself is based on activestate recipe
with some heavy improvements
to adjust flow on constant under/over-flow, plus with a bit more pythonic
style and features, take a look here
. Leaky bucket
implemented by this class
Main dbus magic, however, lies outside the script, since dbus calls cannot be
intercepted and the scheduler can't get'em with notification-daemon already
listening on this interface.
Solution is easy, of course - scheduler can replace the real daemon
and proxy mangled calls to it as necessary. It takes this sed line
for notification-daemon as well, since interface is hard-coded there.
Needs fgc module
, but it's just a hundred
lines on meaningful code.
One more step to making linux desktop more comfortable. Oh, joy ;)