@vscodium/native-keymap 3.3.7-258412
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nvmrc +1 -0
- package/.release-it.yml +8 -0
- package/LICENSE +22 -0
- package/README.md +82 -0
- package/ThirdPartyNotices.txt +42 -0
- package/binding.gyp +92 -0
- package/collected-artifacts/native-keymap-darwin-arm64/native-keymap-3.3.7-258412-darwin-arm64.tar.gz +0 -0
- package/collected-artifacts/native-keymap-darwin-arm64/native-keymap-3.3.7-258412-darwin-arm64.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-darwin-x64/native-keymap-3.3.7-258412-darwin-x64.tar.gz +0 -0
- package/collected-artifacts/native-keymap-darwin-x64/native-keymap-3.3.7-258412-darwin-x64.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-linux-arm64/native-keymap-3.3.7-258412-linux-arm64.tar.gz +0 -0
- package/collected-artifacts/native-keymap-linux-arm64/native-keymap-3.3.7-258412-linux-arm64.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-linux-armhf/native-keymap-3.3.7-258412-linux-armhf.tar.gz +0 -0
- package/collected-artifacts/native-keymap-linux-armhf/native-keymap-3.3.7-258412-linux-armhf.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-linux-loong64/native-keymap-3.3.7-258412-linux-loong64.tar.gz +0 -0
- package/collected-artifacts/native-keymap-linux-loong64/native-keymap-3.3.7-258412-linux-loong64.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-linux-ppc64le/native-keymap-3.3.7-258412-linux-ppc64le.tar.gz +0 -0
- package/collected-artifacts/native-keymap-linux-ppc64le/native-keymap-3.3.7-258412-linux-ppc64le.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-linux-riscv64/native-keymap-3.3.7-258412-linux-riscv64.tar.gz +0 -0
- package/collected-artifacts/native-keymap-linux-riscv64/native-keymap-3.3.7-258412-linux-riscv64.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-linux-s390x/native-keymap-3.3.7-258412-linux-s390x.tar.gz +0 -0
- package/collected-artifacts/native-keymap-linux-s390x/native-keymap-3.3.7-258412-linux-s390x.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-linux-x64/native-keymap-3.3.7-258412-linux-x64.tar.gz +0 -0
- package/collected-artifacts/native-keymap-linux-x64/native-keymap-3.3.7-258412-linux-x64.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-win32-arm64/native-keymap-3.3.7-258412-win32-arm64.tar.gz +0 -0
- package/collected-artifacts/native-keymap-win32-arm64/native-keymap-3.3.7-258412-win32-arm64.tar.gz.sha256 +1 -0
- package/collected-artifacts/native-keymap-win32-x64/native-keymap-3.3.7-258412-win32-x64.tar.gz +0 -0
- package/collected-artifacts/native-keymap-win32-x64/native-keymap-3.3.7-258412-win32-x64.tar.gz.sha256 +1 -0
- package/deps/chromium/dom_code_data.inc +618 -0
- package/deps/chromium/keyboard_codes.h +23 -0
- package/deps/chromium/macros.h +32 -0
- package/deps/chromium/x/keysym_to_unicode.cc +896 -0
- package/deps/chromium/x/keysym_to_unicode.h +26 -0
- package/index.d.ts +70 -0
- package/index.js +70 -0
- package/package.json +25 -0
- package/release/native-keymap-3.3.7-258412-darwin-arm64.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-darwin-arm64.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-darwin-x64.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-darwin-x64.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-linux-arm64.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-linux-arm64.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-linux-armhf.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-linux-armhf.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-linux-loong64.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-linux-loong64.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-linux-ppc64le.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-linux-ppc64le.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-linux-riscv64.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-linux-riscv64.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-linux-s390x.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-linux-s390x.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-linux-x64.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-linux-x64.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-win32-arm64.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-win32-arm64.tar.gz.sha256 +1 -0
- package/release/native-keymap-3.3.7-258412-win32-x64.tar.gz +0 -0
- package/release/native-keymap-3.3.7-258412-win32-x64.tar.gz.sha256 +1 -0
- package/src/common.h +92 -0
- package/src/keyboard_mac.mm +209 -0
- package/src/keyboard_win.cc +476 -0
- package/src/keyboard_x.cc +407 -0
- package/src/keymapping.cc +147 -0
- package/src/keymapping.h +65 -0
- package/src/string_conversion.cc +67 -0
- package/src/string_conversion.h +18 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
#include "keymapping.h"
|
|
7
|
+
#include "string_conversion.h"
|
|
8
|
+
#include "common.h"
|
|
9
|
+
|
|
10
|
+
#include <X11/XKBlib.h>
|
|
11
|
+
#include <X11/Xlib.h>
|
|
12
|
+
#include <X11/Xutil.h>
|
|
13
|
+
#include <X11/extensions/XKBrules.h>
|
|
14
|
+
|
|
15
|
+
#include "../deps/chromium/macros.h"
|
|
16
|
+
#include "../deps/chromium/x/keysym_to_unicode.h"
|
|
17
|
+
|
|
18
|
+
typedef struct _XDisplay XDisplay;
|
|
19
|
+
|
|
20
|
+
namespace {
|
|
21
|
+
|
|
22
|
+
class KeyModifierMaskToXModifierMask {
|
|
23
|
+
public:
|
|
24
|
+
static KeyModifierMaskToXModifierMask& GetInstance() {
|
|
25
|
+
static KeyModifierMaskToXModifierMask instance;
|
|
26
|
+
return instance;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
void Initialize(Display* display) {
|
|
30
|
+
alt_modifier_ = 0;
|
|
31
|
+
meta_modifier_ = 0;
|
|
32
|
+
num_lock_modifier_ = 0;
|
|
33
|
+
mode_switch_modifier_ = 0;
|
|
34
|
+
level3_modifier_ = 0; // AltGr is often mapped to the level3 modifier
|
|
35
|
+
level5_modifier_ = 0; // AltGr is mapped to the level5 modifier in the Neo layout family
|
|
36
|
+
effective_group_index_ = 0;
|
|
37
|
+
|
|
38
|
+
if (!display) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// See https://www.x.org/releases/X11R7.6/doc/libX11/specs/XKB/xkblib.html#determining_keyboard_state
|
|
43
|
+
XkbStateRec xkb_state;
|
|
44
|
+
XkbGetState(display, XkbUseCoreKbd, &xkb_state);
|
|
45
|
+
effective_group_index_ = xkb_state.group;
|
|
46
|
+
|
|
47
|
+
XModifierKeymap* mod_map = XGetModifierMapping(display);
|
|
48
|
+
int max_mod_keys = mod_map->max_keypermod;
|
|
49
|
+
for (int mod_index = 0; mod_index <= 8; ++mod_index) {
|
|
50
|
+
for (int key_index = 0; key_index < max_mod_keys; ++key_index) {
|
|
51
|
+
int key = mod_map->modifiermap[mod_index * max_mod_keys + key_index];
|
|
52
|
+
if (!key) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
int keysym = XkbKeycodeToKeysym(display, key, 0, 0);
|
|
57
|
+
if (!keysym) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
|
|
62
|
+
alt_modifier_ = 1 << mod_index;
|
|
63
|
+
}
|
|
64
|
+
if (keysym == XK_Mode_switch) {
|
|
65
|
+
mode_switch_modifier_ = 1 << mod_index;
|
|
66
|
+
}
|
|
67
|
+
if (keysym == XK_Meta_L || keysym == XK_Super_L || keysym == XK_Meta_R || keysym == XK_Super_R) {
|
|
68
|
+
meta_modifier_ = 1 << mod_index;
|
|
69
|
+
}
|
|
70
|
+
if (keysym == XK_Num_Lock) {
|
|
71
|
+
num_lock_modifier_ = 1 << mod_index;
|
|
72
|
+
}
|
|
73
|
+
if (keysym == XK_ISO_Level3_Shift) {
|
|
74
|
+
level3_modifier_ = 1 << mod_index;
|
|
75
|
+
}
|
|
76
|
+
if (keysym == XK_ISO_Level5_Shift) {
|
|
77
|
+
level5_modifier_ = 1 << mod_index;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
XFreeModifiermap(mod_map);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
int XStateFromKeyMod(int keyMod) {
|
|
86
|
+
int x_modifier = 0;
|
|
87
|
+
|
|
88
|
+
// Ctrl + Alt => AltGr
|
|
89
|
+
if (keyMod & kControlKeyModifierMask && keyMod & kAltKeyModifierMask) {
|
|
90
|
+
x_modifier |= mode_switch_modifier_;//alt_r_modifier;
|
|
91
|
+
} else if (keyMod & kControlKeyModifierMask) {
|
|
92
|
+
x_modifier |= ControlMask;
|
|
93
|
+
} else if (keyMod & kAltKeyModifierMask) {
|
|
94
|
+
x_modifier |= alt_modifier_;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (keyMod & kShiftKeyModifierMask) {
|
|
98
|
+
x_modifier |= ShiftMask;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (keyMod & kMetaKeyModifierMask) {
|
|
102
|
+
x_modifier |= meta_modifier_;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (keyMod & kNumLockKeyModifierMask) {
|
|
106
|
+
x_modifier |= num_lock_modifier_;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (keyMod & kLevel3KeyModifierMask) {
|
|
110
|
+
x_modifier |= level3_modifier_;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (keyMod & kLevel5KeyModifierMask) {
|
|
114
|
+
x_modifier |= level5_modifier_;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// See https://www.x.org/releases/X11R7.6/doc/libX11/specs/XKB/xkblib.html#xkb_state_to_core_protocol_state_transformation
|
|
118
|
+
x_modifier |= (effective_group_index_ << 13);
|
|
119
|
+
|
|
120
|
+
return x_modifier;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private:
|
|
124
|
+
KeyModifierMaskToXModifierMask() {
|
|
125
|
+
Initialize(NULL);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
int alt_modifier_;
|
|
129
|
+
int meta_modifier_;
|
|
130
|
+
int num_lock_modifier_;
|
|
131
|
+
int mode_switch_modifier_;
|
|
132
|
+
int level3_modifier_;
|
|
133
|
+
int level5_modifier_;
|
|
134
|
+
int effective_group_index_;
|
|
135
|
+
|
|
136
|
+
KeyModifierMaskToXModifierMask(const KeyModifierMaskToXModifierMask&) = delete;
|
|
137
|
+
KeyModifierMaskToXModifierMask& operator=(const KeyModifierMaskToXModifierMask&) = delete;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
std::string GetStrFromXEvent(const XEvent* xev) {
|
|
141
|
+
const XKeyEvent* xkey = &xev->xkey;
|
|
142
|
+
KeySym keysym = XK_VoidSymbol;
|
|
143
|
+
XLookupString(const_cast<XKeyEvent*>(xkey), NULL, 0, &keysym, NULL);
|
|
144
|
+
uint16_t character = ui::GetUnicodeCharacterFromXKeySym(keysym);
|
|
145
|
+
|
|
146
|
+
if (!character)
|
|
147
|
+
return std::string();
|
|
148
|
+
|
|
149
|
+
wchar_t value = character;
|
|
150
|
+
|
|
151
|
+
return vscode_keyboard::UTF16toUTF8(&value, 1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
} // namespace
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
namespace vscode_keyboard {
|
|
158
|
+
|
|
159
|
+
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) {usb, xkb, code}
|
|
160
|
+
#define DOM_CODE_DECLARATION const KeycodeMapEntry usb_keycode_map[] =
|
|
161
|
+
#include "../deps/chromium/dom_code_data.inc"
|
|
162
|
+
#undef DOM_CODE
|
|
163
|
+
#undef DOM_CODE_DECLARATION
|
|
164
|
+
|
|
165
|
+
napi_value GetKeyMapImpl(napi_env env, napi_callback_info info) {
|
|
166
|
+
|
|
167
|
+
napi_value result;
|
|
168
|
+
NAPI_CALL(env, napi_create_object(env, &result));
|
|
169
|
+
|
|
170
|
+
Display *display;
|
|
171
|
+
if (!(display = XOpenDisplay(""))) {
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
XEvent event;
|
|
176
|
+
memset(&event, 0, sizeof(XEvent));
|
|
177
|
+
XKeyEvent* key_event = &event.xkey;
|
|
178
|
+
key_event->display = display;
|
|
179
|
+
key_event->type = KeyPress;
|
|
180
|
+
|
|
181
|
+
KeyModifierMaskToXModifierMask *mask_provider = &KeyModifierMaskToXModifierMask::GetInstance();
|
|
182
|
+
mask_provider->Initialize(display);
|
|
183
|
+
|
|
184
|
+
size_t cnt = sizeof(usb_keycode_map) / sizeof(usb_keycode_map[0]);
|
|
185
|
+
|
|
186
|
+
for (size_t i = 0; i < cnt; ++i) {
|
|
187
|
+
const char *code = usb_keycode_map[i].code;
|
|
188
|
+
int native_keycode = usb_keycode_map[i].native_keycode;
|
|
189
|
+
|
|
190
|
+
if (!code || native_keycode <= 0) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
napi_value entry;
|
|
195
|
+
NAPI_CALL(env, napi_create_object(env, &entry));
|
|
196
|
+
|
|
197
|
+
key_event->keycode = native_keycode;
|
|
198
|
+
{
|
|
199
|
+
key_event->state = mask_provider->XStateFromKeyMod(0);
|
|
200
|
+
std::string value = GetStrFromXEvent(&event);
|
|
201
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, entry, "value", value.c_str()));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
{
|
|
205
|
+
key_event->state = mask_provider->XStateFromKeyMod(kShiftKeyModifierMask);
|
|
206
|
+
std::string with_shift = GetStrFromXEvent(&event);
|
|
207
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, entry, "withShift", with_shift.c_str()));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
{
|
|
211
|
+
key_event->state = mask_provider->XStateFromKeyMod(kLevel3KeyModifierMask);
|
|
212
|
+
std::string with_alt_gr = GetStrFromXEvent(&event);
|
|
213
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, entry, "withAltGr", with_alt_gr.c_str()));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
{
|
|
217
|
+
key_event->state = mask_provider->XStateFromKeyMod(kShiftKeyModifierMask | kLevel3KeyModifierMask);
|
|
218
|
+
std::string with_shift_alt_gr = GetStrFromXEvent(&event);
|
|
219
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, entry, "withShiftAltGr", with_shift_alt_gr.c_str()));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
{
|
|
223
|
+
// level 5 is important for the Neo layout family
|
|
224
|
+
key_event->state = mask_provider->XStateFromKeyMod(kLevel5KeyModifierMask);
|
|
225
|
+
std::string with_level5 = GetStrFromXEvent(&event);
|
|
226
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, entry, "withLevel5", with_level5.c_str()));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
{
|
|
230
|
+
// level3 + level5 is Level 6 in terms of the Neo layout family. (Shift + level5 has no special meaning.)
|
|
231
|
+
key_event->state = mask_provider->XStateFromKeyMod(kLevel3KeyModifierMask | kLevel5KeyModifierMask);
|
|
232
|
+
std::string with_level3_level5 = GetStrFromXEvent(&event);
|
|
233
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, entry, "withLevel3Level5", with_level3_level5.c_str()));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
NAPI_CALL(env, napi_set_named_property(env, result, code, entry));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
XFlush(display);
|
|
240
|
+
XCloseDisplay(display);
|
|
241
|
+
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
napi_value GetCurrentKeyboardLayoutImpl(napi_env env, napi_callback_info info) {
|
|
246
|
+
|
|
247
|
+
napi_value result;
|
|
248
|
+
NAPI_CALL(env, napi_get_null(env, &result));
|
|
249
|
+
|
|
250
|
+
Display *display;
|
|
251
|
+
if (!(display = XOpenDisplay(""))) {
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// See https://www.x.org/releases/X11R7.6/doc/libX11/specs/XKB/xkblib.html#determining_keyboard_state
|
|
256
|
+
XkbStateRec xkb_state;
|
|
257
|
+
XkbGetState(display, XkbUseCoreKbd, &xkb_state);
|
|
258
|
+
int effective_group_index = xkb_state.group;
|
|
259
|
+
|
|
260
|
+
XkbRF_VarDefsRec vdr;
|
|
261
|
+
char *tmp = NULL;
|
|
262
|
+
int res = XkbRF_GetNamesProp(display, &tmp, &vdr);
|
|
263
|
+
if (res) {
|
|
264
|
+
NAPI_CALL(env, napi_create_object(env, &result));
|
|
265
|
+
|
|
266
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, result, "model", vdr.model ? vdr.model : ""));
|
|
267
|
+
NAPI_CALL(env, napi_set_named_property_int32(env, result, "group", effective_group_index));
|
|
268
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, result, "layout", vdr.layout ? vdr.layout : ""));
|
|
269
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, result, "variant", vdr.variant ? vdr.variant : ""));
|
|
270
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, result, "options", vdr.options ? vdr.options : ""));
|
|
271
|
+
NAPI_CALL(env, napi_set_named_property_string_utf8(env, result, "rules", tmp ? tmp : ""));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
XFlush(display);
|
|
275
|
+
XCloseDisplay(display);
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
typedef struct {
|
|
280
|
+
int effective_group_index;
|
|
281
|
+
std::string layout;
|
|
282
|
+
std::string variant;
|
|
283
|
+
} KbState;
|
|
284
|
+
|
|
285
|
+
bool KbStatesEqual(KbState *a, KbState *b) {
|
|
286
|
+
return (
|
|
287
|
+
a->effective_group_index == b->effective_group_index
|
|
288
|
+
&& a->layout == b->layout
|
|
289
|
+
&& a->variant == b->variant
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
void ReadKbState(Display *display, KbState *dst) {
|
|
294
|
+
// See https://www.x.org/releases/X11R7.6/doc/libX11/specs/XKB/xkblib.html#determining_keyboard_state
|
|
295
|
+
// Get effective group index
|
|
296
|
+
XkbStateRec xkb_state;
|
|
297
|
+
XkbGetState(display, XkbUseCoreKbd, &xkb_state);
|
|
298
|
+
dst->effective_group_index = xkb_state.group;
|
|
299
|
+
|
|
300
|
+
XkbRF_VarDefsRec vdr;
|
|
301
|
+
char *tmp = NULL;
|
|
302
|
+
int res = XkbRF_GetNamesProp(display, &tmp, &vdr);
|
|
303
|
+
if (res) {
|
|
304
|
+
dst->layout = (vdr.layout ? vdr.layout : "");
|
|
305
|
+
dst->variant = (vdr.variant ? vdr.variant : "");
|
|
306
|
+
} else {
|
|
307
|
+
dst->layout = "";
|
|
308
|
+
dst->variant = "";
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
static void FlushAndCloseDisplay(void *arg) {
|
|
313
|
+
Display *display = static_cast<Display*>(arg);
|
|
314
|
+
XFlush(display);
|
|
315
|
+
XCloseDisplay(display);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
void* ListenToXEvents(void *arg) {
|
|
319
|
+
NotificationCallbackData *data = static_cast<NotificationCallbackData*>(arg);
|
|
320
|
+
|
|
321
|
+
Display *display;
|
|
322
|
+
if (!(display = XOpenDisplay(""))) {
|
|
323
|
+
return NULL;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
pthread_cleanup_push(FlushAndCloseDisplay, display);
|
|
327
|
+
|
|
328
|
+
int xkblib_major = XkbMajorVersion;
|
|
329
|
+
int xkblib_minor = XkbMinorVersion;
|
|
330
|
+
if (!XkbLibraryVersion(&xkblib_major, &xkblib_minor)) {
|
|
331
|
+
return NULL;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
int opcode = 0;
|
|
335
|
+
int xkb_base_event_code = 0;
|
|
336
|
+
int xkb_base_error_code = 0;
|
|
337
|
+
if (!XkbQueryExtension(display, &opcode, &xkb_base_event_code, &xkb_base_error_code, &xkblib_major, &xkblib_minor)) {
|
|
338
|
+
return NULL;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// See https://www.x.org/releases/X11R7.6/doc/libX11/specs/XKB/xkblib.html#xkb_event_types
|
|
342
|
+
// Listen only to the `XkbStateNotify` event
|
|
343
|
+
XkbSelectEvents(display, XkbUseCoreKbd, XkbAllEventsMask, XkbStateNotifyMask);
|
|
344
|
+
|
|
345
|
+
KbState last_state;
|
|
346
|
+
ReadKbState(display, &last_state);
|
|
347
|
+
|
|
348
|
+
XkbEvent event;
|
|
349
|
+
KbState current_state;
|
|
350
|
+
fd_set in_fds;
|
|
351
|
+
struct timeval tv;
|
|
352
|
+
int x11_fd = ConnectionNumber(display);
|
|
353
|
+
|
|
354
|
+
while (true) {
|
|
355
|
+
// See https://stackoverflow.com/a/8592969 which explains
|
|
356
|
+
// the technique of waiting for an XEvent with a timeout
|
|
357
|
+
|
|
358
|
+
// Create a File Description Set containing x11_fd
|
|
359
|
+
FD_ZERO(&in_fds);
|
|
360
|
+
FD_SET(x11_fd, &in_fds);
|
|
361
|
+
|
|
362
|
+
// Set the timer to 1s.
|
|
363
|
+
tv.tv_usec = 0;
|
|
364
|
+
tv.tv_sec = 1;
|
|
365
|
+
|
|
366
|
+
// Wait for X Event or the timer
|
|
367
|
+
select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
|
|
368
|
+
|
|
369
|
+
// Handle pending XEvents
|
|
370
|
+
while (XPending(display)) {
|
|
371
|
+
|
|
372
|
+
XNextEvent(display, &event.core);
|
|
373
|
+
|
|
374
|
+
if (event.type == xkb_base_event_code && event.any.xkb_type == XkbStateNotify) {
|
|
375
|
+
ReadKbState(display, ¤t_state);
|
|
376
|
+
// printf("current state: %d | %s | %s\n", current_state.effective_group_index, current_state.layout.c_str(), current_state.variant.c_str());
|
|
377
|
+
if (!KbStatesEqual(&last_state, ¤t_state)) {
|
|
378
|
+
last_state.effective_group_index = current_state.effective_group_index;
|
|
379
|
+
last_state.layout = current_state.layout;
|
|
380
|
+
last_state.variant = current_state.variant;
|
|
381
|
+
|
|
382
|
+
InvokeNotificationCallback(data);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
pthread_cleanup_pop(1);
|
|
389
|
+
|
|
390
|
+
return NULL;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
void RegisterKeyboardLayoutChangeListenerImpl(NotificationCallbackData *data) {
|
|
394
|
+
pthread_create(&data->tid, NULL, &ListenToXEvents, data);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
void DisposeKeyboardLayoutChangeListenerImpl(NotificationCallbackData *data) {
|
|
398
|
+
pthread_cancel(data->tid);
|
|
399
|
+
void *res;
|
|
400
|
+
pthread_join(data->tid, &res);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
napi_value IsISOKeyboardImpl(napi_env env, napi_callback_info info) {
|
|
404
|
+
return napi_fetch_undefined(env);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
} // namespace vscode_keyboard
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
#define NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT
|
|
7
|
+
#include <node.h>
|
|
8
|
+
#include <map>
|
|
9
|
+
|
|
10
|
+
#include "keymapping.h"
|
|
11
|
+
#include "common.h"
|
|
12
|
+
|
|
13
|
+
namespace vscode_keyboard {
|
|
14
|
+
|
|
15
|
+
napi_status napi_set_named_property_string_utf8(napi_env env, napi_value object, const char *utf8_name, const char *value) {
|
|
16
|
+
napi_value _value;
|
|
17
|
+
NAPI_CALL_RETURN_STATUS(env, napi_create_string_utf8(env, value, NAPI_AUTO_LENGTH, &_value));
|
|
18
|
+
NAPI_CALL_RETURN_STATUS(env, napi_set_named_property(env, object, utf8_name, _value));
|
|
19
|
+
return napi_ok;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
napi_status napi_set_named_property_int32(napi_env env, napi_value object, const char *utf8_name, int value) {
|
|
23
|
+
napi_value _value;
|
|
24
|
+
NAPI_CALL_RETURN_STATUS(env, napi_create_int32(env, value, &_value));
|
|
25
|
+
NAPI_CALL_RETURN_STATUS(env, napi_set_named_property(env, object, utf8_name, _value));
|
|
26
|
+
return napi_ok;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
napi_value napi_fetch_null(napi_env env) {
|
|
30
|
+
napi_value result;
|
|
31
|
+
NAPI_CALL(env, napi_get_null(env, &result));
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
napi_value napi_fetch_undefined(napi_env env) {
|
|
36
|
+
napi_value result;
|
|
37
|
+
NAPI_CALL(env, napi_get_undefined(env, &result));
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
napi_value napi_fetch_boolean(napi_env env, bool value) {
|
|
42
|
+
napi_value result;
|
|
43
|
+
NAPI_CALL(env, napi_get_boolean(env, value, &result));
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
void InvokeNotificationCallback(NotificationCallbackData *data) {
|
|
48
|
+
if (data->tsfn == NULL) {
|
|
49
|
+
// This indicates we are in the shutdown phase and the thread safe function has been finalized
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// No need to call napi_acquire_threadsafe_function because
|
|
54
|
+
// the refcount is set to 1 in the main thread.
|
|
55
|
+
napi_call_threadsafe_function(data->tsfn, NULL, napi_tsfn_blocking);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static void NotifyJS(napi_env env, napi_value func, void* context, void* data) {
|
|
59
|
+
// env may be NULL if nodejs is shutting down
|
|
60
|
+
if (env != NULL) {
|
|
61
|
+
napi_value global;
|
|
62
|
+
NAPI_CALL_RETURN_VOID(env, napi_get_global(env, &global));
|
|
63
|
+
|
|
64
|
+
std::vector<napi_value> argv;
|
|
65
|
+
NAPI_CALL_RETURN_VOID(env, napi_call_function(env, global, func, argv.size(), argv.data(), NULL));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static void FinalizeThreadsafeFunction(napi_env env, void* raw_data, void* hint) {
|
|
70
|
+
NotificationCallbackData *data;
|
|
71
|
+
napi_get_instance_data(env, (void**)&data);
|
|
72
|
+
data->tsfn = NULL;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static void EnvCleanupHook(void *raw_data) {
|
|
76
|
+
NotificationCallbackData* data = static_cast<NotificationCallbackData*>(raw_data);
|
|
77
|
+
DisposeKeyboardLayoutChangeListenerImpl(data);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
napi_value OnDidChangeKeyboardLayoutImpl(napi_env env, napi_callback_info info) {
|
|
81
|
+
size_t argc = 2;
|
|
82
|
+
napi_value args[2];
|
|
83
|
+
NotificationCallbackData *data;
|
|
84
|
+
NAPI_CALL(env, napi_get_instance_data(env, (void**)&data));
|
|
85
|
+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
|
86
|
+
|
|
87
|
+
NAPI_ASSERT(env, argc == 1, "Wrong number of arguments. Expects a single argument.");
|
|
88
|
+
|
|
89
|
+
napi_valuetype valuetype0;
|
|
90
|
+
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
|
|
91
|
+
NAPI_ASSERT(env, valuetype0 == napi_function, "Wrong type of arguments. Expects a function as first argument.");
|
|
92
|
+
|
|
93
|
+
napi_value func = args[0];
|
|
94
|
+
|
|
95
|
+
napi_value resource_name;
|
|
96
|
+
NAPI_CALL(env, napi_create_string_utf8(env, "onDidChangeKeyboardLayoutCallback", NAPI_AUTO_LENGTH, &resource_name));
|
|
97
|
+
|
|
98
|
+
// Convert the callback retrieved from JavaScript into a thread-safe function
|
|
99
|
+
napi_threadsafe_function tsfn;
|
|
100
|
+
NAPI_CALL(env, napi_create_threadsafe_function(env, func, NULL, resource_name, 0, 1, NULL,
|
|
101
|
+
FinalizeThreadsafeFunction, NULL, NotifyJS,
|
|
102
|
+
&tsfn));
|
|
103
|
+
data->tsfn = tsfn;
|
|
104
|
+
|
|
105
|
+
RegisterKeyboardLayoutChangeListenerImpl(data);
|
|
106
|
+
|
|
107
|
+
napi_add_env_cleanup_hook(env, EnvCleanupHook, data);
|
|
108
|
+
|
|
109
|
+
return napi_fetch_undefined(env);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
void DeleteInstanceData(napi_env env, void *raw_data, void *hint) {
|
|
113
|
+
NotificationCallbackData *data = static_cast<NotificationCallbackData*>(raw_data);
|
|
114
|
+
delete data;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
napi_value Init(napi_env env, napi_value exports) {
|
|
118
|
+
NotificationCallbackData *data = new NotificationCallbackData();
|
|
119
|
+
NAPI_CALL(env, napi_set_instance_data(env, data, DeleteInstanceData, NULL));
|
|
120
|
+
|
|
121
|
+
{
|
|
122
|
+
napi_value get_key_map_fn;
|
|
123
|
+
NAPI_CALL(env, napi_create_function(env, NULL, 0, GetKeyMapImpl, NULL, &get_key_map_fn));
|
|
124
|
+
NAPI_CALL(env, napi_set_named_property(env, exports, "getKeyMap", get_key_map_fn));
|
|
125
|
+
}
|
|
126
|
+
{
|
|
127
|
+
napi_value get_current_keyboard_layout_fn;
|
|
128
|
+
NAPI_CALL(env, napi_create_function(env, NULL, 0, GetCurrentKeyboardLayoutImpl, NULL, &get_current_keyboard_layout_fn));
|
|
129
|
+
NAPI_CALL(env, napi_set_named_property(env, exports, "getCurrentKeyboardLayout", get_current_keyboard_layout_fn));
|
|
130
|
+
}
|
|
131
|
+
{
|
|
132
|
+
napi_value on_did_change_keyboard_layout_fn;
|
|
133
|
+
NAPI_CALL(env, napi_create_function(env, NULL, 0, OnDidChangeKeyboardLayoutImpl, NULL, &on_did_change_keyboard_layout_fn));
|
|
134
|
+
NAPI_CALL(env, napi_set_named_property(env, exports, "onDidChangeKeyboardLayout", on_did_change_keyboard_layout_fn));
|
|
135
|
+
}
|
|
136
|
+
{
|
|
137
|
+
napi_value is_iso_keyboard_fn;
|
|
138
|
+
NAPI_CALL(env, napi_create_function(env, NULL, 0, IsISOKeyboardImpl, NULL, &is_iso_keyboard_fn));
|
|
139
|
+
NAPI_CALL(env, napi_set_named_property(env, exports, "isISOKeyboard", is_iso_keyboard_fn));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return exports;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
|
|
146
|
+
|
|
147
|
+
} // namespace vscode_keyboard
|
package/src/keymapping.h
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
#ifndef KEYMAPPING_H_
|
|
7
|
+
#define KEYMAPPING_H_
|
|
8
|
+
|
|
9
|
+
#include <node_api.h>
|
|
10
|
+
|
|
11
|
+
#include <string>
|
|
12
|
+
#include <vector>
|
|
13
|
+
#include "../deps/chromium/keyboard_codes.h"
|
|
14
|
+
|
|
15
|
+
#if defined(__unix__)
|
|
16
|
+
#include <pthread.h>
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
namespace vscode_keyboard {
|
|
20
|
+
|
|
21
|
+
// This structure is used to define the keycode mapping table.
|
|
22
|
+
// It is defined here because the unittests need access to it.
|
|
23
|
+
typedef struct {
|
|
24
|
+
// USB keycode:
|
|
25
|
+
// Upper 16-bits: USB Usage Page.
|
|
26
|
+
// Lower 16-bits: USB Usage Id: Assigned ID within this usage page.
|
|
27
|
+
uint32_t usb_keycode;
|
|
28
|
+
|
|
29
|
+
// Contains one of the following:
|
|
30
|
+
// On Linux: XKB scancode
|
|
31
|
+
// On Windows: Windows OEM scancode
|
|
32
|
+
// On Mac: Mac keycode
|
|
33
|
+
int native_keycode;
|
|
34
|
+
|
|
35
|
+
// The UIEvents (aka: DOM4Events) |code| value as defined in:
|
|
36
|
+
// http://www.w3.org/TR/DOM-Level-3-Events-code/
|
|
37
|
+
const char* code;
|
|
38
|
+
} KeycodeMapEntry;
|
|
39
|
+
|
|
40
|
+
typedef struct {
|
|
41
|
+
#if defined(_WIN32)
|
|
42
|
+
void* listener;
|
|
43
|
+
#endif
|
|
44
|
+
#if defined(__unix__)
|
|
45
|
+
pthread_t tid;
|
|
46
|
+
#endif
|
|
47
|
+
volatile napi_threadsafe_function tsfn;
|
|
48
|
+
} NotificationCallbackData;
|
|
49
|
+
|
|
50
|
+
napi_value GetKeyMapImpl(napi_env env, napi_callback_info info);
|
|
51
|
+
napi_value GetCurrentKeyboardLayoutImpl(napi_env env, napi_callback_info info);
|
|
52
|
+
void RegisterKeyboardLayoutChangeListenerImpl(NotificationCallbackData *data);
|
|
53
|
+
void DisposeKeyboardLayoutChangeListenerImpl(NotificationCallbackData *data);
|
|
54
|
+
napi_value IsISOKeyboardImpl(napi_env env, napi_callback_info info);
|
|
55
|
+
|
|
56
|
+
void InvokeNotificationCallback(NotificationCallbackData *data);
|
|
57
|
+
napi_status napi_set_named_property_string_utf8(napi_env env, napi_value object, const char *utf8_name, const char *value);
|
|
58
|
+
napi_status napi_set_named_property_int32(napi_env env, napi_value object, const char *utf8_name, int value);
|
|
59
|
+
napi_value napi_fetch_null(napi_env env);
|
|
60
|
+
napi_value napi_fetch_undefined(napi_env env);
|
|
61
|
+
napi_value napi_fetch_boolean(napi_env env, bool value);
|
|
62
|
+
|
|
63
|
+
} // namespace vscode_keyboard
|
|
64
|
+
|
|
65
|
+
#endif // KEYMAPPING_H_
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
#include "string_conversion.h"
|
|
7
|
+
|
|
8
|
+
namespace vscode_keyboard {
|
|
9
|
+
|
|
10
|
+
wchar_t conversion_buffer[10];
|
|
11
|
+
|
|
12
|
+
std::string UTF16toUTF8(const wchar_t * in, int length) {
|
|
13
|
+
if (length < 10) {
|
|
14
|
+
for (int i = 0; i < length; ++i) {
|
|
15
|
+
conversion_buffer[i] = in[i];
|
|
16
|
+
}
|
|
17
|
+
conversion_buffer[length] = 0;
|
|
18
|
+
return UTF16to8(conversion_buffer);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
wchar_t *t = new wchar_t[length + 1];
|
|
22
|
+
for (int i = 0; i < length; ++i) {
|
|
23
|
+
t[i] = in[i];
|
|
24
|
+
}
|
|
25
|
+
t[length] = 0;
|
|
26
|
+
std::string result = UTF16to8(t);
|
|
27
|
+
delete []t;
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// http://stackoverflow.com/a/148766
|
|
33
|
+
std::string UTF16to8(const wchar_t * in) {
|
|
34
|
+
std::string out;
|
|
35
|
+
unsigned int codepoint = 0;
|
|
36
|
+
for (; *in != 0; ++in) {
|
|
37
|
+
if (*in >= 0xd800 && *in <= 0xdbff) {
|
|
38
|
+
codepoint = ((*in - 0xd800) << 10) + 0x10000;
|
|
39
|
+
} else {
|
|
40
|
+
if (*in >= 0xdc00 && *in <= 0xdfff) {
|
|
41
|
+
codepoint |= *in - 0xdc00;
|
|
42
|
+
} else {
|
|
43
|
+
codepoint = *in;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (codepoint <= 0x7f) {
|
|
47
|
+
out.append(1, static_cast<char>(codepoint));
|
|
48
|
+
} else if (codepoint <= 0x7ff) {
|
|
49
|
+
out.append(1, static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f)));
|
|
50
|
+
out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
|
|
51
|
+
} else if (codepoint <= 0xffff) {
|
|
52
|
+
out.append(1, static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f)));
|
|
53
|
+
out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
|
|
54
|
+
out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
|
|
55
|
+
} else {
|
|
56
|
+
out.append(1, static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07)));
|
|
57
|
+
out.append(1, static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)));
|
|
58
|
+
out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
|
|
59
|
+
out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
|
|
60
|
+
}
|
|
61
|
+
codepoint = 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
} // namespace vscode_keyboard
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
#ifndef STRING_CONVERSION_H_
|
|
7
|
+
#define STRING_CONVERSION_H_
|
|
8
|
+
|
|
9
|
+
#include <string>
|
|
10
|
+
|
|
11
|
+
namespace vscode_keyboard {
|
|
12
|
+
|
|
13
|
+
std::string UTF16to8(const wchar_t * in);
|
|
14
|
+
std::string UTF16toUTF8(const wchar_t * in, int length);
|
|
15
|
+
|
|
16
|
+
} // namespace vscode_keyboard
|
|
17
|
+
|
|
18
|
+
#endif // STRING_CONVERSION_H_
|