Created
May 17, 2014 19:26
-
-
Save Elv13/9c53759cc51764243d2a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Links: | |
-- * DBus spec: http://dbus.freedesktop.org/doc/dbus-specification.html | |
-- * GVariant API: https://developer.gnome.org/glib/stable/glib-GVariantType.html | |
-- * More GVariant info: https://developer.gnome.org/glib/stable/gvariant-format-strings.html | |
--TODO: | |
-- Create service object, use function myservice:my_method(sdfsdfs,sdfsdf) end | |
-- function myservice.property:get_foo() return dsfdfg end | |
-- add way to call methods after getting a service object | |
-- handle signals myservice:connect_signal("foo",function(43534,345) end) | |
local lgi = require 'lgi' | |
local Gio = lgi.require 'Gio' | |
local core = require 'lgi.core' | |
local GLib = lgi.require 'GLib' | |
local type,unpack = type,unpack | |
-------------- | |
-- LOGIC -- | |
-------------- | |
-- Get the interface_info from its name | |
local ifaces = {} | |
local function iface_lookup(service,name) | |
if not ifaces[name] then | |
local iface = service.introspection_data:lookup_interface(service.iname) | |
ifaces[name] = iface | |
return iface | |
end | |
return ifaces[name] | |
end | |
-- Get a method information | |
local function common_get_info(service,cache,interface_name,method,func) | |
if not cache[interface_name] then | |
cache[interface_name] = {} | |
end | |
local iface_sigs = cache[interface_name] | |
if not iface_sigs[method] then | |
local iface = service:iface_lookup(interface_name) | |
if iface then | |
local info = iface[func](iface,method) | |
if info then | |
iface_sigs[method] = info | |
return info | |
end | |
end | |
end | |
return iface_sigs[method] | |
end | |
local function get_property_info(service,interface_name,property) | |
local prop_info = {} | |
return service:common_get_info(prop_info,interface_name,property,"lookup_property") | |
end | |
local function get_method_info(service,interface_name,method) | |
local method_info = {} | |
return service:common_get_info(method_info,interface_name,method,"lookup_method") | |
end | |
local function get_signal_info(service,interface_name,signal) | |
local sign_info = {} | |
return service:common_get_info(sign_info,interface_name,signal,"lookup_signal") | |
end | |
-- Get a method output signature | |
local function get_out_signature(service,interface_name,method) | |
local info = service:get_method_info(interface_name,method) | |
if info then | |
local ret_t = "(" | |
for k,v in pairs(info.out_args or {}) do | |
ret_t = ret_t .. v.signature | |
end | |
ret_t = ret_t .. ")" | |
return ret_t --TODO add a cache for this | |
end | |
return "" | |
end | |
----------------------- | |
-- Service methods -- | |
----------------------- | |
local function register_object(service,name) | |
-------------- | |
-- CLOSURES -- | |
-------------- | |
-- Called when a remote method is called | |
-- This closure dispatch the calls to the right function | |
local method_call_guard, method_call_addr = core.marshal.callback(Gio.DBusInterfaceMethodCallFunc , | |
function(conn, sender, path, interface_name,method_name,parameters,invok) | |
-- Only call if the method have been defined | |
print("I get here2") | |
if service[method_name] then | |
local rets = {service[method_name](service,unpack(parameters.value))} | |
local out_sig = service:get_out_signature(interface_name,method_name) | |
local gvar = GLib.Variant(out_sig,rets) | |
Gio.DBusMethodInvocation.return_value(invok,gvar) | |
else | |
print("Trying to call "..method_name..[=[ but no implementation was found\n | |
please implement myService:]=]..method_name.."(arg1,arg2)") | |
end | |
end) | |
-- Called when there is a property request (get the current value) | |
local property_get_guard, property_get_addr = core.marshal.callback(Gio.DBusInterfaceGetPropertyFunc , | |
function(conn, sender, path, interface_name,property_name,parameters,error) | |
print("I get here") | |
local sig = service:get_property_info(interface_name,property_name).signature | |
if service.properties["get_"..property_name] then | |
return GLib.Variant(sig,service.properties["get_"..property_name](service)) | |
else | |
print("Trying to read "..property_name..[=[ but no getter was found\n | |
please implement myService.properties.get_]=]..property_name) | |
end | |
return GLib.Variant(sig) | |
end) | |
-- Called when there is a property request (set the current value) | |
local property_set_guard, property_set_addr = core.marshal.callback(Gio.DBusInterfaceSetPropertyFunc , | |
function(conn, sender, path, interface_name,method_name,parameters) | |
print("Set a property") | |
end) | |
local function on_conn_aquired(conn,iname) | |
service.introspection_data = Gio.DBusNodeInfo.new_for_xml(service.xml) | |
print("The bus is aquired!") | |
local iface_info = iface_lookup(service,service.iname) | |
--introspection_data | |
conn:register_object ( | |
name, | |
iface_info, | |
Gio.DBusInterfaceVTable({ | |
method_call = method_call_addr , | |
get_property = property_get_addr, | |
set_property = property_get_addr, | |
}), | |
{}, --/* user_data */ | |
lgi.GObject.Closure(function() | |
print("Closing the object") | |
end), --/* user_data_free_func */ | |
lgi.GObject.Closure(function() | |
print("There was an error") | |
end) | |
) | |
end | |
if service.conn then | |
on_conn_aquired(service.conn,service.iname) | |
else | |
service.on_connection_aquired = on_conn_aquired | |
end | |
end | |
-------------------- | |
-- Module gears -- | |
-------------------- | |
local module = {} | |
-- Create a new service | |
function module.create_service(iname,xml_introspection) | |
--Setup object | |
local service = {properties = {},iname = iname,introspection_data=nil, | |
get_out_signature = get_out_signature, | |
get_property_info = get_property_info, | |
get_method_info = get_method_info, | |
common_get_info = common_get_info, | |
iface_lookup = iface_lookup, | |
register_object = register_object, | |
xml = xml_introspection | |
} | |
print("attempting to create a server") | |
-- Called when the bus is aquired, it is used to register the | |
-- XML spec | |
local bus_aquired = lgi.GObject.Closure(function(conn, name) | |
service.connection = conn | |
if service.on_connection_aquired then | |
service.on_connection_aquired(conn,name) | |
service.on_connection_aquired = nil | |
end | |
end) | |
-- Called when the name is aquired | |
local name_aquired = lgi.GObject.Closure(function(conn, name,c,d,e) | |
print("The name is aquired!",c,d,e) | |
end) | |
-- Called when the name is lost | |
local name_lost = lgi.GObject.Closure(function(conn, name) | |
print("The name is lost!") | |
end) | |
-- First, aquire the Session bus | |
local owner_id = Gio.bus_own_name(Gio.BusType.SESSION, | |
iname, --Interface name | |
Gio.BusNameOwnerFlags.REPLACE, --We want to take control of the existing service | |
bus_aquired, --Called when the bus is aquired | |
name_aquired, -- Called when the name is aquired | |
name_lost -- Called when the name is lost | |
--Errors handling is not implemented | |
) | |
return service | |
end | |
-- Test | |
local service = module.create_service("org.freedesktop.Notifications",[=[<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object | |
Introspection 1.0//EN" | |
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | |
<node> | |
<interface name="org.freedesktop.DBus.Introspectable"> | |
<method name="Introspect"> | |
<arg name="data" direction="out" type="s"/> | |
</method> | |
</interface> | |
<interface name="org.freedesktop.Notifications"> | |
<method name="GetCapabilities"> | |
<arg name="caps" type="as" direction="out"/> | |
</method> | |
<method name="CloseNotification"> | |
<arg name="id" type="u" direction="in"/> | |
</method> | |
<method name="Notify"> | |
<arg name="app_name" type="s" direction="in"/> | |
<arg name="id" type="u" direction="in"/> | |
<arg name="icon" type="s" direction="in"/> | |
<arg name="summary" type="s" direction="in"/> | |
<arg name="body" type="s" direction="in"/> | |
<arg name="actions" type="as" direction="in"/> | |
<arg name="hints" type="a{sv}" direction="in"/> | |
<arg name="timeout" type="i" direction="in"/> | |
<arg name="return_id" type="u" direction="out"/> | |
</method> | |
<method name="GetServerInformation"> | |
<arg name="return_name" type="s" direction="out"/> | |
<arg name="return_vendor" type="s" direction="out"/> | |
<arg name="return_version" type="s" direction="out"/> | |
<arg name="return_spec_version" type="s" direction="out"/> | |
</method> | |
<method name="GetServerInfo"> | |
<arg name="return_name" type="s" direction="out"/> | |
<arg name="return_vendor" type="s" direction="out"/> | |
<arg name="return_version" type="s" direction="out"/> | |
</method> | |
</interface> | |
</node>]=]) | |
function service:GetCapabilities() | |
return { "s", "body", "s", "body-markup", "s", "icon-static" } | |
end | |
function service:Notify(app_name, type, id, icon, summary, body, actions, hints, timeout, return_id) | |
print("NEW NOTIF",app_name,summary.."sdf",body.value.value.type,type,id,"bob") | |
return 12 | |
end | |
function service:CloseNotification() | |
return --TODO | |
end | |
function service:GetServerInfo() | |
return "naughty", "awesome", "3.5.2"--awesome.version:match("%d.%d") | |
end | |
function service:GetServerInformation() | |
local a,b,c = service:GetServerInfo() | |
return a,b,c, "1.0" | |
end | |
function service.properties.get_Bar(service) | |
print("property getter!") | |
return "foo" | |
end | |
service:register_object("/org/freedesktop/Notifications") | |
-- service:register_object("/com/example/SampleInterface/Test2") | |
--TODO check if a mainloop and running or start one | |
-- This is a test app, so we start the loop directly | |
local main_loop = GLib.MainLoop() | |
main_loop:run() | |
return module |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment