-
Notifications
You must be signed in to change notification settings - Fork 21
/
dvorak-qwerty.stp
177 lines (160 loc) · 5.48 KB
/
dvorak-qwerty.stp
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
#! /usr/bin/env stap
# Copyright (c) 2017 Cloudflare, Inc.
#
# Licensed under the MIT License:
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# A systemtap script which implements "Dvorak with Qwerty hotkeys" by MITMing
# key events in the kernel.
#
# You must install systemtap and your kernel's debug symbols. On Debian:
# sudo apt install systemtap linux-headers-$(uname -r) linux-image-$(uname -r)-dbg
#
# Warning: The debug symbols are a HUGE package and will need to be updated each
# time your kernel updates.
#
# Usage:
# sudo stap -g dvorak-qwerty.stp
#
# Ctrl-C to stop intercepting.
%{
typedef struct {
int modifiers;
// Bitmask of modifier keys that are currently pressed (e.g. ctrl, alt).
int remapped_key;
// If non-zero, indicates a key which is currently down which was remapped
// to Qwerty. We must store this in case the user releases the modifiers
// before releasing the key that was modified, because we need to make sure
// the eventual key-up event matches the remapped key-down event.
} DqState;
static DqState dq_states[2] = {{0, 0}, {0, 0}};
// We need two states, one for each kernel input codepath we're modifying
// (evdev and kbd).
static int modifier_bit(int key) {
switch (key) {
case 29: return 1; // l-ctrl
case 97: return 2; // r-ctrl
case 56: return 4; // l-alt
case 100: return 8; // r-alt
case 219: return 16; // meta ("windows")
}
return 0;
}
static int qwerty2dvorak(int key) {
switch (key) {
case 12: return 40;
case 13: return 27;
case 16: return 45;
case 17: return 51;
case 18: return 32;
case 19: return 24;
case 20: return 37;
case 21: return 20;
case 22: return 33;
case 23: return 34;
case 24: return 31;
case 25: return 19;
case 26: return 12;
case 27: return 13;
case 30: return 30;
case 31: return 39;
case 32: return 35;
case 33: return 21;
case 34: return 22;
case 35: return 36;
case 36: return 46;
case 37: return 47;
case 38: return 25;
case 39: return 44;
case 40: return 16;
case 44: return 53;
case 45: return 48;
case 46: return 23;
case 47: return 52;
case 48: return 49;
case 49: return 38;
case 50: return 50;
case 51: return 17;
case 52: return 18;
case 53: return 26;
}
return key;
}
%}
function handle_event:long(code:long, down:long, index:long) %{
DqState* state = dq_states + STAP_ARG_index;
int mod = modifier_bit(STAP_ARG_code);
if (mod) {
// This is a modifier key. Update the modifier state.
if (STAP_ARG_down) {
state->modifiers |= mod;
} else {
state->modifiers &= ~mod;
}
STAP_RETVALUE = STAP_ARG_code;
} else if (STAP_ARG_code == state->remapped_key) {
// The key is currently down and was remapped at the time it was initially
// pressed. We need to remap it even if the modifier keys are no longer
// pressed, since the eventual "up" event has to have the same key code as
// the original "down" event.
if (!STAP_ARG_down) {
// This is a keyup event, so after this we don't need to track this key
// anymore.
state->remapped_key = 0;
}
STAP_RETVALUE = qwerty2dvorak(STAP_ARG_code);
} else if (STAP_ARG_down == 1 && state->modifiers && !state->remapped_key) {
// This is a keydown event for a newly-pressed key, and at least one
// modifier key is already down, and no other key is already down. So,
// record that this key is the remapped key, and remap the keypress.
STAP_RETVALUE = qwerty2dvorak(STAP_ARG_code);
// Only bother recording this key as remapped if the key code in fact
// changed. (In particular we don't want to record, say, the shift key as
// the remapped key, since this breaks ctrl+shift+whatever hotkeys.)
if (STAP_RETVALUE != STAP_ARG_code) {
state->remapped_key = STAP_ARG_code;
}
} else {
// Just a regular keypress.
STAP_RETVALUE = STAP_ARG_code;
}
%}
# X / Wayland events
probe module("evdev").function("evdev_events") {
for (i = 0; i < $count; i++) {
if ($vals[i]->type == 1) {
$vals[i]->code = handle_event($vals[i]->code, $vals[i]->value, 0)
}
}
}
# Text terminal events
probe kernel.function("kbd_event") {
if ($event_type == 1) {
$event_code = handle_event($event_code, $value, 1)
}
}
# It can take a while for the module to compile and load, so print a message
# when it does.
probe begin {
printf("STARTED\n")
}
# For good measure, print on unload as well.
probe end {
printf("STOPPED\n")
}