diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-04-28 12:27:06 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-04-29 08:41:30 +0200 |
commit | 51a410523acd4687a91ff6b48e05a4c7d711126a (patch) | |
tree | 1c3e6059569376c72336ba09f06e9a5ee5e1e833 /ui | |
parent | go.mod: use forked winio with no thirdparty deps (diff) | |
download | wireguard-windows-51a410523acd4687a91ff6b48e05a4c7d711126a.tar.xz wireguard-windows-51a410523acd4687a91ff6b48e05a4c7d711126a.zip |
updater: add initial skeleton
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to '')
-rw-r--r-- | ui/iconprovider.go | 55 | ||||
-rw-r--r-- | ui/managewindow.go | 19 | ||||
-rw-r--r-- | ui/tray.go | 13 | ||||
-rw-r--r-- | ui/tunnelsview.go | 2 | ||||
-rw-r--r-- | ui/ui.go | 41 | ||||
-rw-r--r-- | ui/updatepage.go | 106 |
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) + } +} @@ -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 @@ -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 +} |