-
Notifications
You must be signed in to change notification settings - Fork 11
/
hotkeys.cr
119 lines (112 loc) · 3.48 KB
/
hotkeys.cr
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
require "x11"
module Run
# Manages, calls, pauses, suspends Hotkeys
class Hotkeys
@runner : Runner
def initialize(@runner)
end
def run
@runner.display.register_key_listener do |key_event, keysym, char, is_paused|
handle_event(key_event, keysym, char, is_paused)
end
@runner.display.on_pause { pause }
@runner.display.on_resume { resume }
@runner.display.on_suspend { suspend }
@runner.display.on_unsuspend { unsuspend }
end
@hotkeys = [] of Hotkey
def add(hotkey, *, subscribe = true)
# apparently keycodes are display-dependent so they can't be determined at build time. TODO: check this or change, fix type cast:
hotkey.keycode = @runner.display.adapter.as(X11).keysym_to_keycode(hotkey.keysym)
if subscribe
@hotkeys << hotkey
end
if ! hotkey.no_grab
@runner.display.adapter.grab_hotkey(hotkey)
end
end
def remove(hotkey, *, unsubscribe = true)
@runner.display.adapter.ungrab_hotkey(hotkey)
if unsubscribe
@hotkeys.delete hotkey
end
end
def add_or_update(*, hotkey_label, cmd_label, priority, active_state = nil)
if cmd_label
cmd = @runner.labels[cmd_label]?
raise RuntimeException.new "Add or update Hotkey: Label '#{cmd_label}' not found" if ! cmd
end
hotkey = @hotkeys.find { |h| h.key_str == hotkey_label }
if hotkey
remove(hotkey)
active_state = hotkey.active if active_state.nil?
else
raise RuntimeException.new "Nonexistent Hotkey.\n\nSpecifically: #{hotkey_label}" if ! cmd_label
hotkey = Hotkey.new(cmd.not_nil!, hotkey_label, priority: priority, escape_char: @runner.settings.escape_char, max_threads: @runner.settings.max_threads_per_hotkey)
hotkey.exempt_from_suspension = cmd.is_a?(Cmd::Misc::Suspend)
@hotkeys << hotkey
active_state = true if active_state.nil?
end
hotkey.cmd = cmd if cmd
hotkey.priority = priority if priority
if active_state
add(hotkey)
hotkey.active = true
else
hotkey.active = false
end
hotkey
end
def handle_event(key_event, keysym, char, is_paused)
return if is_paused
up = key_event.type == ::X11::KeyRelease || key_event.type == ::X11::ButtonRelease # TODO: externalize somehow because this is duplicate in various places
hotkey = @hotkeys.find do |hotkey|
hotkey.active &&
hotkey.keysym == keysym &&
hotkey.up == up &&
(hotkey.modifier_variants.any? &.== key_event.state) &&
(! @runner.display.suspended || hotkey.exempt_from_suspension)
end
if hotkey
# TODO: auto test for this
if ! hotkey.up && ! hotkey.no_grab && (hotkey.cmd.is_a?(Cmd::X11::Keyboard::Send) || hotkey.cmd.is_a?(Cmd::X11::Keyboard::SendRaw))
# Fix https://github.com/jordansissel/xdotool/pull/406#issuecomment-1280013095
key_map = XDo::LibXDo::Charcodemap.new
key_map.code = hotkey.keycode
@runner.display.x_do.keys_raw [key_map], pressed: false, delay: 0
end
hotkey.trigger(@runner)
end
end
def pause
@hotkeys.each do |hotkey|
remove hotkey, unsubscribe: false
end
end
def resume
@hotkeys.each do |hotkey|
add hotkey, subscribe: false
end
end
def suspend
@hotkeys.each do |hotkey|
if ! hotkey.exempt_from_suspension
remove hotkey, unsubscribe: false
end
end
end
def unsuspend
@hotkeys.each do |hotkey|
if ! hotkey.exempt_from_suspension
add hotkey, subscribe: false
end
end
end
def block_input
@runner.display.adapter.grab_keyboard
end
def unblock_input
@runner.display.adapter.ungrab_keyboard
end
end
end