From 5cb7a511dbdfc78a6fa5a7361df11420b08afb2b Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Fri, 10 May 2019 11:00:16 +0200 Subject: ui: somewhat aggressively cache icons --- ui/iconprovider.go | 92 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 21 deletions(-) (limited to 'ui/iconprovider.go') diff --git a/ui/iconprovider.go b/ui/iconprovider.go index e2647939..9f6cc433 100644 --- a/ui/iconprovider.go +++ b/ui/iconprovider.go @@ -12,29 +12,47 @@ import ( "golang.org/x/sys/windows" "golang.zx2c4.com/wireguard/windows/service" "path" + "syscall" ) -func iconWithOverlayForState(state service.TunnelState, size int) (*walk.Icon, error) { - wireguardIcon, err := walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{size, size}) +type widthAndState struct { + width int + state service.TunnelState +} + +type widthAndDllIdx struct { + width int + idx int32 + dll string +} + +var cachedOverlayIconsForWidthAndState = make(map[widthAndState]*walk.Icon) + +func iconWithOverlayForState(state service.TunnelState, size int) (icon *walk.Icon, err error) { + icon = cachedOverlayIconsForWidthAndState[widthAndState{size, state}] + if icon != nil { + return + } + wireguardIcon, err := loadLogoIcon(size) if err != nil { - return nil, err + return } - defer wireguardIcon.Dispose() iconSize := wireguardIcon.Size() bmp, err := walk.NewBitmapWithTransparentPixels(iconSize) if err != nil { - return nil, err + return } defer bmp.Dispose() canvas, err := walk.NewCanvasFromImage(bmp) if err != nil { - return nil, err + return } defer canvas.Dispose() - if err := canvas.DrawImage(wireguardIcon, walk.Point{}); err != nil { - return nil, err + err = canvas.DrawImage(wireguardIcon, walk.Point{}) + if err != nil { + return } w := int(float64(iconSize.Width) * 0.65) @@ -42,44 +60,76 @@ func iconWithOverlayForState(state service.TunnelState, size int) (*walk.Icon, e bounds := walk.Rectangle{iconSize.Width - w, iconSize.Height - h, w, h} overlayIcon, err := iconForState(state, bounds.Width) if err != nil { - return nil, err + return } defer overlayIcon.Dispose() - if err := canvas.DrawImageStretched(overlayIcon, bounds); err != nil { - return nil, err + err = canvas.DrawImageStretched(overlayIcon, bounds) + if err != nil { + return } canvas.Dispose() - icon, err := walk.NewIconFromBitmap(bmp) - if err != nil { - return nil, err + icon, err = walk.NewIconFromBitmap(bmp) + if err == nil { + cachedOverlayIconsForWidthAndState[widthAndState{size, state}] = icon } - return icon, nil + return } +var cachedIconsForWidthAndState = make(map[widthAndState]*walk.Icon) + func iconForState(state service.TunnelState, size int) (icon *walk.Icon, err error) { + icon = cachedIconsForWidthAndState[widthAndState{size, state}] + if icon != nil { + return + } switch state { case service.TunnelStarted: icon, err = loadSystemIcon("imageres", 101, size) - case service.TunnelStopped: icon, err = walk.NewIconFromResourceWithSize("dot-gray.ico", walk.Size{size, size}) //TODO: replace with real icon - default: icon, err = loadSystemIcon("shell32", 238, size) //TODO: this doesn't look that great overlayed on the app icon } + if err == nil { + cachedIconsForWidthAndState[widthAndState{size, state}] = icon + } return } -func loadSystemIcon(dll string, index int32, size int) (*walk.Icon, error) { +var cachedSystemIconsForWidthAndDllIdx = make(map[widthAndDllIdx]*walk.Icon) + +func loadSystemIcon(dll string, index int32, size int) (icon *walk.Icon, err error) { + icon = cachedSystemIconsForWidthAndDllIdx[widthAndDllIdx{size, index, dll}] + if icon != nil { + return + } system32, err := windows.GetSystemDirectory() if err != nil { - return nil, err + return } var hicon win.HICON ret := win.SHDefExtractIcon(windows.StringToUTF16Ptr(path.Join(system32, dll+".dll")), index, 0, &hicon, nil, uint32(size)) if ret != 0 { - return nil, fmt.Errorf("Unable to find icon %d of %s due to error %d", index, dll, ret) + return nil, fmt.Errorf("Unable to find icon %d of %s: %v", index, dll, syscall.Errno(ret)) + } + icon, err = walk.NewIconFromHICON(hicon) + if err == nil { + cachedSystemIconsForWidthAndDllIdx[widthAndDllIdx{size, index, dll}] = icon + } + return +} + +var cachedLogoIconsForWidth = make(map[int]*walk.Icon) + +func loadLogoIcon(size int) (icon *walk.Icon, err error) { + icon = cachedLogoIconsForWidth[size] + if icon != nil { + return + } + icon, err = walk.NewIconFromResourceWithSize("$wireguard.ico", walk.Size{size, size}) + if err == nil { + cachedLogoIconsForWidth[size] = icon } - return walk.NewIconFromHICON(hicon) + return } -- cgit v1.2.3-59-g8ed1b