Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 111 additions & 68 deletions awesome/dbus.lua
Original file line number Diff line number Diff line change
Expand Up @@ -102,52 +102,52 @@ function dbus.process_request(req)
return true
end

function dbus.iter_args(iter, alltype)
local args = { len = 0 }
function dbus.iter_args(iter, args_dst)
local args = args_dst or {}
if not iter then return args end
typ = alltype or iter:get_arg_type()
while true do
if not typ then
args.len = args.len + 1
args[args.len] = nil
elseif typ == ldbus.types.variant then
local nargs = dbus.iter_args(iter:recurse())
for i = 1, nargs.len do
args[args.len + i] = nargs[i]
end
args.len = args.len + nargs.len
elseif typ == ldbus.types.dict_entry then
local nargs = dbus.iter_args(iter:recurse())
local kwargs = {}
for i = 1, nargs.len, 2 do
kwargs[nargs[i]] = nargs[i + 1]
local typ = iter:get_arg_type()

while typ do
local nextval = {}
if typ == ldbus.types.array then
local arr_typ = iter:get_element_type()
if arr_typ == ldbus.types.dict_entry then
local arr_it = iter:recurse()
while arr_it:get_arg_type() do
local de_it = arr_it:recurse()
--assert (dbus.raw.set_of_basic_types[de_it:get_arg_type()])
local key = de_it:get_basic()
--assert (de_it:has_next()) --value is mandatory
de_it:next()
local val = dbus.iter_args(de_it)
--assert(#val <= 1) --recursing on one dbus type
nextval[key] = val[1]
--assert (not de_it:has_next())
arr_it:next()
end
elseif arr_typ then
local arr_it = iter:recurse()
while arr_it:get_arg_type() do
-- more than one type can be returned, direct "nextval" write
dbus.iter_args(arr_it, nextval)
arr_it:next()
end
end
args.packed = true
args.len = args.len + 1
args[args.len] = kwargs
elseif typ == ldbus.types.variant then
local val = dbus.iter_args(iter:recurse())
--assert(#val <= 1) --recursing on only one dbus type
nextval = val[1]
elseif typ == ldbus.types.struct then
local nargs = dbus.iter_args(iter:recurse())
args.len = args.len + 1
args[args.len] = nargs
elseif typ == ldbus.types.array then
local nargs = dbus.iter_args(iter:recurse(), iter:get_element_type())
args.len = args.len + 1
args[args.len] = nargs
else
args.len = args.len + 1
args[args.len] = iter:get_basic()
end
if iter:next() then
typ = alltype or iter:get_arg_type()
--struct representation is an array of "n" values
nextval = dbus.iter_args(iter:recurse())
else
break
nextval = iter:get_basic()
end
table.insert(args, nextval)
iter:next()
typ = iter:get_arg_type()
end
if args.packed then
return unpack(args)
else
return args
end
return args
end

function dbus.type(value)
Expand Down Expand Up @@ -175,41 +175,84 @@ function dbus.type(value)
return typ
end

function dbus.append_arg(iter, value, typ, subtyp)
for _, v in pairs(ldbus.basic_types) do
if v == typ then
iter:append_basic(value, typ)
return
end
dbus.set_of_basic_types = {}
for _, v in pairs (ldbus.basic_types) do
dbus.set_of_basic_types[v] = true
end

dbus.variant_mt = {}
function dbus.new_variant(vtype, value)
assert(value)
vtype = vtype or dbus.type(value)
return setmetatable ({ t = vtype, v = value }, dbus.variant_mt)
end

function dbus.consume_type(dtype)
if not dtype then
return nil
end
local subiter
if string.sub(typ, 1, 1) == ldbus.types.array then
local subtyp = string.sub(typ, 2)
subiter = iter:open_container(string.sub(typ, 1, 1), subtyp)
if string.sub(subtyp, 1, 1) ~= '{' then
for _, v in ipairs(value) do
dbus.append_arg(subiter, v, subtyp)
local fchar = dtype:sub(1, 1)
if fchar == "{" then
assert(dtype:sub(-1) == "}")
return ldbus.types.dict_entry, dtype:sub(2, -2)
elseif dbus.set_of_basic_types[fchar] or fchar == ldbus.types.array then
return fchar, dtype:sub(2)
elseif fchar == ldbus.types.variant then
return fchar, nil -- The type of a variant can't be detected
end
error("structs unimplemented for now, type: "..dtype, 2)
end

local function error_on(condition, text, lvl)
if condition then
error(text, lvl and lvl + 1 or 2)
end
end

function dbus.append_arg(iter, value, dbus_type)
local dt, dt_next = dbus.consume_type(dbus_type)
if dbus.set_of_basic_types[dt] then
error_on(type(value) == "table", "expected a basic type, got a table")
iter:append_basic(value, dt)
elseif dt == ldbus.types.array then
error_on(type(value) ~= "table", "expected a table")
local arr_iter = iter:open_container(dt, dt_next)
local arr_dt, arr_dt_next = dbus.consume_type(dt_next)
if arr_dt == ldbus.types.dict_entry then
local key_dt, value_dt = dbus.consume_type(arr_dt_next)
error_on(
dbus.set_of_basic_types[key_dt] == nil,
"the key of a dict entry has to be a basic type"
)
for k, v in pairs(value) do
local dict_iter = arr_iter:open_container(arr_dt)
dict_iter:append_basic(k, key_dt)
dbus.append_arg(dict_iter, v, value_dt)
arr_iter:close_container(dict_iter)
end
else
subtyp = subtyp:match('{(%w+)}')
for k, v in pairs(value) do
dbus.append_arg(subiter, {k,v}, ldbus.types.dict_entry, subtyp)
for _, v in ipairs(value) do
dbus.append_arg(arr_iter, v, dt_next)
end
end
elseif typ == ldbus.types.variant then
local subtyp = dbus.type(value)
subiter = iter:open_container(string.sub(typ, 1, 1), subtyp)
dbus.append_arg(subiter, value, subtyp)
iter:close_container(arr_iter)
elseif dt == ldbus.types.variant then
local val, var_dt
if type(value) == "table" and
getmetatable(value) == dbus.variant_mt then
val = value.v
var_dt = value.t
else
val = value
var_dt = dbus.type(value)
end
local var_iter = iter:open_container(dt, var_dt)
dbus.append_arg(var_iter, val, var_dt)
iter:close_container(var_iter)
else
subiter = iter:open_container(string.sub(typ, 1, 1))
end
if typ == ldbus.types.dict_entry then
dbus.append_arg(subiter, value[1], string.sub(subtyp, 1, 1))
dbus.append_arg(subiter, value[2], string.sub(subtyp, 2, 2))
elseif typ == ldbus.types.struct then
-- TODO
error_on(dt == ldbus.types.dict_entry, "dict_entry outside of array")
error_on(dt == ldbus.types.struct, "structs are unsupported")
end
iter:close_container(subiter)
end

function dbus.get_bus(name)
Expand Down
27 changes: 21 additions & 6 deletions init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@ else
dbus.exit = dbus.raw.exit
end

local function optscopy(opts)
if type(opts) ~= "table" then return nil end
return {
args = opts.args,
bus = opts.bus,
callback = opts.callback,
destination = opts.destination,
handler = opts.handler,
interface = opts.interface,
origin = opts.origin,
path = opts.path,
sender = opts.sender,
type = opts.type,
}
end

function dbus.signal_handler(signal, ...)
signal.events = ((dbus.signals[signal.bus] or {})[signal.interface] or {}).events
if not signal.events then return end
Expand Down Expand Up @@ -58,7 +74,7 @@ function dbus.on(name, callback, opts)
elseif callback and type(callback) ~= 'function' then
callback, opts = nil, callback
end
opts = opts or {}
local opts = optscopy(opts) or {}
opts.type = opts.type or "signal"
opts.bus = opts.bus or "session"
callback = callback or opts.callback
Expand Down Expand Up @@ -115,7 +131,7 @@ end


function dbus.off(name, callback, opts)
opts = opts or {}
local opts = optscopy(opts) or {}
opts.type = opts.type or "signal"
opts.bus = opts.bus or "session"
local signal = (dbus.signals[opts.bus] or {})[opts.interface]
Expand Down Expand Up @@ -156,7 +172,7 @@ function dbus.call(name, callback, opts)
if callback and type(callback) ~= 'function' then
callback, opts = nil, callback
end
opts = opts or {}
local opts = optscopy(opts) or {}
opts.type = opts.type or "method_call"
opts.bus = opts.bus or "session"
callback = callback or opts.callback
Expand All @@ -179,9 +195,8 @@ function dbus.call(name, callback, opts)
return serial
end


function dbus.property.get(name, callback, opts)
opts = opts or {}
local opts = optscopy(opts) or {}
opts.args = {'s', opts.interface, 's', name} -- actual arguments to Get
opts.interface = 'org.freedesktop.DBus.Properties'
callback = callback or opts.callback
Expand All @@ -190,7 +205,7 @@ end


function dbus.property.set(name, value, opts)
opts = opts or {}
local opts = optscopy(opts) or {}
opts.args = {'s', opts.interface, 's', name, 'v', value} -- actual arguments to Get
opts.interface = 'org.freedesktop.DBus.Properties'
return dbus.call('Set', opts)
Expand Down