diff --git a/awesome/dbus.lua b/awesome/dbus.lua index 4809fbe..f9b7cd2 100644 --- a/awesome/dbus.lua +++ b/awesome/dbus.lua @@ -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) @@ -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) diff --git a/init.lua b/init.lua index 5d81802..e4fbe87 100644 --- a/init.lua +++ b/init.lua @@ -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 @@ -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 @@ -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] @@ -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 @@ -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 @@ -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)