aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/tray.go
blob: e1fc65e19f8a4fd9c554ab2d37f6dfcef81092f0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/* SPDX-License-Identifier: MIT
 *
 * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
 */

package ui

import (
	"fmt"
	"sort"

	"github.com/lxn/walk"
	"golang.zx2c4.com/wireguard/windows/service"
)

type Tray struct {
	*walk.NotifyIcon

	// The action that displays the current active CIDRs
	// May be nil if no tunnels are active currently
	activeCIDRs *walk.Action

	// Current known tunnels by name
	tunnels map[string]*walk.Action

	parent *ManageTunnelsWindow
	icon   *walk.Icon
}

func NewTray(parent *ManageTunnelsWindow, icon *walk.Icon) (*Tray, error) {
	var err error

	tray := &Tray{
		parent:  parent,
		icon:    icon,
		tunnels: make(map[string]*walk.Action),
	}
	tray.NotifyIcon, err = walk.NewNotifyIcon(parent.MainWindow)
	if err != nil {
		return nil, err
	}

	return tray, tray.setup()
}

func (tray *Tray) setup() error {
	tray.SetToolTip("WireGuard: Deactivated")
	tray.SetVisible(true)
	tray.SetIcon(tray.icon)

	tray.MouseDown().Attach(func(x, y int, button walk.MouseButton) {
		if button == walk.LeftButton {
			tray.parent.Show()
		}
	})

	// configure initial menu items
	for _, item := range [...]struct {
		label     string
		handler   walk.EventHandler
		enabled   bool
		separator bool
	}{
		{label: "Status: unknown"},
		{separator: true},
		{separator: true},
		{label: "&Manage tunnels...", handler: tray.parent.Show, enabled: true},
		{label: "&Import tunnel(s) from file...", handler: tray.parent.onImport, enabled: true},
		{separator: true},
		{label: "&About WireGuard", handler: onAbout, enabled: true},
		{label: "&Quit", handler: onQuit, enabled: true},
	} {
		var action *walk.Action
		if item.separator {
			action = walk.NewSeparatorAction()
		} else {
			action = walk.NewAction()
			action.SetText(item.label)
			action.SetEnabled(item.enabled)
			if item.handler != nil {
				action.Triggered().Attach(item.handler)
			}
		}

		tray.ContextMenu().Actions().Add(action)
	}

	return nil
}

func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelState) {
	tray.SetTunnelStateWithNotification(tunnel, state, true)
}

func (tray *Tray) SetTunnelStateWithNotification(tunnel *service.Tunnel, state service.TunnelState, showNotifications bool) {
	action, ok := tray.tunnels[tunnel.Name]
	if !ok {
		// First time seeing this tunnel, create a new action
		action = walk.NewAction()
		action.SetText(tunnel.Name)
		action.SetEnabled(true)
		action.SetCheckable(true)
		// TODO: Wire up the click event
		tray.tunnels[tunnel.Name] = action

		// Add the action at the right spot
		var names []string
		for name, _ := range tray.tunnels {
			names = append(names, name)
		}
		sort.Strings(names)

		var (
			idx  int
			name string
		)
		for idx, name = range names {
			if name == tunnel.Name {
				break
			}
		}

		// Status action + separator action
		offset := 2
		if tray.activeCIDRs != nil {
			offset++
		}

		tray.ContextMenu().Actions().Insert(idx+offset, action)
	}

	// TODO: No event for deleting a tunnel?

	//TODO: also set tray icon to reflect state
	switch state {
	case service.TunnelStarting:
		action.SetEnabled(false)

		tray.SetToolTip("WireGuard: Activating...")
	case service.TunnelStarted:
		// TODO: Set the activeCIDRs

		action.SetEnabled(true)
		action.SetChecked(true)

		tray.SetToolTip("WireGuard: Activated")
		if showNotifications {
			tray.ShowInfo("WireGuard Activated", fmt.Sprintf("The %s tunnel has been activated.", tunnel.Name))
		}
	case service.TunnelStopping:
		action.SetEnabled(false)

		tray.SetToolTip("WireGuard: Deactivating...")
	case service.TunnelStopped:
		action.SetEnabled(true)
		action.SetChecked(false)

		tray.SetToolTip("WireGuard: Deactivated")
		if showNotifications {
			tray.ShowInfo("WireGuard Deactivated", fmt.Sprintf("The %s tunnel has been deactivated.", tunnel.Name))
		}
	}
}