Jun 8, 2008

KNotify Client

Korshak asked how to do emerge -uND --world && knotify "Done!". I thougt it would be a piece of cake, but apparently not.
The short answer, zypper in libnotify; notify-send Done, has a disadvantage of requiring a fair amount of GNOME software. But it does use a proposed freedesktop.org standard.
Then I find the method org.kde.KNotify.event, but its nice signature (ssavsayasx) means dbus-send (dbus-1-1.2.1-12) cannot invoke it (it cannot pass empty arrays, and it cannot wrap variants in arrays). No problem, let's use Python. But then we are blocked by knotify4 (kdebase4-runtime-4.0.4-19) reporting a wrong signature via the introspection interface.
# killall knotify4; cp /usr/bin/knotify4{,.bak}
# sed 's/type="a(ss)"/ type = "av"/g' /usr/bin/knotify4.bak > /usr/bin/knotify4
Now which event should we use so that the user actually sees the message? There are many of them, with user configurable actions. warning/kde seems to work out of the box.
#! /usr/bin/python
import sys
import dbus
m = sys.argv[1]
kn = dbus.SessionBus().get_object("org.kde.knotify", "/Notify")
i = kn.event("warning", "kde", [], m, [0,0,0,0], [], 0,
    dbus_interface="org.kde.KNotify")
That did it. But I still hope that there is a simple solution for KDE4 that I have missed.

22 comments:

Anonymous said...

Its a bit hacky, but ok. Do you know what these other parameters are for?

Martin Vidner said...

No. But IMO such API is overkill and we need a simpler one.

Anonymous said...

I think the command korshak is looking for is kdialog:

$ kdialog --title "This is a passive popup" --passivepopup "It will disappear in about 10 seconds" 2

Thomas A said...

Can't get this to work in Emacs =/

(dbus-call-method :session "org.kde.knotify" "/Notify" "org.kde.KNotify"
"event"
"warning" "kde" '() "message" '(0 0 0 0) '() 0)

renders:
Debugger entered--Lisp error: (dbus-error "No such method 'event' in interface 'org.kde.KNotify' at object path '/Notify' (signature 'ssbsaubu')")
dbus-call-method(:session "org.kde.knotify" "/Notify" "org.kde.KNotify" "event" "warning" "kde" nil "message" (0 0 0 0) nil 0)
eval((dbus-call-method :session "org.kde.knotify" "/Notify" "org.kde.KNotify" "event" "warning" "kde" (quote nil) "message" (quote (0 0 0 0)) (quote nil) 0))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp nil nil)


I have patched knotify4 and it works with the python example.

Martin Vidner said...

Oh, Emacs :-) Did you have to build it yourself or are there packages with DBus support included?

Let's see, the backtrace includes signature 'ssbsaubu' whereas the expected signature is ssavsayasx. One thing, where you use '() to mean an empty list it gets transformed to nil of type boolean(b). Another, the numbers come out as u instead of y and x. You will have to consult (or bugfix) the Emacs DBus binding to get the right types.

Thomas A said...

"Oh, Emacs :-) Did you have to build it yourself or are there packages with DBus support included?"

emacs-cvs in Gentoo Portage =)

"Let's see, the backtrace includes signature 'ssbsaubu' whereas the expected signature is ssavsayasx."

Rebuilt kde today (svn :P), applied your patch and got this signature now:
ssassauasu
(still works with python code)

"One thing, where you use '() to mean an empty list it gets transformed to nil of type boolean(b)."

Updated my command:
(dbus-call-method :session "org.kde.knotify" "/Notify" "org.kde.KNotify"
"event"
"warning" "kde" '(:array) "message" '(:array 0 0 0 0) '(:array) 0)

"Another, the numbers come out as u instead of y and x."

What do you mean? Please explain ;)


And thanks for the quick response!

Thomas A said...

Yay! I got it! It was hell figuring out without good docs :P

After fiddling with dbus-monitor and seeing the exact same output I started to look at the signature and realized what you meant ;)

After trying to combine variant with the strings and arrays until I almost gave up I tried putting it in the first array, and voila!

(dbus-call-method :session "org.kde.knotify" "/Notify" "org.kde.KNotify"
"event"
"warning" "kde" '(:array (:variant nil)) "message" '(:array :byte 0 :byte 0 :byte 0 :byte 0) '(:array) :int64 0)

I must say python and perl are much better guessers.

Here is my perl test code for the interested:
#!/usr/bin/perl

use Net::DBus;

my $bus = Net::DBus->session;

my $service = $bus->get_service("org.kde.knotify");

my $object = $service->get_object("/Notify", "org.kde.KNotify");

$object->event("warning", "kde", [], "message", [0, 0, 0, 0], [], 0);


(Im axl btw, changed my name on the google settings page ;))

Martin Vidner said...

Good for you. I was just about to point to section "3.1 Input parameters" of emacs/info/dbus.

Yes, Perl and Python do a better job because they introspect the method call first to learn the expected types. That is also why I originally had to patch the introspection data in the knotify4 binary.

Thomas A said...

I found that there are files called app_name.notifyrc in ~/.kde/share/config and ${KDE_PATH}/share/apps/*/

I made my own called emacs.notifyrc and added this:
[Event/erc_nick]
Action=Sound|Popup|Taskbar
Sound=KDE-Im-Message-In.ogg

and changed "warning" and "kde" to "erc_nick" and "emacs" in the function call.

This gives me a notification with the right sound and Emacs icon =D

My question is, do you know any documents about this? (Google didnt turn up anything)

Anonymous said...

I've been playing with this for a while, but when I run either the given python or perl scripts I run into this:

python test.py hello
Traceback (most recent call last):
File "test.py", line 4, in module
i = kn.event("warning", "kde", [], 'test', [0,0,0,0], [], 0, dbus_interface="org.kde.KNotify")
File "/var/lib/python-support/python2.5/dbus/proxies.py", line 68, in __call__
return self._proxy_method(*args, **keywords)
File "/var/lib/python-support/python2.5/dbus/proxies.py", line 140, in __call__
**keywords)
File "/var/lib/python-support/python2.5/dbus/connection.py", line 622, in call_blocking
message, timeout)
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.UnknownMethod: No such method 'event' in interface 'org.kde.KNotify' at object path '/Notify' (signature 'ssa(ss)sayasx')


'qdbus org.kde.knotify /Notify' doesn't show an event method, but if I run dbus-monitor I can watch konsole generating messages with the event method (the watch-for-activity thing)

Any guesses?

Thomas A said...

Have you tried applying the patch to knotify?

Martin Vidner said...

Yes, notice the part "a(ss)" in the traceback. That is incorrect introspection data (should be "av") and Python gets confused by it. That's why I patched the knotify4 binary.

Martin Vidner said...

> My question is, do you know any documents about this? (Google didnt turn up anything)

So I paid a visit to Seli's desk and got pointed to KNotification Class Reference.

He also fixed the introspection data, grumbling that it should be autogenerated in the first place.

With the Hack Week 3 coming, there is some likelihood that we will make a proper command line client (whatever proper means) :-)

Thomas A said...

Thank you! And thanks to Seli =)

Anonymous said...

Hey everyone!
try kdebindings/dcoppython :)

Sven said...

Hi, a year went by and I still seem to have problems with this:

ERROR:dbus.connection:Unable to set arguments ('warning', 'kde', [], mytext, [0, 0, 0, 0], [], 0) according to signature u'ssavssayasix': <type 'exceptions.TypeError'>: Expected a string or unicode object

I tried to apply the named patch, but that did not change anything. I'm not too much into programming, but I seem to be getting

ssavssayasix whereas you state that
ssavsayasx is correct

Trying td sed one to the other did not help either.

Any ideas?

This cannot be so difficult...

Thanks in advance,
Sven

Martin Vidner said...

ERROR:dbus.connection:Unable to set arguments ('warning', 'kde', [], mytext, [0, 0, 0, 0], [], 0) according to signature u'ssavssayasix': <type 'exceptions.TypeError'>: Expected a string or unicode object

Oh nice, the API has changed in the meantime and now it expects 9 arguments instead of 7. Which KDE release are you using (on which distro)? Even more fun is that on my openSUSE 11.2, with KDE 4.3.1, there are 8 arguments (ssavssayasx).

In my case, the change is from one message string to two strings, a header and a message.
- kn.event("warning", "kde", [], m, [0,0,0,0], [], 0, ...
+ kn.event("warning", "kde", [], "Notification", m, [0,0,0,0], [], 0, ...

For me it only proves that this API is hopeless.

Sven said...

ERROR:dbus.connection:Unable to set arguments ('warning', 'kde', [], 'Notification', message, [0, 0, 0, 0], [], 0) according to signature u'ssavssayasix': <type 'exceptions.TypeError'>: More items found in D-Bus signature than in Python arguments
Traceback (most recent call last):
File "feierabend.py", line 12, in <module>
i=kn.event("warning", "kde", [], "Notification", text, [0,0,0,0], [], 0, dbus_interface='org.kde.KNotify')
File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 68, in __call__
return self._proxy_method(*args, **keywords)
File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 140, in __call__
**keywords)
File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 610, in call_blocking
message.append(signature=signature, *args)
TypeError: More items found in D-Bus signature than in Python arguments


cool, that makes it even more broken...
I'm using KDE 4.3.2 in Kubuntu 9.10.

Sven said...

got it, it's
(for me): ssavssayasix
(for you): ssavssayasx

i=kn.event("warning", "kde", [], 'Notification', text, [0,0,0,0], [], 0, 0, dbus_interface='org.kde.KNotify')

(note the second 0 before dbus_interface), then it works.

Thanks for helping me to find it.

Yep - this API is as broken as undocumented. I guess it is because in KDE4.3+ there are diffrerent kinds of notifications

Sven said...

a last one - hopefully - do you happen to know how to make the notification expire after some time?

It does "vanish" from the OSD, but then it is still there and does not expire before one clicks the X...

XG-網頁設計 said...

thanks for your"KNotify Client"

sassman said...

The method not works for me.
KDE 4.4.2 | QT 4.6.2

a perl script works for me:

#!/usr/bin/perl -w

use strict;
use Net::DBus;

my $bus = Net::DBus->session;
my $service = $bus->get_service("org.kde.knotify");
my $object = $service->get_object("/Notify", "org.kde.KNotify");
$object->event("warning", "kde", [], "message title","message text", [], [], 5*1000, 0);


And in QT it looks like:

QDBusInterface knotify("org.kde.knotify", "/Notify", "org.kde.KNotify");
QList args;
args.append(QVariant("warning"));
args.append(QVariant("kde"));
args.append(QVariant(QVariant::List));
args.append(QVariant("Message Title"));
args.append(QVariant("Message Text"));
args.append(QVariant(QVariant::ByteArray));
args.append(QVariant(QVariant::StringList));
args.append(QVariant((int)5000));
args.append(QVariant(QVariant::LongLong));

knotify.callWithArgumentList( QDBus::AutoDetect, "event", args );
}