aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ui/confview.go55
-rw-r--r--ui/iconprovider.go (renamed from ui/tunnelstatusimageprovider.go)88
-rw-r--r--ui/logpage.go80
-rw-r--r--ui/managewindow.go (renamed from ui/manage_tunnels.go)59
-rw-r--r--ui/tray.go173
-rw-r--r--ui/tunnelspage.go102
-rw-r--r--ui/tunnelsview.go92
-rw-r--r--ui/tunneltracker.go99
-rw-r--r--ui/ui.go56
9 files changed, 347 insertions, 457 deletions
diff --git a/ui/confview.go b/ui/confview.go
index aa939945..5dd5c626 100644
--- a/ui/confview.go
+++ b/ui/confview.go
@@ -31,7 +31,6 @@ type labelStatusLine struct {
statusComposite *walk.Composite
statusImage *walk.ImageView
statusLabel *walk.TextLabel
- imageProvider *TunnelStatusImageProvider
}
type labelTextLine struct {
@@ -40,9 +39,8 @@ type labelTextLine struct {
}
type toggleActiveLine struct {
- composite *walk.Composite
- button *walk.PushButton
- tunnelTracker *TunnelTracker
+ composite *walk.Composite
+ button *walk.PushButton
}
type interfaceView struct {
@@ -77,20 +75,15 @@ type ConfView struct {
tunnel *service.Tunnel
}
-func (lsl *labelStatusLine) Dispose() {
- if lsl.imageProvider != nil {
- lsl.imageProvider.Dispose()
- lsl.imageProvider = nil
- }
-}
-
func (lsl *labelStatusLine) widgets() (walk.Widget, walk.Widget) {
return lsl.label, lsl.statusComposite
}
func (lsl *labelStatusLine) update(state service.TunnelState) {
- img, _ := lsl.imageProvider.ImageForState(state, walk.Size{statusImageSize, statusImageSize})
- lsl.statusImage.SetImage(img)
+ img, err := iconProvider.ImageForState(state, walk.Size{statusImageSize, statusImageSize})
+ if err == nil {
+ lsl.statusImage.SetImage(img)
+ }
switch state {
case service.TunnelStarted:
@@ -109,7 +102,6 @@ func (lsl *labelStatusLine) update(state service.TunnelState) {
func newLabelStatusLine(parent walk.Container) *labelStatusLine {
lsl := new(labelStatusLine)
- parent.AddDisposable(lsl)
lsl.label, _ = walk.NewTextLabel(parent)
lsl.label.SetText("Status:")
@@ -120,7 +112,6 @@ func newLabelStatusLine(parent walk.Container) *labelStatusLine {
layout.SetMargins(walk.Margins{})
lsl.statusComposite.SetLayout(layout)
- lsl.imageProvider, _ = NewTunnelStatusImageProvider()
lsl.statusImage, _ = walk.NewImageView(lsl.statusComposite)
lsl.statusLabel, _ = walk.NewTextLabel(lsl.statusComposite)
lsl.statusLabel.SetTextAlignment(walk.AlignHNearVCenter)
@@ -192,10 +183,6 @@ func (tal *toggleActiveLine) update(state service.TunnelState) {
enabled, text = false, ""
}
- if tt := tal.tunnelTracker; tt != nil && tt.InTransition() {
- enabled = false
- }
-
tal.button.SetEnabled(enabled)
tal.button.SetText(text)
tal.button.SetVisible(state != service.TunnelUnknown)
@@ -416,32 +403,18 @@ func (cv *ConfView) Dispose() {
cv.ScrollView.Dispose()
}
-func (cv *ConfView) TunnelTracker() *TunnelTracker {
- return cv.interfaze.toggleActive.tunnelTracker
-}
-
-func (cv *ConfView) SetTunnelTracker(tunnelTracker *TunnelTracker) {
- cv.interfaze.toggleActive.tunnelTracker = tunnelTracker
-}
-
func (cv *ConfView) onToggleActiveClicked() {
cv.interfaze.toggleActive.button.SetEnabled(false)
-
- var title string
- var err error
- tt := cv.TunnelTracker()
- if activeTunnel := tt.ActiveTunnel(); activeTunnel != nil && activeTunnel.Name == cv.tunnel.Name {
- title = "Failed to deactivate tunnel"
- err = tt.DeactivateTunnel()
- } else {
- title = "Failed to activate tunnel"
- err = tt.ActivateTunnel(cv.tunnel)
- }
+ oldState, err := cv.tunnel.Toggle()
if err != nil {
- walk.MsgBox(cv.Form(), title, err.Error(), walk.MsgBoxIconError)
- return
+ if oldState == service.TunnelUnknown {
+ walk.MsgBox(cv.Form(), "Failed to determine tunnel state", err.Error(), walk.MsgBoxIconError)
+ } else if oldState == service.TunnelStopped {
+ walk.MsgBox(cv.Form(), "Failed to activate tunnel", err.Error(), walk.MsgBoxIconError)
+ } else if oldState == service.TunnelStarted {
+ walk.MsgBox(cv.Form(), "Failed to deactivate tunnel", err.Error(), walk.MsgBoxIconError)
+ }
}
-
cv.SetTunnel(cv.tunnel)
}
diff --git a/ui/tunnelstatusimageprovider.go b/ui/iconprovider.go
index 1120ecea..da88e830 100644
--- a/ui/tunnelstatusimageprovider.go
+++ b/ui/iconprovider.go
@@ -16,20 +16,16 @@ type sizeAndState struct {
state service.TunnelState
}
-type iconAndState struct {
- icon *walk.Icon
- state service.TunnelState
-}
-
-type TunnelStatusImageProvider struct {
- imagesBySizeAndState map[sizeAndState]*walk.Bitmap
- iconsByBaseIconAndState map[iconAndState]*walk.Icon
- stoppedBrush *walk.SolidColorBrush
- startingBrush *walk.SolidColorBrush
- startedBrush *walk.SolidColorBrush
- stoppedPen *walk.CosmeticPen
- startingPen *walk.CosmeticPen
- startedPen *walk.CosmeticPen
+type IconProvider struct {
+ baseIcon *walk.Icon
+ imagesBySizeAndState map[sizeAndState]*walk.Bitmap
+ iconsByState map[service.TunnelState]*walk.Icon
+ stoppedBrush *walk.SolidColorBrush
+ startingBrush *walk.SolidColorBrush
+ startedBrush *walk.SolidColorBrush
+ stoppedPen *walk.CosmeticPen
+ startingPen *walk.CosmeticPen
+ startedPen *walk.CosmeticPen
}
const (
@@ -110,10 +106,10 @@ func darkColor(c walk.Color) walk.Color {
return walk.Color((uint32(r*255) << 16) | (uint32(g*255) << 8) | (uint32(b*255) << 0))
}
-func NewTunnelStatusImageProvider() (*TunnelStatusImageProvider, error) {
- tsip := &TunnelStatusImageProvider{
- imagesBySizeAndState: make(map[sizeAndState]*walk.Bitmap),
- iconsByBaseIconAndState: make(map[iconAndState]*walk.Icon),
+func NewIconProvider() (*IconProvider, error) {
+ tsip := &IconProvider{
+ imagesBySizeAndState: make(map[sizeAndState]*walk.Bitmap),
+ iconsByState: make(map[service.TunnelState]*walk.Icon),
}
var err error
@@ -121,6 +117,11 @@ func NewTunnelStatusImageProvider() (*TunnelStatusImageProvider, error) {
var disposables walk.Disposables
defer disposables.Treat()
+ if tsip.baseIcon, err = walk.NewIconFromResourceId(1); err != nil {
+ return nil, err
+ }
+ disposables.Add(tsip.baseIcon)
+
if tsip.stoppedBrush, err = walk.NewSolidColorBrush(hexColor(colorStopped)); err != nil {
return nil, err
}
@@ -156,18 +157,18 @@ func NewTunnelStatusImageProvider() (*TunnelStatusImageProvider, error) {
return tsip, nil
}
-func (tsip *TunnelStatusImageProvider) Dispose() {
+func (tsip *IconProvider) Dispose() {
if tsip.imagesBySizeAndState != nil {
for _, img := range tsip.imagesBySizeAndState {
img.Dispose()
}
tsip.imagesBySizeAndState = nil
}
- if tsip.iconsByBaseIconAndState != nil {
- for _, icon := range tsip.iconsByBaseIconAndState {
+ if tsip.iconsByState != nil {
+ for _, icon := range tsip.iconsByState {
icon.Dispose()
}
- tsip.iconsByBaseIconAndState = nil
+ tsip.iconsByState = nil
}
if tsip.stoppedBrush != nil {
tsip.stoppedBrush.Dispose()
@@ -193,9 +194,13 @@ func (tsip *TunnelStatusImageProvider) Dispose() {
tsip.startedPen.Dispose()
tsip.startedPen = nil
}
+ if tsip.baseIcon != nil {
+ tsip.baseIcon.Dispose()
+ tsip.baseIcon = nil
+ }
}
-func (tsip *TunnelStatusImageProvider) ImageForTunnel(tunnel *service.Tunnel, size walk.Size) (*walk.Bitmap, error) {
+func (tsip *IconProvider) ImageForTunnel(tunnel *service.Tunnel, size walk.Size) (*walk.Bitmap, error) {
state, err := tunnel.State()
if err != nil {
return nil, err
@@ -204,7 +209,7 @@ func (tsip *TunnelStatusImageProvider) ImageForTunnel(tunnel *service.Tunnel, si
return tsip.ImageForState(state, size)
}
-func (tsip *TunnelStatusImageProvider) ImageForState(state service.TunnelState, size walk.Size) (*walk.Bitmap, error) {
+func (tsip *IconProvider) ImageForState(state service.TunnelState, size walk.Size) (*walk.Bitmap, error) {
key := sizeAndState{size, state}
if img, ok := tsip.imagesBySizeAndState[key]; ok {
@@ -231,23 +236,12 @@ func (tsip *TunnelStatusImageProvider) ImageForState(state service.TunnelState,
return img, nil
}
-func (tsip *TunnelStatusImageProvider) IconWithOverlayForTunnel(baseIcon *walk.Icon, tunnel *service.Tunnel) (*walk.Icon, error) {
- state, err := tunnel.State()
- if err != nil {
- return nil, err
- }
-
- return tsip.IconWithOverlayForState(baseIcon, state)
-}
-
-func (tsip *TunnelStatusImageProvider) IconWithOverlayForState(baseIcon *walk.Icon, state service.TunnelState) (*walk.Icon, error) {
- key := iconAndState{baseIcon, state}
-
- if icon, ok := tsip.iconsByBaseIconAndState[key]; ok {
+func (tsip *IconProvider) IconWithOverlayForState(state service.TunnelState) (*walk.Icon, error) {
+ if icon, ok := tsip.iconsByState[state]; ok {
return icon, nil
}
- size := baseIcon.Size()
+ size := tsip.baseIcon.Size()
bmp, err := walk.NewBitmapWithTransparentPixels(size)
if err != nil {
@@ -261,7 +255,7 @@ func (tsip *TunnelStatusImageProvider) IconWithOverlayForState(baseIcon *walk.Ic
}
defer canvas.Dispose()
- if err := canvas.DrawImage(baseIcon, walk.Point{}); err != nil {
+ if err := canvas.DrawImage(tsip.baseIcon, walk.Point{}); err != nil {
return nil, err
}
@@ -280,12 +274,12 @@ func (tsip *TunnelStatusImageProvider) IconWithOverlayForState(baseIcon *walk.Ic
return nil, err
}
- tsip.iconsByBaseIconAndState[key] = icon
+ tsip.iconsByState[state] = icon
return icon, nil
}
-func (tsip *TunnelStatusImageProvider) PaintForTunnel(tunnel *service.Tunnel, canvas *walk.Canvas, bounds walk.Rectangle) error {
+func (tsip *IconProvider) PaintForTunnel(tunnel *service.Tunnel, canvas *walk.Canvas, bounds walk.Rectangle) error {
state, err := tunnel.State()
if err != nil {
return err
@@ -294,7 +288,7 @@ func (tsip *TunnelStatusImageProvider) PaintForTunnel(tunnel *service.Tunnel, ca
return tsip.PaintForState(state, canvas, bounds)
}
-func (tsip *TunnelStatusImageProvider) PaintForState(state service.TunnelState, canvas *walk.Canvas, bounds walk.Rectangle) error {
+func (tsip *IconProvider) PaintForState(state service.TunnelState, canvas *walk.Canvas, bounds walk.Rectangle) error {
var (
brush *walk.SolidColorBrush
pen *walk.CosmeticPen
@@ -305,13 +299,13 @@ func (tsip *TunnelStatusImageProvider) PaintForState(state service.TunnelState,
brush = tsip.startedBrush
pen = tsip.startedPen
- case service.TunnelStarting:
- brush = tsip.startingBrush
- pen = tsip.startingPen
-
- default:
+ case service.TunnelStopped:
brush = tsip.stoppedBrush
pen = tsip.stoppedPen
+
+ default:
+ brush = tsip.startingBrush
+ pen = tsip.startingPen
}
b := bounds
diff --git a/ui/logpage.go b/ui/logpage.go
index 4f39ae87..d3b57a70 100644
--- a/ui/logpage.go
+++ b/ui/logpage.go
@@ -17,8 +17,12 @@ import (
"golang.zx2c4.com/wireguard/windows/ringlogger"
)
-func NewLogPage(logger *ringlogger.Ringlogger) (*LogPage, error) {
- lp := &LogPage{logger: logger}
+const (
+ maxLogLinesDisplayed = 10000
+)
+
+func NewLogPage() (*LogPage, error) {
+ lp := &LogPage{}
var disposables walk.Disposables
defer disposables.Treat()
@@ -56,7 +60,7 @@ func NewLogPage(logger *ringlogger.Ringlogger) (*LogPage, error) {
msgCol.SetTitle("Log message")
lp.logView.Columns().Add(msgCol)
- lp.model = newLogModel(lp, logger)
+ lp.model = newLogModel(lp)
lp.logView.SetModel(lp.model)
buttonsContainer, err := walk.NewComposite(lp)
@@ -83,12 +87,11 @@ func NewLogPage(logger *ringlogger.Ringlogger) (*LogPage, error) {
type LogPage struct {
*walk.TabPage
logView *walk.TableView
- logger *ringlogger.Ringlogger
model *logModel
}
func (lp *LogPage) isAtBottom() bool {
- return lp.logView.ItemVisible(len(lp.model.items) - 1)
+ return len(lp.model.items) == 0 || lp.logView.ItemVisible(len(lp.model.items)-1)
}
func (lp *LogPage) scrollToBottom() {
@@ -108,13 +111,12 @@ func (lp *LogPage) onSaveButtonClicked() {
return
}
- extensions := []string{".log", ".txt"}
- if fd.FilterIndex < 3 && !strings.HasSuffix(fd.FilePath, extensions[fd.FilterIndex-1]) {
- fd.FilePath = fd.FilePath + extensions[fd.FilterIndex-1]
+ if fd.FilterIndex == 1 && !strings.HasSuffix(fd.FilePath, ".txt") {
+ fd.FilePath = fd.FilePath + ".txt"
}
writeFileWithOverwriteHandling(form, fd.FilePath, func(file *os.File) error {
- if _, err := lp.logger.WriteTo(file); err != nil {
+ if _, err := ringlogger.Global.WriteTo(file); err != nil {
return fmt.Errorf("exportLog: Ringlogger.WriteTo failed: %v", err)
}
@@ -124,50 +126,38 @@ func (lp *LogPage) onSaveButtonClicked() {
type logModel struct {
walk.ReflectTableModelBase
- lp *LogPage
- quit chan bool
- logger *ringlogger.Ringlogger
- items []ringlogger.FollowLine
+ lp *LogPage
+ quit chan bool
+ items []ringlogger.FollowLine
}
-func newLogModel(lp *LogPage, logger *ringlogger.Ringlogger) *logModel {
- mdl := &logModel{lp: lp, quit: make(chan bool), logger: logger}
- var lastCursor uint32
- mdl.items, lastCursor = logger.FollowFromCursor(ringlogger.CursorAll)
-
- var lastStamp time.Time
- if len(mdl.items) > 0 {
- lastStamp = mdl.items[len(mdl.items)-1].Stamp
- }
-
+func newLogModel(lp *LogPage) *logModel {
+ mdl := &logModel{lp: lp, quit: make(chan bool)}
go func() {
- ticker := time.NewTicker(time.Second)
+ ticker := time.NewTicker(time.Millisecond * 300)
+ cursor := ringlogger.CursorAll
+ var items []ringlogger.FollowLine
for {
select {
case <-ticker.C:
- items, cursor := mdl.logger.FollowFromCursor(ringlogger.CursorAll)
-
- var stamp time.Time
- if len(items) > 0 {
- stamp = items[len(items)-1].Stamp
- }
-
- if cursor != lastCursor || stamp.After(lastStamp) {
- lastCursor = cursor
- lastStamp = stamp
-
- mdl.lp.Synchronize(func() {
- isAtBottom := mdl.lp.isAtBottom()
-
- mdl.items = items
- mdl.PublishRowsReset()
-
- if isAtBottom {
- mdl.lp.scrollToBottom()
- }
- })
+ items, cursor = ringlogger.Global.FollowFromCursor(cursor)
+ if len(items) == 0 {
+ continue
}
+ mdl.lp.Synchronize(func() {
+ isAtBottom := mdl.lp.isAtBottom()
+
+ mdl.items = append(mdl.items, items...)
+ if len(mdl.items) > maxLogLinesDisplayed {
+ mdl.items = mdl.items[len(mdl.items)-maxLogLinesDisplayed:]
+ }
+ mdl.PublishRowsReset()
+
+ if isAtBottom {
+ mdl.lp.scrollToBottom()
+ }
+ })
case <-mdl.quit:
ticker.Stop()
diff --git a/ui/manage_tunnels.go b/ui/managewindow.go
index 584cbd9b..85c55844 100644
--- a/ui/manage_tunnels.go
+++ b/ui/managewindow.go
@@ -8,36 +8,33 @@ package ui
import (
"github.com/lxn/walk"
"github.com/lxn/win"
- "golang.zx2c4.com/wireguard/windows/ringlogger"
"golang.zx2c4.com/wireguard/windows/service"
)
type ManageTunnelsWindow struct {
*walk.MainWindow
- icon *walk.Icon
- logger *ringlogger.Ringlogger
tunnelsPage *TunnelsPage
logPage *LogPage
+
+ tunnelChangedCB *service.TunnelChangeCallback
}
-func NewManageTunnelsWindow(icon *walk.Icon, logger *ringlogger.Ringlogger) (*ManageTunnelsWindow, error) {
+func NewManageTunnelsWindow() (*ManageTunnelsWindow, error) {
var err error
var disposables walk.Disposables
defer disposables.Treat()
- mtw := &ManageTunnelsWindow{
- icon: icon,
- logger: logger,
- }
+ mtw := &ManageTunnelsWindow{}
+
mtw.MainWindow, err = walk.NewMainWindowWithName("WireGuard")
if err != nil {
return nil, err
}
disposables.Add(mtw)
- mtw.SetIcon(mtw.icon)
+ mtw.SetIcon(iconProvider.baseIcon)
mtw.SetTitle("WireGuard")
font, err := walk.NewFont("Segoe UI", 9, 0)
if err != nil {
@@ -66,31 +63,41 @@ func NewManageTunnelsWindow(icon *walk.Icon, logger *ringlogger.Ringlogger) (*Ma
mtw.tunnelsPage, _ = NewTunnelsPage()
tabWidget.Pages().Add(mtw.tunnelsPage.TabPage)
- mtw.logPage, _ = NewLogPage(logger)
+ mtw.logPage, _ = NewLogPage()
tabWidget.Pages().Add(mtw.logPage.TabPage)
disposables.Spare()
- return mtw, nil
-}
+ mtw.tunnelChangedCB = service.IPCClientRegisterTunnelChange(mtw.onTunnelChange)
+ mtw.onTunnelChange(nil, service.TunnelUnknown, nil)
-func (mtw *ManageTunnelsWindow) TunnelTracker() *TunnelTracker {
- return mtw.tunnelsPage.tunnelTracker
+ return mtw, nil
}
-func (mtw *ManageTunnelsWindow) SetTunnelTracker(tunnelTracker *TunnelTracker) {
- mtw.tunnelsPage.tunnelTracker = tunnelTracker
-
- mtw.tunnelsPage.confView.SetTunnelTracker(tunnelTracker)
+func (mtw *ManageTunnelsWindow) Dispose() {
+ if mtw.tunnelChangedCB != nil {
+ mtw.tunnelChangedCB.Unregister()
+ mtw.tunnelChangedCB = nil
+ }
+ mtw.MainWindow.Dispose()
}
-func (mtw *ManageTunnelsWindow) SetTunnelState(tunnel *service.Tunnel, state service.TunnelState) {
- mtw.tunnelsPage.SetTunnelState(tunnel, state)
-
- icon, err := mtw.tunnelsPage.tunnelsView.imageProvider.IconWithOverlayForState(mtw.icon, state)
- if err != nil {
- return
- }
+func (mtw *ManageTunnelsWindow) onTunnelChange(tunnel *service.Tunnel, state service.TunnelState, err error) {
+ globalState, err2 := service.IPCClientGlobalState()
+ mtw.Synchronize(func() {
+ if err2 == nil {
+ icon, err2 := iconProvider.IconWithOverlayForState(globalState)
+ if err2 == nil {
+ mtw.SetIcon(icon)
+ }
+ }
- mtw.SetIcon(icon)
+ if err != nil && mtw.Visible() {
+ errMsg := err.Error()
+ if len(errMsg) > 0 && errMsg[len(errMsg)-1] != '.' {
+ errMsg += "."
+ }
+ walk.MsgBox(mtw, "Tunnel Error", errMsg+"\n\nPlease consult the log for more information.", walk.MsgBoxIconWarning)
+ }
+ })
}
diff --git a/ui/tray.go b/ui/tray.go
index 2650530d..3204d861 100644
--- a/ui/tray.go
+++ b/ui/tray.go
@@ -14,8 +14,8 @@ import (
"golang.zx2c4.com/wireguard/windows/service"
)
-// Status + active CIDRs + deactivate + separator
-const trayTunnelActionsOffset = 4
+// Status + active CIDRs + separator
+const trayTunnelActionsOffset = 3
type Tray struct {
*walk.NotifyIcon
@@ -23,18 +23,20 @@ type Tray struct {
// Current known tunnels by name
tunnels map[string]*walk.Action
- mtw *ManageTunnelsWindow
- icon *walk.Icon
+ mtw *ManageTunnelsWindow
+
+ tunnelChangedCB *service.TunnelChangeCallback
+ tunnelsChangedCB *service.TunnelsChangeCallback
}
-func NewTray(mtw *ManageTunnelsWindow, icon *walk.Icon) (*Tray, error) {
+func NewTray(mtw *ManageTunnelsWindow) (*Tray, error) {
var err error
tray := &Tray{
mtw: mtw,
- icon: icon,
tunnels: make(map[string]*walk.Action),
}
+
tray.NotifyIcon, err = walk.NewNotifyIcon(mtw.MainWindow)
if err != nil {
return nil, err
@@ -46,7 +48,7 @@ func NewTray(mtw *ManageTunnelsWindow, icon *walk.Icon) (*Tray, error) {
func (tray *Tray) setup() error {
tray.SetToolTip("WireGuard: Deactivated")
tray.SetVisible(true)
- tray.SetIcon(tray.icon)
+ tray.SetIcon(iconProvider.baseIcon)
tray.MouseDown().Attach(func(x, y int, button walk.MouseButton) {
if button == walk.LeftButton {
@@ -64,7 +66,6 @@ func (tray *Tray) setup() error {
}{
{label: "Status: Unknown"},
{label: "Networks: None", hidden: true},
- {label: "Deactivate", handler: tray.onDeactivateTunnel, enabled: true, hidden: true},
{separator: true},
{separator: true},
{label: "&Manage tunnels...", handler: tray.mtw.Show, enabled: true},
@@ -88,54 +89,94 @@ func (tray *Tray) setup() error {
tray.ContextMenu().Actions().Add(action)
}
+ tray.tunnelChangedCB = service.IPCClientRegisterTunnelChange(tray.onTunnelChange)
+ tray.tunnelsChangedCB = service.IPCClientRegisterTunnelsChange(tray.onTunnelsChange)
+ tray.onTunnelsChange()
+ tray.updateGlobalState()
- tunnels, err := service.IPCClientTunnels()
- if err != nil {
- return err
- }
+ return nil
+}
- for _, tunnel := range tunnels {
- tray.addTunnelAction(tunnel.Name)
+func (tray *Tray) Dispose() error {
+ if tray.tunnelChangedCB != nil {
+ tray.tunnelChangedCB.Unregister()
+ tray.tunnelChangedCB = nil
}
+ if tray.tunnelsChangedCB != nil {
+ tray.tunnelsChangedCB.Unregister()
+ tray.tunnelsChangedCB = nil
+ }
+ return tray.NotifyIcon.Dispose()
+}
- tray.mtw.tunnelsPage.TunnelAdded().Attach(tray.addTunnelAction)
- tray.mtw.tunnelsPage.TunnelDeleted().Attach(tray.removeTunnelAction)
-
- return nil
+func (tray *Tray) onTunnelsChange() {
+ tunnels, err := service.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(tunnelName string) {
+func (tray *Tray) addTunnelAction(tunnel *service.Tunnel) {
tunnelAction := walk.NewAction()
- tunnelAction.SetText(tunnelName)
+ tunnelAction.SetText(tunnel.Name)
tunnelAction.SetEnabled(true)
tunnelAction.SetCheckable(true)
+ tclosure := *tunnel
tunnelAction.Triggered().Attach(func() {
- if activeTunnel := tray.mtw.tunnelsPage.tunnelTracker.activeTunnel; activeTunnel != nil && activeTunnel.Name == tunnelName {
- tray.onDeactivateTunnel()
- } else {
- tray.onActivateTunnel(tunnelName)
+ oldState, err := tclosure.Toggle()
+ if err != nil {
+ tray.mtw.Show()
+ //TODO: select tunnel that we're showing the error for in mtw
+ if oldState == service.TunnelUnknown {
+ walk.MsgBox(tray.mtw, "Failed to determine tunnel state", err.Error(), walk.MsgBoxIconError)
+ } else if oldState == service.TunnelStopped {
+ walk.MsgBox(tray.mtw, "Failed to activate tunnel", err.Error(), walk.MsgBoxIconError)
+ } else if oldState == service.TunnelStarted {
+ walk.MsgBox(tray.mtw, "Failed to deactivate tunnel", err.Error(), walk.MsgBoxIconError)
+ }
+ return
}
})
- tray.tunnels[tunnelName] = tunnelAction
+ tray.tunnels[tunnel.Name] = tunnelAction
// Add the action at the right spot
var names []string
- for name, _ := range tray.tunnels {
+ for name := range tray.tunnels {
names = append(names, name)
}
- sort.Strings(names)
+ sort.Strings(names) //TODO: use correct sorting order for this
var (
idx int
name string
)
for idx, name = range names {
- if name == tunnelName {
+ if name == tunnel.Name {
break
}
}
tray.ContextMenu().Actions().Insert(trayTunnelActionsOffset+idx, tunnelAction)
+
+ state, err := tunnel.State()
+ if err != nil {
+ return
+ }
+ tray.SetTunnelState(tunnel, state, false)
}
func (tray *Tray) removeTunnelAction(tunnelName string) {
@@ -143,21 +184,28 @@ func (tray *Tray) removeTunnelAction(tunnelName string) {
delete(tray.tunnels, tunnelName)
}
-func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelState) {
- tray.SetTunnelStateWithNotification(tunnel, state, true)
+func (tray *Tray) onTunnelChange(tunnel *service.Tunnel, state service.TunnelState, err error) {
+ tray.mtw.Synchronize(func() {
+ tray.SetTunnelState(tunnel, state, err == nil)
+ if !tray.mtw.Visible() && err != nil {
+ tray.ShowError("WireGuard Tunnel Error", err.Error())
+ }
+ })
}
-func (tray *Tray) SetTunnelStateWithNotification(tunnel *service.Tunnel, state service.TunnelState, showNotifications bool) {
- if icon, err := tray.mtw.tunnelsPage.tunnelsView.imageProvider.IconWithOverlayForState(tray.icon, state); err == nil {
- tray.SetIcon(icon)
+func (tray *Tray) updateGlobalState() {
+ state, err := service.IPCClientGlobalState()
+ if err != nil {
+ return
}
- tunnelAction := tray.tunnels[tunnel.Name]
+ if icon, err := iconProvider.IconWithOverlayForState(state); err == nil {
+ tray.SetIcon(icon)
+ }
actions := tray.ContextMenu().Actions()
statusAction := actions.At(0)
activeCIDRsAction := actions.At(1)
- deactivateAction := actions.At(2)
setTunnelActionsEnabled := func(enabled bool) {
for i := 0; i < len(tray.tunnels); i++ {
@@ -170,16 +218,37 @@ func (tray *Tray) SetTunnelStateWithNotification(tunnel *service.Tunnel, state s
case service.TunnelStarting:
statusAction.SetText("Status: Activating")
setTunnelActionsEnabled(false)
-
tray.SetToolTip("WireGuard: Activating...")
case service.TunnelStarted:
+ activeCIDRsAction.SetVisible(err == nil)
statusAction.SetText("Status: Active")
setTunnelActionsEnabled(true)
+ tray.SetToolTip("WireGuard: Activated")
+
+ case service.TunnelStopping:
+ statusAction.SetText("Status: Deactivating")
+ setTunnelActionsEnabled(false)
+ tray.SetToolTip("WireGuard: Deactivating...")
+
+ case service.TunnelStopped:
+ activeCIDRsAction.SetVisible(false)
+ statusAction.SetText("Status: Inactive")
+ setTunnelActionsEnabled(true)
+ tray.SetToolTip("WireGuard: Deactivated")
+ }
+}
+func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelState, showNotifications bool) {
+ tunnelAction := tray.tunnels[tunnel.Name]
+
+ actions := tray.ContextMenu().Actions()
+ activeCIDRsAction := actions.At(1)
+
+ switch state {
+ case service.TunnelStarted:
+ activeCIDRsAction.SetText("")
config, err := tunnel.RuntimeConfig()
- activeCIDRsAction.SetVisible(err == nil)
- deactivateAction.SetVisible(err == nil)
if err == nil {
var sb strings.Builder
for i, addr := range config.Interface.Addresses {
@@ -189,46 +258,20 @@ func (tray *Tray) SetTunnelStateWithNotification(tunnel *service.Tunnel, state s
sb.WriteString(addr.String())
}
-
activeCIDRsAction.SetText(fmt.Sprintf("Networks: %s", sb.String()))
}
-
tunnelAction.SetEnabled(true)
tunnelAction.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:
- statusAction.SetText("Status: Deactivating")
- setTunnelActionsEnabled(false)
-
- tray.SetToolTip("WireGuard: Deactivating...")
-
case service.TunnelStopped:
- statusAction.SetText("Status: Inactive")
- activeCIDRsAction.SetVisible(false)
- deactivateAction.SetVisible(false)
- setTunnelActionsEnabled(true)
tunnelAction.SetChecked(false)
-
- tray.SetToolTip("WireGuard: Deactivated")
if showNotifications {
tray.ShowInfo("WireGuard Deactivated", fmt.Sprintf("The %s tunnel has been deactivated.", tunnel.Name))
}
}
-}
-func (tray *Tray) onActivateTunnel(tunnelName string) {
- if err := tray.mtw.TunnelTracker().ActivateTunnel(&service.Tunnel{tunnelName}); err != nil {
- walk.MsgBox(tray.mtw, "Failed to activate tunnel", err.Error(), walk.MsgBoxIconError)
- }
-}
-
-func (tray *Tray) onDeactivateTunnel() {
- if err := tray.mtw.TunnelTracker().DeactivateTunnel(); err != nil {
- walk.MsgBox(tray.mtw, "Failed to deactivate tunnel", err.Error(), walk.MsgBoxIconError)
- }
+ tray.updateGlobalState()
}
diff --git a/ui/tunnelspage.go b/ui/tunnelspage.go
index ac778b3a..a3ecf0c0 100644
--- a/ui/tunnelspage.go
+++ b/ui/tunnelspage.go
@@ -22,11 +22,8 @@ import (
type TunnelsPage struct {
*walk.TabPage
- tunnelTracker *TunnelTracker
- tunnelsView *TunnelsView
- confView *ConfView
- tunnelAddedPublisher walk.StringEventPublisher
- tunnelDeletedPublisher walk.StringEventPublisher
+ tunnelsView *TunnelsView
+ confView *ConfView
}
func NewTunnelsPage() (*TunnelsPage, error) {
@@ -128,20 +125,6 @@ func NewTunnelsPage() (*TunnelsPage, error) {
return tp, nil
}
-func (tp *TunnelsPage) TunnelTracker() *TunnelTracker {
- return tp.tunnelTracker
-}
-
-func (tp *TunnelsPage) SetTunnelTracker(tunnelTracker *TunnelTracker) {
- tp.tunnelTracker = tunnelTracker
-
- tp.confView.SetTunnelTracker(tunnelTracker)
-}
-
-func (tp *TunnelsPage) SetTunnelState(tunnel *service.Tunnel, state service.TunnelState) {
- tp.tunnelsView.SetTunnelState(tunnel, state)
-}
-
func (tp *TunnelsPage) updateConfView() {
if !tp.Visible() {
return
@@ -261,71 +244,33 @@ func (tp *TunnelsPage) exportTunnels(filePath string) {
}
func (tp *TunnelsPage) addTunnel(config *conf.Config) {
- tunnel, err := service.IPCClientNewTunnel(config)
+ _, err := service.IPCClientNewTunnel(config)
if err != nil {
walk.MsgBox(tp.Form(), "Unable to create tunnel", err.Error(), walk.MsgBoxIconError)
- return
}
- model := tp.tunnelsView.model
- model.tunnels = append(model.tunnels, tunnel)
- model.PublishRowsReset()
- model.Sort(model.SortedColumn(), model.SortOrder())
-
- for i, t := range model.tunnels {
- if t.Name == tunnel.Name {
- tp.tunnelsView.SetCurrentIndex(i)
- break
- }
- }
-
- tp.confView.SetTunnel(&tunnel)
-
- tp.tunnelAddedPublisher.Publish(tunnel.Name)
}
func (tp *TunnelsPage) deleteTunnel(tunnel *service.Tunnel) {
- tunnel.Delete()
-
- model := tp.tunnelsView.model
-
- for i, t := range model.tunnels {
- if t.Name == tunnel.Name {
- model.tunnels = append(model.tunnels[:i], model.tunnels[i+1:]...)
- model.PublishRowsRemoved(i, i)
- break
- }
+ err := tunnel.Delete()
+ if err != nil {
+ walk.MsgBox(tp.Form(), "Unable to delete tunnel", err.Error(), walk.MsgBoxIconError)
}
-
- tp.tunnelDeletedPublisher.Publish(tunnel.Name)
-}
-
-func (tp *TunnelsPage) TunnelAdded() *walk.StringEvent {
- return tp.tunnelAddedPublisher.Event()
-}
-
-func (tp *TunnelsPage) TunnelDeleted() *walk.StringEvent {
- return tp.tunnelDeletedPublisher.Event()
}
// Handlers
func (tp *TunnelsPage) onTunnelsViewItemActivated() {
- if tp.tunnelTracker.InTransition() {
- return
- }
-
- var err error
- var title string
- tunnel := tp.tunnelsView.CurrentTunnel()
- activeTunnel := tp.tunnelTracker.ActiveTunnel()
- if tunnel != nil && activeTunnel != nil && tunnel.Name == activeTunnel.Name {
- err, title = tp.tunnelTracker.DeactivateTunnel(), "Deactivating tunnel failed"
- } else {
- err, title = tp.tunnelTracker.ActivateTunnel(tunnel), "Activating tunnel failed"
- }
+ oldState, err := tp.tunnelsView.CurrentTunnel().Toggle()
if err != nil {
- walk.MsgBox(tp.Form(), title, fmt.Sprintf("Error: %s", err.Error()), walk.MsgBoxIconError)
+ if oldState == service.TunnelUnknown {
+ walk.MsgBox(tp.Form(), "Failed to determine tunnel state", err.Error(), walk.MsgBoxIconError)
+ } else if oldState == service.TunnelStopped {
+ walk.MsgBox(tp.Form(), "Failed to activate tunnel", err.Error(), walk.MsgBoxIconError)
+ } else if oldState == service.TunnelStarted {
+ walk.MsgBox(tp.Form(), "Failed to deactivate tunnel", err.Error(), walk.MsgBoxIconError)
+ }
+ return
}
}
@@ -337,11 +282,16 @@ func (tp *TunnelsPage) onEditTunnel() {
}
if config := runTunnelConfigDialog(tp.Form(), tunnel); config != nil {
- // Delete old one
- tp.deleteTunnel(tunnel)
-
- // Save new one
- tp.addTunnel(config)
+ go func() {
+ priorState, err := tunnel.State()
+ tunnel.Delete()
+ tunnel.WaitForStop()
+ tunnel, err2 := service.IPCClientNewTunnel(config)
+ if err == nil && err2 == nil && (priorState == service.TunnelStarting || priorState == service.TunnelStarted) {
+ tunnel.Start()
+ }
+ //TODO: synchronize and select newly added tunnel
+ }()
}
}
@@ -368,8 +318,6 @@ func (tp *TunnelsPage) onDelete() {
}
tp.deleteTunnel(currentTunnel)
-
- tp.tunnelDeletedPublisher.Publish(currentTunnel.Name)
}
func (tp *TunnelsPage) onImport() {
diff --git a/ui/tunnelsview.go b/ui/tunnelsview.go
index 2f6ea2a4..c5fe5bb5 100644
--- a/ui/tunnelsview.go
+++ b/ui/tunnelsview.go
@@ -38,6 +38,7 @@ func (t *TunnelModel) Value(row, col int) interface{} {
func (t *TunnelModel) Sort(col int, order walk.SortOrder) error {
sort.SliceStable(t.tunnels, func(i, j int) bool {
+ //TODO: use real string comparison for sorting with proper tunnel order
return t.tunnels[i].Name < t.tunnels[j].Name
})
@@ -47,8 +48,10 @@ func (t *TunnelModel) Sort(col int, order walk.SortOrder) error {
type TunnelsView struct {
*walk.TableView
- model *TunnelModel
- imageProvider *TunnelStatusImageProvider
+ model *TunnelModel
+
+ tunnelChangedCB *service.TunnelChangeCallback
+ tunnelsChangedCB *service.TunnelsChangeCallback
}
func NewTunnelsView(parent walk.Container) (*TunnelsView, error) {
@@ -77,18 +80,29 @@ func NewTunnelsView(parent walk.Container) (*TunnelsView, error) {
model: model,
}
- if tunnelsView.imageProvider, err = NewTunnelStatusImageProvider(); err != nil {
- return nil, err
- }
- tunnelsView.AddDisposable(tunnelsView.imageProvider)
-
tv.SetCellStyler(tunnelsView)
disposables.Spare()
+ tunnelsView.tunnelChangedCB = service.IPCClientRegisterTunnelChange(tunnelsView.onTunnelChange)
+ tunnelsView.tunnelsChangedCB = service.IPCClientRegisterTunnelsChange(tunnelsView.onTunnelsChange)
+ tunnelsView.onTunnelsChange()
+
return tunnelsView, nil
}
+func (tv *TunnelsView) Dispose() {
+ if tv.tunnelChangedCB != nil {
+ tv.tunnelChangedCB.Unregister()
+ tv.tunnelChangedCB = nil
+ }
+ if tv.tunnelsChangedCB != nil {
+ tv.tunnelsChangedCB.Unregister()
+ tv.tunnelsChangedCB = nil
+ }
+ tv.TableView.Dispose()
+}
+
func (tv *TunnelsView) StyleCell(style *walk.CellStyle) {
canvas := style.Canvas()
if canvas == nil {
@@ -106,7 +120,7 @@ func (tv *TunnelsView) StyleCell(style *walk.CellStyle) {
b.X = 0
b.Width = b.Height
- tv.imageProvider.PaintForTunnel(tunnel, canvas, b)
+ iconProvider.PaintForTunnel(tunnel, canvas, b)
}
func (tv *TunnelsView) CurrentTunnel() *service.Tunnel {
@@ -118,17 +132,61 @@ func (tv *TunnelsView) CurrentTunnel() *service.Tunnel {
return &tv.model.tunnels[idx]
}
-func (tv *TunnelsView) SetTunnelState(tunnel *service.Tunnel, state service.TunnelState) {
- idx := -1
- for i, _ := range tv.model.tunnels {
- if tv.model.tunnels[i].Name == tunnel.Name {
- idx = i
- break
+func (tv *TunnelsView) onTunnelChange(tunnel *service.Tunnel, state service.TunnelState, err error) {
+ tv.Synchronize(func() {
+ idx := -1
+ for i := range tv.model.tunnels {
+ if tv.model.tunnels[i].Name == tunnel.Name {
+ idx = i
+ break
+ }
}
- }
- if idx != -1 {
- tv.model.PublishRowChanged(idx)
+ if idx != -1 {
+ tv.model.PublishRowChanged(idx)
+ return
+ }
+ })
+}
+
+func (tv *TunnelsView) onTunnelsChange() {
+ tunnels, err := service.IPCClientTunnels()
+ if err != nil {
return
}
+ tv.Synchronize(func() {
+ newTunnels := make(map[service.Tunnel]bool, len(tunnels))
+ oldTunnels := make(map[service.Tunnel]bool, len(tv.model.tunnels))
+ for _, tunnel := range tunnels {
+ newTunnels[tunnel] = true
+ }
+ for _, tunnel := range tv.model.tunnels {
+ oldTunnels[tunnel] = true
+ }
+
+ for tunnel := range oldTunnels {
+ if !newTunnels[tunnel] {
+ for i, t := range tv.model.tunnels {
+ //TODO: this is inefficient. Use a map here instead.
+ if t.Name == tunnel.Name {
+ tv.model.tunnels = append(tv.model.tunnels[:i], tv.model.tunnels[i+1:]...)
+ tv.model.PublishRowsRemoved(i, i)
+ break
+ }
+ }
+ }
+ }
+ didAdd := false
+ for tunnel := range newTunnels {
+ if !oldTunnels[tunnel] {
+ tv.model.tunnels = append(tv.model.tunnels, tunnel)
+ didAdd = true
+ //TODO: If adding a tunnel for the first time when the previously were none, select it
+ }
+ }
+ if didAdd {
+ tv.model.PublishRowsReset()
+ tv.model.Sort(tv.model.SortedColumn(), tv.model.SortOrder())
+ }
+ })
}
diff --git a/ui/tunneltracker.go b/ui/tunneltracker.go
deleted file mode 100644
index 73d48538..00000000
--- a/ui/tunneltracker.go
+++ /dev/null
@@ -1,99 +0,0 @@
-/* SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
- */
-
-package ui
-
-import (
- "fmt"
-
- "github.com/lxn/walk"
- "golang.zx2c4.com/wireguard/windows/service"
-)
-
-type TunnelTracker struct {
- activeTunnel *service.Tunnel
- activeTunnelChanged walk.EventPublisher
- tunnelChangeCB *service.TunnelChangeCallback
- inTransition bool
-}
-
-func (tt *TunnelTracker) ActiveTunnel() *service.Tunnel {
- return tt.activeTunnel
-}
-
-func (tt *TunnelTracker) ActivateTunnel(tunnel *service.Tunnel) error {
- if tunnel == tt.activeTunnel {
- return nil
- }
-
- if err := tt.DeactivateTunnel(); err != nil {
- return fmt.Errorf("ActivateTunnel: Failed to deactivate tunnel '%s': %v", tunnel.Name, err)
- }
-
- if err := tunnel.Start(); err != nil {
- return fmt.Errorf("ActivateTunnel: Failed to start tunnel '%s': %v", tunnel.Name, err)
- }
-
- return nil
-}
-
-func (tt *TunnelTracker) DeactivateTunnel() error {
- if tt.activeTunnel == nil {
- return nil
- }
-
- state, err := tt.activeTunnel.State()
- if err != nil {
- return fmt.Errorf("DeactivateTunnel: Failed to retrieve state for tunnel %s: %v", tt.activeTunnel.Name, err)
- }
-
- if state == service.TunnelStarted {
- if err := tt.activeTunnel.Stop(); err != nil {
- return fmt.Errorf("DeactivateTunnel: Failed to stop tunnel '%s': %v", tt.activeTunnel.Name, err)
- }
- }
-
- if state == service.TunnelStarted || state == service.TunnelStopping {
- if err := tt.activeTunnel.WaitForStop(); err != nil {
- return fmt.Errorf("DeactivateTunnel: Failed to wait for tunnel '%s' to stop: %v", tt.activeTunnel.Name, err)
- }
- }
-
- return nil
-}
-
-func (tt *TunnelTracker) ActiveTunnelChanged() *walk.Event {
- return tt.activeTunnelChanged.Event()
-}
-
-func (tt *TunnelTracker) InTransition() bool {
- return tt.inTransition
-}
-
-func (tt *TunnelTracker) SetTunnelState(tunnel *service.Tunnel, state service.TunnelState, err error) {
- if err != nil {
- tt.inTransition = false
- }
-
- switch state {
- case service.TunnelStarted:
- tt.inTransition = false
- tt.activeTunnel = tunnel
-
- case service.TunnelStarting, service.TunnelStopping:
- tt.inTransition = true
-
- case service.TunnelStopped:
- if tt.activeTunnel != nil && tt.activeTunnel.Name == tunnel.Name {
- tt.inTransition = false
- }
- tt.activeTunnel = nil
-
- default:
- return
- }
-
- tt.activeTunnelChanged.Publish()
-}
diff --git a/ui/ui.go b/ui/ui.go
index 58b52e46..d186a905 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -7,72 +7,50 @@ package ui
import (
"fmt"
- "os"
"runtime"
+ "runtime/debug"
"github.com/lxn/walk"
"golang.zx2c4.com/wireguard/device"
- "golang.zx2c4.com/wireguard/windows/ringlogger"
"golang.zx2c4.com/wireguard/windows/service"
)
// #include "../version.h"
import "C"
+var iconProvider *IconProvider
+
func RunUI() {
runtime.LockOSThread()
- logger, err := ringlogger.NewRingloggerFromInheritedMappingHandle(os.Args[5], "GUI")
- if err != nil {
- walk.MsgBox(nil, "Unable to initialize logging", fmt.Sprint(err), walk.MsgBoxIconError)
- return
- }
+ defer func() {
+ if err := recover(); err != nil {
+ walk.MsgBox(nil, "Panic", fmt.Sprint(err, "\n\n", string(debug.Stack())), walk.MsgBoxIconError)
+ panic(err)
+ }
+ }()
- tunnelTracker := new(TunnelTracker)
+ var err error
- icon, err := walk.NewIconFromResourceId(1)
+ iconProvider, err = NewIconProvider()
if err != nil {
- panic(err)
+ walk.MsgBox(nil, "Unable to initialize icon provider", fmt.Sprint(err), walk.MsgBoxIconError)
+ return
}
- defer icon.Dispose()
+ defer iconProvider.Dispose()
- mtw, err := NewManageTunnelsWindow(icon, logger)
+ mtw, err := NewManageTunnelsWindow()
if err != nil {
panic(err)
}
defer mtw.Dispose()
- mtw.SetTunnelTracker(tunnelTracker)
-
- tray, err := NewTray(mtw, icon)
+ tray, err := NewTray(mtw)
if err != nil {
panic(err)
}
defer tray.Dispose()
- // Bind to updates
- service.IPCClientRegisterTunnelChange(func(tunnel *service.Tunnel, state service.TunnelState, err error) {
- mtw.Synchronize(func() {
- tunnelTracker.SetTunnelState(tunnel, state, err)
- mtw.SetTunnelState(tunnel, state)
- tray.SetTunnelStateWithNotification(tunnel, state, err == nil)
- })
-
- if err == nil {
- return
- }
-
- if mtw.Visible() {
- errMsg := err.Error()
- if len(errMsg) > 0 && errMsg[len(errMsg)-1] != '.' {
- errMsg += "."
- }
- walk.MsgBox(mtw, "Tunnel Error", errMsg+"\n\nPlease consult the log for more information.", walk.MsgBoxIconWarning)
- } else {
- tray.ShowError("WireGuard Tunnel Error", err.Error())
- }
- })
-
mtw.Run()
}
@@ -80,9 +58,7 @@ func onQuit() {
_, err := service.IPCClientQuit(true)
if err != nil {
walk.MsgBox(nil, "Error Exiting WireGuard", fmt.Sprintf("Unable to exit service due to: %s. You may want to stop WireGuard from the service manager.", err), walk.MsgBoxIconError)
- os.Exit(1)
}
-
walk.App().Exit(0)
}