Skip to content

Commit 9a7a643

Browse files
committed
Regenerate missing scan codes in win32-input-mode
There are terminal emulators that implement this protocol but do not report scan codes (e.g. Konsole), so apply the same workarounds that were being used for far2l.
1 parent 6141d9e commit 9a7a643

5 files changed

Lines changed: 71 additions & 61 deletions

File tree

include/tvision/internal/win32con.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace tvision
1212

1313
bool getWin32Key(const KEY_EVENT_RECORD &, TEvent &, InputState &) noexcept;
1414
void getWin32Mouse(const MOUSE_EVENT_RECORD &, TEvent &, InputState &) noexcept;
15+
void regenerateMissingScanCodeFromVirtualKeyCode(KEY_EVENT_RECORD &) noexcept;
1516

1617
#ifdef _WIN32
1718

source/platform/far2l.cpp

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
#define Uses_TKeys
21
#include <tvision/tv.h>
32

43
#include <internal/far2l.h>
54
#include <internal/win32con.h>
6-
#include <internal/constmap.h>
75
#include <internal/base64.h>
86
#include <internal/events.h>
97
#include <internal/conctl.h>
@@ -26,50 +24,6 @@ static TStringView f2lClientId =
2624
f2lClientIdData
2725
);
2826

29-
static const const_unordered_map<uchar, ushort> virtualKeyCodeToKeyCode =
30-
{
31-
{ VK_BACK, kbBack }, { VK_TAB, kbTab },
32-
{ VK_RETURN, kbEnter }, { VK_ESCAPE, kbEsc },
33-
{ '0', 0x0B00 | '0' }, { '1', 0x0200 | '1' },
34-
{ '2', 0x0300 | '2' }, { '3', 0x0400 | '3' },
35-
{ '4', 0x0500 | '4' }, { '5', 0x0600 | '5' },
36-
{ '6', 0x0700 | '6' }, { '7', 0x0800 | '7' },
37-
{ '8', 0x0900 | '8' }, { '9', 0x0A00 | '9' },
38-
{ VK_SPACE, 0x3900 | ' ' }, { 'A', 0x1E00 | 'A' },
39-
{ 'B', 0x3000 | 'B' }, { 'C', 0x2E00 | 'C' },
40-
{ 'D', 0x2000 | 'D' }, { 'E', 0x1200 | 'E' },
41-
{ 'F', 0x2100 | 'F' }, { 'G', 0x2200 | 'G' },
42-
{ 'H', 0x2300 | 'H' }, { 'I', 0x1700 | 'I' },
43-
{ 'J', 0x2400 | 'J' }, { 'K', 0x2500 | 'K' },
44-
{ 'L', 0x2600 | 'L' }, { 'M', 0x3200 | 'M' },
45-
{ 'N', 0x3100 | 'N' }, { 'O', 0x1800 | 'O' },
46-
{ 'P', 0x1900 | 'P' }, { 'Q', 0x1000 | 'Q' },
47-
{ 'R', 0x1300 | 'R' }, { 'S', 0x1F00 | 'S' },
48-
{ 'T', 0x1400 | 'T' }, { 'U', 0x1600 | 'U' },
49-
{ 'V', 0x2F00 | 'V' }, { 'W', 0x1100 | 'W' },
50-
{ 'X', 0x2D00 | 'X' }, { 'Y', 0x1500 | 'Y' },
51-
{ 'Z', 0x2C00 | 'Z' }, { VK_PRIOR, kbPgUp },
52-
{ VK_NEXT, kbPgDn }, { VK_END, kbEnd },
53-
{ VK_HOME, kbHome }, { VK_LEFT, kbLeft },
54-
{ VK_UP, kbUp }, { VK_RIGHT, kbRight },
55-
{ VK_DOWN, kbDown }, { VK_INSERT, kbIns },
56-
{ VK_DELETE, kbDel }, { VK_NUMPAD0, 0x5200 | '0' },
57-
{ VK_NUMPAD1, 0x4F00 | '1' }, { VK_NUMPAD2, 0x5000 | '2' },
58-
{ VK_NUMPAD3, 0x5100 | '3' }, { VK_NUMPAD4, 0x4B00 | '4' },
59-
{ VK_NUMPAD5, 0x4C00 | '5' }, { VK_NUMPAD6, 0x4D00 | '6' },
60-
{ VK_NUMPAD7, 0x4700 | '7' }, { VK_NUMPAD8, 0x4800 | '8' },
61-
{ VK_NUMPAD9, 0x4900 | '9' }, { VK_MULTIPLY, 0x3700 | '*' },
62-
{ VK_ADD, 0x4E00 | '+' }, { VK_SEPARATOR, 0x7E00 | ',' },
63-
{ VK_SUBTRACT, 0x4A00 | '-' }, { VK_DECIMAL, 0x5300 | '.' },
64-
{ VK_DIVIDE, 0x3500 | '/' }, { VK_F1, kbF1 },
65-
{ VK_F2, kbF2 }, { VK_F3, kbF3 },
66-
{ VK_F4, kbF4 }, { VK_F5, kbF5 },
67-
{ VK_F6, kbF6 }, { VK_F7, kbF7 },
68-
{ VK_F8, kbF8 }, { VK_F9, kbF9 },
69-
{ VK_F10, kbF10 }, { VK_F11, kbF11 },
70-
{ VK_F12, kbF12 },
71-
};
72-
7327
ParseResult parseFar2lInput(GetChBuf &buf, TEvent &ev, InputState &state) noexcept
7428
// Pre: "\x1B_f2l" has just been read.
7529
{
@@ -104,21 +58,7 @@ ParseResult parseFar2lInput(GetChBuf &buf, TEvent &ev, InputState &state) noexce
10458
reverseBytes((uint32_t &) kev.uChar.UnicodeChar);
10559
#endif
10660

107-
if (uint16_t keyCode = virtualKeyCodeToKeyCode[kev.wVirtualKeyCode])
108-
{
109-
kev.wVirtualScanCode = keyCode >> 8;
110-
// Overwrite the UnicodeChar unless it is expected to be
111-
// non-null but it is already not.
112-
if (kev.uChar.UnicodeChar == L'\0' || (keyCode & 0xFF) == 0)
113-
kev.uChar.UnicodeChar = keyCode & 0xFF;
114-
// If the Ctrl or Alt modifiers are present, the event is not
115-
// meant to produce text, so clear the UnicodeChar. Unlike on
116-
// Windows, we do not expect AltGr to be reported as Ctrl+Alt.
117-
if ( (kev.dwControlKeyState & (kbCtrlShift | kbAltShift)) &&
118-
L' ' <= kev.uChar.UnicodeChar &&
119-
L'\x7F' != kev.uChar.UnicodeChar )
120-
kev.uChar.UnicodeChar = L'\0';
121-
}
61+
regenerateMissingScanCodeFromVirtualKeyCode(kev);
12262

12363
if (getWin32Key(kev, ev, state))
12464
{

source/platform/termio.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,8 @@ static ParseResult parseWin32InputModeKey(const CSIData &csi, TEvent &ev, InputS
814814
kev.dwControlKeyState = (ushort) csi.getValue(4, 0);
815815
kev.wRepeatCount = (ushort) csi.getValue(5, 1);
816816

817+
regenerateMissingScanCodeFromVirtualKeyCode(kev);
818+
817819
if (kev.bKeyDown && getWin32Key(kev, ev, state))
818820
{
819821
TermIO::normalizeKey(ev.keyDown);

source/platform/win32con.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <internal/codepage.h>
99
#include <internal/termio.h>
1010
#include <internal/utf8.h>
11+
#include <internal/constmap.h>
1112
#include <locale.h>
1213

1314
namespace tvision
@@ -625,5 +626,69 @@ void getWin32Mouse(const MOUSE_EVENT_RECORD &MouseEvent, TEvent &ev, InputState
625626
ev.mouse.wheel = 0;
626627
}
627628

629+
static const const_unordered_map<uchar, ushort> virtualKeyCodeToTVisionKeyCode =
630+
{
631+
{ VK_BACK, kbBack }, { VK_TAB, kbTab },
632+
{ VK_RETURN, kbEnter }, { VK_ESCAPE, kbEsc },
633+
{ '0', 0x0B00 | '0' }, { '1', 0x0200 | '1' },
634+
{ '2', 0x0300 | '2' }, { '3', 0x0400 | '3' },
635+
{ '4', 0x0500 | '4' }, { '5', 0x0600 | '5' },
636+
{ '6', 0x0700 | '6' }, { '7', 0x0800 | '7' },
637+
{ '8', 0x0900 | '8' }, { '9', 0x0A00 | '9' },
638+
{ VK_SPACE, 0x3900 | ' ' }, { 'A', 0x1E00 | 'A' },
639+
{ 'B', 0x3000 | 'B' }, { 'C', 0x2E00 | 'C' },
640+
{ 'D', 0x2000 | 'D' }, { 'E', 0x1200 | 'E' },
641+
{ 'F', 0x2100 | 'F' }, { 'G', 0x2200 | 'G' },
642+
{ 'H', 0x2300 | 'H' }, { 'I', 0x1700 | 'I' },
643+
{ 'J', 0x2400 | 'J' }, { 'K', 0x2500 | 'K' },
644+
{ 'L', 0x2600 | 'L' }, { 'M', 0x3200 | 'M' },
645+
{ 'N', 0x3100 | 'N' }, { 'O', 0x1800 | 'O' },
646+
{ 'P', 0x1900 | 'P' }, { 'Q', 0x1000 | 'Q' },
647+
{ 'R', 0x1300 | 'R' }, { 'S', 0x1F00 | 'S' },
648+
{ 'T', 0x1400 | 'T' }, { 'U', 0x1600 | 'U' },
649+
{ 'V', 0x2F00 | 'V' }, { 'W', 0x1100 | 'W' },
650+
{ 'X', 0x2D00 | 'X' }, { 'Y', 0x1500 | 'Y' },
651+
{ 'Z', 0x2C00 | 'Z' }, { VK_PRIOR, kbPgUp },
652+
{ VK_NEXT, kbPgDn }, { VK_END, kbEnd },
653+
{ VK_HOME, kbHome }, { VK_LEFT, kbLeft },
654+
{ VK_UP, kbUp }, { VK_RIGHT, kbRight },
655+
{ VK_DOWN, kbDown }, { VK_INSERT, kbIns },
656+
{ VK_DELETE, kbDel }, { VK_NUMPAD0, 0x5200 | '0' },
657+
{ VK_NUMPAD1, 0x4F00 | '1' }, { VK_NUMPAD2, 0x5000 | '2' },
658+
{ VK_NUMPAD3, 0x5100 | '3' }, { VK_NUMPAD4, 0x4B00 | '4' },
659+
{ VK_NUMPAD5, 0x4C00 | '5' }, { VK_NUMPAD6, 0x4D00 | '6' },
660+
{ VK_NUMPAD7, 0x4700 | '7' }, { VK_NUMPAD8, 0x4800 | '8' },
661+
{ VK_NUMPAD9, 0x4900 | '9' }, { VK_MULTIPLY, 0x3700 | '*' },
662+
{ VK_ADD, 0x4E00 | '+' }, { VK_SEPARATOR, 0x7E00 | ',' },
663+
{ VK_SUBTRACT, 0x4A00 | '-' }, { VK_DECIMAL, 0x5300 | '.' },
664+
{ VK_DIVIDE, 0x3500 | '/' }, { VK_F1, kbF1 },
665+
{ VK_F2, kbF2 }, { VK_F3, kbF3 },
666+
{ VK_F4, kbF4 }, { VK_F5, kbF5 },
667+
{ VK_F6, kbF6 }, { VK_F7, kbF7 },
668+
{ VK_F8, kbF8 }, { VK_F9, kbF9 },
669+
{ VK_F10, kbF10 }, { VK_F11, kbF11 },
670+
{ VK_F12, kbF12 },
671+
};
672+
673+
void regenerateMissingScanCodeFromVirtualKeyCode(KEY_EVENT_RECORD &KeyEvent) noexcept
674+
{
675+
if (uint16_t keyCode = virtualKeyCodeToTVisionKeyCode[KeyEvent.wVirtualKeyCode])
676+
{
677+
KeyEvent.wVirtualScanCode = keyCode >> 8;
678+
// Overwrite the UnicodeChar unless it is expected to be
679+
// non-null and it is already not.
680+
if (KeyEvent.uChar.UnicodeChar == L'\0' || (keyCode & 0xFF) == 0)
681+
KeyEvent.uChar.UnicodeChar = keyCode & 0xFF;
682+
683+
// If the Ctrl or Alt modifiers are present, the event is not
684+
// meant to produce text, so clear the UnicodeChar. Unlike on
685+
// Windows, we do not expect AltGr to be reported as Ctrl+Alt.
686+
if ( (KeyEvent.dwControlKeyState & (kbCtrlShift | kbAltShift)) &&
687+
L' ' <= KeyEvent.uChar.UnicodeChar &&
688+
L'\x7F' != KeyEvent.uChar.UnicodeChar )
689+
KeyEvent.uChar.UnicodeChar = L'\0';
690+
}
691+
}
692+
628693
} // namespace tvision
629694

test/platform/termio.test.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ TEST(TermIO, ShouldReadWin32InputModeKeys)
4646
{"\x1B[112;59;0;1;8;1_", {keyDownEv(kbCtrlF1, kbLeftCtrl)}},
4747
{"\x1B[112;59;;1;8_", {keyDownEv(kbCtrlF1, kbLeftCtrl)}},
4848
{"\x1B[112;59;0;0;8;1_", {}},
49+
// Zeroed scan code
50+
{"\x1B[65;0;65;1;16;1_", {keyDownEv(0x1e41, kbShift, "A")}},
4951
// https://github.com/microsoft/terminal/issues/15083
5052
{ // SGR mouse event
5153
"\x1B[0;0;27;1;0;1_" // \x1B[<0;52;12M

0 commit comments

Comments
 (0)