There are quite a few of the notification spec implementations, but this one is designed to be not tied to any DE, unlike (unfortunately) most of the others.
Another thing is that simple implementation of the spec doesn't work for me - I need rate-limiting (but without silent dropping of any messages - it's much worse!) to maintain sanity while still paying any attention to these popups.
Flexible-enough filtering is another thing.
Auto-disabling and general ability to block them at will (and again, emphasis on not losing messages, which makes you always doubt and second-guess notifications, ending up doing the manual info-polling) of these popups during fullscreen apps (like when watching video or working on some urgent matter) is yet another...
Actual notification rendering is inspired (and based in part on) notipy project.
I wrote a few extended notes on the subject over time (link1, link2, link3), but it's mostly summarized above.
- Python 2.7 (not 3.X)
- GTK+ 3.X and gobject-introspection-enabled pygobject
- Some fgc modules (fgc.fc for rate-limiting, fgc.scheme for filtering, etc).
Note that libnotify is not needed here - it's usually used to send the messages, not receive and display them.
It's a regular package for Python 2.7 (not 3.X), but not in pypi, so can be installed from a checkout with something like that:
% python setup.py install
Note that to install stuff in system-wide PATH and site-packages, elevated privileges are often required. Use ~/.pydistutils.cfg or virtualenv to do unprivileged installs into custom paths.
Better way would be to use pip to install all the necessary dependencies as well:
% pip install -e 'git://github.com/mk-fg/notification-thing.git#egg=notification-thing'
Alternatively, ./notification-thing
can be run right from the checkout tree,
without any installation.
Just make sure nothing else is already listening on the same dbus path/interface and start the daemon by hand.
Alternatively, dbus service file can be installed, so daemon can be started whenever notifications arrive (and exiting during silence timeouts):
cp org.freedesktop.Notifications.service /usr/share/dbus-1/services/
File ~/.notification_filter can be used to control filtering mechanism at runtime.
It's the simple scheme script, see fgc.scheme or original Peter Norvig's implementation for details.
It's evaluation should return the function which will be called for each notification and should return either #t or #f verdict for whether to display it or not. Example:
(define-macro define-matcher (lambda
(name op comp last rev-args)
`(define ,name (lambda args
(if (= (length args) 1) ,last
(let ((atom (car args)) (args (cdr args)))
(,comp
(,op ,@(if rev-args '((car args) atom) '(atom (car args))))
(apply ,name (cons atom (cdr args))))))))))
(define-matcher ~all ~ and #t #f)
(define-matcher all~ ~ and #t #t)
(define-matcher ~any ~ or #f #f)
(define-matcher any~ ~ or #f #t)
(lambda (summary body)
(not (or
;; hl-only high-traffic channels
(and
(any~ summary
"^erc: #(gunicorn|zeromq|bookz)$"
"^erc: #anon")
(not (~ "MK_FG" body)))
;; irrelevant service messages
(~ "Undefined CTCP query received. Silently ignored" body)
(and
(~ "^erc: #\S+" summary)
(~ "^\*\*\* #\S+ (was created on|modes:) " body)))))
~/.notification_filter is reloaded on-the-fly if updated, any errors there will yield backtraces in notification windows.
Lots of tunable options are available, but all-defaults should be the norm (naturally I use the defaults myself, because I'm the one who sets them;).
% notification-thing -h
usage: notification-thing [-h] [-f] [-u] [-c ACTIVITY_TIMEOUT]
[--no-status-notify] [--filter-file PATH]
[--filter-test SUMMARY BODY] [-t POPUP_TIMEOUT]
[-q QUEUE_LEN]
[--layout-anchor {top_right,bottom_left,bottom_right,top_left}]
[--layout-direction {horizontal,vertical}]
[--layout-margin LAYOUT_MARGIN]
[--tbf-size TBF_SIZE] [--tbf-tick TBF_TICK]
[--tbf-max-delay TBF_MAX_DELAY] [--tbf-inc TBF_INC]
[--tbf-dec TBF_DEC] [--debug]
Desktop notification server.
optional arguments:
-h, --help show this help message and exit
-f, --no-fs-check Dont queue messages if active window is fullscreen
-u, --no-urgency-check
Queue messages even if urgency is critical
-c ACTIVITY_TIMEOUT, --activity-timeout ACTIVITY_TIMEOUT
No-activity (dbus calls) timeout before closing the
daemon instance (less or equal zero - infinite,
default: 600s)
--no-status-notify Do not send notification on changes in proxy settings.
--filter-file PATH Read simple scheme rules for filtering notifications
from file (default: ~/.notification_filter).
--filter-test SUMMARY BODY
Do not start daemon, just test given summary and body
against filter-file and print the result back to
terminal.
-t POPUP_TIMEOUT, --popup-timeout POPUP_TIMEOUT
Default timeout for notification popups removal
(default: 5000ms)
-q QUEUE_LEN, --queue-len QUEUE_LEN
How many messages should be queued on tbf overflow
(default: 10)
--layout-anchor {top_right,bottom_left,bottom_right,top_left}
Screen corner notifications gravitate to (default:
top_left).
--layout-direction {horizontal,vertical}
Direction for notification stack growth from --layout-
anchor corner (default: vertical).
--layout-margin LAYOUT_MARGIN
Margin between notifications, screen edges, and some
misc stuff (default: 3px).
--tbf-size TBF_SIZE Token-bucket message-flow filter (tbf) bucket size
(default: 4)
--tbf-tick TBF_TICK tbf update interval (new token), so token_inflow =
token / tbf_tick (default: 15s)
--tbf-max-delay TBF_MAX_DELAY
Maxmum amount of seconds, between message queue flush
(default: 60s)
--tbf-inc TBF_INC tbf_tick multiplier on consequent tbf overflow
(default: 2)
--tbf-dec TBF_DEC tbf_tick divider on successful grab from non-empty
bucket, wont lower multiplier below 1 (default: 2)
--debug Enable debug logging to stderr.
Use --debug option to get a verbose log of all that's happening there, which decisions are made and based on what data.
DBus interface lacks proper introspection at the moment, but is extended with a few functions, more info on which can be found here and here. For example, to temporarily block/unblock all but the urgent notifications:
dbus-send --type=method_call\
--dest=org.freedesktop.Notifications
/org/freedesktop/Notifications\
org.freedesktop.Notifications.Set\
dict:string:boolean:plug_toggle,true
Appearance (and some behavior) of the popup windows is subject to gtk3 styles (simple css files), with default being the light one (see the actual code for up-to-date stylesheet though):
#notification { background-color: white; }
#notification #hs { background-color: black; }
#notification #critical { background-color: #ffaeae; }
#notification #normal { background-color: #f0ffec; }
#notification #low { background-color: #bee3c6; }
#notification #summary {
font-size: 10;
text-shadow: 1 1 0 gray;
}
#notification #body { font-size: 8; }