The attached patches update the Connect game activity to use the new high-level API in the latest git dbus-python, rather than the existing low-level and non-public interfaces that Daf was previously using.
There's still a lot of room for improvement - my plan is to move some of the code into telepathy-python - but it's considerably cleaner than before, and uses the familiar dbus-python proxy and service objects on the Tubes connection. Also available from: git+ssh://people.freedesktop.org/home/smcv/public_html/connect-activity.git http://people.freedesktop.org/~smcv/connect-activity.git Daf, could you review these? If you're happy with them, pull from one of the above repos. -- Simon McVittie, Collabora Ltd.: http://www.collabora.co.uk/
From d2fe241bac2515b6cb27073880b1bb75cf50d0a5 Mon Sep 17 00:00:00 2001
From: Simon McVittie <[EMAIL PROTECTED]>
Date: Fri, 4 May 2007 15:01:20 +0100
Subject: [PATCH] gtkui.py: parse .account files better
---
gtkui.py | 63 +++++++++++++++++++++++++++++++++++++------------------------
1 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/gtkui.py b/gtkui.py
index 10a9e42..27e950a 100644
--- a/gtkui.py
+++ b/gtkui.py
@@ -8,36 +8,43 @@ import telepathy
import gridwidget
import client
-def guess(x):
- if x.lower() in ('true', 'false'):
- return
-
- if x == 'true':
- return True
-
- if x == 'false':
- return False
-
- try:
- return int(x)
- except ValueError:
- pass
-
- return x
+def decode(x, sig):
+ # XXX: dbus-python ought to do most of the work for this
+ if sig == 'b':
+ x = x.lower()
+ if x in ('true', '1'):
+ return True
+ if x in ('false', '0'):
+ return False
+ raise ValueError('Not true or false: %r' % x)
+ elif sig == 'n':
+ return dbus.Int16(x)
+ elif sig == 'q':
+ return dbus.UInt16(x)
+ elif sig == 'i':
+ return dbus.Int32(x)
+ elif sig == 'u':
+ return dbus.UInt32(x)
+ elif sig == 'x':
+ return dbus.Int64(x)
+ elif sig == 't':
+ return dbus.UInt64(x)
+ elif sig == 'd':
+ return dbus.Double(x)
+ elif sig == 's':
+ return dbus.UTF8String(x)
+ else:
+ raise TypeError('Unhandled D-Bus signature %r' % sig)
def read_account(path):
account = {}
for line in file(path):
key, value = line.split(': ', 1)
- account[key] = guess(value[:-1])
+ account[key] = value.rstrip('\r\n')
- assert 'manager' in account
- assert 'protocol' in account
- manager = account['manager']
- protocol = account['protocol']
- del account['manager']
- del account['protocol']
+ manager = account.pop('manager')
+ protocol = account.pop('protocol')
return manager, protocol, account
def main():
@@ -45,8 +52,14 @@ def main():
print params
reg = telepathy.client.ManagerRegistry()
reg.LoadManagers()
- mgr = reg.GetManager('gabble')
- conn = mgr.request_connection('jabber', params)
+ mgr = reg.GetManager(manager)
+ param_specs = mgr.GetParameters(protocol)
+ for name, flags, sig, default in param_specs:
+ if name in params:
+ value = params[name]
+ value = decode(value, sig)
+ params[name] = value
+ conn = mgr.request_connection(protocol, params)
#assert CHANNEL_TYPE_TUBES in conn
window = gtk.Window()
--
1.5.1.3
From 97d2c6890ca613f73cd7d879025ffe60ee6b582e Mon Sep 17 00:00:00 2001
From: Simon McVittie <[EMAIL PROTECTED]>
Date: Fri, 4 May 2007 15:01:54 +0100
Subject: [PATCH] Start using high-level dbus-python API
---
client.py | 50 ++++++++++++++++++++++++++------------------------
1 files changed, 26 insertions(+), 24 deletions(-)
diff --git a/client.py b/client.py
index 5a27dbd..7373f07 100644
--- a/client.py
+++ b/client.py
@@ -1,25 +1,28 @@
-
import pprint
-import dbus.glib
import gtk
import telepathy
-# Needed for now, as dbus-python's high-level API doesn't support connecting
-# to arbitrary addresses, or daemonless connections.
import _dbus_bindings
import dbus.lowlevel
+from dbus.connection import Connection
+from dbus.mainloop.glib import DBusGMainLoop
+from dbus.service import Object
+
+from telepathy.interfaces import CHANNEL_TYPE_TUBES, \
+ CHANNEL_TYPE_TEXT, CONN_INTERFACE, CHANNEL_INTERFACE_GROUP
+from telepathy.constants import TUBE_TYPE_DBUS, \
+ TUBE_STATE_LOCAL_PENDING, TUBE_STATE_REMOTE_PENDING, TUBE_STATE_OPEN, \
+ CONNECTION_STATUS_CONNECTED, HANDLE_TYPE_ROOM
+
-CHANNEL_TYPE_TUBES = "org.freedesktop.Telepathy.Channel.Type.Tubes"
+# XXX: I'm not convinced this is in the right namespace
SERVICE = "org.freedesktop.Telepathy.Tube.Connect"
PATH = "/org/freedesktop/Telepathy/Tube/Connect"
-ROOM = "[EMAIL PROTECTED]"
-TUBE_TYPE_DBUS = 0
+ROOM = "[EMAIL PROTECTED]"
-TUBE_STATE_LOCAL_PENDING = 0
-TUBE_STATE_REMOTE_PENDING = 1
-TUBE_STATE_OPEN = 2
+dbus_main_loop = DBusGMainLoop(set_as_default=True)
def print_dbus_message(msg):
print 'got %s' % (msg.__class__.__name__)
@@ -45,28 +48,28 @@ class ConnectClient:
event_widget.connect('key-press-event', self.key_press_cb)
- conn[telepathy.CONN_INTERFACE].connect_to_signal('StatusChanged',
+ conn[CONN_INTERFACE].connect_to_signal('StatusChanged',
self.status_changed_cb)
- if conn.GetStatus() == telepathy.CONNECTION_STATUS_CONNECTED:
+ if conn.GetStatus() == CONNECTION_STATUS_CONNECTED:
self.connected_cb()
def key_press_cb(self, widget, event):
- if gtk.gdk.keyval_name(event.keyval) in ('Escape', 'q'):
+ if event.keyval in (gtk.keysyms.Escape, gtk.keysyms.q):
gtk.main_quit()
if self.grid.selected_column is None:
return
- if gtk.gdk.keyval_name(event.keyval) in ('Left',):
+ if event.keyval in (gtk.keysyms.Left,):
if self.grid.selected_column > 0:
self.grid.selected_column -= 1
redraw(self.grid)
- elif gtk.gdk.keyval_name(event.keyval) in ('Right',):
+ elif event.keyval in (gtk.keysyms.Right,):
if self.grid.selected_column < 6:
self.grid.selected_column += 1
redraw(self.grid)
- elif gtk.gdk.keyval_name(event.keyval) in ('Down', 'space'):
+ elif event.keyval in (gtk.keysyms.Down, gtk.keysyms.space):
if self.grid.insert(self.grid.selected_column, self.player_id):
redraw(self.grid)
msg = dbus.lowlevel.SignalMessage(PATH, SERVICE, 'Insert')
@@ -75,22 +78,21 @@ class ConnectClient:
self.grid.selected_column = None
def status_changed_cb(self, status, reason):
- if status == telepathy.CONNECTION_STATUS_CONNECTED:
+ if status == CONNECTION_STATUS_CONNECTED:
self.connected_cb()
def connected_cb(self):
print 'connected'
- handle = self.conn.RequestHandles(
- telepathy.CONNECTION_HANDLE_TYPE_ROOM, [ROOM])[0]
- text_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TEXT,
- telepathy.CONNECTION_HANDLE_TYPE_ROOM, handle, True)
+ handle = self.conn.RequestHandles(HANDLE_TYPE_ROOM, [ROOM])[0]
+ text_chan = self.conn.request_channel(CHANNEL_TYPE_TEXT,
+ HANDLE_TYPE_ROOM, handle, True)
current, lp, rp = map(lambda x: map(int, x),
- text_chan[telepathy.CHANNEL_INTERFACE_GROUP].GetAllMembers())
+ text_chan[CHANNEL_INTERFACE_GROUP].GetAllMembers())
print 'activity members: %r, %r, %r' % (current, lp, rp)
self.tubes_self_handle = text_chan[
- telepathy.CHANNEL_INTERFACE_GROUP].GetSelfHandle()
+ CHANNEL_INTERFACE_GROUP].GetSelfHandle()
tubes_chan = self.conn.request_channel(
- CHANNEL_TYPE_TUBES, telepathy.CONNECTION_HANDLE_TYPE_ROOM, handle,
+ CHANNEL_TYPE_TUBES, HANDLE_TYPE_ROOM, handle,
True)
tubes_chan[CHANNEL_TYPE_TUBES].connect_to_signal('NewTube',
self.new_tube_cb)
--
1.5.1.3
From bdc429db799fb8f18a48ba75fd81e673bb50da01 Mon Sep 17 00:00:00 2001
From: Simon McVittie <[EMAIL PROTECTED]>
Date: Fri, 4 May 2007 17:40:20 +0100
Subject: [PATCH] client.py: split out game code into a service.Object
---
client.py | 227 +++++++++++++++++++++++++------------------------------------
1 files changed, 93 insertions(+), 134 deletions(-)
diff --git a/client.py b/client.py
index 7373f07..9f9cfa0 100644
--- a/client.py
+++ b/client.py
@@ -1,13 +1,13 @@
+import logging
import pprint
import gtk
import telepathy
-import _dbus_bindings
-import dbus.lowlevel
+from dbus import Interface
from dbus.connection import Connection
from dbus.mainloop.glib import DBusGMainLoop
-from dbus.service import Object
+from dbus.service import Object, method, signal
from telepathy.interfaces import CHANNEL_TYPE_TUBES, \
CHANNEL_TYPE_TEXT, CONN_INTERFACE, CHANNEL_INTERFACE_GROUP
@@ -18,12 +18,16 @@ from telepathy.constants import TUBE_TYPE_DBUS, \
# XXX: I'm not convinced this is in the right namespace
SERVICE = "org.freedesktop.Telepathy.Tube.Connect"
+IFACE = SERVICE
PATH = "/org/freedesktop/Telepathy/Tube/Connect"
ROOM = "[EMAIL PROTECTED]"
dbus_main_loop = DBusGMainLoop(set_as_default=True)
+logging.basicConfig()
+_logger = logging.getLogger('connect-activity.client')
+
def print_dbus_message(msg):
print 'got %s' % (msg.__class__.__name__)
print ' sender: %r' % msg.get_sender()
@@ -37,11 +41,91 @@ def redraw(grid):
"""Utility function to force a redraw of a Gtk widget."""
grid.window.invalidate_rect(grid.get_allocation(), False)
+class ConnectGame(Object):
+ def __init__(self, tube, grid, self_handle, initiator, dbus_names):
+ super(ConnectGame, self).__init__(tube, PATH)
+ self.tube = tube
+ self.grid = grid
+
+ # Initiator is player 1, other player is player 2.
+
+ self.self_handle = self_handle
+ self.initiator = initiator
+
+ if initiator == self_handle:
+ self.player_id = 1
+ else:
+ opponent = self.tube.get_object(dbus_names[initiator], PATH)
+ self.opponent = Interface(opponent, IFACE)
+ self.opponent.Hello(reply_handler=self.hello_cb,
+ error_handler=self.error_cb)
+ self.player_id = 2
+
+ # XXX: care about signal sender
+ self.tube.add_signal_receiver(self.insert_cb, 'Insert', IFACE,
+ path=PATH)
+
+ print 'intiator: %d' % initiator
+ print 'self: %d' % self.self_handle
+ print 'player ID: %d' % self.player_id
+
+ def hello_cb(self, success):
+ self.opponent.GetGrid(reply_handler=self.get_grid_cb,
+ error_handler=self.error_cb)
+
+ def get_grid_cb(self, grid):
+ self.grid.grid = grid
+ redraw(self.grid)
+
+ def error_cb(self, e):
+ _logger.error('Error connecting to opponent:\n%s', e)
+
+ @method(dbus_interface=IFACE, in_signature='', out_signature='b')
+ def Hello(self):
+ self.grid.selected_column = 3
+ redraw(self.grid)
+ # XXX: return player ID too, to allow for swapping who goes first in
+ # for second and later games
+ return True
+
+ @method(dbus_interface=IFACE, in_signature='', out_signature='aan')
+ def GetGrid(self):
+ return self.grid.grid
+
+ @signal(dbus_interface=IFACE, signature='n')
+ def Insert(self, column):
+ assert column >= 0
+ assert column < 7
+
+ def insert_cb(self, column):
+ #print 'Insert: %r' % args
+
+ if self.grid.insert(column, self.get_active_player()):
+ if self.get_active_player() == self.player_id:
+ # This should always be the case for 2 players, as we only get
+ # this signal when the other player plays.
+ self.grid.selected_column = 3
+ redraw(self.grid)
+
+ def get_active_player(self):
+ count = {}
+
+ for row in self.grid.grid:
+ for player in row:
+ if player != 0:
+ count[player] = count.get(player, 0) + 1
+
+ if count.get(1, 0) > count.get(2, 0):
+ return 2
+ else:
+ return 1
+
+
class ConnectClient:
def __init__(self, conn, event_widget, grid):
self.conn = conn
self.grid = grid
- self.dbus_conn = None
+ self.game = None
self.tubes_self_handle = 0
self.pending_calls = {}
self.player_id = 0
@@ -72,9 +156,7 @@ class ConnectClient:
elif event.keyval in (gtk.keysyms.Down, gtk.keysyms.space):
if self.grid.insert(self.grid.selected_column, self.player_id):
redraw(self.grid)
- msg = dbus.lowlevel.SignalMessage(PATH, SERVICE, 'Insert')
- msg.append(self.grid.selected_column)
- self.dbus_conn.send_message(msg)
+ self.game.Insert(self.grid.selected_column)
self.grid.selected_column = None
def status_changed_cb(self, status, reason):
@@ -138,16 +220,6 @@ class ConnectClient:
return None
- def dbus_call(self, dst, path, service, method, args, cb):
- """Make an asynchronous D-Bus method call."""
- msg = dbus.lowlevel.MethodCallMessage(dst, path, service, method)
- id = self.dbus_conn.send_message(msg)
- self.pending_calls[id] = cb
-
- def call_peer(self, method, args, cb):
- self.dbus_call(self.dbus_names[self.initiator], PATH, SERVICE, method,
- args, cb)
-
def use_tube(self, id, initiator):
print 'using tube %d' % id
addr = self.tubes_chan[CHANNEL_TYPE_TUBES].GetDBusServerAddress(id)
@@ -157,29 +229,16 @@ class ConnectClient:
print 'names:', pprint.pformat(list(self.dbus_names))
assert self.tubes_self_handle in self.dbus_names
- conn = _dbus_bindings.Connection(addr)
- conn.add_message_filter(self.message_cb)
-
- self.dbus_conn = conn
- self.initiator = initiator
-
- # Initiator is player 1, other player is player 2.
+ conn = Connection(addr)
- if initiator == self.tubes_self_handle:
- self.player_id = 1
- else:
- self.call_peer('Hello', [], self.hello_cb)
- self.player_id = 2
-
- print 'intiator: %d' % initiator
- print 'self: %d' % self.tubes_self_handle
- print 'player ID: %d' % self.player_id
+ self.game = ConnectGame(conn, self.grid, self.tubes_self_handle,
+ initiator, self.dbus_names)
def new_tube_cb(self, id, initiator, type, service, parameters, state):
print 'new tube: %r' % (
(id, initiator, type, service, parameters, state),)
- if (self.dbus_conn is None and
+ if (self.game is None and
type == TUBE_TYPE_DBUS and
service == SERVICE):
@@ -188,103 +247,3 @@ class ConnectClient:
self.tubes_chan[CHANNEL_TYPE_TUBES].AcceptTube(id)
self.use_tube(id, initiator)
-
- def hello_cb(self, args):
- #print 'Hello() -> %r' % args
- self.call_peer('GetGrid', [], self.get_grid_cb)
-
- def get_active_player(self):
- count = {}
-
- for row in self.grid.grid:
- for player in row:
- if player != 0:
- count[player] = count.get(player, 0) + 1
-
- if count.get(1, 0) > count.get(2, 0):
- return 2
- else:
- return 1
-
- def get_grid_cb(self, args):
- self.grid.grid = args[0]
- redraw(self.grid)
-
- def message_cb_unsafe(self, conn, msg):
- """This method is called when we receive a D-Bus message from the tube.
-
- It does stuff that dbus-python will do for us once the high-level
- API is fixed, namely mapping D-Bus messages to and from method calls.
- """
-
- print_dbus_message(msg)
- dst = msg.get_destination()
- reply_serial = msg.get_reply_serial()
- interface = msg.get_interface()
-
- # handle method replies
- # XXX: handle errors better
-
- if reply_serial in self.pending_calls:
- self.pending_calls[reply_serial](msg.get_args_list())
- del self.pending_calls[reply_serial]
- return _dbus_bindings.HANDLER_RESULT_HANDLED
-
- # handle method calls
-
- if (msg.__class__ == dbus.lowlevel.MethodCallMessage and
- dst == self.dbus_names[self.tubes_self_handle] and
- interface == SERVICE):
- method = getattr(self, 'dbus_' + msg.get_member(), None)
-
- if method:
- # XXX: catch TypeError from wrong arity
- args = method(*(msg.get_args_list()))
- reply = dbus.lowlevel.MethodReturnMessage(msg)
- reply.append(*args)
- self.dbus_conn.send_message(reply)
- return _dbus_bindings.HANDLER_RESULT_HANDLED
-
- # handle signals
-
- if (msg.__class__ == dbus.lowlevel.SignalMessage and
- interface == SERVICE and
- msg.get_sender() != self.dbus_names[self.tubes_self_handle]):
- handler = getattr(self, 'dbus_sig_' + msg.get_member(), None)
-
- if handler:
- handler(msg.get_sender(), msg.get_args_list())
- return _dbus_bindings.HANDLER_RESULT_HANDLED
-
- return _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
-
- def message_cb(self, conn, msg):
- try:
- return self.message_cb_unsafe(conn, msg)
- except Exception, e:
- print e
-
- # D-Bus signal handlers.
-
- def dbus_sig_Insert(self, sender, args):
- #print 'Insert: %r' % args
-
- if self.grid.insert(args[0], self.get_active_player()):
- if self.get_active_player() == self.player_id:
- # This should always be the case for 2 players, as we only get
- # this signal when the other player plays.
- self.grid.selected_column = 3
- redraw(self.grid)
-
- # D-Bus service methods.
-
- def dbus_Hello(self):
- self.grid.selected_column = 3
- redraw(self.grid)
- # XXX: return player ID too, to allow for swapping who goes first in
- # for second and later games
- return [True]
-
- def dbus_GetGrid(self):
- return [self.grid.grid]
-
--
1.5.1.3
signature.asc
Description: Digital signature
_______________________________________________ Sugar mailing list [email protected] http://mailman.laptop.org/mailman/listinfo/sugar
