From b0e1bd223368f09e49f13b9ecf04fb0570e04dc0 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 25 Oct 2017 01:52:11 +0200 Subject: [feature] display vpn status on systray --- src/leap/bitmask/gui/app.py | 47 ++++++++++++++++++++++++++++++++++--- src/leap/bitmask/vpn/_status.py | 2 +- src/leap/bitmask/vpn/fw/firewall.py | 4 ++-- src/leap/bitmask/vpn/process.py | 25 ++++++++++++++++---- src/leap/bitmask/vpn/tunnel.py | 3 +-- 5 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/leap/bitmask/gui/app.py b/src/leap/bitmask/gui/app.py index 91e8c870..8f763579 100644 --- a/src/leap/bitmask/gui/app.py +++ b/src/leap/bitmask/gui/app.py @@ -34,6 +34,8 @@ from multiprocessing import Process from leap.bitmask.core.launcher import run_bitmaskd, pid from leap.bitmask.gui import app_rc from leap.common.config import get_path_prefix +from leap.common.events import client as leap_events +from leap.common.events import catalog if platform.system() == 'Windows': from multiprocessing import freeze_support @@ -48,6 +50,7 @@ else: from PyQt5.QtCore import QObject, pyqtSlot from PyQt5.QtWidgets import QApplication from PyQt5.QtGui import QIcon + from PyQt5.QtGui import QPixmap from PyQt5.QtWidgets import QMenu from PyQt5.QtWidgets import QSystemTrayIcon from PyQt5.QtWidgets import QDialog @@ -70,10 +73,49 @@ qApp = None bitmaskd = None browser = None +# TODO do switch based on theme + +TRAY_ICONS = ( + ':/black/22/wait.png', + ':/black/22/on.png', + ':/black/22/off.png') class WithTrayIcon(QDialog): - def createTrayIcon(self): + def setupSysTray(self): + self._createIcons() + self._createTrayIcon() + self.setVPNStatus('off') + self.trayIcon.show() + self.setUpEventListener() + + def setVPNStatus(self, status): + seticon = self.trayIcon.setIcon + if status == 'off': + seticon(self.ICON_OFF) + elif status == 'on': + seticon(self.ICON_ON) + elif status == 'starting': + seticon(self.ICON_WAIT) + elif status == 'stopping': + seticon(self.ICON_WAIT) + + def setUpEventListener(self): + leap_events.register(catalog.VPN_STATUS_CHANGED, self._handle_vpn_event) + + def _handle_vpn_event(self, *args): + print ">>>>>> GOT EVENT", str(args) + status = None + if len(args) > 1: + status = args[1] + self.setVPNStatus(status.lower()) + + def _createIcons(self): + self.ICON_WAIT = QIcon(QPixmap(TRAY_ICONS[0])) + self.ICON_ON = QIcon(QPixmap(TRAY_ICONS[1])) + self.ICON_OFF = QIcon(QPixmap(TRAY_ICONS[2])) + + def _createTrayIcon(self): self.trayIcon = QSystemTrayIcon(self) @@ -227,8 +269,7 @@ def launch_gui(): print('ERROR: ' + e.message) sys.exit(1) - browser.createTrayIcon() - browser.trayIcon.show() + browser.setupSysTray() qApp.setQuitOnLastWindowClosed(True) qApp.lastWindowClosed.connect(browser.shutdown) diff --git a/src/leap/bitmask/vpn/_status.py b/src/leap/bitmask/vpn/_status.py index 5cf0cc77..4bf8d004 100644 --- a/src/leap/bitmask/vpn/_status.py +++ b/src/leap/bitmask/vpn/_status.py @@ -80,7 +80,7 @@ class VPNStatus(object): if self._status != status: self._status = status self.errcode = errcode - emit_async(catalog.VPN_STATUS_CHANGED) + emit_async(catalog.VPN_STATUS_CHANGED, status.upper()) def get_traffic_status(self): down = None diff --git a/src/leap/bitmask/vpn/fw/firewall.py b/src/leap/bitmask/vpn/fw/firewall.py index 63aac36e..d5b1f018 100644 --- a/src/leap/bitmask/vpn/fw/firewall.py +++ b/src/leap/bitmask/vpn/fw/firewall.py @@ -134,7 +134,7 @@ class _LinuxFirewallManager(object): raise FirewallError(msg) finally: log.debug(result) - emit_async(catalog.VPN_STATUS_CHANGED) + emit_async(catalog.VPN_STATUS_CHANGED, "FW_ON") return True def stop(self): @@ -144,7 +144,7 @@ class _LinuxFirewallManager(object): cmd = [self.BITMASK_ROOT, "firewall", "stop"] cmd = check_root(cmd) exitCode = subprocess.call(cmd) - emit_async(catalog.VPN_STATUS_CHANGED) + emit_async(catalog.VPN_STATUS_CHANGED, "FW_OFF") if exitCode == 0: return True else: diff --git a/src/leap/bitmask/vpn/process.py b/src/leap/bitmask/vpn/process.py index 7f33e83a..862215f8 100644 --- a/src/leap/bitmask/vpn/process.py +++ b/src/leap/bitmask/vpn/process.py @@ -50,7 +50,7 @@ class VPNStateListener(object): # and make VPNProcess the implementer itself - or the service. def change_state(self, state): - emit_async(catalog.VPN_STATUS_CHANGED) + emit_async(catalog.VPN_STATUS_CHANGED, state.simple) class _VPNProcess(protocol.ProcessProtocol): @@ -109,6 +109,7 @@ class _VPNProcess(protocol.ProcessProtocol): self.errmsg = None self.proto = None self._remotes = remotes + self._listener = VPNStateListener() # processProtocol methods @@ -120,8 +121,7 @@ class _VPNProcess(protocol.ProcessProtocol): @defer.inlineCallbacks def _got_management_protocol(self, proto): self.proto = proto - listener = VPNStateListener() - proto.addStateListener(listener) + proto.addStateListener(self._listener) try: yield proto.logOn() @@ -134,7 +134,7 @@ class _VPNProcess(protocol.ProcessProtocol): def _connect_to_management(self, retries=30): if retries == 0: - self.log.error('Timeout whilte connecting to management') + self.log.error('Timeout while connecting to management') self.failed = True return @@ -213,8 +213,23 @@ class _VPNProcess(protocol.ProcessProtocol): def status(self): if self.failed: return {'status': 'failed', 'error': self.errmsg} + + # FIXME - hack, doesn't belong here ---------------------------------- + # needs to go with implementing the history within the VPNProcess. + # this only will work before the UI is pulling the state and therefore + # checking this condition as a side-effect of reading the property. + + # TODO trigger off transition when proto dissapears (at processExited) if not self.proto: - return {'status': 'off', 'error': None} + OFF = 'off' + if self._status != OFF: + self._status = OFF + class _state(object): + simple = 'OFF' + off = _state() + self._listener.change_state(off) + return {'status': OFF, 'error': None} + # -------------------------------------------------------------------- try: self._status = self.proto.state.simple.lower() diff --git a/src/leap/bitmask/vpn/tunnel.py b/src/leap/bitmask/vpn/tunnel.py index b49135df..f6080b85 100644 --- a/src/leap/bitmask/vpn/tunnel.py +++ b/src/leap/bitmask/vpn/tunnel.py @@ -92,8 +92,7 @@ class ConfiguredTunnel(object): else: status = self._vpnproc.status # Currently, there's some UI flickering that needs to be debugged #9049 - # XXX remove this print after that. - print ">>>STATUS", status + print ">>>> STATUS", status return status @property -- cgit v1.2.3