3
""" wicd - wireless connection daemon frontend implementation
5
This module implements a usermode frontend for wicd. It updates connection
6
information, provides an (optional) tray icon, and allows for launching of
7
the wicd GUI and Wired Profile Chooser.
9
class TrayIcon() -- Parent class of TrayIconGUI and IconConnectionInfo.
10
class TrayConnectionInfo() -- Child class of TrayIcon which provides
11
and updates connection status.
12
class TrayIconGUI() -- Child class of TrayIcon which implements the tray.
13
icon itself. Parent class of StatusTrayIconGUI and EggTrayIconGUI.
14
class StatusTrayIconGUI() -- Implements the tray icon using a
16
class EggTrayIconGUI() -- Implements the tray icon using egg.trayicon.
17
def usage() -- Prints usage information.
18
def main() -- Runs the wicd frontend main loop.
23
# Copyright (C) 2007 - 2008 Adam Blackburn
24
# Copyright (C) 2007 - 2008 Dan O'Reilly
26
# This program is free software; you can redistribute it and/or modify
27
# it under the terms of the GNU General Public License Version 2 as
28
# published by the Free Software Foundation.
30
# This program is distributed in the hope that it will be useful,
31
# but WITHOUT ANY WARRANTY; without even the implied warranty of
32
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33
# GNU General Public License for more details.
35
# You should have received a copy of the GNU General Public License
36
# along with this program. If not, see <http://www.gnu.org/licenses/>.
46
from dbus import DBusException
47
from dbus import version as dbus_version
49
# Wicd specific imports
50
from wicd import wpath
53
from wicd.dbusmanager import DBusManager
56
# Import egg.trayicon if we're using an older gtk version
57
if not (gtk.gtk_version[0] >= 2 and gtk.gtk_version[1] >= 10):
62
logging.debug( 'Unable to load wicd.py: Missing egg.trayicon module.' )
67
if not dbus_version or (dbus_version < (0, 80, 0)):
70
from dbus.mainloop.glib import DBusGMainLoop
71
DBusGMainLoop(set_as_default=True)
73
misc.RenameProcess("wicd-client")
75
if __name__ == '__main__':
84
language = misc.get_language_list_tray()
86
class NetworkMenuItem(gtk.ImageMenuItem):
87
def __init__(self, lbl, is_active=False):
88
gtk.ImageMenuItem.__init__(self)
89
self.label = gtk.Label(lbl)
91
atrlist = pango.AttrList()
92
atrlist.insert(pango.AttrWeight(pango.WEIGHT_BOLD, 0, 50))
93
self.label.set_attributes(atrlist)
94
self.label.set_justify(gtk.JUSTIFY_LEFT)
95
self.label.set_alignment(0, 0)
101
""" Base Tray Icon class.
103
Base Class for implementing a tray icon to display network status.
106
def __init__(self, use_tray, animate):
108
self.tr = self.EggTrayIconGUI(use_tray)
110
self.tr = self.StatusTrayIconGUI(use_tray)
111
self.icon_info = self.TrayConnectionInfo(self.tr, use_tray, animate)
114
class TrayConnectionInfo:
115
""" Class for updating the tray icon status. """
116
def __init__(self, tr, use_tray=True, animate=True):
117
""" Initialize variables needed for the icon status methods. """
118
self.last_strength = -2
119
self.still_wired = False
121
self.tried_reconnect = False
122
self.connection_lost_counter = 0
124
self.use_tray = use_tray
125
self.last_sndbytes = -1
126
self.last_rcvbytes = -1
127
self.max_snd_gain = 10000
128
self.max_rcv_gain = 10000
129
self.animate = animate
130
self.update_tray_icon()
132
def wired_profile_chooser(self):
133
""" Launch the wired profile chooser. """
134
gui.WiredProfileChooser()
135
daemon.SetNeedWiredProfileChooser(False)
137
def set_wired_state(self, info):
138
""" Sets the icon info for a wired state. """
140
self.tr.set_from_file(wpath.images + "wired.png")
141
self.tr.set_tooltip(language['connected_to_wired'].replace('$A',
144
def set_wireless_state(self, info):
145
""" Sets the icon info for a wireless state. """
147
wireless_ip = info[0]
148
self.network = info[1]
150
cur_net_id = int(info[3])
151
sig_string = daemon.FormatSignalForPrinting(str(strength))
153
if wireless.GetWirelessProperty(cur_net_id, "encryption"):
156
self.tr.set_tooltip(language['connected_to_wireless']
157
.replace('$A', self.network)
158
.replace('$B', sig_string)
159
.replace('$C', str(wireless_ip)))
160
self.set_signal_image(int(strength), lock)
162
def set_connecting_state(self, info):
163
""" Sets the icon info for a connecting state. """
164
if info[0] == 'wired' and len(info) == 1:
165
cur_network = language['wired']
167
cur_network = info[1]
168
self.tr.set_tooltip(language['connecting'] + " to " +
170
self.tr.set_from_file(wpath.images + "no-signal.png")
172
def set_not_connected_state(self, info):
173
""" Set the icon info for the not connected state. """
174
self.tr.set_from_file(wpath.images + "no-signal.png")
175
if wireless.GetKillSwitchEnabled():
176
status = (language['not_connected'] + " (" +
177
language['killswitch_enabled'] + ")")
179
status = language['not_connected']
180
self.tr.set_tooltip(status)
182
def update_tray_icon(self, state=None, info=None):
183
""" Updates the tray icon and current connection status. """
184
if not self.use_tray: return False
186
if not state or not info:
187
[state, info] = daemon.GetConnectionStatus()
189
if state == misc.WIRED:
190
self.set_wired_state(info)
191
elif state == misc.WIRELESS:
192
self.set_wireless_state(info)
193
elif state == misc.CONNECTING:
194
self.set_connecting_state(info)
195
elif state in (misc.SUSPENDED, misc.NOT_CONNECTED):
196
self.set_not_connected_state(info)
198
logging.debug( 'Invalid state returned!!!' )
202
def set_signal_image(self, wireless_signal, lock):
203
""" Sets the tray icon image for an active wireless connection. """
205
prefix = self.get_bandwidth_state()
208
if daemon.GetSignalDisplayType() == 0:
209
if wireless_signal > 75:
210
signal_img = "high-signal"
211
elif wireless_signal > 50:
212
signal_img = "good-signal"
213
elif wireless_signal > 25:
214
signal_img = "low-signal"
216
signal_img = "bad-signal"
218
if wireless_signal >= -60:
219
signal_img = "high-signal"
220
elif wireless_signal >= -70:
221
signal_img = "good-signal"
222
elif wireless_signal >= -80:
223
signal_img = "low-signal"
225
signal_img = "bad-signal"
227
img_file = ''.join([wpath.images, prefix, signal_img, lock, ".png"])
228
self.tr.set_from_file(img_file)
230
def get_bandwidth_state(self):
231
""" Determines what network activity state we are in. """
235
dev_dir = '/sys/class/net/'
236
wiface = daemon.GetWirelessInterface()
237
for fldr in os.listdir(dev_dir):
239
dev_dir = dev_dir + fldr + "/statistics/"
242
rcvbytes = int(open(dev_dir + "rx_bytes", "r").read().strip())
243
sndbytes = int(open(dev_dir + "tx_bytes", "r").read().strip())
248
if not rcvbytes or not sndbytes:
251
# Figure out receiving data info.
252
activity = self.is_network_active(rcvbytes, self.max_rcv_gain,
254
receiving = activity[0]
255
self.max_rcv_gain = activity[1]
256
self.last_rcvbytes = activity[2]
258
# Figure out out transmitting data info.
259
activity = self.is_network_active(sndbytes, self.max_snd_gain,
261
transmitting = activity[0]
262
self.max_snd_gain = activity[1]
263
self.last_sndbytes = activity[2]
265
if transmitting and receiving:
268
return 'transmitting-'
274
def is_network_active(self, bytes, max_gain, last_bytes):
275
""" Determines if a network is active.
277
Determines if a network is active by looking at the
278
number of bytes sent since the previous check. This method
279
is generic, and can be used to determine activity in both
280
the sending and receiving directions.
283
A tuple containing three elements:
284
1) a boolean specifying if the network is active.
285
2) an int specifying the maximum gain the network has had.
286
3) an int specifying the last recorded number of bytes sent.
292
elif bytes > (last_bytes + float(max_gain / 20.0)):
296
gain = bytes - last_bytes
299
return (active, max_gain, last_bytes)
302
class TrayIconGUI(object):
303
""" Base Tray Icon UI class.
305
Implements methods and variables used by both egg/StatusIcon
309
def __init__(self, use_tray):
312
<menubar name="Menubar">
314
<menu action="Connect">
317
<menuitem action="About"/>
318
<menuitem action="Quit"/>
324
('Menu', None, 'Menu'),
325
('Connect', gtk.STOCK_CONNECT, "Connect"),
326
('About', gtk.STOCK_ABOUT, '_About...', None,
327
'About wicd-tray-icon', self.on_about),
328
('Quit',gtk.STOCK_QUIT,'_Quit',None,'Quit wicd-tray-icon',
331
actg = gtk.ActionGroup('Actions')
332
actg.add_actions(actions)
333
self.manager = gtk.UIManager()
334
self.manager.insert_action_group(actg, 0)
335
self.manager.add_ui_from_string(menu)
336
self.menu = (self.manager.get_widget('/Menubar/Menu/About').
339
self.current_icon_path = None
340
self.use_tray = use_tray
341
self._is_scanning = False
342
net_menuitem = self.manager.get_widget("/Menubar/Menu/Connect/")
343
net_menuitem.connect("activate", self.on_net_menu_activate)
345
def tray_scan_started(self):
346
""" Callback for when a wireless scan is started. """
347
self._is_scanning = True
348
self.init_network_menu()
350
def tray_scan_ended(self):
351
""" Callback for when a wireless scan finishes. """
352
self._is_scanning = False
353
self.populate_network_menu()
355
def on_activate(self, data=None):
356
""" Opens the wicd GUI. """
357
self.toggle_wicd_gui()
359
def on_quit(self, widget=None):
360
""" Closes the tray icon. """
363
def on_about(self, data=None):
364
""" Opens the About Dialog. """
365
dialog = gtk.AboutDialog()
366
dialog.set_name('Wicd Tray Icon')
367
dialog.set_version('2.0')
368
dialog.set_comments('An icon that shows your network connectivity')
369
dialog.set_website('http://wicd.net')
373
def _add_item_to_menu(self, net_menu, lbl, type_, n_id, is_connecting,
375
""" Add an item to the network list submenu. """
376
def network_selected(widget, net_type, net_id):
377
""" Callback method for a menu item selection. """
378
if net_type == "__wired__":
381
wireless.ConnectWireless(net_id)
383
item = NetworkMenuItem(lbl, is_active)
386
if type_ == "__wired__":
387
image.set_from_icon_name("network-wired", 2)
389
pb = gtk.gdk.pixbuf_new_from_file_at_size(self._get_img(n_id),
391
image.set_from_pixbuf(pb)
393
item.set_image(image)
395
item.connect("activate", network_selected, type_, n_id)
396
net_menu.append(item)
399
item.set_sensitive(False)
402
def _get_img(self, net_id):
403
""" Determines which image to use for the wireless entries. """
404
def fix_strength(val, default):
405
""" Assigns given strength to a default value if needed. """
406
return val is not None and int(val) or default
409
return wireless.GetWirelessProperty(net_id, prop)
411
strength = fix_strength(get_prop("quality"), -1)
412
dbm_strength = fix_strength(get_prop('strength'), -100)
414
if daemon.GetWPADriver() == 'ralink legacy' or \
415
daemon.GetSignalDisplayType() == 1:
416
if dbm_strength >= -60:
417
signal_img = 'signal-100.png'
418
elif dbm_strength >= -70:
419
signal_img = 'signal-75.png'
420
elif dbm_strength >= -80:
421
signal_img = 'signal-50.png'
423
signal_img = 'signal-25.png'
426
signal_img = 'signal-100.png'
428
signal_img = 'signal-75.png'
430
signal_img = 'signal-50.png'
432
signal_img = 'signal-25.png'
433
return wpath.images + signal_img
435
def on_net_menu_activate(self, item):
436
""" Trigger a background scan to populate the network menu.
438
Called when the network submenu is moused over. We
439
sleep briefly, clear pending gtk events, and if
440
we're still being moused over we trigger a scan.
441
This is to prevent scans when the user is just
442
mousing past the menu to select another menu item.
445
def dummy(x=None): pass
447
if self._is_scanning:
450
self.init_network_menu()
452
while gtk.events_pending():
454
if item.state != gtk.STATE_PRELIGHT:
456
wireless.Scan(reply_handler=dummy, error_handler=dummy)
458
def populate_network_menu(self, data=None):
459
""" Populates the network list submenu. """
460
def get_prop(net_id, prop):
461
return wireless.GetWirelessProperty(net_id, prop)
463
net_menuitem = self.manager.get_widget("/Menubar/Menu/Connect/")
464
submenu = net_menuitem.get_submenu()
465
self._clear_menu(submenu)
467
is_connecting = daemon.CheckIfConnecting()
468
num_networks = wireless.GetNumberOfNetworks()
469
[status, info] = daemon.GetConnectionStatus()
471
if daemon.GetAlwaysShowWiredInterface() or \
472
wired.CheckPluggedIn():
473
if status == misc.WIRED:
477
self._add_item_to_menu(submenu, "Wired Network", "__wired__", 0,
478
is_connecting, is_active)
479
sep = gtk.SeparatorMenuItem()
484
for x in range(0, num_networks):
485
essid = get_prop(x, "essid")
486
if status == misc.WIRELESS and info[1] == essid:
490
self._add_item_to_menu(submenu, essid, "wifi", x,
491
is_connecting, is_active)
493
no_nets_item = gtk.MenuItem(language['no_wireless_networks_found'])
494
no_nets_item.set_sensitive(False)
496
submenu.append(no_nets_item)
500
def init_network_menu(self):
501
""" Set the right-click menu for to the scanning state. """
502
net_menuitem = self.manager.get_widget("/Menubar/Menu/Connect/")
503
submenu = net_menuitem.get_submenu()
504
self._clear_menu(submenu)
506
loading_item = gtk.MenuItem(language['scanning'] + "...")
507
loading_item.set_sensitive(False)
509
submenu.append(loading_item)
512
def _clear_menu(self, menu):
513
""" Clear the right-click menu. """
514
for item in menu.get_children():
518
def toggle_wicd_gui(self):
519
""" Toggles the wicd GUI. """
521
self.gui_win = gui.appGui(dbus_manager)
522
bus = dbus_manager.get_bus()
523
bus.add_signal_receiver(self.gui_win.dbus_scan_finished,
525
'org.wicd.daemon.wireless')
526
bus.add_signal_receiver(self.gui_win.dbus_scan_started,
527
'SendStartScanSignal',
528
'org.wicd.daemon.wireless')
529
bus.add_signal_receiver(self.gui_win.update_connect_buttons,
530
'StatusChanged', 'org.wicd.daemon')
531
elif not self.gui_win.is_visible:
532
self.gui_win.show_win()
538
class EggTrayIconGUI(TrayIconGUI):
539
""" Tray Icon for gtk < 2.10.
541
Uses the deprecated egg.trayicon module to implement the tray icon.
542
Since it relies on a deprecated module, this class is only used
543
for machines running versions of GTK < 2.10.
546
def __init__(self, use_tray=True):
547
"""Initializes the tray icon"""
548
TrayIcon.TrayIconGUI.__init__(self, use_tray)
549
self.use_tray = use_tray
551
self.toggle_wicd_gui()
554
self.tooltip = gtk.Tooltips()
555
self.eb = gtk.EventBox()
556
self.tray = egg.trayicon.TrayIcon("WicdTrayIcon")
557
self.pic = gtk.Image()
558
self.tooltip.set_tip(self.eb, "Initializing wicd...")
559
self.pic.set_from_file("images/no-signal.png")
561
self.eb.connect('button_press_event', self.tray_clicked)
562
self.eb.add(self.pic)
563
self.tray.add(self.eb)
566
def tray_clicked(self, widget, event):
567
""" Handles tray mouse click events. """
568
if event.button == 1:
569
self.toggle_wicd_gui()
570
elif event.button == 3:
571
self.init_network_menu()
572
self.menu.popup(None, None, None, event.button, event.time)
574
def set_from_file(self, val=None):
575
""" Calls set_from_file on the gtk.Image for the tray icon. """
576
if not self.use_tray: return
577
self.pic.set_from_file(val)
579
def set_tooltip(self, val):
580
""" Set the tooltip for this tray icon.
582
Sets the tooltip for the gtk.ToolTips associated with this
586
if not self.use_tray: return
587
self.tooltip.set_tip(self.eb, val)
590
class StatusTrayIconGUI(gtk.StatusIcon, TrayIconGUI):
591
""" Class for creating the wicd tray icon on gtk > 2.10.
593
Uses gtk.StatusIcon to implement a tray icon.
596
def __init__(self, use_tray=True):
597
TrayIcon.TrayIconGUI.__init__(self, use_tray)
598
self.use_tray = use_tray
600
self.toggle_wicd_gui()
603
gtk.StatusIcon.__init__(self)
605
self.current_icon_path = ''
606
self.set_visible(True)
607
self.connect('activate', self.on_activate)
608
self.connect('popup-menu', self.on_popup_menu)
609
self.set_from_file(wpath.images + "no-signal.png")
610
self.set_tooltip("Initializing wicd...")
612
def on_popup_menu(self, status, button, timestamp):
613
""" Opens the right click menu for the tray icon. """
614
self.init_network_menu()
615
self.menu.popup(None, None, None, button, timestamp)
617
def set_from_file(self, path = None):
618
""" Sets a new tray icon picture. """
619
if not self.use_tray: return
620
if path != self.current_icon_path:
621
self.current_icon_path = path
622
gtk.StatusIcon.set_from_file(self, path)
626
""" Print usage information. """
629
wireless (and wired) connection daemon front-end.
632
\t-n\t--no-tray\tRun wicd without the tray icon.
633
\t-h\t--help\t\tPrint this help information.
634
\t-a\t--no-animate\tRun the tray without network traffic tray animations.
638
global bus, daemon, wireless, wired, dbus_manager
640
dbus_manager = DBusManager()
642
dbus_manager.connect_to_dbus()
643
except DBusException:
644
logging.debug( "Can't connect to the daemon, trying to start it automatically..." )
645
misc.PromptToStartDaemon()
647
dbus_manager.connect_to_dbus()
648
except DBusException:
649
gui.error(None, "Could not connect to wicd's D-Bus interface. " +
650
"Make sure the daemon is started.")
652
dbus_ifaces = dbus_manager.get_dbus_ifaces()
653
daemon = dbus_ifaces['daemon']
654
wireless = dbus_ifaces['wireless']
655
wired = dbus_ifaces['wired']
659
""" The main frontend program.
662
argv -- The arguments passed to the script.
669
opts, args = getopt.getopt(sys.argv[1:], 'nha', ['help', 'no-tray',
671
except getopt.GetoptError:
672
# Print help information and exit
677
if opt in ('-h', '--help'):
680
elif opt in ('-n', '--no-tray'):
682
elif opt in ('-a', '--no-animate'):
688
logging.debug( 'Loading...')
692
the_gui = gui.appGui()
693
the_gui.standalone = True
694
mainloop = gobject.MainLoop()
698
# Set up the tray icon GUI and backend
699
tray_icon = TrayIcon(use_tray, animate)
701
# Check to see if wired profile chooser was called before icon
702
# was launched (typically happens on startup or daemon restart).
703
if daemon.GetNeedWiredProfileChooser():
704
daemon.SetNeedWiredProfileChooser(False)
705
tray_icon.icon_info.wired_profile_chooser()
707
bus = dbus_manager.get_bus()
708
bus.add_signal_receiver(tray_icon.icon_info.wired_profile_chooser,
709
'LaunchChooser', 'org.wicd.daemon')
710
bus.add_signal_receiver(tray_icon.icon_info.update_tray_icon,
711
'StatusChanged', 'org.wicd.daemon')
712
bus.add_signal_receiver(tray_icon.tr.tray_scan_ended, 'SendEndScanSignal',
713
'org.wicd.daemon.wireless')
714
bus.add_signal_receiver(tray_icon.tr.tray_scan_started,
715
'SendStartScanSignal', 'org.wicd.daemon.wireless')
716
logging.debug( 'Done.')
717
mainloop = gobject.MainLoop()
721
if __name__ == '__main__':