In my intentions, the code below is a work-in-progress¹ Haskell notification server.
However, even with respect to these unpretentious intentions, the program has a bug that I don't understand.
Here's what I do and observe
- I make sure my "official" notification server is not running (e.g.
kill $(pidof dunst)from terminal), - I execute the code below from terminal, which will print
NamePrimaryOwnerand wait, - from another terminal, I'll execute
notify-send 'sum' 'body 1' -t 1234
The result is that where I executed step 2, the following is printed
Variant "notify-send"
Variant 0
Variant ""
Variant "sum"
Variant "body 1"
Variant []
Variant {"sender-pid": Variant 2106341, "urgency": Variant 1}
Variant 1234
which is precisely what I expect, but in the terminal of step 3, this error is shown:
Unexpected reply type
Which I think has to do with one of
(signature_ [TypeInt32]), but that seems ok, because consistent with the protocol, no?- the return type of
notify,DBusR Reply, but that's the only thing I could put there for the code to compile - the value returned by
notify,ReplyReturn [toVariant (0::Int32)], but again I have no clue what could be wrong with it.
But most importantly, what is erroring is not the Haskell program, but the notify-send!
As far as the other two methods go, everything seems to work:
$ dbus-send --session --print-reply --dest="org.freedesktop.Notifications" /org/freedesktop/Notifications org.freedesktop.Notifications.GetServerInformation
method return time=1709151751.802870 sender=:1.161522 -> destination=:1.161528 serial=3 reply_serial=2
string "name"
string "vendor"
string "version"
string "spec version"
$ dbus-send --session --print-reply --dest="org.freedesktop.Notifications" /org/freedesktop/Notifications org.freedesktop.Notifications.GetCapabilities
method return time=1709151753.997828 sender=:1.161522 -> destination=:1.161531 serial=4 reply_serial=2
array [
string "body"
]
But I can't use dbus-send to test Notify because it's just not possible, so I tried with notify-send.
The complete code, which I came up with thanks to comments and answers to my previous questions (1, 2, 3) and this implementation that was linked by one of those answers.
{-# LANGUAGE OverloadedStrings #-}
import DBus
import DBus.Client
import Data.Int
import Control.Monad
import Control.Monad.IO.Class
import Control.Concurrent
notifyInSignature = [
TypeString,
TypeInt32,
TypeString,
TypeString,
TypeString,
TypeArray TypeString,
TypeDictionary TypeString TypeString,
TypeInt32
]
notify :: MethodCall -> DBusR Reply
notify mCall = do
liftIO $ mapM_ print [name, rid, icon, summary, body, actions, hints, expire]
return reply
where reply = ReplyReturn [toVariant (0::Int32)]
[name, rid, icon, summary, body, actions, hints, expire] = methodCallBody mCall
getServerInformation :: IO (String, String, String, String)
getServerInformation = return ("name", "vendor", "version", "spec version")
getCapabilities :: IO [String]
getCapabilities = return ["body"]
main :: IO ()
main = do
client <- connectSession
export client "/org/freedesktop/Notifications" defaultInterface {
interfaceName = "org.freedesktop.Notifications",
interfaceMethods = [
autoMethod "GetServerInformation" getServerInformation,
autoMethod "GetCapabilities" getCapabilities,
makeMethod "Notify" (signature_ notifyInSignature) (signature_ [TypeInt32]) notify
]
}
reply <- requestName client "org.freedesktop.Notifications" []
print reply
forever (threadDelay 1000000)
(¹) In the sense that it prints to stdout the insides of a notification upon receiving it but doesn't do any thing else (like deleting it upon expiration, or whatever an actual notification server should do, such as checking if a notification server is already running, thus erroring accordingly, for instance).
The issue seems to be the difference between the unsigned and signed integer types. If you look at the protocol spec, you'll see that both the input parameter
replaces_idand the reply type areUINT32, notINT32. The Haskelldbuslibrary calls the unsigned versionsWord32, so the following modified version (with just a fewInt32->Word32changes) should work: