-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
hotkey_windows.go
225 lines (201 loc) · 4.44 KB
/
hotkey_windows.go
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// Copyright 2021 The golang.design Initiative Authors.
// All rights reserved. Use of this source code is governed
// by a MIT license that can be found in the LICENSE file.
//
// Written by Changkun Ou <changkun.de>
//go:build windows
package hotkey
import (
"errors"
"runtime"
"sync"
"sync/atomic"
"time"
"golang.design/x/hotkey/internal/win"
)
type platformHotkey struct {
mu sync.Mutex
hotkeyId uint64
registered bool
funcs chan func()
canceled chan struct{}
}
var hotkeyId uint64 // atomic
// register registers a system hotkey. It returns an error if
// the registration is failed. This could be that the hotkey is
// conflict with other hotkeys.
func (hk *Hotkey) register() error {
hk.mu.Lock()
if hk.registered {
hk.mu.Unlock()
return errors.New("hotkey already registered")
}
mod := uint8(0)
for _, m := range hk.mods {
mod = mod | uint8(m)
}
hk.hotkeyId = atomic.AddUint64(&hotkeyId, 1)
hk.funcs = make(chan func())
hk.canceled = make(chan struct{})
go hk.handle()
var (
ok bool
err error
done = make(chan struct{})
)
hk.funcs <- func() {
ok, err = win.RegisterHotKey(0, uintptr(hk.hotkeyId), uintptr(mod), uintptr(hk.key))
done <- struct{}{}
}
<-done
if !ok {
close(hk.canceled)
hk.mu.Unlock()
return err
}
hk.registered = true
hk.mu.Unlock()
return nil
}
// unregister deregisteres a system hotkey.
func (hk *Hotkey) unregister() error {
hk.mu.Lock()
defer hk.mu.Unlock()
if !hk.registered {
return errors.New("hotkey is not registered")
}
done := make(chan struct{})
hk.funcs <- func() {
win.UnregisterHotKey(0, uintptr(hk.hotkeyId))
done <- struct{}{}
close(hk.canceled)
}
<-done
<-hk.canceled
hk.registered = false
return nil
}
const (
// wmHotkey represents hotkey message
wmHotkey uint32 = 0x0312
wmQuit uint32 = 0x0012
)
// handle handles the hotkey event loop.
func (hk *Hotkey) handle() {
// We could optimize this. So far each hotkey is served in an
// individual thread. If we have too many hotkeys, then a program
// have to create too many threads to serve them.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
isKeyDown := false
tk := time.NewTicker(time.Second / 100)
for range tk.C {
msg := win.MSG{}
if !win.PeekMessage(&msg, 0, 0, 0) {
select {
case f := <-hk.funcs:
f()
case <-hk.canceled:
return
default:
// If the latest status is KeyDown, and AsyncKeyState is 0, consider key is up.
if win.GetAsyncKeyState(int(hk.key)) == 0 && isKeyDown {
hk.keyupIn <- Event{}
isKeyDown = false
}
}
continue
}
if !win.GetMessage(&msg, 0, 0, 0) {
return
}
switch msg.Message {
case wmHotkey:
hk.keydownIn <- Event{}
isKeyDown = true
case wmQuit:
return
}
}
}
// Modifier represents a modifier.
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey
type Modifier uint8
// All kinds of Modifiers
const (
ModAlt Modifier = 0x1
ModCtrl Modifier = 0x2
ModShift Modifier = 0x4
ModWin Modifier = 0x8
)
// Key represents a key.
// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
type Key uint16
// All kinds of Keys
const (
KeySpace Key = 0x20
Key0 Key = 0x30
Key1 Key = 0x31
Key2 Key = 0x32
Key3 Key = 0x33
Key4 Key = 0x34
Key5 Key = 0x35
Key6 Key = 0x36
Key7 Key = 0x37
Key8 Key = 0x38
Key9 Key = 0x39
KeyA Key = 0x41
KeyB Key = 0x42
KeyC Key = 0x43
KeyD Key = 0x44
KeyE Key = 0x45
KeyF Key = 0x46
KeyG Key = 0x47
KeyH Key = 0x48
KeyI Key = 0x49
KeyJ Key = 0x4A
KeyK Key = 0x4B
KeyL Key = 0x4C
KeyM Key = 0x4D
KeyN Key = 0x4E
KeyO Key = 0x4F
KeyP Key = 0x50
KeyQ Key = 0x51
KeyR Key = 0x52
KeyS Key = 0x53
KeyT Key = 0x54
KeyU Key = 0x55
KeyV Key = 0x56
KeyW Key = 0x57
KeyX Key = 0x58
KeyY Key = 0x59
KeyZ Key = 0x5A
KeyReturn Key = 0x0D
KeyEscape Key = 0x1B
KeyDelete Key = 0x2E
KeyTab Key = 0x09
KeyLeft Key = 0x25
KeyRight Key = 0x27
KeyUp Key = 0x26
KeyDown Key = 0x28
KeyF1 Key = 0x70
KeyF2 Key = 0x71
KeyF3 Key = 0x72
KeyF4 Key = 0x73
KeyF5 Key = 0x74
KeyF6 Key = 0x75
KeyF7 Key = 0x76
KeyF8 Key = 0x77
KeyF9 Key = 0x78
KeyF10 Key = 0x79
KeyF11 Key = 0x7A
KeyF12 Key = 0x7B
KeyF13 Key = 0x7C
KeyF14 Key = 0x7D
KeyF15 Key = 0x7E
KeyF16 Key = 0x7F
KeyF17 Key = 0x80
KeyF18 Key = 0x81
KeyF19 Key = 0x82
KeyF20 Key = 0x83
)