mirror of
https://github.com/LizardByte/libvirtualhid.git
synced 2026-10-23 03:27:34 +09:00
feat(keyboard): support explicitly extended keys (#166)
This commit is contained in:
1 parent
52d788119a
commit
2db2cc2d7f
6 files changed
+93
-13
No files matched your search
@@ -31,6 +31,11 @@ a per-controller XUSB software device plus a correlated VHF child; other
|
||||
profiles use the root control driver and Windows Virtual HID Framework.
|
||||
Keyboard text input, absolute mouse input, and the keyboard and mouse fallbacks
|
||||
use Win32 APIs.
|
||||
Set `KeyboardEvent::extended` when the input source positively identifies an
|
||||
extended key, such as keypad Enter. The Windows HID keyboard maps it to the
|
||||
corresponding HID usage, and the Win32 fallback sets the extended-key input
|
||||
flag. Leave it unset when the input source does not report this detail; existing
|
||||
key-code and scan-code classification still applies.
|
||||
|
||||
The C++ library communicates with the driver through fixed-size protocol
|
||||
structures and `DeviceIoControl`, not C++ STL types. This keeps the public API
|
||||
|
||||
@@ -748,6 +748,14 @@ namespace lvh {
|
||||
* @brief Whether the backend should prefer a native scan-code translation when `scan_code` is not provided.
|
||||
*/
|
||||
bool prefer_native_scan_code = false;
|
||||
|
||||
/**
|
||||
* @brief Whether this is an extended key, such as keypad Enter.
|
||||
*
|
||||
* A false value leaves backend key classification unchanged for clients
|
||||
* that do not report extended keys.
|
||||
*/
|
||||
bool extended = false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1650,7 +1650,7 @@ namespace lvh::detail {
|
||||
input.ki.dwFlags |= KEYEVENTF_SCANCODE;
|
||||
}
|
||||
}
|
||||
if (extended_key(event.key_code)) {
|
||||
if (event.extended || extended_key(event.key_code)) {
|
||||
input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||
}
|
||||
if (!event.pressed) {
|
||||
@@ -1755,25 +1755,26 @@ namespace lvh::detail {
|
||||
return profile;
|
||||
}
|
||||
|
||||
bool hid_scan_code_is_extended(std::uint16_t scan_code, KeyboardKeyCode key_code) {
|
||||
bool hid_scan_code_is_extended(std::uint16_t scan_code, KeyboardKeyCode key_code, bool extended) {
|
||||
const auto prefix = scan_code & 0xFF00U;
|
||||
return prefix == 0xE000U || prefix == 0xE100U || extended_key(key_code);
|
||||
return extended || prefix == 0xE000U || prefix == 0xE100U || extended_key(key_code);
|
||||
}
|
||||
|
||||
std::optional<unsigned> hid_modifier_bit_from_scan_code(
|
||||
std::uint16_t scan_code,
|
||||
KeyboardKeyCode key_code
|
||||
KeyboardKeyCode key_code,
|
||||
bool extended
|
||||
) {
|
||||
const auto extended = hid_scan_code_is_extended(scan_code, key_code);
|
||||
const auto is_extended = hid_scan_code_is_extended(scan_code, key_code, extended);
|
||||
switch (scan_code & 0xFFU) {
|
||||
case 0x1DU:
|
||||
return extended ? 4U : 0U;
|
||||
return is_extended ? 4U : 0U;
|
||||
case 0x2AU:
|
||||
return 1U;
|
||||
case 0x36U:
|
||||
return 5U;
|
||||
case 0x38U:
|
||||
return extended ? 6U : 2U;
|
||||
return is_extended ? 6U : 2U;
|
||||
case 0x5BU:
|
||||
return 3U;
|
||||
case 0x5CU:
|
||||
@@ -1828,7 +1829,8 @@ namespace lvh::detail {
|
||||
|
||||
std::optional<std::uint8_t> hid_usage_from_scan_code(
|
||||
std::uint16_t scan_code,
|
||||
KeyboardKeyCode key_code
|
||||
KeyboardKeyCode key_code,
|
||||
bool extended
|
||||
) {
|
||||
static constexpr auto extended_usages = std::to_array<HidUsageMapping<std::uint8_t>>({
|
||||
{0x1CU, 0x58U}, // Keypad Enter
|
||||
@@ -1927,7 +1929,7 @@ namespace lvh::detail {
|
||||
});
|
||||
|
||||
const auto code = static_cast<std::uint8_t>(scan_code & 0xFFU);
|
||||
if (hid_scan_code_is_extended(scan_code, key_code)) {
|
||||
if (hid_scan_code_is_extended(scan_code, key_code, extended)) {
|
||||
return mapped_hid_usage(code, extended_usages);
|
||||
}
|
||||
if (code >= 0x3BU && code <= 0x44U) {
|
||||
@@ -2017,7 +2019,7 @@ namespace lvh::detail {
|
||||
if (event.uses_normalized_key_code) {
|
||||
return windows_us_english_scan_code(event.key_code);
|
||||
}
|
||||
if (event.prefer_native_scan_code && can_map_virtual_key_to_scan_code(event.key_code)) {
|
||||
if ((event.prefer_native_scan_code || event.extended) && can_map_virtual_key_to_scan_code(event.key_code)) {
|
||||
return static_cast<std::uint16_t>(::MapVirtualKeyW(event.key_code, MAPVK_VK_TO_VSC));
|
||||
}
|
||||
return 0U;
|
||||
@@ -2025,14 +2027,14 @@ namespace lvh::detail {
|
||||
|
||||
std::optional<unsigned> hid_keyboard_modifier_bit(const KeyboardEvent &event) {
|
||||
if (const auto scan_code = hid_keyboard_scan_code(event); scan_code != 0U) {
|
||||
return hid_modifier_bit_from_scan_code(scan_code, event.key_code);
|
||||
return hid_modifier_bit_from_scan_code(scan_code, event.key_code, event.extended);
|
||||
}
|
||||
return hid_modifier_bit_from_virtual_key(event.key_code);
|
||||
}
|
||||
|
||||
std::optional<std::uint8_t> hid_keyboard_usage(const KeyboardEvent &event) {
|
||||
if (const auto scan_code = hid_keyboard_scan_code(event); scan_code != 0U) {
|
||||
return hid_usage_from_scan_code(scan_code, event.key_code);
|
||||
return hid_usage_from_scan_code(scan_code, event.key_code, event.extended);
|
||||
}
|
||||
return hid_usage_from_virtual_key(event.key_code);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,10 @@ namespace lvh::detail::test {
|
||||
OperationStatus modifier_press_status;
|
||||
OperationStatus extended_press_status;
|
||||
OperationStatus letter_release_status;
|
||||
OperationStatus enter_press_status;
|
||||
OperationStatus enter_release_status;
|
||||
OperationStatus keypad_enter_press_status;
|
||||
OperationStatus keypad_enter_release_status;
|
||||
OperationStatus unmapped_submit_status;
|
||||
OperationStatus text_status;
|
||||
OperationStatus close_status;
|
||||
@@ -195,8 +199,12 @@ namespace lvh::detail::test {
|
||||
OperationStatus invalid_text_status;
|
||||
OperationStatus failure_status;
|
||||
OperationStatus normalized_status;
|
||||
OperationStatus enter_status;
|
||||
OperationStatus keypad_enter_status;
|
||||
OperationStatus invalid_profile_status;
|
||||
WindowsSendInputRecord normalized_input;
|
||||
WindowsSendInputRecord enter_input;
|
||||
WindowsSendInputRecord keypad_enter_input;
|
||||
};
|
||||
|
||||
struct WindowsMouseSendInputResult {
|
||||
|
||||
+41
@@ -703,6 +703,26 @@ namespace lvh::detail {
|
||||
.scan_code = 0x4DU,
|
||||
});
|
||||
result.operations.letter_release_status = created.keyboard->submit({.key_code = 'A', .pressed = false});
|
||||
result.operations.enter_press_status = created.keyboard->submit({
|
||||
.key_code = VK_RETURN,
|
||||
.pressed = true,
|
||||
.uses_normalized_key_code = true,
|
||||
});
|
||||
result.operations.enter_release_status = created.keyboard->submit({
|
||||
.key_code = VK_RETURN,
|
||||
.pressed = false,
|
||||
.uses_normalized_key_code = true,
|
||||
});
|
||||
result.operations.keypad_enter_press_status = created.keyboard->submit({
|
||||
.key_code = VK_RETURN,
|
||||
.pressed = true,
|
||||
.extended = true,
|
||||
});
|
||||
result.operations.keypad_enter_release_status = created.keyboard->submit({
|
||||
.key_code = VK_RETURN,
|
||||
.pressed = false,
|
||||
.extended = true,
|
||||
});
|
||||
result.operations.unmapped_submit_status =
|
||||
created.keyboard->submit({.key_code = VK_BROWSER_BACK, .pressed = true});
|
||||
result.operations.text_status = created.keyboard->type_text({.text = "A"});
|
||||
@@ -1129,6 +1149,27 @@ namespace lvh::detail {
|
||||
result.keyboard.normalized_input = fake_send_input.sent_inputs.back();
|
||||
fake_send_input.sent_inputs.resize(prior_input_count);
|
||||
}
|
||||
|
||||
result.keyboard.enter_status = keyboard.keyboard->submit({
|
||||
.key_code = VK_RETURN,
|
||||
.pressed = true,
|
||||
.uses_normalized_key_code = true,
|
||||
});
|
||||
if (fake_send_input.sent_inputs.size() > prior_input_count) {
|
||||
result.keyboard.enter_input = fake_send_input.sent_inputs.back();
|
||||
fake_send_input.sent_inputs.resize(prior_input_count);
|
||||
}
|
||||
|
||||
result.keyboard.keypad_enter_status = keyboard.keyboard->submit({
|
||||
.key_code = VK_RETURN,
|
||||
.pressed = true,
|
||||
.uses_normalized_key_code = true,
|
||||
.extended = true,
|
||||
});
|
||||
if (fake_send_input.sent_inputs.size() > prior_input_count) {
|
||||
result.keyboard.keypad_enter_input = fake_send_input.sent_inputs.back();
|
||||
fake_send_input.sent_inputs.resize(prior_input_count);
|
||||
}
|
||||
} else {
|
||||
result.keyboard.down_status = keyboard.status;
|
||||
}
|
||||
|
||||
@@ -220,10 +220,14 @@ TEST_F(WindowsBackendTest, HidKeyboardPreservesIdentityTracksStateAndUsesNarrowF
|
||||
expect_ok(result.operations.modifier_press_status);
|
||||
expect_ok(result.operations.extended_press_status);
|
||||
expect_ok(result.operations.letter_release_status);
|
||||
expect_ok(result.operations.enter_press_status);
|
||||
expect_ok(result.operations.enter_release_status);
|
||||
expect_ok(result.operations.keypad_enter_press_status);
|
||||
expect_ok(result.operations.keypad_enter_release_status);
|
||||
expect_ok(result.operations.unmapped_submit_status);
|
||||
expect_ok(result.operations.text_status);
|
||||
expect_ok(result.operations.close_status);
|
||||
ASSERT_EQ(result.observations.reports.size(), 4U);
|
||||
ASSERT_EQ(result.observations.reports.size(), 8U);
|
||||
for (const auto &report : result.observations.reports) {
|
||||
EXPECT_EQ(report.size(), LVH_WINDOWS_KEYBOARD_INPUT_REPORT_SIZE);
|
||||
}
|
||||
@@ -236,6 +240,12 @@ TEST_F(WindowsBackendTest, HidKeyboardPreservesIdentityTracksStateAndUsesNarrowF
|
||||
EXPECT_EQ(result.observations.reports[2][3], 0x4FU);
|
||||
EXPECT_EQ(result.observations.reports[3][0], 0x02U);
|
||||
EXPECT_EQ(result.observations.reports[3][2], 0x4FU);
|
||||
EXPECT_EQ(result.observations.reports[4][2], 0x28U);
|
||||
EXPECT_EQ(result.observations.reports[4][3], 0x4FU);
|
||||
EXPECT_EQ(result.observations.reports[5][2], 0x4FU);
|
||||
EXPECT_EQ(result.observations.reports[6][2], 0x4FU);
|
||||
EXPECT_EQ(result.observations.reports[6][3], 0x58U);
|
||||
EXPECT_EQ(result.observations.reports[7][2], 0x4FU);
|
||||
EXPECT_EQ(result.observations.destroy_requests, 1U);
|
||||
EXPECT_EQ(result.observations.fallback_send_inputs, 3U);
|
||||
|
||||
@@ -334,6 +344,12 @@ TEST_F(WindowsBackendTest, SendInputDevicesTranslateKeyboardMouseFailuresAndUnsu
|
||||
EXPECT_EQ(result.keyboard.normalized_input.virtual_key, 0U);
|
||||
EXPECT_EQ(result.keyboard.normalized_input.scan_code, 30U);
|
||||
EXPECT_EQ(result.keyboard.normalized_input.key_flags, KEYEVENTF_SCANCODE);
|
||||
expect_ok(result.keyboard.enter_status);
|
||||
expect_ok(result.keyboard.keypad_enter_status);
|
||||
EXPECT_EQ(result.keyboard.enter_input.scan_code, 0x1CU);
|
||||
EXPECT_EQ(result.keyboard.enter_input.key_flags, KEYEVENTF_SCANCODE);
|
||||
EXPECT_EQ(result.keyboard.keypad_enter_input.scan_code, 0x1CU);
|
||||
EXPECT_EQ(result.keyboard.keypad_enter_input.key_flags, KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY);
|
||||
|
||||
expect_ok(result.mouse.relative_status);
|
||||
expect_ok(result.mouse.absolute_status);
|
||||
|
||||
Reference in new issue
Block a user