Skip to content

Commit

Permalink
#228 / #476: more example code for dealing with gtk menu api
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@10679 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Sep 25, 2015
1 parent 3b470b0 commit 35b2f8f
Show file tree
Hide file tree
Showing 3 changed files with 326 additions and 39 deletions.
44 changes: 28 additions & 16 deletions src/tests/xpra/x11/get_gtk_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ def pget(key, etype, ignore_errors=True):
"application-id" : app_id,
}
print("gtk menu properties for window %s on display %s: %s" % (wid, display.get_name(), props))
if not (menu_path and window_path and app_path and bus_name and app_id):
print("some properties are missing - cannot continue")
return

loop_init()
import gobject
Expand Down Expand Up @@ -71,20 +74,24 @@ def list_cb(*args):
print("list_cb: values=%s" % str(values))
def list_err(*args):
print("list_err%s" % str(args))
dbus_helper.call_function(bus_name, window_path, interface, "List", args, list_cb, list_err)
dbus_helper.call_function(bus_name, window_path, interface, "List", [], list_cb, list_err)
dbus_helper.call_function(bus_name, app_path, interface, "List", [], list_cb, list_err)

def describe_cb(*args):
print("describe_cb:")
#print("describe_cb%s" % str(args))
def describe_actions_cb(*args):
print("describe_actions_cb:")
values = dbus_helper.dbus_to_native(args[0])
#print("describe_cb: values=%s" % str(values))
actions = {}
for k,v in values.items():
#enabled, parameter type, state
mdef = [bool(n(v[0])), n(v[1]), [n(x) for x in v[2]]]
print(" %s=%s" % (k, mdef))
def describe_err(*args):
print("describe_err%s" % str(args))
dbus_helper.call_function(bus_name, window_path, interface, "DescribeAll", [], describe_cb, describe_err)
#print(" %s=%s" % (k, mdef))
actions[k] = mdef
print("actions=%s" % actions)
def describe_actions_err(*args):
print("describe_actions_err%s" % str(args))
dbus_helper.call_function(bus_name, window_path, interface, "DescribeAll", [], describe_actions_cb, describe_actions_err)
dbus_helper.call_function(bus_name, app_path, interface, "DescribeAll", [], describe_actions_cb, describe_actions_err)

#app menu:
interface = "org.gtk.Menus"
Expand All @@ -94,12 +101,11 @@ def menus_changed(*args):
print("menus_changed%s" % str(args))
iface.connect_to_signal("Changed", menus_changed)

def start_cb(*args):
print("start_cb args=%s" % str(args))
print("start_cb args[0]=%s" % str(args[0]))
#print("start_cb args[0][0]=%s" % str(args[0][0]))
def menus_start_cb(*args):
#print("menus_start_cb args=%s" % str(args))
#print("menus_start_cb args[0]=%s" % str(args[0]))
values = n(args[0])
print("start_cb values=%s" % str(values))
#print("menus_start_cb values=%s" % str(values))
menus = {}
for sgroup, menuno, items in values:
print(" %s: %s - %s" % (sgroup, menuno, n(items)))
Expand All @@ -115,17 +121,23 @@ def start_cb(*args):
#subscription, menu
menu[":submenu"] = ni(section[0]), ni(section[1])
else:
#action?
for k in ("action", "label"):
if k in d:
menu[k] = n(d[k])
if menu:
target = d.get("target")
if target:
menu["target"] = [n(target[x]) for x in range(len(target))]
#print("target=%s (len=%s)" % (target, len(target)))
if menu:
dmenus.append(menu)
menus.setdefault(ni(sgroup), {})[ni(menuno)] = dmenus
print("menus=%s" % menus)
#values = dbus_helper.dbus_to_native(args[0])
def start_err(*args):
print("describe_cb%s" % str(args))
dbus_helper.call_function(bus_name, menu_path, interface, "Start", [[0]], start_cb, start_err)
def menus_start_err(*args):
print("menus_start_err%s" % str(args))
dbus_helper.call_function(bus_name, menu_path, interface, "Start", [[0]], menus_start_cb, menus_start_err)

loop.run()

Expand Down
271 changes: 271 additions & 0 deletions src/tests/xpra/x11/set_gtk_menu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
#!/usr/bin/env python
# This file is part of Xpra.
# Copyright (C) 2015 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import sys
from xpra.x11.gtk2.gdk_display_source import display #@UnresolvedImport
from xpra.x11.gtk_x11.prop import prop_set
from xpra.dbus.helper import DBusHelper, dbus_to_native

#beware: this import has side-effects:
import dbus.glib
assert dbus.glib
import dbus.service
import gtk

from xpra.dbus.common import loop_init


class Actions(dbus.service.Object):
SUPPORTS_MULTIPLE_OBJECT_PATHS = True

def __init__(self, name, path, session_bus, actions={}):
self.actions = actions
bus_name = dbus.service.BusName(name, session_bus)
dbus.service.Object.__init__(self, bus_name, path)

def set_actions(self, actions):
self.actions = actions
#TODO: diff and emit the "Change" signal


@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='s', out_signature='v')
def Get(self, property_name):
raise dbus.exceptions.DBusException("this object does not have any properties")

@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='', out_signature='a{sv}')
def GetAll(self, interface_name):
return []

@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv')
def Set(self, interface_name, property_name, new_value):
self.PropertiesChanged(interface_name, { property_name: new_value }, [])

@dbus.service.signal(dbus.PROPERTIES_IFACE, signature='sa{sv}as')
def PropertiesChanged(self, interface_name, changed_properties, invalidated_properties):
pass


@dbus.service.method('org.gtk.Actions', out_signature="as")
def List(self):
print("List()")
return dbus.Array(dbus.String(x) for x in self.actions.keys())


def _make_action(self, enabled, ptype, state):
return dbus.Boolean(enabled), dbus.Signature(ptype), dbus.Array(state)

@dbus.service.method('org.gtk.Actions', in_signature="s", out_signature="(bgav)")
def Describe(self, action):
v = self.actions.get(action)
if not v:
raise dbus.exceptions.DBusException("unknown action '%s'" % action)
r = self._make_action(*v[:3])
print("Describe(%s)=%s" % (action, r))
return r

@dbus.service.method('org.gtk.Actions', out_signature="a{s(bgav)}")
def DescribeAll(self):
#build the dbus struct list on demand (as it may change):
d = {}
for k,v in self.actions.items():
#enabled, arg, state = v
d[k] = dbus.Struct(self._make_action(*v[:3]))
print("DescribeAll()=%s" % d)
return dbus.Dictionary(d)

#async_callbacks=("ok_cb", "err_cb")
@dbus.service.method('org.gtk.Actions', in_signature="sava{sv}")
def Activate(self, action, state, pdata):
v = self.actions.get(action)
if not v:
raise dbus.exceptions.DBusException("unknown action '%s'" % action)
if len(v)<4:
print("no callback for %s" % action)
cb = v[3]
pstate = dbus_to_native(state)
ppdata = dbus_to_native(pdata)
print("Activate(%s, %s, %s) calling %s%s" % (action, state, pdata, cb, (pstate, ppdata)))
cb(action, pstate, ppdata)


@dbus.service.method('org.gtk.Actions', in_signature="sva{sv}")
def SetState(self, s, v, a):
return

@dbus.service.signal('org.gtk.Actions', signature='s')
def Changed(self, arg):
return []


class Menus(dbus.service.Object):

def __init__(self, name, path, session_bus, menus={}):
self.menus = menus
self.subscribed = {}
bus_name = dbus.service.BusName(name, session_bus)
dbus.service.Object.__init__(self, bus_name, path)
self.set_menus(menus)

def set_menus(self, menus):
self.menus = menus


@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v')
def Get(self, interface_name, property_name):
raise dbus.exceptions.DBusException("this object does not have any properties")

@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}')
def GetAll(self, interface_name):
return []

@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv')
def Set(self, interface_name, property_name, new_value):
raise dbus.exceptions.DBusException("this object does not have any properties")

@dbus.service.signal(dbus.PROPERTIES_IFACE, signature='sa{sv}as')
def PropertiesChanged(self, interface_name, changed_properties, invalidated_properties):
pass


def _make_menu_item(self, d):
def s(v):
try:
return dbus.String(v.decode())
except:
return dbus.String(str(v))
section = d.get(":section")
if section:
#{':section': (0, 1)}
return dbus.Dictionary({s(":section") : (dbus.UInt32(section[0]), dbus.UInt32(section[1]))})
submenu = d.get(":submenu")
if submenu:
#{':submenu': (0, 1)}
return dbus.Dictionary({s(":submenu") : (dbus.UInt32(section[0]), dbus.UInt32(section[1]))})
action = d.get("action")
if not action:
print("unknown menu item type: %s" % d)
return None
menu_item = {s("action") : s(action)}
if "label" in d:
menu_item[s("label")] = s(d["label"])
target = d.get("target")
if target:
menu_item["target"] = dbus.Struct(target)
return dbus.Dictionary(menu_item)

@dbus.service.method('org.gtk.Menus', in_signature="au", out_signature="a(uuaa{sv})")
def Start(self, ids):
#print("Start(%s)" % str(ids))
menus = []
for group in ids:
group_menus = self.menus.get(group)
if group_menus is None:
print("Warning: invalid subscription group %i", group)
continue
self.subscribed[group] = self.subscribed.get(group, 0)+1
for n, items in group_menus.items():
menu_items = []
for i in items:
try:
menu_item = self._make_menu_item(i)
except:
continue
#print("make_menu_item(%s)=%s" % (i, menu_item))
if menu_item:
menu_items.append(menu_item)
if menu_items:
menus.append((group, dbus.UInt32(n), menu_items))
print("Start(%s)=%s" % (ids, menus))
return menus

@dbus.service.method('org.gtk.Menus', in_signature="au")
def End(self, ids):
for group in ids:
c = self.subscribed.get(group, 0)
if c > 0:
self.subscribed[group] = c - 1

@dbus.service.signal('org.gtk.Menus', signature='a(uuuuaa{sv})')
def Changed(self, arg):
return



def main(args):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_size_request(200, 200)
window.connect("delete_event", gtk.mainquit)
window.realize()
w = window.get_window()

dbus_helper = DBusHelper()
loop_init()
session_bus = dbus_helper.get_session_bus()

app_id = u"org.xpra.Terminal"
app_path = u"/org/xpra/Terminal"
menu_path = u"%s/menus/appmenu" % app_path
window_path = u"%s/window/1" % app_path
bus_name = session_bus.get_unique_name().decode()

def action_cb(*args):
print("action_cb%s" % str(args))
window_actions={'reset' : [True, 'b', [], action_cb],
'fullscreen' : [True, '', [0], action_cb],
'about' : [True, '', [], action_cb],
'preferences' : [True, '', [], action_cb],
'switch-tab' : [True, 'i', [], action_cb],
'detach-tab' : [True, '', []],
'save-contents' : [True, '', []],
'edit-profile' : [True, 's', []],
'zoom' : [True, 'i', []],
'move-tab' : [True, 'i', []],
'new-terminal' : [True, '(ss)', []],
'select-all' : [True, '', []],
'new-profile' : [True, '', []],
'show-menubar' : [True, '', [1]],
'close' : [True, 's', []],
'copy' : [True, '', []],
'paste' : [True, 's', []],
'find' : [True, 's', []],
'help' : [True, '', []]}
menuactions_service = Actions(app_id, window_path, session_bus, window_actions)
app_actions = {
'quit' : [True, '', []],
'about' : [True, '', []],
'activate-tab' : [True, 's', []],
'preferences' : [True, '', []],
'help' : [True, '', []]
}
appactions_service = Actions(app_id, app_path, session_bus, app_actions)
menus = {0:
{0: [{':section': (0, 1)}, {':section': (0, 2)}, {':section': (0, 3)}],
1: [{'action': 'win.new-terminal', 'label': '_New Terminal', 'target': ['default', 'default']}],
2: [{'action': 'app.preferences', 'label': '_Preferences'}],
3: [{'action': 'app.help', 'label': '_Help'},
{'action': 'app.about', 'label': '_About'},
{'action': 'app.quit', 'label': '_Quit'}
]
}
}
menus_service = Menus(app_id, menu_path, session_bus, menus)

def pset(key, etype, value, ignore_errors=True):
return prop_set(w, key, etype, value)
pset("_GTK_APP_MENU_OBJECT_PATH", "utf8", menu_path)
pset("_GTK_WINDOW_OBJECT_PATH", "utf8", window_path)
pset("_GTK_APPLICATION_OBJECT_PATH", "utf8", app_path)
pset("_GTK_UNIQUE_BUS_NAME", "utf8", bus_name)
pset("_GTK_APPLICATION_ID", "utf8", app_id)
print("gtk menu properties for window %#x on display %s" % (w.xid, display.get_name()))

window.show()
gtk.main()
del menuactions_service, appactions_service, menus_service


if __name__ == '__main__':
sys.exit(main(sys.argv))
Loading

0 comments on commit 35b2f8f

Please sign in to comment.