-
Notifications
You must be signed in to change notification settings - Fork 135
/
globalshortcut.go
133 lines (118 loc) · 3 KB
/
globalshortcut.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
package gallium
/*
#cgo CFLAGS: -mmacosx-version-min=10.8
#cgo CFLAGS: -DGALLIUM_DIR=${SRCDIR}
#cgo CFLAGS: -Idist/include
#include <stdlib.h>
#include "gallium/globalshortcut.h"
extern void cgo_onGlobalShortcut(int64_t);
// This is a wrapper around NSMenu_AddMenuItem that adds the function pointer
// argument, since this does not seem to be possible from Go directly.
static inline void helper_AddGlobalShortcut(
int ID,
const char* shortcutKey,
gallium_modifier_t shortcutModifier) {
GalliumAddGlobalShortcut(
ID,
shortcutKey,
shortcutModifier,
&cgo_onGlobalShortcut);
}
*/
import "C"
import (
"errors"
"fmt"
"log"
"strings"
)
var (
nextID int
handlers = make(map[int]func())
)
//export cgo_onGlobalShortcut
func cgo_onGlobalShortcut(id int) {
fmt.Println("cgo_onGlobalShortcut received ID", id)
if handler, found := handlers[id]; found {
handler()
} else {
log.Println("no handler for global shortcut ID", id)
}
}
// Modifier represents zero or more modifier keys (control, shift, option, etc)
type Modifier int
// these must be kept in sync with the gallium_modifier_t enum in cocoa.h
const (
ModifierCmd Modifier = 1 << iota
ModifierCtrl
ModifierCmdOrCtrl
ModifierAltOrOption
ModifierFn
ModifierShift
)
// KeyCombination represents a key together with zero or more modifiers. It
// is used to set up keyboard shortcuts.
type KeyCombination struct {
Key string
Modifiers Modifier
}
var (
errEmptyKey = errors.New("empty key")
errEmptyShortcut = errors.New("empty shortcut")
)
// ParseKeys parses a key combination specified as a string like "cmd shift a".
func ParseKeys(s string) (KeyCombination, error) {
// for backwards compatibility split on both "+" and " "
parts := strings.Split(s, " ")
if strings.Contains(s, "+") {
parts = strings.Split(s, "+")
}
if len(parts) == 0 {
return KeyCombination{}, errEmptyShortcut
}
var keys KeyCombination
keys.Key = parts[len(parts)-1]
if len(keys.Key) == 0 {
return KeyCombination{}, errEmptyKey
}
for _, part := range parts[:len(parts)-1] {
switch strings.ToLower(part) {
case "cmd":
keys.Modifiers |= ModifierCmd
case "ctrl":
keys.Modifiers |= ModifierCtrl
case "cmdctrl":
keys.Modifiers |= ModifierCmdOrCtrl
case "alt":
keys.Modifiers |= ModifierAltOrOption
case "option":
keys.Modifiers |= ModifierAltOrOption
case "fn":
keys.Modifiers |= ModifierFn
case "shift":
keys.Modifiers |= ModifierShift
default:
return KeyCombination{}, fmt.Errorf("unknown modifier: %s", part)
}
}
return keys, nil
}
// MustParseKeys is like ParseKeys but panics on error
func MustParseKeys(s string) KeyCombination {
keys, err := ParseKeys(s)
if err != nil {
panic(err)
}
return keys
}
// AddGlobalShortcut calls the handler whenever the key combination is pressed
// in any application.
func AddGlobalShortcut(keys KeyCombination, handler func()) {
id := nextID
nextID++
handlers[id] = handler
C.helper_AddGlobalShortcut(
C.int(id),
C.CString(keys.Key),
C.gallium_modifier_t(keys.Modifiers))
}