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
|
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
*/
package ui
import (
"fmt"
"github.com/lxn/walk"
"github.com/lxn/win"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/service"
"path"
)
type IconProvider struct {
wireguardIcon *walk.Icon
overlayIconsByState map[service.TunnelState]*walk.Icon
}
func NewIconProvider() (*IconProvider, error) {
tsip := &IconProvider{overlayIconsByState: make(map[service.TunnelState]*walk.Icon)}
var err error
if tsip.wireguardIcon, err = walk.NewIconFromResource("$wireguard.ico"); err != nil {
return nil, err
}
return tsip, nil
}
func (tsip *IconProvider) Dispose() {
if tsip.overlayIconsByState != nil {
for _, icon := range tsip.overlayIconsByState {
icon.Dispose()
}
tsip.overlayIconsByState = nil
}
if tsip.wireguardIcon != nil {
tsip.wireguardIcon.Dispose()
tsip.wireguardIcon = nil
}
}
func (tsip *IconProvider) IconWithOverlayForState(state service.TunnelState) (*walk.Icon, error) {
if icon, ok := tsip.overlayIconsByState[state]; ok {
return icon, nil
}
size := tsip.wireguardIcon.Size()
bmp, err := walk.NewBitmapWithTransparentPixels(size)
if err != nil {
return nil, err
}
defer bmp.Dispose()
canvas, err := walk.NewCanvasFromImage(bmp)
if err != nil {
return nil, err
}
defer canvas.Dispose()
if err := canvas.DrawImage(tsip.wireguardIcon, walk.Point{}); err != nil {
return nil, err
}
overlayIcon, err := tsip.IconForState(state)
if err != nil {
return nil, err
}
defer overlayIcon.Dispose()
w := int(float64(size.Width) * 0.65)
h := int(float64(size.Height) * 0.65)
bounds := walk.Rectangle{size.Width - w, size.Height - h, w, h}
if err := canvas.DrawImageStretched(overlayIcon, bounds); err != nil {
return nil, err
}
canvas.Dispose()
icon, err := walk.NewIconFromBitmap(bmp)
if err != nil {
return nil, err
}
tsip.overlayIconsByState[state] = icon
return icon, nil
}
func (tsip *IconProvider) IconForState(state service.TunnelState) (icon *walk.Icon, err error) {
switch state {
case service.TunnelStarted:
icon, err = loadSystemIcon("imageres", 101)
case service.TunnelStopped:
icon, err = walk.NewIconFromResource("dot-gray.ico") //TODO: replace with real icon
default:
icon, err = loadSystemIcon("shell32", 238) //TODO: this doesn't look that great overlayed on the app icon
}
return
}
func loadSystemIcon(dll string, index uint) (*walk.Icon, error) {
system32, err := windows.GetSystemDirectory()
if err != nil {
return nil, err
}
hicon := win.ExtractIcon(win.GetModuleHandle(nil), windows.StringToUTF16Ptr(path.Join(system32, dll+".dll")), int32(index))
if hicon <= 1 {
return nil, fmt.Errorf("Unable to find icon %d of %s", index, dll)
}
return walk.NewIconFromHICON(hicon)
}
|