aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2020-11-11 14:43:48 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2020-11-11 15:01:50 +0100
commitddd7d446b2b202d37edb51442aedb78e622b71e2 (patch)
tree32878a96004591dd05534a7c86ca1b70c1c4d924
parentconf: move configuration to C:\ProgramData\WireGuard (diff)
downloadwireguard-windows-ddd7d446b2b202d37edb51442aedb78e622b71e2.tar.xz
wireguard-windows-ddd7d446b2b202d37edb51442aedb78e622b71e2.zip
ui: remove systray popup menu tunnel list
Should user have a huge list of tunnels, the menu becomes impossible to navigate. In the absence of any better idea, how to make the popup menu usable in such tunnel quantities, the tunnel activation via systray popup menu was removed, and instead we have a single deactivate button. Signed-off-by: Simon Rozman <simon@rozman.si> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--ui/tray.go233
1 files changed, 55 insertions, 178 deletions
diff --git a/ui/tray.go b/ui/tray.go
index f1fb727e..37c3af25 100644
--- a/ui/tray.go
+++ b/ui/tray.go
@@ -6,41 +6,26 @@
package ui
import (
- "sort"
"strings"
"time"
- "golang.zx2c4.com/wireguard/windows/conf"
"golang.zx2c4.com/wireguard/windows/l18n"
"golang.zx2c4.com/wireguard/windows/manager"
"github.com/lxn/walk"
)
-// Status + active CIDRs + separator
-const trayTunnelActionsOffset = 3
-
type Tray struct {
*walk.NotifyIcon
-
- // Current known tunnels by name
- tunnels map[string]*walk.Action
-
- mtw *ManageTunnelsWindow
-
- tunnelChangedCB *manager.TunnelChangeCallback
- tunnelsChangedCB *manager.TunnelsChangeCallback
-
- clicked func()
+ mtw *ManageTunnelsWindow
+ tunnelChangedCB *manager.TunnelChangeCallback
+ clicked func()
}
func NewTray(mtw *ManageTunnelsWindow) (*Tray, error) {
var err error
- tray := &Tray{
- mtw: mtw,
- tunnels: make(map[string]*walk.Action),
- }
+ tray := &Tray{mtw: mtw}
tray.NotifyIcon, err = walk.NewNotifyIcon(mtw)
if err != nil {
@@ -78,6 +63,7 @@ func (tray *Tray) setup() error {
}{
{label: l18n.Sprintf("Status: Unknown")},
{label: l18n.Sprintf("Addresses: None"), hidden: true},
+ {label: l18n.Sprintf("&Deactivate"), handler: tray.onDeactivateTunnel, enabled: true, hidden: true},
{separator: true},
{separator: true},
{label: l18n.Sprintf("&Manage tunnels…"), handler: tray.onManageTunnels, enabled: true, defawlt: true},
@@ -103,8 +89,6 @@ func (tray *Tray) setup() error {
tray.ContextMenu().Actions().Add(action)
}
tray.tunnelChangedCB = manager.IPCClientRegisterTunnelChange(tray.onTunnelChange)
- tray.tunnelsChangedCB = manager.IPCClientRegisterTunnelsChange(tray.onTunnelsChange)
- tray.onTunnelsChange()
globalState, _ := manager.IPCClientGlobalState()
tray.updateGlobalState(globalState)
@@ -116,103 +100,23 @@ func (tray *Tray) Dispose() error {
tray.tunnelChangedCB.Unregister()
tray.tunnelChangedCB = nil
}
- if tray.tunnelsChangedCB != nil {
- tray.tunnelsChangedCB.Unregister()
- tray.tunnelsChangedCB = nil
- }
return tray.NotifyIcon.Dispose()
}
-func (tray *Tray) onTunnelsChange() {
- tunnels, err := manager.IPCClientTunnels()
- if err != nil {
- return
- }
- tray.mtw.Synchronize(func() {
- tunnelSet := make(map[string]bool, len(tunnels))
- for _, tunnel := range tunnels {
- tunnelSet[tunnel.Name] = true
- if tray.tunnels[tunnel.Name] == nil {
- tray.addTunnelAction(&tunnel)
- }
- }
- for trayTunnel := range tray.tunnels {
- if !tunnelSet[trayTunnel] {
- tray.removeTunnelAction(trayTunnel)
- }
- }
- })
-}
-
-func (tray *Tray) addTunnelAction(tunnel *manager.Tunnel) {
- tunnelAction := walk.NewAction()
- tunnelAction.SetText(tunnel.Name)
- tunnelAction.SetEnabled(true)
- tunnelAction.SetCheckable(true)
- tclosure := *tunnel
- tunnelAction.Triggered().Attach(func() {
- tunnelAction.SetChecked(!tunnelAction.Checked())
- go func() {
- oldState, err := tclosure.Toggle()
- if err != nil {
- tray.mtw.Synchronize(func() {
- raise(tray.mtw.Handle())
- tray.mtw.tunnelsPage.listView.selectTunnel(tclosure.Name)
- tray.mtw.tabs.SetCurrentIndex(0)
- if oldState == manager.TunnelUnknown {
- showErrorCustom(tray.mtw, l18n.Sprintf("Failed to determine tunnel state"), err.Error())
- } else if oldState == manager.TunnelStopped {
- showErrorCustom(tray.mtw, l18n.Sprintf("Failed to activate tunnel"), err.Error())
- } else if oldState == manager.TunnelStarted {
- showErrorCustom(tray.mtw, l18n.Sprintf("Failed to deactivate tunnel"), err.Error())
- }
- })
- }
- }()
- })
- tray.tunnels[tunnel.Name] = tunnelAction
-
- var names []string
- for name := range tray.tunnels {
- names = append(names, name)
- }
- sort.SliceStable(names, func(i, j int) bool {
- return conf.TunnelNameIsLess(names[i], names[j])
- })
-
- var (
- idx int
- name string
- )
- for idx, name = range names {
- if name == tunnel.Name {
- break
- }
- }
-
- tray.ContextMenu().Actions().Insert(trayTunnelActionsOffset+idx, tunnelAction)
-
- go func() {
- state, err := tclosure.State()
- if err != nil {
- return
- }
- tray.mtw.Synchronize(func() {
- tray.SetTunnelState(&tclosure, state, false)
- })
- }()
-}
-
-func (tray *Tray) removeTunnelAction(tunnelName string) {
- tray.ContextMenu().Actions().Remove(tray.tunnels[tunnelName])
- delete(tray.tunnels, tunnelName)
-}
-
func (tray *Tray) onTunnelChange(tunnel *manager.Tunnel, state manager.TunnelState, globalState manager.TunnelState, err error) {
tray.mtw.Synchronize(func() {
tray.updateGlobalState(globalState)
- tray.SetTunnelState(tunnel, state, err == nil)
- if !tray.mtw.Visible() && err != nil {
+ if err == nil {
+ switch state {
+ case manager.TunnelStarted:
+ icon, _ := iconWithOverlayForState(state, 128)
+ tray.ShowCustom(l18n.Sprintf("WireGuard Activated"), l18n.Sprintf("The %s tunnel has been activated.", tunnel.Name), icon)
+
+ case manager.TunnelStopped:
+ icon, _ := loadSystemIcon("imageres", 26, 128) // TODO: this icon isn't very good...
+ tray.ShowCustom(l18n.Sprintf("WireGuard Deactivated"), l18n.Sprintf("The %s tunnel has been deactivated.", tunnel.Name), icon)
+ }
+ } else if !tray.mtw.Visible() {
tray.ShowError(l18n.Sprintf("WireGuard Tunnel Error"), err.Error())
}
})
@@ -225,80 +129,34 @@ func (tray *Tray) updateGlobalState(globalState manager.TunnelState) {
actions := tray.ContextMenu().Actions()
statusAction := actions.At(0)
- activeCIDRsAction := actions.At(1)
-
- setTunnelActionsEnabled := func(enabled bool) {
- for i := 0; i < len(tray.tunnels); i++ {
- action := actions.At(trayTunnelActionsOffset + i)
- action.SetEnabled(enabled)
- }
- }
+ deactivateTunnelAction := actions.At(2)
tray.SetToolTip(l18n.Sprintf("WireGuard: %s", textForState(globalState, true)))
stateText := textForState(globalState, false)
statusAction.SetText(l18n.Sprintf("Status: %s", stateText))
-
- switch globalState {
- case manager.TunnelStarting:
- setTunnelActionsEnabled(false)
-
- case manager.TunnelStarted:
- activeCIDRsAction.SetVisible(true)
- setTunnelActionsEnabled(true)
-
- case manager.TunnelStopping:
- setTunnelActionsEnabled(false)
-
- case manager.TunnelStopped:
- activeCIDRsAction.SetVisible(false)
- setTunnelActionsEnabled(true)
- }
-}
-
-func (tray *Tray) SetTunnelState(tunnel *manager.Tunnel, state manager.TunnelState, showNotifications bool) {
- tunnelAction := tray.tunnels[tunnel.Name]
- if tunnelAction == nil {
- return
- }
-
- actions := tray.ContextMenu().Actions()
- activeCIDRsAction := actions.At(1)
-
- wasChecked := tunnelAction.Checked()
-
- switch state {
- case manager.TunnelStarted:
- activeCIDRsAction.SetText("")
- go func() {
- config, err := tunnel.RuntimeConfig()
- if err == nil {
- var sb strings.Builder
- for i, addr := range config.Interface.Addresses {
- if i > 0 {
- sb.WriteString(l18n.EnumerationSeparator())
+ deactivateTunnelAction.SetVisible(globalState == manager.TunnelStarted)
+ go func() {
+ var addrs []string
+ tunnels, err := manager.IPCClientTunnels()
+ if err == nil {
+ for i := range tunnels {
+ state, err := tunnels[i].State()
+ if err == nil && state == manager.TunnelStarted {
+ config, err := tunnels[i].RuntimeConfig()
+ if err == nil {
+ for _, addr := range config.Interface.Addresses {
+ addrs = append(addrs, addr.String())
+ }
}
-
- sb.WriteString(addr.String())
}
- tray.mtw.Synchronize(func() {
- activeCIDRsAction.SetText(l18n.Sprintf("Addresses: %s", sb.String()))
- })
}
- }()
- tunnelAction.SetEnabled(true)
- tunnelAction.SetChecked(true)
- if !wasChecked && showNotifications {
- icon, _ := iconWithOverlayForState(state, 128)
- tray.ShowCustom(l18n.Sprintf("WireGuard Activated"), l18n.Sprintf("The %s tunnel has been activated.", tunnel.Name), icon)
}
-
- case manager.TunnelStopped:
- tunnelAction.SetChecked(false)
- if wasChecked && showNotifications {
- icon, _ := loadSystemIcon("imageres", 26, 128) // TODO: this icon isn't very good...
- tray.ShowCustom(l18n.Sprintf("WireGuard Deactivated"), l18n.Sprintf("The %s tunnel has been deactivated.", tunnel.Name), icon)
- }
- }
+ tray.mtw.Synchronize(func() {
+ activeCIDRsAction := tray.ContextMenu().Actions().At(1)
+ activeCIDRsAction.SetText(l18n.Sprintf("Addresses: %s", strings.Join(addrs, l18n.EnumerationSeparator())))
+ activeCIDRsAction.SetVisible(len(addrs) > 0)
+ })
+ }()
}
func (tray *Tray) UpdateFound() {
@@ -333,6 +191,25 @@ func (tray *Tray) UpdateFound() {
}
}
+func (tray *Tray) onDeactivateTunnel() {
+ go func() {
+ tunnels, err := manager.IPCClientTunnels()
+ if err == nil {
+ for i := range tunnels {
+ state, err := tunnels[i].State()
+ if err == nil && state != manager.TunnelStopped {
+ err = tunnels[i].Stop()
+ }
+ }
+ }
+ if err != nil {
+ tray.mtw.Synchronize(func() {
+ showErrorCustom(tray.mtw, l18n.Sprintf("Failed to deactivate tunnel"), err.Error())
+ })
+ }
+ }()
+}
+
func (tray *Tray) onManageTunnels() {
tray.mtw.tunnelsPage.listView.SelectFirstActiveTunnel()
tray.mtw.tabs.SetCurrentIndex(0)