-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwininput.c
772 lines (682 loc) · 20.7 KB
/
wininput.c
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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
// wininput.c (part of mintty)
// Copyright 2008-12 Andy Koppe
// Licensed under the terms of the GNU General Public License v3 or later.
#include "winpriv.h"
#include "charset.h"
#include "child.h"
#include <math.h>
#include <windowsx.h>
#include <winnls.h>
#include <termios.h>
static HMENU menu, sysmenu;
void
win_update_menus(void)
{
bool shorts = !term.shortcut_override;
bool clip = shorts && cfg.clip_shortcuts;
bool alt_fn = shorts && cfg.alt_fn_shortcuts;
bool ct_sh = shorts && cfg.ctrl_shift_shortcuts;
ModifyMenu(
sysmenu, IDM_NEW, 0, IDM_NEW,
alt_fn ? "Ne&w\tAlt+F2" : ct_sh ? "Ne&w\tCtrl+Shift+N" : "Ne&w"
);
ModifyMenu(
sysmenu, SC_CLOSE, 0, SC_CLOSE,
alt_fn ? "&Close\tAlt+F4" : ct_sh ? "&Close\tCtrl+Shift+W" : "&Close"
);
uint sel_enabled = term.selected ? MF_ENABLED : MF_GRAYED;
EnableMenuItem(menu, IDM_OPEN, sel_enabled);
ModifyMenu(
menu, IDM_COPY, sel_enabled, IDM_COPY,
clip ? "&Copy\tCtrl+Ins" : ct_sh ? "&Copy\tCtrl+Shift+C" : "&Copy"
);
uint paste_enabled =
IsClipboardFormatAvailable(CF_TEXT) ||
IsClipboardFormatAvailable(CF_UNICODETEXT) ||
IsClipboardFormatAvailable(CF_HDROP)
? MF_ENABLED : MF_GRAYED;
ModifyMenu(
menu, IDM_PASTE, paste_enabled, IDM_PASTE,
clip ? "&Paste\tShift+Ins" : ct_sh ? "&Paste\tCtrl+Shift+V" : "&Paste"
);
ModifyMenu(
menu, IDM_RESET, 0, IDM_RESET,
alt_fn ? "&Reset\tAlt+F8" : ct_sh ? "&Reset\tCtrl+Shift+R" : "&Reset"
);
uint defsize_enabled =
IsZoomed(wnd) || term.cols != cfg.cols || term.rows != cfg.rows
? MF_ENABLED : MF_GRAYED;
ModifyMenu(
menu, IDM_DEFSIZE, defsize_enabled, IDM_DEFSIZE,
alt_fn ? "&Default size\tAlt+F10" :
ct_sh ? "&Default size\tCtrl+Shift+D" : "&Default size"
);
uint fullscreen_checked = win_is_fullscreen ? MF_CHECKED : MF_UNCHECKED;
ModifyMenu(
menu, IDM_FULLSCREEN, fullscreen_checked, IDM_FULLSCREEN,
alt_fn ? "&Full Screen\tAlt+F11" :
ct_sh ? "&Full Screen\tCtrl+Shift+F" : "&Full Screen"
);
uint otherscreen_checked = term.show_other_screen ? MF_CHECKED : MF_UNCHECKED;
ModifyMenu(
menu, IDM_FLIPSCREEN, otherscreen_checked, IDM_FLIPSCREEN,
alt_fn ? "Flip &Screen\tAlt+F12" :
ct_sh ? "Flip &Screen\tCtrl+Shift+S" : "Flip &Screen"
);
uint options_enabled = config_wnd ? MF_GRAYED : MF_ENABLED;
EnableMenuItem(menu, IDM_OPTIONS, options_enabled);
EnableMenuItem(sysmenu, IDM_OPTIONS, options_enabled);
}
void
win_init_menus(void)
{
menu = CreatePopupMenu();
AppendMenu(menu, MF_ENABLED, IDM_OPEN, "Ope&n");
AppendMenu(menu, MF_SEPARATOR, 0, 0);
AppendMenu(menu, MF_ENABLED, IDM_COPY, 0);
AppendMenu(menu, MF_ENABLED, IDM_PASTE, 0);
AppendMenu(menu, MF_ENABLED, IDM_SELALL, "Select &All");
AppendMenu(menu, MF_SEPARATOR, 0, 0);
AppendMenu(menu, MF_ENABLED, IDM_RESET, 0);
AppendMenu(menu, MF_SEPARATOR, 0, 0);
AppendMenu(menu, MF_ENABLED | MF_UNCHECKED, IDM_DEFSIZE, 0);
AppendMenu(menu, MF_ENABLED | MF_UNCHECKED, IDM_FULLSCREEN, 0);
AppendMenu(menu, MF_ENABLED | MF_UNCHECKED, IDM_FLIPSCREEN, 0);
AppendMenu(menu, MF_SEPARATOR, 0, 0);
AppendMenu(menu, MF_ENABLED, IDM_OPTIONS, "&Options...");
sysmenu = GetSystemMenu(wnd, false);
InsertMenu(sysmenu, SC_CLOSE, MF_ENABLED, IDM_COPYTITLE, "Copy &Title");
InsertMenu(sysmenu, SC_CLOSE, MF_ENABLED, IDM_OPTIONS, "&Options...");
InsertMenu(sysmenu, SC_CLOSE, MF_ENABLED, IDM_NEW, 0);
InsertMenu(sysmenu, SC_CLOSE, MF_SEPARATOR, 0, 0);
}
void
win_popup_menu(void)
{
POINT p;
GetCursorPos(&p);
TrackPopupMenu(
menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
p.x, p.y, 0, wnd, null
);
}
typedef enum {
ALT_CANCELLED = -1, ALT_NONE = 0, ALT_ALONE = 1,
ALT_OCT = 8, ALT_DEC = 10, ALT_HEX = 16
} alt_state_t;
static alt_state_t alt_state;
static uint alt_code;
static bool lctrl; // Is left Ctrl pressed?
static int lctrl_time;
static mod_keys
get_mods(void)
{
inline bool is_key_down(uchar vk) { return GetKeyState(vk) & 0x80; }
lctrl_time = 0;
lctrl = is_key_down(VK_LCONTROL) && (lctrl || !is_key_down(VK_RMENU));
return
is_key_down(VK_SHIFT) * MDK_SHIFT |
is_key_down(VK_MENU) * MDK_ALT |
(lctrl | is_key_down(VK_RCONTROL)) * MDK_CTRL;
}
static void
update_mouse(mod_keys mods)
{
static bool app_mouse;
bool new_app_mouse =
term.mouse_mode && !term.show_other_screen &&
cfg.clicks_target_app ^ ((mods & cfg.click_target_mod) != 0);
if (new_app_mouse != app_mouse) {
HCURSOR cursor = LoadCursor(null, new_app_mouse ? IDC_ARROW : IDC_IBEAM);
SetClassLongPtr(wnd, GCLP_HCURSOR, (LONG_PTR)cursor);
SetCursor(cursor);
app_mouse = new_app_mouse;
}
}
void
win_update_mouse(void)
{ update_mouse(get_mods()); }
void
win_capture_mouse(void)
{ SetCapture(wnd); }
static bool mouse_showing = true;
void
win_show_mouse()
{
if (!mouse_showing) {
ShowCursor(true);
mouse_showing = true;
}
}
static void
hide_mouse()
{
POINT p;
if (mouse_showing && GetCursorPos(&p) && WindowFromPoint(p) == wnd) {
ShowCursor(false);
mouse_showing = false;
}
}
static pos
translate_pos(int x, int y)
{
return (pos){
.x = floorf((x - PADDING) / (float)font_width ),
.y = floorf((y - PADDING) / (float)font_height),
};
}
static LPARAM last_lp = -1;
static pos last_pos = {-1, -1};
static pos
get_mouse_pos(LPARAM lp)
{
last_lp = lp;
return translate_pos(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
}
void
win_mouse_click(mouse_button b, LPARAM lp)
{
static mouse_button last_button;
static uint last_time, count;
static pos last_click_pos;
win_show_mouse();
mod_keys mods = get_mods();
pos p = get_mouse_pos(lp);
uint t = GetMessageTime();
if (b != last_button ||
p.x != last_click_pos.x || p.y != last_click_pos.y ||
t - last_time > GetDoubleClickTime() || ++count > 3)
count = 1;
term_mouse_click(b, mods, p, count);
last_pos = (pos){INT_MIN, INT_MIN};
last_click_pos = p;
last_time = t;
last_button = b;
if (alt_state > ALT_NONE)
alt_state = ALT_CANCELLED;
}
void
win_mouse_release(mouse_button b, LPARAM lp)
{
term_mouse_release(b, get_mods(), get_mouse_pos(lp));
ReleaseCapture();
}
void
win_mouse_move(bool nc, LPARAM lp)
{
if (lp == last_lp)
return;
win_show_mouse();
pos p = get_mouse_pos(lp);
if (nc || (p.x == last_pos.x && p.y == last_pos.y))
return;
last_pos = p;
term_mouse_move(get_mods(), p);
}
void
win_mouse_wheel(WPARAM wp, LPARAM lp)
{
// WM_MOUSEWHEEL reports screen coordinates rather than client coordinates
POINT wpos = {.x = GET_X_LPARAM(lp), .y = GET_Y_LPARAM(lp)};
ScreenToClient(wnd, &wpos);
pos tpos = translate_pos(wpos.x, wpos.y);
int delta = GET_WHEEL_DELTA_WPARAM(wp); // positive means up
int lines_per_notch;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_notch, 0);
term_mouse_wheel(delta, lines_per_notch, get_mods(), tpos);
}
/* Keyboard handling */
static void
send_syscommand(WPARAM cmd)
{
SendMessage(wnd, WM_SYSCOMMAND, cmd, ' ');
}
bool
win_key_down(WPARAM wp, LPARAM lp)
{
uint key = wp;
if (key == VK_PROCESSKEY) {
TranslateMessage(
&(MSG){.hwnd = wnd, .message = WM_KEYDOWN, .wParam = wp, .lParam = lp}
);
return 1;
}
uint scancode = HIWORD(lp) & (KF_EXTENDED | 0xFF);
bool extended = HIWORD(lp) & KF_EXTENDED;
bool repeat = HIWORD(lp) & KF_REPEAT;
uint count = LOWORD(lp);
uchar kbd[256];
GetKeyboardState(kbd);
inline bool is_key_down(uchar vk) { return kbd[vk] & 0x80; }
// Distinguish real LCONTROL keypresses from fake messages sent for AltGr.
// It's a fake if the next message is an RMENU with the same timestamp.
if (key == VK_CONTROL && !extended) {
lctrl = true;
lctrl_time = GetMessageTime();
}
else if (lctrl_time) {
lctrl = !(key == VK_MENU && extended && lctrl_time == GetMessageTime());
lctrl_time = 0;
}
else
lctrl = is_key_down(VK_LCONTROL) && (lctrl || !is_key_down(VK_RMENU));
bool
numlock = kbd[VK_NUMLOCK] & 1,
shift = is_key_down(VK_SHIFT),
lalt = is_key_down(VK_LMENU),
ralt = is_key_down(VK_RMENU),
alt = lalt | ralt,
rctrl = is_key_down(VK_RCONTROL),
ctrl = lctrl | rctrl,
ctrl_lalt_altgr = cfg.ctrl_alt_is_altgr & ctrl & lalt & !ralt,
altgr = ralt | ctrl_lalt_altgr;
mod_keys mods = shift * MDK_SHIFT | alt * MDK_ALT | ctrl * MDK_CTRL;
update_mouse(mods);
if (key == VK_MENU) {
if (!repeat && mods == MDK_ALT && alt_state == ALT_NONE)
alt_state = ALT_ALONE;
return 1;
}
alt_state_t old_alt_state = alt_state;
if (alt_state > ALT_NONE)
alt_state = ALT_CANCELLED;
// Context and window menus
if (key == VK_APPS) {
if (shift)
send_syscommand(SC_KEYMENU);
else {
win_show_mouse();
POINT p;
GetCaretPos(&p);
ClientToScreen(wnd, &p);
TrackPopupMenu(
menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
p.x, p.y, 0, wnd, null
);
}
return 1;
}
// Exit when pressing Enter or Escape while holding the window open after
// the child process has died.
if ((key == VK_RETURN || key == VK_ESCAPE) && !mods && !child_is_alive())
exit(0);
if (!term.shortcut_override) {
// Copy&paste
if (cfg.clip_shortcuts && key == VK_INSERT && mods && !alt) {
if (ctrl)
term_copy();
if (shift)
win_paste();
return 1;
}
// Window menu and fullscreen
if (cfg.window_shortcuts && alt && !ctrl) {
if (key == VK_RETURN) {
send_syscommand(IDM_FULLSCREEN);
return 1;
}
else if (key == VK_SPACE) {
send_syscommand(SC_KEYMENU);
return 1;
}
}
// Font zooming
if (cfg.zoom_shortcuts && mods == MDK_CTRL) {
int zoom;
switch (key) {
when VK_OEM_PLUS or VK_ADD: zoom = 1;
when VK_OEM_MINUS or VK_SUBTRACT: zoom = -1;
when '0' or VK_NUMPAD0: zoom = 0;
otherwise: goto not_zoom;
}
win_zoom_font(zoom);
return 1;
not_zoom:;
}
// Alt+Fn shortcuts
if (cfg.alt_fn_shortcuts && alt && VK_F1 <= key && key <= VK_F24) {
if (!ctrl) {
switch (key) {
when VK_F2: send_syscommand(IDM_NEW);
when VK_F4: send_syscommand(SC_CLOSE);
when VK_F8: send_syscommand(IDM_RESET);
when VK_F10: send_syscommand(IDM_DEFSIZE);
when VK_F11: send_syscommand(IDM_FULLSCREEN);
when VK_F12: send_syscommand(IDM_FLIPSCREEN);
}
}
return 1;
}
// Ctrl+Shift+letter shortcuts
if (cfg.ctrl_shift_shortcuts &&
mods == (MDK_CTRL | MDK_SHIFT) && 'A' <= key && key <= 'Z') {
switch (key) {
when 'C': term_copy();
when 'V': win_paste();
when 'N': send_syscommand(IDM_NEW);
when 'W': send_syscommand(SC_CLOSE);
when 'R': send_syscommand(IDM_RESET);
when 'D': send_syscommand(IDM_DEFSIZE);
when 'F': send_syscommand(IDM_FULLSCREEN);
when 'S': send_syscommand(IDM_FLIPSCREEN);
}
return 1;
}
// Scrollback
if (!term.on_alt_screen || term.show_other_screen) {
mod_keys scroll_mod = cfg.scroll_mod ?: 8;
if (cfg.pgupdn_scroll && (key == VK_PRIOR || key == VK_NEXT) &&
!(mods & ~scroll_mod))
mods ^= scroll_mod;
if (mods == scroll_mod) {
WPARAM scroll;
switch (key) {
when VK_HOME: scroll = SB_TOP;
when VK_END: scroll = SB_BOTTOM;
when VK_PRIOR: scroll = SB_PAGEUP;
when VK_NEXT: scroll = SB_PAGEDOWN;
when VK_UP: scroll = SB_LINEUP;
when VK_DOWN: scroll = SB_LINEDOWN;
otherwise: goto not_scroll;
}
SendMessage(wnd, WM_VSCROLL, scroll, 0);
return 1;
not_scroll:;
}
}
}
// Keycode buffers
char buf[32];
int len = 0;
inline void ch(char c) { buf[len++] = c; }
inline void esc_if(bool b) { if (b) ch('\e'); }
void ss3(char c) { ch('\e'); ch('O'); ch(c); }
void csi(char c) { ch('\e'); ch('['); ch(c); }
void mod_csi(char c) { len = sprintf(buf, "\e[1;%c%c", mods + '1', c); }
void mod_ss3(char c) { mods ? mod_csi(c) : ss3(c); }
void tilde_code(uchar code) {
len = sprintf(buf, mods ? "\e[%i;%c~" : "\e[%i~", code, mods + '1');
}
void other_code(wchar c) {
len = sprintf(buf, "\e[%u;%cu", c, mods + '1');
}
void app_pad_code(char c) { mod_ss3(c - '0' + 'p'); }
bool alt_code_key(char digit) {
if (old_alt_state > ALT_ALONE && digit < old_alt_state) {
alt_state = old_alt_state;
alt_code = alt_code * alt_state + digit;
return true;
}
return false;
}
bool alt_code_numpad_key(char digit) {
if (old_alt_state == ALT_ALONE) {
alt_code = digit;
alt_state = digit ? ALT_DEC : ALT_OCT;
return true;
}
return alt_code_key(digit);
}
bool app_pad_key(char symbol) {
if (extended)
return false;
// Mintty-specific: produce app_pad codes not only when vt220 mode is on,
// but also in PC-style mode when app_cursor_keys is off, to allow the
// numpad keys to be distinguished from the cursor/editing keys.
if (term.app_keypad && (!term.app_cursor_keys || term.vt220_keys)) {
// If NumLock is on, Shift must have been pressed to override it and
// get a VK code for an editing or cursor key code.
if (numlock)
mods |= MDK_SHIFT;
app_pad_code(symbol);
return true;
}
return symbol != '.' && alt_code_numpad_key(symbol - '0');
}
void edit_key(uchar code, char symbol) {
if (!app_pad_key(symbol))
tilde_code(code);
}
void cursor_key(char code, char symbol) {
if (!app_pad_key(symbol))
mods ? mod_csi(code) : term.app_cursor_keys ? ss3(code) : csi(code);
}
// Keyboard layout
bool layout(void) {
// ToUnicode returns up to 4 wchars according to
// http://blogs.msdn.com/b/michkap/archive/2006/03/24/559169.aspx.
wchar wbuf[4];
int wlen = ToUnicode(key, scancode, kbd, wbuf, lengthof(wbuf), 0);
if (!wlen) // Unassigned.
return false;
if (wlen < 0) // Dead key.
return true;
esc_if(alt);
// Check that the keycode can be converted to the current charset
// before returning success.
int mblen = cs_wcntombn(buf + len, wbuf, lengthof(buf) - len, wlen);
bool ok = mblen > 0;
len = ok ? len + mblen : 0;
return ok;
}
wchar undead_keycode(void) {
wchar wc;
int len = ToUnicode(key, scancode, kbd, &wc, 1, 0);
if (len < 0) {
// Ugly hack to clear dead key state, a la Michael Kaplan.
uchar empty_kbd[256];
memset(empty_kbd, 0, sizeof empty_kbd);
uint scancode = MapVirtualKey(VK_DECIMAL, 0);
wchar dummy;
while (ToUnicode(VK_DECIMAL, scancode, empty_kbd, &dummy, 1, 0) < 0);
return wc;
}
return len == 1 ? wc : 0;
}
void modify_other_key(void) {
kbd[VK_CONTROL] = 0;
wchar wc = undead_keycode();
if (wc)
other_code(wc);
}
bool char_key(void) {
alt = lalt & !ctrl_lalt_altgr;
// Sync keyboard layout with our idea of AltGr.
kbd[VK_CONTROL] = altgr ? 0x80 : 0;
// Don't handle Ctrl combinations here.
// Need to check there's a Ctrl that isn't part of Ctrl+LeftAlt==AltGr.
if ((ctrl & !ctrl_lalt_altgr) | (lctrl & rctrl))
return false;
// Try the layout.
if (layout())
return true;
if (ralt) {
// Try with RightAlt/AltGr key treated as Alt.
kbd[VK_CONTROL] = 0;
alt = true;
layout();
return true;
}
return !ctrl;
}
void ctrl_ch(uchar c) {
esc_if(alt);
if (shift) {
// Send C1 control char if the charset supports it.
// Otherwise prefix the C0 char with ESC.
if (c < 0x20) {
wchar wc = c | 0x80;
int l = cs_wcntombn(buf + len, &wc, cs_cur_max, 1);
if (l > 0 && buf[len] != '?') {
len += l;
return;
}
};
esc_if(!alt);
}
ch(c);
}
bool ctrl_key(void) {
bool try_key(void) {
wchar wc = undead_keycode();
char c;
switch (wc) {
when '@' or '[' ... '_' or 'a' ... 'z': c = CTRL(wc);
when '/': c = CTRL('_');
when '?': c = CDEL;
otherwise: return false;
}
ctrl_ch(c);
return true;
}
bool try_shifts(void) {
shift = is_key_down(VK_LSHIFT) & is_key_down(VK_RSHIFT);
if (try_key())
return true;
shift = is_key_down(VK_SHIFT);
if (shift || (key >= '0' && key <= '9' && !term.modify_other_keys)) {
kbd[VK_SHIFT] ^= 0x80;
if (try_key())
return true;
kbd[VK_SHIFT] ^= 0x80;
}
return false;
}
if (try_shifts())
return true;
if (altgr) {
// Try with AltGr treated as Alt.
kbd[VK_CONTROL] = 0;
alt = true;
return try_shifts();
}
return false;
}
switch(key) {
when VK_RETURN:
if (extended && !numlock && term.app_keypad)
mod_ss3('M');
else if (!extended && term.modify_other_keys && (shift || ctrl))
other_code('\r');
else if (!ctrl)
esc_if(alt),
term.newline_mode ? ch('\r'), ch('\n') : ch(shift ? '\n' : '\r');
else
ctrl_ch(CTRL('^'));
when VK_BACK:
if (!ctrl)
esc_if(alt), ch(term.backspace_sends_bs ? '\b' : CDEL);
else if (term.modify_other_keys)
other_code(term.backspace_sends_bs ? '\b' : CDEL);
else
ctrl_ch(term.backspace_sends_bs ? CDEL : CTRL('_'));
when VK_TAB:
if (alt)
return 0;
if (!ctrl)
shift ? csi('Z') : ch('\t');
else if (cfg.switch_shortcuts) {
win_switch(shift);
return 1;
}
else
term.modify_other_keys ? other_code('\t') : mod_csi('I');
when VK_ESCAPE:
term.app_escape_key
? ss3('[')
: ctrl_ch(term.escape_sends_fs ? CTRL('\\') : CTRL('['));
when VK_PAUSE:
ctrl_ch(ctrl & !extended ? CTRL('\\') : CTRL(']'));
when VK_CANCEL:
ctrl_ch(CTRL('\\'));
when VK_F1 ... VK_F24:
if (term.vt220_keys && ctrl && VK_F3 <= key && key <= VK_F10)
key += 10, mods &= ~MDK_CTRL;
if (key <= VK_F4)
mod_ss3(key - VK_F1 + 'P');
else {
tilde_code(
(uchar[]){
15, 17, 18, 19, 20, 21, 23, 24, 25, 26,
28, 29, 31, 32, 33, 34, 42, 43, 44, 45
}[key - VK_F5]
);
}
when VK_INSERT: edit_key(2, '0');
when VK_DELETE: edit_key(3, '.');
when VK_PRIOR: edit_key(5, '9');
when VK_NEXT: edit_key(6, '3');
when VK_HOME: term.vt220_keys ? edit_key(1, '7') : cursor_key('H', '7');
when VK_END: term.vt220_keys ? edit_key(4, '1') : cursor_key('F', '1');
when VK_UP: cursor_key('A', '8');
when VK_DOWN: cursor_key('B', '2');
when VK_LEFT: cursor_key('D', '4');
when VK_RIGHT: cursor_key('C', '6');
when VK_CLEAR: cursor_key('E', '5');
when VK_MULTIPLY ... VK_DIVIDE:
if (key == VK_ADD && old_alt_state == ALT_ALONE)
alt_state = ALT_HEX, alt_code = 0;
else if (mods || (term.app_keypad && !numlock) || !layout())
app_pad_code(key - VK_MULTIPLY + '*');
when VK_NUMPAD0 ... VK_NUMPAD9:
if ((term.app_cursor_keys || !term.app_keypad) &&
alt_code_numpad_key(key - VK_NUMPAD0));
else if (layout());
else app_pad_code(key - VK_NUMPAD0 + '0');
when 'A' ... 'Z' or ' ':
if (key != ' ' && alt_code_key(key - 'A' + 0xA));
else if (char_key());
else if (term.modify_other_keys > 1) modify_other_key();
else if (ctrl_key());
else ctrl_ch(CTRL(key));
when '0' ... '9' or VK_OEM_1 ... VK_OEM_102:
if (key <= '9' && alt_code_key(key - '0'));
else if (char_key());
else if (term.modify_other_keys <= 1 && ctrl_key());
else if (term.modify_other_keys) modify_other_key();
else if (key <= '9') app_pad_code(key);
else if (VK_OEM_PLUS <= key && key <= VK_OEM_PERIOD)
app_pad_code(key - VK_OEM_PLUS + '+');
when VK_PACKET:
layout();
otherwise:
return 0;
}
hide_mouse();
term_cancel_paste();
if (len) {
while (count--)
child_send(buf, len);
}
return 1;
}
bool
win_key_up(WPARAM wp, LPARAM unused(lp))
{
win_update_mouse();
if (wp != VK_MENU)
return false;
if (alt_state > ALT_ALONE && alt_code) {
if (cs_cur_max < 4) {
char buf[4];
int pos = sizeof buf;
do
buf[--pos] = alt_code;
while (alt_code >>= 8);
child_send(buf + pos, sizeof buf - pos);
}
else if (alt_code < 0x10000) {
wchar wc = alt_code;
if (wc < 0x20)
MultiByteToWideChar(CP_OEMCP, MB_USEGLYPHCHARS,
(char[]){wc}, 1, &wc, 1);
child_sendw(&wc, 1);
}
else {
xchar xc = alt_code;
child_sendw((wchar[]){high_surrogate(xc), low_surrogate(xc)}, 2);
}
}
alt_state = ALT_NONE;
return true;
}