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

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Sugar mailing list
[email protected]
http://mailman.laptop.org/mailman/listinfo/sugar

Reply via email to