X-Git-Url: https://git.distorted.org.uk/~mdw/tripe/blobdiff_plain/984b6d3310be68c87d1f278102bc4a5ef61645ff..0adda4f8b3ea34104c3b43ea15e26177aaf53dae:/mon/tripemon.in
diff --git a/mon/tripemon.in b/mon/tripemon.in
index e666ab9f..f5b8f8d6 100644
--- a/mon/tripemon.in
+++ b/mon/tripemon.in
@@ -10,19 +10,18 @@
###
### This file is part of Trivial IP Encryption (TrIPE).
###
-### TrIPE is free software; you can redistribute it and/or modify
-### it under the terms of the GNU General Public License as published by
-### the Free Software Foundation; either version 2 of the License, or
-### (at your option) any later version.
+### TrIPE is free software: you can redistribute it and/or modify it under
+### the terms of the GNU General Public License as published by the Free
+### Software Foundation; either version 3 of the License, or (at your
+### option) any later version.
###
-### TrIPE is distributed in the hope that it will be useful,
-### but WITHOUT ANY WARRANTY; without even the implied warranty of
-### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-### GNU General Public License for more details.
+### TrIPE is distributed in the hope that it will be useful, but WITHOUT
+### ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+### FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+### for more details.
###
### You should have received a copy of the GNU General Public License
-### along with TrIPE; if not, write to the Free Software Foundation,
-### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+### along with TrIPE. If not, see .
###--------------------------------------------------------------------------
### Dependencies.
@@ -40,11 +39,34 @@ import time as TIME
import re as RX
from cStringIO import StringIO
-import pygtk
-pygtk.require('2.0')
-import gtk as G
-import gobject as GO
-import gtk.gdk as GDK
+try:
+ if OS.getenv('TRIPEMON_FORCE_GI'): raise ImportError
+ import pygtk
+ pygtk.require('2.0')
+ import gtk as G
+ import gobject as GO
+ import gtk.gdk as GDK
+ GL = GO
+ GDK.KEY_Escape = G.keysyms.Escape
+ def raise_window(w): w.window.raise_()
+ combo_box_text = G.combo_box_new_text
+ def set_entry_bg(e, c): e.modify_base(G.STATE_NORMAL, c)
+except ImportError:
+ from gi.repository import GObject as GO, GLib as GL, Gtk as G, Gdk as GDK
+ G.WINDOW_TOPLEVEL = G.WindowType.TOPLEVEL
+ G.EXPAND = G.AttachOptions.EXPAND
+ G.SHRINK = G.AttachOptions.SHRINK
+ G.FILL = G.AttachOptions.FILL
+ G.SORT_ASCENDING = G.SortType.ASCENDING
+ G.POLICY_AUTOMATIC = G.PolicyType.AUTOMATIC
+ G.SHADOW_IN = G.ShadowType.IN
+ G.SELECTION_NONE = G.SelectionMode.NONE
+ G.DIALOG_MODAL = G.DialogFlags.MODAL
+ G.RESPONSE_CANCEL = G.ResponseType.CANCEL
+ G.RESPONSE_NONE = G.ResponseType.NONE
+ def raise_window(w): getattr(w.get_window(), 'raise')()
+ combo_box_text = G.ComboBoxText
+ def set_entry_bg(e, c): e.modify_bg(G.StateType.NORMAL, c)
if OS.getenv('TRIPE_DEBUG_MONITOR') is not None:
T._debug = 1
@@ -56,46 +78,6 @@ def uncaught():
"""Report an uncaught exception."""
excepthook(*exc_info())
-_idles = []
-def _runidles():
- """Invoke the functions on the idles queue."""
- global _idles
- while _idles:
- old = _idles
- _idles = []
- for func, args, kw in old:
- try:
- func(*args, **kw)
- except:
- uncaught()
- return False
-
-def idly(func, *args, **kw):
- """Invoke FUNC(*ARGS, **KW) at some later point in time."""
- if not _idles:
- GO.idle_add(_runidles)
- _idles.append((func, args, kw))
-
-_asides = T.Queue()
-def _runasides():
- """
- Coroutine function: reads (FUNC, ARGS, KW) triples from a queue and invokes
- FUNC(*ARGS, **KW)
- """
- while True:
- func, args, kw = _asides.get()
- try:
- func(*args, **kw)
- except:
- uncaught()
-
-def aside(func, *args, **kw):
- """
- Arrange for FUNC(*ARGS, **KW) to be performed at some point in the future,
- and not from the main coroutine.
- """
- idly(_asides.put, (func, args, kw))
-
def xwrap(func):
"""
Return a function which behaves like FUNC, but reports exceptions via
@@ -123,13 +105,15 @@ def invoker(func, *args, **kw):
def cr(func, *args, **kw):
"""Return a function which invokes FUNC(*ARGS, **KW) in a coroutine."""
- def _(*hunoz, **hukairz):
- T.Coroutine(xwrap(func)).switch(*args, **kw)
- return _
+ name = T.funargstr(func, args, kw)
+ return lambda *hunoz, **hukairz: \
+ T.Coroutine(xwrap(func), name = name).switch(*args, **kw)
def incr(func):
"""Decorator: runs its function in a coroutine of its own."""
- return lambda *args, **kw: T.Coroutine(func).switch(*args, **kw)
+ return lambda *args, **kw: \
+ (T.Coroutine(func, name = T.funargstr(func, args, kw))
+ .switch(*args, **kw))
###--------------------------------------------------------------------------
### Random bits of infrastructure.
@@ -170,12 +154,6 @@ class HookList (object):
if rc is not None: return rc
return None
- def runidly(me, *args, **kw):
- """
- Invoke the hook functions as for run, but at some point in the future.
- """
- idly(me.run, *args, **kw)
-
class HookClient (object):
"""
Mixin for classes which are clients of hooks.
@@ -215,6 +193,23 @@ rx_time = RX.compile(r'^(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)$')
###--------------------------------------------------------------------------
### Connections.
+class GIOWatcher (object):
+ """
+ Monitor I/O events using glib.
+ """
+ def __init__(me, conn, mc = GL.main_context_default()):
+ me._conn = conn
+ me._watch = None
+ me._mc = mc
+ def connected(me, sock):
+ me._watch = GL.io_add_watch(sock, GL.IO_IN,
+ lambda *hunoz: me._conn.receive())
+ def disconnected(me):
+ GL.source_remove(me._watch)
+ me._watch = None
+ def iterate(me):
+ me._mc.iteration(True)
+
class Connection (T.TripeCommandDispatcher):
"""
The main connection to the server.
@@ -245,18 +240,15 @@ class Connection (T.TripeCommandDispatcher):
me.handler['NOTE'] = lambda _, *rest: me.notehook.run(*rest)
me.handler['WARN'] = lambda _, *rest: me.warnhook.run(*rest)
me.handler['TRACE'] = lambda _, *rest: me.tracehook.run(*rest)
- me._watch = None
+ me.iowatch = GIOWatcher(me)
def connected(me):
"""Handles reconnection to the server, and signals the hook."""
T.TripeCommandDispatcher.connected(me)
- me._watch = GO.io_add_watch(me.sock, GO.IO_IN, invoker(me.receive))
me.connecthook.run()
def disconnected(me, reason):
"""Handles disconnection from the server, and signals the hook."""
- GO.source_remove(me._watch)
- me._watch = None
me.disconnecthook.run(reason)
T.TripeCommandDispatcher.disconnected(me, reason)
@@ -319,11 +311,18 @@ class Peer (MonitorObject):
"""Initialize the object with the given name."""
MonitorObject.__init__(me, name)
me.pinghook = HookList()
+ me.__dict__.update(conn.algs(name))
me.update()
def update(me, hunoz = None):
"""Update the peer, fetching information about it from the server."""
- addr = conn.addr(me.name)
+ me._setaddr(conn.addr(me.name))
+ me.ifname = conn.ifname(me.name)
+ me.__dict__.update(conn.peerinfo(me.name))
+ me.changehook.run()
+
+ def _setaddr(me, addr):
+ """Set the peer's address."""
if addr[0] == 'INET':
ipaddr, port = addr[1:]
try:
@@ -333,8 +332,10 @@ class Peer (MonitorObject):
me.addr = 'INET %s:%s' % (ipaddr, port)
else:
me.addr = ' '.join(addr)
- me.ifname = conn.ifname(me.name)
- me.__dict__.update(conn.peerinfo(me.name))
+
+ def setaddr(me, addr):
+ """Informs the object of a change to its address to ADDR."""
+ me._setaddr(addr)
me.changehook.run()
def setifname(me, newname):
@@ -482,7 +483,7 @@ class Monitor (HookClient):
"""Update the auto-peers list from the connect service."""
if 'connect' in me.services.table:
me.autopeers = [' '.join(line)
- for line in conn.svcsubmit('connect', 'list')]
+ for line in conn.svcsubmit('connect', 'list-active')]
me.autopeers.sort()
else:
me.autopeers = None
@@ -498,27 +499,32 @@ class Monitor (HookClient):
the auto-peers list.
"""
if code == 'ADD':
- aside(me.peers.add, rest[0], None)
+ T.aside(me.peers.add, rest[0], None)
elif code == 'KILL':
- aside(me.peers.remove, rest[0])
+ T.aside(me.peers.remove, rest[0])
elif code == 'NEWIFNAME':
try:
me.peers[rest[0]].setifname(rest[2])
except KeyError:
pass
+ elif code == 'NEWADDR':
+ try:
+ me.peers[rest[0]].setaddr(rest[1:])
+ except KeyError:
+ pass
elif code == 'SVCCLAIM':
- aside(me.services.add, rest[0], rest[1])
+ T.aside(me.services.add, rest[0], rest[1])
if rest[0] == 'connect':
- aside(me._updateautopeers)
+ T.aside(me._updateautopeers)
elif code == 'SVCRELEASE':
- aside(me.services.remove, rest[0])
+ T.aside(me.services.remove, rest[0])
if rest[0] == 'connect':
- aside(me._updateautopeers)
+ T.aside(me._updateautopeers)
elif code == 'USER':
if not rest: return
if rest[0] == 'watch' and \
rest[1] == 'peerdb-update':
- aside(me._updateautopeers)
+ T.aside(me._updateautopeers)
###--------------------------------------------------------------------------
### Window management cruft.
@@ -551,6 +557,17 @@ class MyWindow (MyWindowMixin):
G.Window.__init__(me, kind)
me.mywininit()
+class TrivialWindowMixin (MyWindowMixin):
+ """A simple window which you can close with Escape."""
+ def mywininit(me):
+ super(TrivialWindowMixin, me).mywininit()
+ me.connect('key-press-event', me._keypress)
+ def _keypress(me, _, ev):
+ if ev.keyval == GDK.KEY_Escape: me.destroy()
+
+class TrivialWindow (MyWindow, TrivialWindowMixin):
+ pass
+
class MyDialog (G.Dialog, MyWindowMixin, HookClient):
"""A dialogue box with a closehook and sensible button binding."""
@@ -681,7 +698,7 @@ class WindowSlot (HookClient):
def open(me):
"""Opens the window, creating it if necessary."""
if me.window:
- me.window.window.raise_()
+ raise_window(me.window)
else:
me.window = me.createfunc()
me.hook(me.window.closehook, me.closed)
@@ -706,7 +723,7 @@ class MyScrolledWindow (G.ScrolledWindow):
rx_num = RX.compile(r'^[-+]?\d+$')
## The colour red.
-c_red = GDK.color_parse('red')
+c_red = GDK.color_parse('#ff6666')
class ValidationError (Exception):
"""Raised by ValidatingEntry.get_text() if the text isn't valid."""
@@ -729,27 +746,26 @@ class ValidatingEntry (G.Entry):
characters (ish). Other arguments are passed to Entry.
"""
G.Entry.__init__(me, *arg, **kw)
- me.connect("changed", me.check)
+ me.connect("changed", me._check)
+ me.connect("state-changed", me._check)
if callable(valid):
me.validate = valid
else:
me.validate = RX.compile(valid).match
me.ensure_style()
- me.c_ok = me.get_style().text[G.STATE_NORMAL]
- me.c_bad = c_red
if size != -1: me.set_width_chars(size)
me.set_activates_default(True)
me.set_text(text)
- me.check()
+ me._check()
- def check(me, *hunoz):
+ def _check(me, *hunoz):
"""Check the current text and update validp and the text colour."""
if me.validate(G.Entry.get_text(me)):
me.validp = True
- me.modify_text(G.STATE_NORMAL, me.c_ok)
+ set_entry_bg(me, None)
else:
me.validp = False
- me.modify_text(G.STATE_NORMAL, me.c_bad)
+ set_entry_bg(me, me.is_sensitive() and c_red or None)
def get_text(me):
"""
@@ -774,21 +790,21 @@ def numericvalidate(min = None, max = None):
###--------------------------------------------------------------------------
### Various minor dialog boxen.
-GPL = """This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+GPL = """\
+TrIPE is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3 of the License, or (at your
+option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+TrIPE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software Foundation,
-Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA."""
+along with TrIPE. If not, see ."""
-class AboutBox (G.AboutDialog, MyWindowMixin):
+class AboutBox (G.AboutDialog, TrivialWindowMixin):
"""The program `About' box."""
def __init__(me):
G.AboutDialog.__init__(me)
@@ -813,7 +829,7 @@ def moanbox(msg):
buttons = ((G.STOCK_OK, G.RESPONSE_NONE)))
label = G.Label(msg)
label.set_padding(20, 20)
- d.vbox.pack_start(label)
+ d.vbox.pack_start(label, True, True, 0)
label.show()
d.run()
d.destroy()
@@ -869,7 +885,7 @@ class WarningLogModel (LogModel):
"""Call with a new warning message."""
me.add(tag, ' '.join([T.quotify(w) for w in rest]))
-class LogViewer (MyWindow):
+class LogViewer (TrivialWindow):
"""
A log viewer window.
@@ -885,7 +901,7 @@ class LogViewer (MyWindow):
"""
Create a log viewer showing the LogModel MODEL.
"""
- MyWindow.__init__(me)
+ TrivialWindow.__init__(me)
me.model = model
scr = MyScrolledWindow()
me.list = MyTreeView(me.model)
@@ -949,14 +965,14 @@ class Pinger (T.Coroutine, HookClient):
me.hook(conn.connecthook, me._connected)
me.hook(conn.disconnecthook, me._disconnected)
me.hook(monitor.peers.addhook,
- lambda p: idly(me._q.put, (p, 'ADD', None)))
+ lambda p: T.defer(me._q.put, (p, 'ADD', None)))
me.hook(monitor.peers.delhook,
- lambda p: idly(me._q.put, (p, 'KILL', None)))
+ lambda p: T.defer(me._q.put, (p, 'KILL', None)))
if conn.connectedp(): me.connected()
def _connected(me):
"""Respond to connection: start pinging thngs."""
- me._timer = GO.timeout_add(1000, me._timerfunc)
+ me._timer = GL.timeout_add(1000, me._timerfunc)
def _timerfunc(me):
"""Timer function: put a timer event on the queue."""
@@ -965,7 +981,7 @@ class Pinger (T.Coroutine, HookClient):
def _disconnected(me, reason):
"""Respond to disconnection: stop pinging."""
- GO.source_remove(me._timer)
+ GL.source_remove(me._timer)
def run(me):
"""
@@ -1037,7 +1053,7 @@ class AddPeerDialog (MyDialog):
def _setup(me):
"""Coroutine function: background setup for AddPeerDialog."""
table = GridPacker()
- me.vbox.pack_start(table)
+ me.vbox.pack_start(table, True, True, 0)
me.e_name = table.labelled('Name',
ValidatingEntry(r'^[^\s.:]+$', '', 16),
width = 3)
@@ -1048,54 +1064,76 @@ class AddPeerDialog (MyDialog):
ValidatingEntry(numericvalidate(0, 65535),
'4070',
5))
- me.c_keepalive = G.CheckButton('Keepalives')
- me.l_tunnel = table.labelled('Tunnel',
- G.combo_box_new_text(),
+ me.l_tunnel = table.labelled('Tunnel', combo_box_text(),
newlinep = True, width = 3)
- me.tuns = conn.tunnels()
+ me.tuns = ['(Default)'] + conn.tunnels()
for t in me.tuns:
me.l_tunnel.append_text(t)
me.l_tunnel.set_active(0)
+
+ def tickybox_sensitivity(tickybox, target):
+ tickybox.connect('toggled',
+ lambda t: target.set_sensitive (t.get_active()))
+
+ me.c_keepalive = G.CheckButton('Keepalives')
table.pack(me.c_keepalive, newlinep = True, xopt = G.FILL)
- me.c_keepalive.connect('toggled',
- lambda t: me.e_keepalive.set_sensitive\
- (t.get_active()))
me.e_keepalive = ValidatingEntry(r'^\d+[hms]?$', '', 5)
me.e_keepalive.set_sensitive(False)
+ tickybox_sensitivity(me.c_keepalive, me.e_keepalive)
table.pack(me.e_keepalive, width = 3)
+
+ me.c_cork = G.CheckButton('Cork')
+ table.pack(me.c_cork, newlinep = True, width = 4, xopt = G.FILL)
+
+ me.c_mobile = G.CheckButton('Mobile')
+ table.pack(me.c_mobile, newlinep = True, width = 4, xopt = G.FILL)
+
+ me.c_peerkey = G.CheckButton('Peer key tag')
+ table.pack(me.c_peerkey, newlinep = True, xopt = G.FILL)
+ me.e_peerkey = ValidatingEntry(r'^[^.:\s]+$', '', 16)
+ me.e_peerkey.set_sensitive(False)
+ tickybox_sensitivity(me.c_peerkey, me.e_peerkey)
+ table.pack(me.e_peerkey, width = 3)
+
+ me.c_privkey = G.CheckButton('Private key tag')
+ table.pack(me.c_privkey, newlinep = True, xopt = G.FILL)
+ me.e_privkey = ValidatingEntry(r'^[^.:\s]+$', '', 16)
+ me.e_privkey.set_sensitive(False)
+ tickybox_sensitivity(me.c_privkey, me.e_privkey)
+ table.pack(me.e_privkey, width = 3)
+
me.show_all()
def ok(me):
"""Handle an OK press: create the peer."""
try:
- if me.c_keepalive.get_active():
- ka = me.e_keepalive.get_text()
- else:
- ka = None
t = me.l_tunnel.get_active()
- if t == 0:
- tun = None
- else:
- tun = me.tuns[t]
- me._addpeer(me.e_name.get_text(),
- me.e_addr.get_text(),
- me.e_port.get_text(),
- ka,
- tun)
+ me._addpeer(me.e_name.get_text(),
+ me.e_addr.get_text(),
+ me.e_port.get_text(),
+ keepalive = (me.c_keepalive.get_active() and
+ me.e_keepalive.get_text() or None),
+ tunnel = t and me.tuns[t] or None,
+ cork = me.c_cork.get_active() or None,
+ mobile = me.c_mobile.get_active() or None,
+ key = (me.c_peerkey.get_active() and
+ me.e_peerkey.get_text() or None),
+ priv = (me.c_privkey.get_active() and
+ me.e_privkey.get_text() or None))
except ValidationError:
GDK.beep()
return
@incr
- def _addpeer(me, name, addr, port, keepalive, tunnel):
+ def _addpeer(me, *args, **kw):
"""Coroutine function: actually do the ADD command."""
try:
- conn.add(name, addr, port, keepalive = keepalive, tunnel = tunnel)
+ conn.add(*args, **kw)
me.destroy()
except T.TripeError, exc:
- idly(moanbox, ' '.join(exc))
+ T.defer(moanbox, ' '.join(exc))
-class ServInfo (MyWindow):
+class ServInfo (TrivialWindow):
"""
Show information about the server and available services.
@@ -1106,7 +1144,7 @@ class ServInfo (MyWindow):
"""
def __init__(me):
- MyWindow.__init__(me)
+ TrivialWindow.__init__(me)
me.set_title('TrIPE server info')
table = GridPacker()
me.add(table)
@@ -1165,7 +1203,7 @@ class TraceOptions (MyDialog):
text = desc[0].upper() + desc[1:]
ticky = G.CheckButton(text)
ticky.set_active(st == '+')
- me.vbox.pack_start(ticky)
+ me.vbox.pack_start(ticky, True, True, 0)
me.opts.append((ch, ticky))
me.show_all()
def ok(me):
@@ -1223,6 +1261,15 @@ statsxlate = \
## the entry.
statslayout = \
[('Start time', '%(start-time)s'),
+ ('Private key', '%(current-key)s'),
+ ('Diffie-Hellman group',
+ '%(kx-group)s '
+ '(%(kx-group-order-bits)s-bit order, '
+ '%(kx-group-elt-bits)s-bit elements)'),
+ ('Cipher',
+ '%(cipher)s (%(cipher-keysz)s-bit key, %(cipher-blksz)s-bit block)'),
+ ('Mac', '%(mac)s (%(mac-keysz)s-bit key, %(mac-tagsz)s-bit tag)'),
+ ('Hash', '%(hash)s (%(hash-sz)s-bit output)'),
('Last key-exchange', '%(last-keyexch-time)s'),
('Last packet', '%(last-packet-time)s'),
('Packets in/out',
@@ -1233,7 +1280,7 @@ statslayout = \
'%(ip-packets-in)s (%(ip-bytes-in)s) / %(ip-packets-out)s (%(ip-bytes-out)s)'),
('Rejected packets', '%(rejected-packets)s')]
-class PeerWindow (MyWindow):
+class PeerWindow (TrivialWindow):
"""
Show information about a peer.
@@ -1251,7 +1298,7 @@ class PeerWindow (MyWindow):
def __init__(me, peer):
"""Construct a PeerWindow, showing information about PEER."""
- MyWindow.__init__(me)
+ TrivialWindow.__init__(me)
me.set_title('TrIPE statistics: %s' % peer.name)
me.peer = peer
@@ -1310,16 +1357,18 @@ class PeerWindow (MyWindow):
stat = conn.stats(me.peer.name)
for s, trans in statsxlate:
stat[s] = trans(stat[s])
+ stat.update(me.peer.__dict__)
for label, format in statslayout:
me.e[label].set_text(format % stat)
- GO.timeout_add(1000, lambda: me.cr.switch() and False)
+ GL.timeout_add(1000, lambda: me.cr.switch() and False)
me.cr.parent.switch()
me.cr = None
def tryupdate(me):
"""Start the updater coroutine, if it's not going already."""
if me.cr is None:
- me.cr = T.Coroutine(me._update)
+ me.cr = T.Coroutine(me._update,
+ name = 'update-peer-window %s' % me.peer.name)
me.cr.switch()
def stopupdate(me, *hunoz, **hukairz):
@@ -1344,12 +1393,12 @@ class PeerWindow (MyWindow):
###--------------------------------------------------------------------------
### Cryptographic status.
-class CryptoInfo (MyWindow):
+class CryptoInfo (TrivialWindow):
"""Simple display of cryptographic algorithms in use."""
def __init__(me):
- MyWindow.__init__(me)
+ TrivialWindow.__init__(me)
me.set_title('Cryptographic algorithms')
- aside(me.populate)
+ T.aside(me.populate)
def populate(me):
table = GridPacker()
me.add(table)
@@ -1507,7 +1556,7 @@ class MonitorWindow (MyWindow):
me.ui.add_ui_from_string(uidef)
## Construct the menu bar.
- vbox.pack_start(me.ui.get_widget('/menubar'), expand = False)
+ vbox.pack_start(me.ui.get_widget('/menubar'), False, True, 0)
me.add_accel_group(me.ui.get_accel_group())
## Construct and attach the auto-peers menu. (This is a horrible bodge
@@ -1548,12 +1597,12 @@ class MonitorWindow (MyWindow):
me.list.set_reorderable(True)
me.list.get_selection().set_mode(G.SELECTION_NONE)
scr.add(me.list)
- vbox.pack_start(scr)
+ vbox.pack_start(scr, True, True, 0)
## Construct the status bar, and listen on hooks which report changes to
## connection status.
me.status = G.Statusbar()
- vbox.pack_start(me.status, expand = False)
+ vbox.pack_start(me.status, False, True, 0)
me.hook(conn.connecthook, cr(me.connected))
me.hook(conn.disconnecthook, me.disconnected)
me.hook(conn.notehook, me.notify)
@@ -1611,7 +1660,7 @@ class MonitorWindow (MyWindow):
else:
## Insert the new items into the menu. (XXX this seems buggy XXX)
- ## Tick the peers which are actually connected.
+ ## Tick the peers which are actually connected.
i = j = 0
for peer in monitor.autopeers:
if j < len(existing) and \
@@ -1646,7 +1695,8 @@ class MonitorWindow (MyWindow):
"""
if me._kidding:
return
- T.Coroutine(me._addautopeer_hack).switch(peer)
+ T.Coroutine(me._addautopeer_hack,
+ name = '_addautopeerhack %s' % peer).switch(peer)
def _addautopeer_hack(me, peer):
"""Make an automated connection to PEER in response to a user click."""
@@ -1655,7 +1705,7 @@ class MonitorWindow (MyWindow):
try:
T._simple(conn.svcsubmit('connect', 'active', peer))
except T.TripeError, exc:
- idly(moanbox, ' '.join(exc.args))
+ T.defer(moanbox, ' '.join(exc.args))
me.apchange()
def activate(me, l, path, col):
@@ -1780,9 +1830,6 @@ def init(opts):
global conn, monitor, pinger
- ## Run jobs put off for later.
- T.Coroutine(_runasides).switch()
-
## Try to establish a connection.
conn = Connection(opts.tripesock)
@@ -1800,7 +1847,7 @@ def main():
## Main loop.
HookClient().hook(root.closehook, exit)
- G.main()
+ conn.mainloop()
if __name__ == '__main__':
opts = parse_options()