1

I want to send dbus messages to other applications from Awesome WM. However, the documentation for Awesome's dbus interfact is pretty minimalistic, and I can't find any examples. How to do that? For example, I want to use the Inhibit function in org.freedesktop.login1.

petersohn
  • 11,292
  • 13
  • 61
  • 98
  • Use D-Feet to get a nice list of available interfaces and its method, allowing you to also play with these methods. – AndreLDM Apr 24 '17 at 12:41

1 Answers1

4

Awesome's built-in poor man's DBus wrapper barely contains enough support for DBus to have its built-in notification daemon (naughty) work: https://github.com/awesomeWM/awesome/blob/259c4f716fbd08b4507ebb4cb4d40fe5cbabed0f/dbus.c#L858-L870

This API allows you to implement an object available via DBus and to emit signals, but not to call DBus methods. Looking at some docs, it seems like the Inhibit interface is a method that you have to call. Even worse - it returns a file descriptor!

With lots of reading of GIO's documentation, I came up with the following (hopefully self-explanatory; but also please read https://github.com/pavouk/lgi/blob/master/docs/gio.md) example:

local lgi = require("lgi")
local Gio = lgi.require("Gio")
local GLib = lgi.require("GLib")

-- Workaround for https://github.com/pavouk/lgi/issues/142
local function bus_get_async(type)
    Gio.bus_get(type, nil, coroutine.running())
    local a, b = coroutine.yield()
    return Gio.bus_get_finish(b)
end

local function inhibit(bus, what, who, why, mode)
    local name = "org.freedesktop.login1"
    local object = "/org/freedesktop/login1"
    local interface = "org.freedesktop.login1.Manager"
    local message = Gio.DBusMessage.new_method_call(name, object, interface, "Inhibit")
    message:set_body(GLib.Variant("(ssss)",
        { what, who, why, mode }))

    local timeout = -1 -- Just use the default
    local result, err = bus:async_send_message_with_reply(message, Gio.DBusSendMessageFlags.NONE,
        timeout, nil)

    if err then
        print("error: " .. tostring(err))
        return
    end

    if result:get_message_type() == "ERROR" then
        local _, err = result:to_gerror()
        print("error: " .. tostring(err))
        return
    end

    local fd_list = result:get_unix_fd_list()
    local fd, err = fd_list:get(0)
    if err then
        print("error: " .. tostring(err))
        return
    end

    -- Now... somehow turn this fd into something we can close
    return Gio.UnixInputStream.new(fd, true)
end

Gio.Async.call(function()
    local bus = bus_get_async(Gio.BusType.SYSTEM)
    local a = inhibit(bus, "shutdown:sleep", "hi, it's me!", "Just because", "delay")
    print("got lock")
    io.popen("sleep 10"):read("*a")
    a:async_close()
    -- Speed up deletion of the GDBusMessage that still references the FD
    collectgarbage("collect")
    collectgarbage("collect")

    print("released lock")
    io.popen("sleep 10"):read("*a")
end)()

If you want, you can turn the above into something synchronous by replacing calls to async_foo with calls to foo_sync. That also allows to get rid of the hack to make bus_get_async() work and the Gio.Async.call wrapper around everything.

Uli Schlachter
  • 9,337
  • 1
  • 23
  • 39