aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/iconprovider.go55
-rw-r--r--ui/managewindow.go19
-rw-r--r--ui/tray.go13
-rw-r--r--ui/tunnelsview.go2
-rw-r--r--ui/ui.go41
-rw-r--r--ui/updatepage.go106
6 files changed, 221 insertions, 15 deletions
diff --git a/ui/iconprovider.go b/ui/iconprovider.go
index 56b8cfbc..01c10675 100644
--- a/ui/iconprovider.go
+++ b/ui/iconprovider.go
@@ -26,12 +26,14 @@ type IconProvider struct {
stoppedPen *walk.CosmeticPen
startingPen *walk.CosmeticPen
startedPen *walk.CosmeticPen
+ updateAvailableImage *walk.Bitmap
}
const (
- colorStopped = 0xe1e1e1
- colorStarting = 0xfec031
- colorStarted = 0x36ce42
+ colorStopped = 0xe1e1e1
+ colorStarting = 0xfec031
+ colorStarted = 0x36ce42
+ colorUpdateAvailable = 0xf03a17
)
func hexColor(c uint32) walk.Color {
@@ -152,8 +154,12 @@ func NewIconProvider() (*IconProvider, error) {
}
disposables.Add(tsip.startedPen)
- disposables.Spare()
+ if tsip.updateAvailableImage, err = tsip.drawUpdateAvailableImage(16); err != nil {
+ return nil, err
+ }
+ disposables.Add(tsip.updateAvailableImage)
+ disposables.Spare()
return tsip, nil
}
@@ -198,6 +204,47 @@ func (tsip *IconProvider) Dispose() {
tsip.baseIcon.Dispose()
tsip.baseIcon = nil
}
+ if tsip.updateAvailableImage != nil {
+ tsip.updateAvailableImage.Dispose()
+ tsip.updateAvailableImage = nil
+ }
+}
+
+func (tsip *IconProvider) drawUpdateAvailableImage(size int) (*walk.Bitmap, error) {
+ updateAvailableBrush, err := walk.NewSolidColorBrush(hexColor(colorUpdateAvailable))
+ if err != nil {
+ return nil, err
+ }
+ defer updateAvailableBrush.Dispose()
+ updateAvailablePen, err := walk.NewCosmeticPen(walk.PenSolid, darkColor(hexColor(colorUpdateAvailable)))
+ if err != nil {
+ return nil, err
+ }
+ defer updateAvailablePen.Dispose()
+
+ img, err := walk.NewBitmapWithTransparentPixels(walk.Size{size, size})
+ if err != nil {
+ return nil, err
+ }
+
+ canvas, err := walk.NewCanvasFromImage(img)
+ if err != nil {
+ img.Dispose()
+ return nil, err
+ }
+ defer canvas.Dispose()
+
+ rect := walk.Rectangle{0, 0, size, size}
+
+ if err := canvas.FillEllipse(updateAvailableBrush, rect); err != nil {
+ img.Dispose()
+ return nil, err
+ }
+ if err := canvas.DrawEllipse(updateAvailablePen, rect); err != nil {
+ img.Dispose()
+ return nil, err
+ }
+ return img, nil
}
func (tsip *IconProvider) ImageForTunnel(tunnel *service.Tunnel, size walk.Size) (*walk.Bitmap, error) {
diff --git a/ui/managewindow.go b/ui/managewindow.go
index c4d6dc3c..4046d6db 100644
--- a/ui/managewindow.go
+++ b/ui/managewindow.go
@@ -14,8 +14,10 @@ import (
type ManageTunnelsWindow struct {
*walk.MainWindow
+ tabs *walk.TabWidget
tunnelsPage *TunnelsPage
logPage *LogPage
+ updatePage *UpdatePage
tunnelChangedCB *service.TunnelChangeCallback
}
@@ -59,13 +61,13 @@ func NewManageTunnelsWindow() (*ManageTunnelsWindow, error) {
}
})
- tabWidget, _ := walk.NewTabWidget(mtw)
+ mtw.tabs, _ = walk.NewTabWidget(mtw)
mtw.tunnelsPage, _ = NewTunnelsPage()
- tabWidget.Pages().Add(mtw.tunnelsPage.TabPage)
+ mtw.tabs.Pages().Add(mtw.tunnelsPage.TabPage)
mtw.logPage, _ = NewLogPage()
- tabWidget.Pages().Add(mtw.logPage.TabPage)
+ mtw.tabs.Pages().Add(mtw.logPage.TabPage)
disposables.Spare()
@@ -102,3 +104,14 @@ func (mtw *ManageTunnelsWindow) onTunnelChange(tunnel *service.Tunnel, state ser
}
})
}
+
+func (mtw *ManageTunnelsWindow) UpdateFound() {
+ if mtw.updatePage != nil {
+ return
+ }
+ updatePage, err := NewUpdatePage()
+ if err == nil {
+ mtw.updatePage = updatePage
+ mtw.tabs.Pages().Add(updatePage.TabPage)
+ }
+}
diff --git a/ui/tray.go b/ui/tray.go
index 22c37ab4..ca839a83 100644
--- a/ui/tray.go
+++ b/ui/tray.go
@@ -278,3 +278,16 @@ func (tray *Tray) SetTunnelState(tunnel *service.Tunnel, state service.TunnelSta
tray.updateGlobalState()
}
+
+func (tray *Tray) UpdateFound() {
+ action := walk.NewAction()
+ action.SetText("An Update is Available!")
+ action.SetImage(iconProvider.updateAvailableImage)
+ //TODO: Make bold
+ action.Triggered().Attach(func() {
+ tray.mtw.Show()
+ tray.mtw.tabs.SetCurrentIndex(2)
+ })
+ tray.ContextMenu().Actions().Insert(tray.ContextMenu().Actions().Len()-2, action)
+ tray.ShowWarning("WireGuard Update Available", "An update to WireGuard is now available. You are advised to update as soon as possible.")
+}
diff --git a/ui/tunnelsview.go b/ui/tunnelsview.go
index 5ad733f9..2a963b58 100644
--- a/ui/tunnelsview.go
+++ b/ui/tunnelsview.go
@@ -115,7 +115,7 @@ func (tv *TunnelsView) StyleCell(style *walk.CellStyle) {
b.X = b.Height
b.Width -= b.Height
- canvas.DrawText(tunnel.Name, tv.Font(), 0, b, walk.TextVCenter | walk.TextSingleLine)
+ canvas.DrawText(tunnel.Name, tv.Font(), 0, b, walk.TextVCenter|walk.TextSingleLine)
b.X = 0
b.Width = b.Height
diff --git a/ui/ui.go b/ui/ui.go
index e24fc6c8..316fbefe 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -7,17 +7,17 @@ package ui
import (
"fmt"
- "runtime"
- "runtime/debug"
-
"github.com/lxn/walk"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/windows/service"
+ "golang.zx2c4.com/wireguard/windows/updater"
+ "golang.zx2c4.com/wireguard/windows/version"
+ "log"
+ "runtime"
+ "runtime/debug"
+ "time"
)
-// #include "../version.h"
-import "C"
-
var iconProvider *IconProvider
var shouldQuitManagerWhenExiting = false
@@ -50,6 +50,31 @@ func RunUI() {
panic(err)
}
+ go func() {
+ first := true
+ for {
+ update, err := updater.CheckForUpdate()
+ if err == nil && update != nil {
+ mtw.Synchronize(func() {
+ mtw.UpdateFound()
+ tray.UpdateFound()
+ })
+ return
+ }
+ if err != nil {
+ log.Printf("Update checker: %v", err)
+ if first {
+ time.Sleep(time.Minute * 4)
+ first = false
+ } else {
+ time.Sleep(time.Minute * 25)
+ }
+ } else {
+ time.Sleep(time.Hour)
+ }
+ }
+ }()
+
mtw.Run()
tray.Dispose()
mtw.Dispose()
@@ -102,10 +127,12 @@ func onAbout(owner walk.Form) {
detailsLbl.SetText(fmt.Sprintf(`App version: %s
Go backend version: %s
+Golang version: %s %s
+%s
Copyright © 2015-2019 WireGuard LLC.
All Rights Reserved.`,
- C.WIREGUARD_WINDOWS_VERSION, device.WireGuardGoVersion))
+ version.WireGuardWindowsVersion, device.WireGuardGoVersion, runtime.Version(), runtime.GOARCH, version.OsName()))
hbl := walk.NewHBoxLayout()
hbl.SetMargins(walk.Margins{VNear: 10})
diff --git a/ui/updatepage.go b/ui/updatepage.go
new file mode 100644
index 00000000..85b8558b
--- /dev/null
+++ b/ui/updatepage.go
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package ui
+
+import (
+ "fmt"
+ "golang.zx2c4.com/wireguard/windows/updater"
+
+ "github.com/lxn/walk"
+)
+
+type UpdatePage struct {
+ *walk.TabPage
+}
+
+func NewUpdatePage() (*UpdatePage, error) {
+ up := &UpdatePage{}
+ var err error
+
+ if up.TabPage, err = walk.NewTabPage(); err != nil {
+ return nil, err
+ }
+
+ up.SetTitle("An Update is Available!")
+ up.SetImage(iconProvider.updateAvailableImage)
+ //TODO: make title bold
+ up.SetLayout(walk.NewVBoxLayout())
+ up.Layout().SetMargins(walk.Margins{18, 18, 18, 18})
+
+ instructions, _ := walk.NewTextLabel(up)
+ instructions.SetText("An update to WireGuard is available. It is highly advisable to update without delay.")
+
+ status, _ := walk.NewTextLabel(up)
+ status.SetText("Status: Waiting for user")
+
+ bar, _ := walk.NewProgressBar(up)
+ bar.SetVisible(false)
+
+ button, _ := walk.NewPushButton(up)
+ button.SetText("Update Now")
+
+ walk.NewVSpacer(up)
+
+ button.Clicked().Attach(func() {
+ up.SetSuspended(true)
+ button.SetEnabled(false)
+ button.SetVisible(false)
+ bar.SetVisible(true)
+ bar.SetMarqueeMode(true)
+ up.SetSuspended(false)
+ progress := updater.DownloadVerifyAndExecute()
+ go func() {
+ for {
+ dp := <-progress
+ retNow := false
+ up.Synchronize(func() {
+ if dp.Error != nil {
+ up.SetSuspended(true)
+ bar.SetVisible(false)
+ bar.SetValue(0)
+ bar.SetRange(0, 1)
+ bar.SetMarqueeMode(false)
+ button.SetVisible(true)
+ button.SetEnabled(true)
+ status.SetText(fmt.Sprintf("Error: %v. Please try again.", dp.Error))
+ up.SetSuspended(false)
+ retNow = true
+ return
+ }
+ if len(dp.Activity) > 0 {
+ status.SetText(fmt.Sprintf("Status: %s", dp.Activity))
+ }
+ if dp.BytesTotal > 0 {
+ bar.SetMarqueeMode(false)
+ bar.SetRange(0, int(dp.BytesTotal))
+ bar.SetValue(int(dp.BytesDownloaded))
+ } else {
+ bar.SetMarqueeMode(true)
+ bar.SetValue(0)
+ bar.SetRange(0, 1)
+ }
+ if dp.Complete {
+ up.SetSuspended(true)
+ bar.SetVisible(false)
+ bar.SetValue(0)
+ bar.SetRange(0, 0)
+ bar.SetMarqueeMode(false)
+ button.SetVisible(true)
+ button.SetEnabled(true)
+ status.SetText("Status: Complete!")
+ up.SetSuspended(false)
+ retNow = true
+ return
+ }
+ })
+ if retNow {
+ return
+ }
+ }
+ }()
+ })
+ return up, nil
+}