diff options
-rw-r--r-- | ui/tray.go | 233 |
1 files changed, 55 insertions, 178 deletions
@@ -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) |