diff options
Diffstat (limited to 'pkg/systray/systray.go')
-rw-r--r-- | pkg/systray/systray.go | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/pkg/systray/systray.go b/pkg/systray/systray.go new file mode 100644 index 0000000..3505958 --- /dev/null +++ b/pkg/systray/systray.go @@ -0,0 +1,257 @@ +// Copyright (C) 2018 LEAP +// +// 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 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. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package systray + +import ( + "fmt" + "log" + "os" + "os/signal" + "time" + + "0xacab.org/leap/bitmask-systray/icon" + "0xacab.org/leap/bitmask-systray/pkg/bitmask" + "github.com/getlantern/systray" + "github.com/skratchdot/open-golang/open" +) + +type bmTray struct { + bm bitmask.Bitmask + conf *SystrayConfig + notify *notificator + waitCh chan bool + mStatus *systray.MenuItem + mTurnOn *systray.MenuItem + mTurnOff *systray.MenuItem + mHelp *systray.MenuItem + mDonate *systray.MenuItem + mAbout *systray.MenuItem + mQuit *systray.MenuItem + activeGateway *gatewayTray + autostart bitmask.Autostart +} + +type gatewayTray struct { + menuItem *systray.MenuItem + name string +} + +func (bt *bmTray) start() { + // XXX this removes the snap error message, but produces an invisible icon. + // https://0xacab.org/leap/riseup_vpn/issues/44 + // os.Setenv("TMPDIR", "/var/tmp") + systray.Run(bt.onReady, bt.onExit) +} + +func (bt *bmTray) onExit() { + log.Println("Closing systray") +} + +func (bt *bmTray) onReady() { + printer := bt.conf.Printer + systray.SetIcon(icon.Off) + + bt.mStatus = systray.AddMenuItem(printer.Sprintf("Checking status..."), "") + bt.mStatus.Disable() + bt.mTurnOn = systray.AddMenuItem(printer.Sprintf("Turn on"), "") + bt.mTurnOn.Hide() + bt.mTurnOff = systray.AddMenuItem(printer.Sprintf("Turn off"), "") + bt.mTurnOff.Hide() + systray.AddSeparator() + + if bt.conf.SelectGateway { + bt.addGateways() + } + + bt.mHelp = systray.AddMenuItem(printer.Sprintf("Help..."), "") + bt.mDonate = systray.AddMenuItem(printer.Sprintf("Donate..."), "") + bt.mAbout = systray.AddMenuItem(printer.Sprintf("About..."), "") + systray.AddSeparator() + + bt.mQuit = systray.AddMenuItem(printer.Sprintf("Quit"), "") +} + +func (bt *bmTray) loop(bm bitmask.Bitmask, notify *notificator, as bitmask.Autostart) { + bt.bm = bm + bt.notify = notify + bt.autostart = as + + signalCh := make(chan os.Signal, 1) + signal.Notify(signalCh, os.Interrupt) + + ch := bt.bm.GetStatusCh() + if status, err := bt.bm.GetStatus(); err != nil { + log.Printf("Error getting status: %v", err) + } else { + bt.changeStatus(status) + } + + for { + select { + case status := <-ch: + log.Println("status: " + status) + bt.changeStatus(status) + + case <-bt.mTurnOn.ClickedCh: + log.Println("on") + bt.changeStatus("starting") + bt.bm.StartVPN(bt.conf.Provider) + bt.conf.setUserStoppedVPN(false) + case <-bt.mTurnOff.ClickedCh: + log.Println("off") + bt.changeStatus("stopping") + bt.bm.StopVPN() + bt.conf.setUserStoppedVPN(true) + + case <-bt.mHelp.ClickedCh: + open.Run("https://riseup.net/vpn/support") + case <-bt.mDonate.ClickedCh: + bt.conf.setDonated() + open.Run("https://riseup.net/vpn/donate") + case <-bt.mAbout.ClickedCh: + bitmaskVersion, err := bt.bm.Version() + versionStr := bt.conf.Version + if err != nil { + log.Printf("Error getting version: %v", err) + } else if bitmaskVersion != "" { + versionStr = fmt.Sprintf("%s (bitmaskd %s)", bt.conf.Version, bitmaskVersion) + } + bt.notify.about(versionStr) + + case <-bt.mQuit.ClickedCh: + err := bt.autostart.Disable() + if err != nil { + log.Printf("Error disabling autostart: %v", err) + } + systray.Quit() + return + case <-signalCh: + systray.Quit() + return + + case <-time.After(5 * time.Second): + if status, err := bt.bm.GetStatus(); err != nil { + log.Printf("Error getting status: %v", err) + } else { + bt.changeStatus(status) + } + } + } +} + +func (bt *bmTray) addGateways() { + gatewayList, err := bt.bm.ListGateways(bt.conf.Provider) + if err != nil { + log.Printf("Gateway initialization error: %v", err) + return + } + + mGateway := systray.AddMenuItem(bt.conf.Printer.Sprintf("Route traffic through"), "") + mGateway.Disable() + for i, city := range gatewayList { + menuItem := systray.AddMenuItem(city, bt.conf.Printer.Sprintf("Use %s %v gateway", bt.conf.ApplicationName, city)) + gateway := gatewayTray{menuItem, city} + + if i == 0 { + menuItem.Check() + menuItem.SetTitle("*" + city) + bt.activeGateway = &gateway + } else { + menuItem.Uncheck() + } + + go func(gateway gatewayTray) { + for { + <-menuItem.ClickedCh + gateway.menuItem.SetTitle("*" + gateway.name) + gateway.menuItem.Check() + + bt.activeGateway.menuItem.Uncheck() + bt.activeGateway.menuItem.SetTitle(bt.activeGateway.name) + bt.activeGateway = &gateway + + bt.bm.UseGateway(gateway.name) + } + }(gateway) + } + + systray.AddSeparator() +} + +func (bt *bmTray) changeStatus(status string) { + printer := bt.conf.Printer + if bt.waitCh != nil { + bt.waitCh <- true + bt.waitCh = nil + } + + var statusStr string + switch status { + case "on": + systray.SetIcon(icon.On) + bt.mTurnOff.SetTitle(printer.Sprintf("Turn off")) + statusStr = printer.Sprintf("%s on", bt.conf.ApplicationName) + bt.mTurnOn.Hide() + bt.mTurnOff.Show() + + case "off": + systray.SetIcon(icon.Off) + bt.mTurnOn.SetTitle(printer.Sprintf("Turn on")) + statusStr = printer.Sprintf("%s off", bt.conf.ApplicationName) + bt.mTurnOn.Show() + bt.mTurnOff.Hide() + + case "starting": + bt.waitCh = make(chan bool) + go bt.waitIcon() + bt.mTurnOff.SetTitle(printer.Sprintf("Cancel")) + statusStr = printer.Sprintf("Connecting to %s", bt.conf.ApplicationName) + bt.mTurnOn.Hide() + bt.mTurnOff.Show() + + case "stopping": + bt.waitCh = make(chan bool) + go bt.waitIcon() + statusStr = printer.Sprintf("Stopping %s", bt.conf.ApplicationName) + bt.mTurnOn.Hide() + bt.mTurnOff.Hide() + + case "failed": + systray.SetIcon(icon.Blocked) + bt.mTurnOn.SetTitle(printer.Sprintf("Retry")) + bt.mTurnOff.SetTitle(printer.Sprintf("Turn off")) + statusStr = printer.Sprintf("%s blocking internet", bt.conf.ApplicationName) + bt.mTurnOn.Show() + bt.mTurnOff.Show() + } + + systray.SetTooltip(statusStr) + bt.mStatus.SetTitle(statusStr) +} + +func (bt *bmTray) waitIcon() { + icons := [][]byte{icon.Wait0, icon.Wait1, icon.Wait2, icon.Wait3} + for i := 0; true; i = (i + 1) % 4 { + systray.SetIcon(icons[i]) + + select { + case <-bt.waitCh: + return + case <-time.After(time.Millisecond * 500): + continue + } + } +} |