mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-11 04:38:24 -06:00
Fix SendInput handling (#7900)
While not explicitly permitted, a wide range of software (including
Windows' own touch keyboard) sets the `wScan` member of the `KEYBDINPUT`
structure to 0, resulting in `scanCode` being 0 as well. In these
situations we'll now use the `vkey` to get a `scanCode`.
Validation
----------
* AutoHotkey
* Use a keyboard layout with `AltGr` key
* Execute the following script:
```ahk
#NoEnv
#Warn
SendMode Input
SetWorkingDir %A_ScriptDir%
<^>!8::SendInput {Raw}»
```
* Press `AltGr+8` while the Terminal is in the foreground
* Ensure » is being echoed ✔️
* PowerToys
* Add a `Ctrl+I -> ↑ (up arrow)` keyboard shortcut
* Press `Ctrl+I` while the Terminal is in the foreground
* Ensure the shell history is being navigated backwards ✔️
* Windows Touch Keyboard
* Right-click or tap and hold the taskbar and select "Show touch
keyboard" button
* Open touch keyboard
* Ensure keyboard works like a regular keyboard ✔️
* Ensure unicode characters are echoed on the Terminal as well (except
for Emojis) ✔️
Closes #7438
Closes #7495
Closes #7843
This commit is contained in:
parent
1df3182865
commit
d51d8dc768
7
.github/actions/spell-check/expect/655f007265b351e140d20b3976792523ad689241.txt
vendored
Normal file
7
.github/actions/spell-check/expect/655f007265b351e140d20b3976792523ad689241.txt
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
autogenerated
|
||||||
|
CPPCORECHECK
|
||||||
|
Debian
|
||||||
|
filepath
|
||||||
|
inplace
|
||||||
|
KEYBDINPUT
|
||||||
|
WINVER
|
||||||
28
.github/actions/spell-check/expect/expect.txt
vendored
28
.github/actions/spell-check/expect/expect.txt
vendored
@ -33,7 +33,6 @@ AHelper
|
|||||||
ahz
|
ahz
|
||||||
AImpl
|
AImpl
|
||||||
AInplace
|
AInplace
|
||||||
akb
|
|
||||||
ALIGNRIGHT
|
ALIGNRIGHT
|
||||||
alloc
|
alloc
|
||||||
allocing
|
allocing
|
||||||
@ -62,7 +61,6 @@ apiset
|
|||||||
apos
|
apos
|
||||||
APPBARDATA
|
APPBARDATA
|
||||||
appconsult
|
appconsult
|
||||||
appdata
|
|
||||||
APPICON
|
APPICON
|
||||||
appium
|
appium
|
||||||
applet
|
applet
|
||||||
@ -109,10 +107,8 @@ aumid
|
|||||||
Authenticode
|
Authenticode
|
||||||
AUTOBUDDY
|
AUTOBUDDY
|
||||||
AUTOCHECKBOX
|
AUTOCHECKBOX
|
||||||
Autogenerated
|
|
||||||
autohide
|
autohide
|
||||||
AUTOHSCROLL
|
AUTOHSCROLL
|
||||||
autologin
|
|
||||||
automagically
|
automagically
|
||||||
autopositioning
|
autopositioning
|
||||||
AUTORADIOBUTTON
|
AUTORADIOBUTTON
|
||||||
@ -393,7 +389,6 @@ CPINFOEX
|
|||||||
cplinfo
|
cplinfo
|
||||||
cplusplus
|
cplusplus
|
||||||
cpp
|
cpp
|
||||||
cppcorecheck
|
|
||||||
cppcorecheckrules
|
cppcorecheckrules
|
||||||
cpprest
|
cpprest
|
||||||
cpprestsdk
|
cpprestsdk
|
||||||
@ -510,10 +505,8 @@ DDESHARE
|
|||||||
DDevice
|
DDevice
|
||||||
DEADCHAR
|
DEADCHAR
|
||||||
dealloc
|
dealloc
|
||||||
debian
|
|
||||||
debolden
|
debolden
|
||||||
debounce
|
debounce
|
||||||
debugbreak
|
|
||||||
DECALN
|
DECALN
|
||||||
DECANM
|
DECANM
|
||||||
DECAUPSS
|
DECAUPSS
|
||||||
@ -521,11 +514,9 @@ DECAWM
|
|||||||
DECCKM
|
DECCKM
|
||||||
DECCOLM
|
DECCOLM
|
||||||
DECEKBD
|
DECEKBD
|
||||||
decf
|
|
||||||
DECKPAM
|
DECKPAM
|
||||||
DECKPM
|
DECKPM
|
||||||
DECKPNM
|
DECKPNM
|
||||||
DECLL
|
|
||||||
DECLRMM
|
DECLRMM
|
||||||
decls
|
decls
|
||||||
declspec
|
declspec
|
||||||
@ -552,7 +543,6 @@ DECSEL
|
|||||||
DECSET
|
DECSET
|
||||||
DECSLPP
|
DECSLPP
|
||||||
DECSLRM
|
DECSLRM
|
||||||
DECSMBV
|
|
||||||
DECSMKR
|
DECSMKR
|
||||||
DECSR
|
DECSR
|
||||||
decstandar
|
decstandar
|
||||||
@ -716,7 +706,6 @@ EPres
|
|||||||
ERASEBKGND
|
ERASEBKGND
|
||||||
errno
|
errno
|
||||||
errorlevel
|
errorlevel
|
||||||
esa
|
|
||||||
ETB
|
ETB
|
||||||
etcoreapp
|
etcoreapp
|
||||||
ETW
|
ETW
|
||||||
@ -771,7 +760,6 @@ fgetwc
|
|||||||
fgidx
|
fgidx
|
||||||
FILEDESCRIPTION
|
FILEDESCRIPTION
|
||||||
fileno
|
fileno
|
||||||
FILEPATH
|
|
||||||
FILESUBTYPE
|
FILESUBTYPE
|
||||||
FILESYSPATH
|
FILESYSPATH
|
||||||
filesystem
|
filesystem
|
||||||
@ -932,7 +920,6 @@ GTP
|
|||||||
guc
|
guc
|
||||||
gui
|
gui
|
||||||
guidatom
|
guidatom
|
||||||
guidgenerator
|
|
||||||
GValue
|
GValue
|
||||||
GWL
|
GWL
|
||||||
GWLP
|
GWLP
|
||||||
@ -1105,7 +1092,6 @@ Inlines
|
|||||||
INotify
|
INotify
|
||||||
inout
|
inout
|
||||||
INPATHROOT
|
INPATHROOT
|
||||||
Inplace
|
|
||||||
inproc
|
inproc
|
||||||
Inputkeyinfo
|
Inputkeyinfo
|
||||||
INPUTPROCESSORPROFILE
|
INPUTPROCESSORPROFILE
|
||||||
@ -1194,11 +1180,9 @@ kcud
|
|||||||
kcuf
|
kcuf
|
||||||
kcuu
|
kcuu
|
||||||
Kd
|
Kd
|
||||||
keith
|
|
||||||
kernelbase
|
kernelbase
|
||||||
kernelbasestaging
|
kernelbasestaging
|
||||||
keybinding
|
keybinding
|
||||||
keybound
|
|
||||||
keychord
|
keychord
|
||||||
keydown
|
keydown
|
||||||
keyevent
|
keyevent
|
||||||
@ -1471,7 +1455,6 @@ namestream
|
|||||||
Namquiseratal
|
Namquiseratal
|
||||||
nano
|
nano
|
||||||
natvis
|
natvis
|
||||||
naws
|
|
||||||
nbsp
|
nbsp
|
||||||
Nc
|
Nc
|
||||||
NCCALCSIZE
|
NCCALCSIZE
|
||||||
@ -1553,7 +1536,6 @@ NOTNULL
|
|||||||
NOTOPMOST
|
NOTOPMOST
|
||||||
NOTRACK
|
NOTRACK
|
||||||
NOTSUPPORTED
|
NOTSUPPORTED
|
||||||
notypeopt
|
|
||||||
nouicompat
|
nouicompat
|
||||||
nounihan
|
nounihan
|
||||||
NOUPDATE
|
NOUPDATE
|
||||||
@ -1628,7 +1610,6 @@ opencon
|
|||||||
openconsole
|
openconsole
|
||||||
OPENIF
|
OPENIF
|
||||||
OPENLINK
|
OPENLINK
|
||||||
openlogo
|
|
||||||
openps
|
openps
|
||||||
opensource
|
opensource
|
||||||
openvt
|
openvt
|
||||||
@ -1968,7 +1949,6 @@ resheader
|
|||||||
resizable
|
resizable
|
||||||
resmimetype
|
resmimetype
|
||||||
restrictedcapabilities
|
restrictedcapabilities
|
||||||
restrictederrorinfo
|
|
||||||
resw
|
resw
|
||||||
resx
|
resx
|
||||||
retval
|
retval
|
||||||
@ -1995,7 +1975,6 @@ rgw
|
|||||||
rgwch
|
rgwch
|
||||||
rhs
|
rhs
|
||||||
ri
|
ri
|
||||||
richturn
|
|
||||||
RIGHTALIGN
|
RIGHTALIGN
|
||||||
RIGHTBUTTON
|
RIGHTBUTTON
|
||||||
riid
|
riid
|
||||||
@ -2069,7 +2048,6 @@ SCROLLSCALE
|
|||||||
SCROLLSCREENBUFFER
|
SCROLLSCREENBUFFER
|
||||||
Scrollup
|
Scrollup
|
||||||
Scrolluppage
|
Scrolluppage
|
||||||
Scs
|
|
||||||
scursor
|
scursor
|
||||||
sddl
|
sddl
|
||||||
sdeleted
|
sdeleted
|
||||||
@ -2243,7 +2221,6 @@ subkey
|
|||||||
SUBLANG
|
SUBLANG
|
||||||
sublicensable
|
sublicensable
|
||||||
submenu
|
submenu
|
||||||
subnegotiation
|
|
||||||
subresource
|
subresource
|
||||||
subspan
|
subspan
|
||||||
substr
|
substr
|
||||||
@ -2254,7 +2231,6 @@ svg
|
|||||||
swapchain
|
swapchain
|
||||||
swapchainpanel
|
swapchainpanel
|
||||||
swappable
|
swappable
|
||||||
Switchto
|
|
||||||
SWMR
|
SWMR
|
||||||
SWP
|
SWP
|
||||||
swprintf
|
swprintf
|
||||||
@ -2309,7 +2285,6 @@ technet
|
|||||||
tellp
|
tellp
|
||||||
telnet
|
telnet
|
||||||
telnetd
|
telnetd
|
||||||
telnetpp
|
|
||||||
templated
|
templated
|
||||||
terminalcore
|
terminalcore
|
||||||
TERMINALSCROLLING
|
TERMINALSCROLLING
|
||||||
@ -2706,12 +2681,10 @@ wintelnet
|
|||||||
winternl
|
winternl
|
||||||
winuser
|
winuser
|
||||||
winuserp
|
winuserp
|
||||||
winver
|
|
||||||
wistd
|
wistd
|
||||||
wixproj
|
wixproj
|
||||||
wline
|
wline
|
||||||
wlinestream
|
wlinestream
|
||||||
Wlk
|
|
||||||
wmain
|
wmain
|
||||||
WMSZ
|
WMSZ
|
||||||
wnd
|
wnd
|
||||||
@ -2754,7 +2727,6 @@ WRunoff
|
|||||||
WScript
|
WScript
|
||||||
wsl
|
wsl
|
||||||
WSLENV
|
WSLENV
|
||||||
wslhome
|
|
||||||
wsmatch
|
wsmatch
|
||||||
WSpace
|
WSpace
|
||||||
wss
|
wss
|
||||||
|
|||||||
@ -472,15 +472,20 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
|||||||
|
|
||||||
_StoreKeyEvent(vkey, scanCode);
|
_StoreKeyEvent(vkey, scanCode);
|
||||||
|
|
||||||
// As a Terminal we're mostly interested in getting key events from physical hardware (mouse & keyboard).
|
// Certain applications like AutoHotKey and its keyboard remapping feature,
|
||||||
// We're thus ignoring events whose values are outside the valid range and unlikely to be generated by the current keyboard.
|
// send us key events using SendInput() whose values are outside of the valid range.
|
||||||
// It's very likely that a proper followup character event will be sent to us.
|
|
||||||
// This prominently happens using AutoHotKey's keyboard remapping feature,
|
|
||||||
// which sends input events whose vkey is 0xff and scanCode is 0.
|
|
||||||
// We need to check for this early, as _CharacterFromKeyEvent() always returns 0 for such invalid values,
|
|
||||||
// making us believe that this is an actual non-character input, while it usually isn't.
|
|
||||||
// GH#7064
|
// GH#7064
|
||||||
if (vkey == 0 || vkey >= 0xff || scanCode == 0)
|
if (vkey == 0 || vkey >= 0xff)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// While not explicitly permitted, a wide range of software, including Windows' own touch keyboard,
|
||||||
|
// sets the wScan member of the KEYBDINPUT structure to 0, resulting in scanCode being 0 as well.
|
||||||
|
// --> Alternatively get the scanCode from the vkey if possible.
|
||||||
|
// GH#7495
|
||||||
|
const auto sc = scanCode ? scanCode : _ScanCodeFromVirtualKey(vkey);
|
||||||
|
if (sc == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -506,7 +511,7 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
|||||||
// is the underlying ASCII character (e.g. A-Z) on the keyboard in our case.
|
// is the underlying ASCII character (e.g. A-Z) on the keyboard in our case.
|
||||||
// See GH#5525/GH#6211 for more details
|
// See GH#5525/GH#6211 for more details
|
||||||
const auto isSuppressedAltGrAlias = !_altGrAliasing && states.IsAltPressed() && states.IsCtrlPressed() && !states.IsAltGrPressed();
|
const auto isSuppressedAltGrAlias = !_altGrAliasing && states.IsAltPressed() && states.IsCtrlPressed() && !states.IsAltGrPressed();
|
||||||
const auto ch = isSuppressedAltGrAlias ? UNICODE_NULL : _CharacterFromKeyEvent(vkey, scanCode, states);
|
const auto ch = isSuppressedAltGrAlias ? UNICODE_NULL : _CharacterFromKeyEvent(vkey, sc, states);
|
||||||
|
|
||||||
// Delegate it to the character event handler if this key event can be
|
// Delegate it to the character event handler if this key event can be
|
||||||
// mapped to one (see method description above). For Alt+key combinations
|
// mapped to one (see method description above). For Alt+key combinations
|
||||||
@ -521,7 +526,7 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent keyEv{ keyDown, 1, vkey, scanCode, ch, states.Value() };
|
KeyEvent keyEv{ keyDown, 1, vkey, sc, ch, states.Value() };
|
||||||
return _terminalInput->HandleKey(&keyEv);
|
return _terminalInput->HandleKey(&keyEv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,8 +643,6 @@ WORD Terminal::_VirtualKeyFromCharacter(const wchar_t ch) noexcept
|
|||||||
wchar_t Terminal::_CharacterFromKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states) noexcept
|
wchar_t Terminal::_CharacterFromKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states) noexcept
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto sc = scanCode != 0 ? scanCode : _ScanCodeFromVirtualKey(vkey);
|
|
||||||
|
|
||||||
// We might want to use GetKeyboardState() instead of building our own keyState.
|
// We might want to use GetKeyboardState() instead of building our own keyState.
|
||||||
// The question is whether that's necessary though. For now it seems to work fine as it is.
|
// The question is whether that's necessary though. For now it seems to work fine as it is.
|
||||||
std::array<BYTE, 256> keyState = {};
|
std::array<BYTE, 256> keyState = {};
|
||||||
@ -658,7 +661,7 @@ try
|
|||||||
// * If bit 0 is set, a menu is active.
|
// * If bit 0 is set, a menu is active.
|
||||||
// If this flag is not specified ToUnicodeEx will send us character events on certain Alt+Key combinations (e.g. Alt+Arrow-Up).
|
// If this flag is not specified ToUnicodeEx will send us character events on certain Alt+Key combinations (e.g. Alt+Arrow-Up).
|
||||||
// * If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer)
|
// * If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer)
|
||||||
const auto result = ToUnicodeEx(vkey, sc, keyState.data(), buffer.data(), gsl::narrow_cast<int>(buffer.size()), 0b101, nullptr);
|
const auto result = ToUnicodeEx(vkey, scanCode, keyState.data(), buffer.data(), gsl::narrow_cast<int>(buffer.size()), 0b101, nullptr);
|
||||||
|
|
||||||
// TODO:GH#2853 We're only handling single UTF-16 code points right now, since that's the only thing KeyEvent supports.
|
// TODO:GH#2853 We're only handling single UTF-16 code points right now, since that's the only thing KeyEvent supports.
|
||||||
return result == 1 || result == -1 ? buffer.at(0) : 0;
|
return result == 1 || result == -1 ? buffer.at(0) : 0;
|
||||||
|
|||||||
@ -71,9 +71,7 @@ namespace TerminalCoreUnitTests
|
|||||||
{
|
{
|
||||||
// Certain applications like AutoHotKey and its keyboard remapping feature,
|
// Certain applications like AutoHotKey and its keyboard remapping feature,
|
||||||
// send us key events using SendInput() whose values are outside of the valid range.
|
// send us key events using SendInput() whose values are outside of the valid range.
|
||||||
// We don't want to handle those events as we're probably going to get a proper followup character event.
|
|
||||||
VERIFY_IS_FALSE(term.SendKeyEvent(0, 123, {}, true));
|
VERIFY_IS_FALSE(term.SendKeyEvent(0, 123, {}, true));
|
||||||
VERIFY_IS_FALSE(term.SendKeyEvent(255, 123, {}, true));
|
VERIFY_IS_FALSE(term.SendKeyEvent(255, 123, {}, true));
|
||||||
VERIFY_IS_FALSE(term.SendKeyEvent(123, 0, {}, true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user