[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/cholin/lua-dbus/master/interface.lua [Back]  [Original]

local dbus = require("lua-dbus.init")


local function createinstance(meta, ...)
    local instance = setmetatable({}, meta)
    if instance.init then instance:init(...) end
    return instance
end


local Interface = { new = createinstance }
Interface.__index = Interface

function Interface:init(opts)
    opts = opts or {}
    self.bus = opts.bus or 'session'
    self.paths = {}
    self.name = opts.name
    if self.name then
        dbus.raw.request_name(self.bus, self.name)
    end
end

-- opts: interface, path='/', methods={}, signals={}, properties={}
function Interface:add(opts)
    opts = opts or {}
    if not opts.interface then return end
    opts.path = opts.path or '/'
    local path = self.paths[opts.path] or {}
    self.paths[opts.path] = path
    if not path.introspection then
        path.introspection = true
        self:_introspection(opts)
    end
    local iface = path[opts.interface] or {}
    path[opts.interface] = iface
    iface.methods = iface.methods or {}
    for method, args in pairs(opts.methods or {}) do
        iface.methods[method] = args
    end
    iface.signals = iface.signals or {}
    for name, args in pairs(opts.signals or {}) do
        iface.signals[name] = args
    end
    local hasproperties = false
    iface.properties = iface.properties or {}
    for name, property in pairs(opts.properties or {}) do
        iface.properties[name] = property
        hasproperties = true
    end
    if not path.properties then
        path.properties = true
        self:_properties(opts)
    end
    local iface = self
    dbus.on({ bus = self.bus, interface = opts.interface,
        handler = function (sig, ...)
            if sig.type ~= 'method_call' then return end
            local interface = iface.paths[opts.path][sig.interface]
            if not interface then return end
            local method = (interface.methods or {})[sig.member]
            if method and method.callback then
                local i, result, ret = 1, {}, {method.callback(sig, ...)}
                if not method.result then
                    result = ret
                else
                    -- prepare callback results with actual interface result types
                    for j=1,#method.result,2 do
                        table.insert(result, method.result[j]) -- type
                        table.insert(result, ret[i]) -- value
                        i = i + 1
                    end
                end
                return unpack(result)
            end
        end,
    })
end

local function introspect(iface, path, header)
local xml = header ~= false and
[[]] or ""

    if path == "/" then
        xml = xml .. ""
        xml = xml .. introspect(iface, nil, false)
        for facepath, interfaces in pairs(iface.paths or {}) do
            xml = xml .. string.format([[]], string.sub(facepath, 2))
        end
        xml = xml .. ""
        return xml
    end

    local interfaces = iface.paths and iface.paths[path or "/"]
    if interfaces then
        if path then
            xml = xml .. string.format([[]], path)
        end
        for name, interface in pairs(interfaces) do
            if name ~= 'introspection' and name ~= 'properties' then
                xml = xml .. string.format([[]], name)
                for method, args in pairs(interface.methods) do
                    xml = xml .. string.format([[]], method)
                    for i = 1, #args, 2 do
                        xml = xml .. string.format(
                            [[]],
                            args[i+1], args[i], 'in')
                    end
                    if args.result then
                        for i = 1, #args.result, 2 do
                            xml = xml .. string.format(
                                [[]],
                                args.result[i+1], args.result[i], 'out')
                        end
                    end
                    xml = xml .. ""
                end
                for name, args in pairs(interface.signals) do
                    xml = xml .. string.format([[]], name)
                    for i = 1, #args, 2 do
                        xml = xml .. string.format(
                            [[]],
                            args[i+1], args[i])
                    end
                    xml = xml .. ""
                end
                for name, property in pairs(interface.properties) do
                    local access = 'read'
                    if property.read and property.write then
                        access = 'readwrite'
                    elseif property.write then
                        access = 'write'
                    end
                    xml = xml .. string.format(
                        [[]],
                        name, property.type, access)
                end
                xml = xml .. ""
            end
        end
        if path then
            xml = xml .. ""
        end
    end
    return xml
end
Interface.introspect = introspect

function Interface:_introspection(opts)
    local iface = self
    self:add({
        interface = 'org.freedesktop.DBus.Introspectable',
        path = opts.path,
        methods = {
            Introspect = {
                result = {'s', "data"},
                callback = function (sig) return introspect(iface, sig.path) end,
            },
        },
    })
end

function Interface:_properties(opts)
    local iface = self
    self:add({
        interface = 'org.freedesktop.DBus.Properties',
        path = opts.path,
        methods = {
            Get = { 's', "interface", 's', "name",
                result = {'v', "value"},
                callback = function (sig, interface, name)
                    local interfaces = iface.paths and iface.paths[sig.path or "/"]
                    local face = interfaces and interfaces[interface]
                    if face and face.properties then
                        local property = face.properties[name]
                        if property and property.read then
                            return property.read()
                        end
                    end
                end,
            },
            GetAll = { 's', "interface",
                result = {'a{sv}', "values"},
                callback = function (sig, interface)
                    local interfaces = iface.paths and iface.paths[sig.path or "/"]
                    local face = interfaces and interfaces[interface]
                    local values = {}
                    if face and face.properties then
                        for name, property in pairs(face.properties) do
                            if property.read then
                                values[name] = property.read()
                            end
                        end
                    end
                    return values
                end,
            },
            Set = { 's', "interface", 's', "name", 'v', "value",
                callback = function (sig, interface, name, value)
                    local interfaces = iface.paths and iface.paths[sig.path or "/"]
                    local face = interfaces and interfaces[interface]
                    if face and face.properties then
                        local property = face.properties[name]
                        if property and property.write then
                            return property.write(value)
                        end
                    end
                end,
            },
        },
        signals = {
            PropertiesChanged = {
                's', "interface", 'a{sv}', "values", 'as', "keys"
            },
        },
    })
end

function Interface:properties(keys, values, opts)
    opts = opts or {}
    dbus.raw.emit_signal(self.bus, opts.path,
        'org.freedesktop.DBus.Properties', 'PropertiesChanged',
        's', opts.interface, 'a{sv}', values, 'as', keys)
end

function Interface:property(name, value, opts)
    local values = {}
    values[name] = value
    self:properties({name}, values, opts)
end


return Interface

Web Proxy Viewer  |  New URL  |  Original Page