/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos-monitor

  • Committer: Teddy Hogeborn
  • Date: 2009-11-09 05:10:35 UTC
  • Revision ID: teddy@fukt.bsnet.se-20091109051035-zcmvuhioo9gjs0cb
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
                                              D-Bus types to native
                                              Python types.
  (MandosClientPropertyCache.type_map): Removed attribute.
  (MandosClientPropertyCache.conversion): Removed method.
  (ConstrainedListBox): New; a ListBox which will keep the focus.
  (UserInterface.__init__): Take new argument "max_log_length".
  (UserInterface.divider, UserInterface.log,
  UserInterface.max_log_length, UserInterface.logbox,
  UserInterface.log_visible, UserInterface.log_wrap): New attributes.
  (UserInterface.rebuild, UserInterface.log_message,
  UserInterface.toggle_log_display, UserInterface.change_log_display):
  New methods.
  (UserInterface.remove_client): Bug fix: Eliminate double client
                                 listing when an empty list gets a
                                 ClientAdded signal.
  (UserInterface.process_input): Add more key translations.

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
    """
38
38
    def __init__(self, proxy_object=None, properties=None, *args,
39
39
                 **kwargs):
40
 
        # Type conversion mapping
41
 
        self.type_map = {
42
 
            dbus.ObjectPath: unicode,
43
 
            dbus.ByteArray: str,
44
 
            dbus.Signature: unicode,
45
 
            dbus.Byte: chr,
46
 
            dbus.Int16: int,
47
 
            dbus.UInt16: int,
48
 
            dbus.Int32: int,
49
 
            dbus.UInt32: int,
50
 
            dbus.Int64: int,
51
 
            dbus.UInt64: int,
52
 
            dbus.Dictionary: dict,
53
 
            dbus.Array: list,
54
 
            dbus.String: unicode,
55
 
            dbus.Boolean: bool,
56
 
            dbus.Double: float,
57
 
            dbus.Struct: tuple,
58
 
            }
59
40
        self.proxy = proxy_object # Mandos Client proxy object
60
41
        
61
42
        if properties is None:
62
43
            self.properties = dict()
63
44
        else:
64
 
            self.properties = dict(self.convert_property(prop, val)
65
 
                                   for prop, val in
66
 
                                   properties.iteritems())
 
45
            self.properties = properties
67
46
        self.proxy.connect_to_signal("PropertyChanged",
68
47
                                     self.property_changed,
69
48
                                     client_interface,
70
49
                                     byte_arrays=True)
71
50
        
72
51
        if properties is None:
73
 
            self.properties.update(
74
 
                self.convert_property(prop, val)
75
 
                for prop, val in
76
 
                self.proxy.GetAll(client_interface,
77
 
                                  dbus_interface =
78
 
                                  dbus.PROPERTIES_IFACE).iteritems())
 
52
            self.properties.update(self.proxy.GetAll(client_interface,
 
53
                                                     dbus_interface =
 
54
                                                     dbus.PROPERTIES_IFACE))
79
55
        super(MandosClientPropertyCache, self).__init__(
80
56
            proxy_object=proxy_object,
81
57
            properties=properties, *args, **kwargs)
82
58
    
83
 
    def convert_property(self, property, value):
84
 
        """This converts the arguments from a D-Bus signal, which are
85
 
        D-Bus types, into normal Python types, using a conversion
86
 
        function from "self.type_map".
87
 
        """
88
 
        property_name = unicode(property) # Always a dbus.String
89
 
        if isinstance(value, dbus.UTF8String):
90
 
            # Should not happen, but prepare for it anyway
91
 
            value = dbus.String(str(value).decode("utf-8"))
92
 
        try:
93
 
            convfunc = self.type_map[type(value)]
94
 
        except KeyError:
95
 
            # Unknown type, return unmodified
96
 
            return property_name, value
97
 
        return property_name, convfunc(value)
98
59
    def property_changed(self, property=None, value=None):
99
60
        """This is called whenever we get a PropertyChanged signal
100
61
        It updates the changed property in the "properties" dict.
101
62
        """
102
 
        # Convert name and value
103
 
        property_name, cvalue = self.convert_property(property, value)
104
63
        # Update properties dict with new value
105
 
        self.properties[property_name] = cvalue
 
64
        self.properties[property] = value
106
65
 
107
66
 
108
67
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
160
119
                      % self.properties)
161
120
        if not urwid.supports_unicode():
162
121
            self._text = self._text.encode("ascii", "replace")
163
 
        textlist = [(u"normal", u"BLÄRGH: "), (u"bold", self._text)]
 
122
        textlist = [(u"normal", u"BLARGH: "), (u"bold", self._text)]
164
123
        self._text_widget.set_text(textlist)
165
124
        self._focus_text_widget.set_text([(with_standout[text[0]],
166
125
                                           text[1])
222
181
            self.update()
223
182
 
224
183
 
 
184
class ConstrainedListBox(urwid.ListBox):
 
185
    """Like a normal urwid.ListBox, but will consume all "up" or
 
186
    "down" key presses, thus not allowing any containing widgets to
 
187
    use them as an excuse to shift focus away from this widget.
 
188
    """
 
189
    def keypress(self, (maxcol, maxrow), key):
 
190
        ret = super(ConstrainedListBox, self).keypress((maxcol, maxrow), key)
 
191
        if ret in (u"up", u"down"):
 
192
            return
 
193
        return ret
 
194
 
 
195
 
225
196
class UserInterface(object):
226
197
    """This is the entire user interface - the whole screen
227
198
    with boxes, lists of client widgets, etc.
228
199
    """
229
 
    def __init__(self):
230
 
        DBusGMainLoop(set_as_default=True )
 
200
    def __init__(self, max_log_length=1000):
 
201
        DBusGMainLoop(set_as_default=True)
231
202
        
232
203
        self.screen = urwid.curses_display.Screen()
233
204
        
251
222
                                          u"standout")),
252
223
                ))
253
224
        
 
225
        if urwid.supports_unicode():
 
226
            #self.divider = u"─" # \u2500
 
227
            self.divider = u"━" # \u2501
 
228
        else:
 
229
            #self.divider = u"-" # \u002d
 
230
            self.divider = u"_" # \u005f
 
231
        
254
232
        self.screen.start()
255
233
        
256
234
        self.size = self.screen.get_cols_rows()
257
235
        
258
236
        self.clients = urwid.SimpleListWalker([])
259
237
        self.clients_dict = {}
260
 
        self.topwidget = urwid.LineBox(urwid.ListBox(self.clients))
261
 
        #self.topwidget = urwid.ListBox(clients)
 
238
        
 
239
        # We will add Text widgets to this list
 
240
        self.log = []
 
241
        self.max_log_length = max_log_length
 
242
        
 
243
        # We keep a reference to the log widget so we can remove it
 
244
        # from the ListWalker without it getting destroyed
 
245
        self.logbox = ConstrainedListBox(self.log)
 
246
        
 
247
        # This keeps track of whether self.uilist currently has
 
248
        # self.logbox in it or not
 
249
        self.log_visible = True
 
250
        self.log_wrap = u"any"
 
251
        
 
252
        self.rebuild()
 
253
        self.log_message(u"Message")
 
254
        self.log_message(u"Message0 Message1 Message2 Message3 Message4 Message5 Message6 Message7 Message8 Message9")
 
255
        self.log_message(u"Message10 Message11 Message12 Message13 Message14 Message15 Message16 Message17 Message18 Message19")
 
256
        self.log_message(u"Message20 Message21 Message22 Message23 Message24 Message25 Message26 Message27 Message28 Message29")
262
257
        
263
258
        self.busname = domain + '.Mandos'
264
259
        self.main_loop = gobject.MainLoop()
284
279
                            self.add_new_client,
285
280
                            dbus_interface=server_interface,
286
281
                            byte_arrays=True))
287
 
        for path, client in (mandos_clients.iteritems()):
 
282
        for path, client in mandos_clients.iteritems():
288
283
            client_proxy_object = self.bus.get_object(self.busname,
289
284
                                                      path)
290
285
            self.add_client(MandosClientWidget(server_proxy_object
298
293
                                               =self.remove_client),
299
294
                            path=path)
300
295
    
 
296
    def rebuild(self):
 
297
        """This rebuilds the User Interface.
 
298
        Call this when the widget layout needs to change"""
 
299
        self.uilist = []
 
300
        #self.uilist.append(urwid.ListBox(self.clients))
 
301
        self.uilist.append(urwid.Frame(ConstrainedListBox(self.clients),
 
302
                                       #header=urwid.Divider(),
 
303
                                       header=None,
 
304
                                       footer=urwid.Divider(div_char=self.divider)))
 
305
        if self.log_visible:
 
306
            self.uilist.append(self.logbox)
 
307
            pass
 
308
        self.topwidget = urwid.Pile(self.uilist)
 
309
    
 
310
    def log_message(self, markup):
 
311
        """Add a log message to the log buffer."""
 
312
        self.log.append(urwid.Text(markup, wrap=self.log_wrap))
 
313
        if (self.max_log_length
 
314
            and len(self.log) > self.max_log_length):
 
315
            del self.log[0:len(self.log)-self.max_log_length-1]
 
316
    
 
317
    def toggle_log_display(self):
 
318
        """Toggle visibility of the log buffer."""
 
319
        self.log_visible = not self.log_visible
 
320
        self.rebuild()
 
321
        self.log_message(u"Log visibility changed to: "
 
322
                         + unicode(self.log_visible))
 
323
    
 
324
    def change_log_display(self):
 
325
        """Change type of log display.
 
326
        Currently, this toggles wrapping of text lines."""
 
327
        if self.log_wrap == u"clip":
 
328
            self.log_wrap = u"any"
 
329
        else:
 
330
            self.log_wrap = u"clip"
 
331
        for textwidget in self.log:
 
332
            textwidget.set_wrap_mode(self.log_wrap)
 
333
        self.log_message(u"Wrap mode: " + self.log_wrap)
 
334
    
301
335
    def find_and_remove_client(self, path, name):
302
336
        """Find an client from its object path and remove it.
303
337
        
336
370
        if path is None:
337
371
            path = client.proxy.object_path
338
372
        del self.clients_dict[path]
 
373
        if not self.clients_dict:
 
374
            # Work around bug in Urwid 0.9.8.3 - if a SimpleListWalker
 
375
            # is completely emptied, we need to recreate it.
 
376
            self.clients = urwid.SimpleListWalker([])
 
377
            self.rebuild()
339
378
        self.refresh()
340
379
    
341
380
    def refresh(self):
360
399
    
361
400
    def process_input(self, source, condition):
362
401
        keys = self.screen.get_input()
363
 
        translations = { u"j": u"down",
364
 
                         u"k": u"up",
 
402
        translations = { u"ctrl n": u"down",      # Emacs
 
403
                         u"ctrl p": u"up",        # Emacs
 
404
                         u"ctrl v": u"page down", # Emacs
 
405
                         u"meta v": u"page up",   # Emacs
 
406
                         u" ": u"page down",      # less
 
407
                         u"f": u"page down",      # less
 
408
                         u"b": u"page up",        # less
 
409
                         u"j": u"down",           # vi
 
410
                         u"k": u"up",             # vi
365
411
                         }
366
412
        for key in keys:
367
413
            try:
375
421
            elif key == u"window resize":
376
422
                self.size = self.screen.get_cols_rows()
377
423
                self.refresh()
378
 
            elif key == " ":
379
 
                self.refresh()
 
424
            elif key == u"\f":  # Ctrl-L
 
425
                self.refresh()
 
426
            elif key == u"l" or key == u"D":
 
427
                self.toggle_log_display()
 
428
                self.refresh()
 
429
            elif key == u"w" or key == u"i":
 
430
                self.change_log_display()
 
431
                self.refresh()
 
432
            elif key == u"?" or key == u"f1":
 
433
                self.log_message(u"Help!")
 
434
                self.refresh()
 
435
            elif key == u"tab":
 
436
                if self.topwidget.get_focus() is self.logbox:
 
437
                    self.topwidget.set_focus(0)
 
438
                else:
 
439
                    self.topwidget.set_focus(self.logbox)
 
440
                self.refresh()
 
441
            elif (key == u"end" or key == u"meta >" or key == u"G"
 
442
                  or key == u">"):
 
443
                pass            # xxx end-of-buffer
 
444
            elif (key == u"home" or key == u"meta <" or key == u"g"
 
445
                  or key == u"<"):
 
446
                pass            # xxx beginning-of-buffer
 
447
            elif key == u"ctrl e" or key == u"$":
 
448
                pass            # xxx move-end-of-line
 
449
            elif key == u"ctrl a" or key == u"^":
 
450
                pass            # xxx move-beginning-of-line
 
451
            elif key == u"ctrl b" or key == u"meta (" or key == u"h":
 
452
                pass            # xxx left
 
453
            elif key == u"ctrl f" or key == u"meta )" or key == u"l":
 
454
                pass            # xxx right
 
455
            elif key == u"a":
 
456
                pass            # scroll up log
 
457
            elif key == u"z":
 
458
                pass            # scroll down log
380
459
            elif self.topwidget.selectable():
381
460
                self.topwidget.keypress(self.size, key)
382
461
                self.refresh()