Skip to content

Commit 4e94dfb

Browse files
refactor(sonar): remove duplication
1 parent ccad17c commit 4e94dfb

4 files changed

Lines changed: 199 additions & 225 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ but reports the affected capability as unavailable and returns
111111
The Linux backend uses `libevdev` internally to construct uinput keyboard,
112112
mouse, touchscreen, trackpad, and pen tablet devices. Consumers still use the
113113
same platform-neutral C++ API; `libevdev` is a Linux build dependency, not a
114-
public API dependency.
114+
public API dependency. UTF-8 keyboard text submission is supported through the
115+
same Unicode compose sequence for both uinput keyboards and the XTest fallback.
115116

116117
The Linux packaging model needs `/dev/uinput` and `/dev/uhid` access. Install a udev rules file such
117118
as `/etc/udev/rules.d/60-libvirtualhid.rules` with:

src/core/runtime.cpp

Lines changed: 60 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,30 @@ namespace lvh {
269269
});
270270
}
271271

272+
template<class BackendAction, class DeviceUpdate>
273+
OperationStatus submit_touch_event(
274+
const auto &device_ptr,
275+
const char *closed_message,
276+
BackendAction backend_action,
277+
DeviceUpdate device_update
278+
) {
279+
return with_device(device_ptr, [closed_message, backend_action, device_update](auto &device) {
280+
if (!device.open) {
281+
return OperationStatus::failure(ErrorCode::device_closed, closed_message);
282+
}
283+
284+
if (device.backend) {
285+
if (const auto status = backend_action(*device.backend); !status.ok()) {
286+
return status;
287+
}
288+
}
289+
290+
device_update(device);
291+
++device.submitted_events;
292+
return OperationStatus::success();
293+
});
294+
}
295+
272296
template<class DeviceList>
273297
std::size_t count_open_devices(const DeviceList &devices) {
274298
std::size_t count = 0;
@@ -707,43 +731,33 @@ namespace lvh {
707731
return validation;
708732
}
709733

710-
return with_device(device_, [&contact](auto &device) {
711-
if (!device.open) {
712-
return OperationStatus::failure(ErrorCode::device_closed, "touchscreen is closed");
713-
}
714-
715-
if (device.backend) {
716-
if (const auto status = device.backend->place_contact(contact); !status.ok()) {
717-
return status;
718-
}
734+
return submit_touch_event(
735+
device_,
736+
"touchscreen is closed",
737+
[&contact](auto &backend) {
738+
return backend.place_contact(contact);
739+
},
740+
[&contact](auto &device) {
741+
device.last_contact = contact;
719742
}
720-
721-
device.last_contact = contact;
722-
++device.submitted_events;
723-
return OperationStatus::success();
724-
});
743+
);
725744
}
726745

727746
OperationStatus Touchscreen::release_contact(std::int32_t contact_id) {
728747
if (contact_id < 0) {
729748
return OperationStatus::failure(ErrorCode::invalid_argument, "touch contact id must not be negative");
730749
}
731750

732-
return with_device(device_, [contact_id](auto &device) {
733-
if (!device.open) {
734-
return OperationStatus::failure(ErrorCode::device_closed, "touchscreen is closed");
751+
return submit_touch_event(
752+
device_,
753+
"touchscreen is closed",
754+
[contact_id](auto &backend) {
755+
return backend.release_contact(contact_id);
756+
},
757+
[contact_id](auto &device) {
758+
device.last_contact.id = contact_id;
735759
}
736-
737-
if (device.backend) {
738-
if (const auto status = device.backend->release_contact(contact_id); !status.ok()) {
739-
return status;
740-
}
741-
}
742-
743-
device.last_contact.id = contact_id;
744-
++device.submitted_events;
745-
return OperationStatus::success();
746-
});
760+
);
747761
}
748762

749763
TouchContact Touchscreen::last_submitted_contact() const {
@@ -814,43 +828,33 @@ namespace lvh {
814828
return validation;
815829
}
816830

817-
return with_device(device_, [&contact](auto &device) {
818-
if (!device.open) {
819-
return OperationStatus::failure(ErrorCode::device_closed, "trackpad is closed");
831+
return submit_touch_event(
832+
device_,
833+
"trackpad is closed",
834+
[&contact](auto &backend) {
835+
return backend.place_contact(contact);
836+
},
837+
[&contact](auto &device) {
838+
device.last_contact = contact;
820839
}
821-
822-
if (device.backend) {
823-
if (const auto status = device.backend->place_contact(contact); !status.ok()) {
824-
return status;
825-
}
826-
}
827-
828-
device.last_contact = contact;
829-
++device.submitted_events;
830-
return OperationStatus::success();
831-
});
840+
);
832841
}
833842

834843
OperationStatus Trackpad::release_contact(std::int32_t contact_id) {
835844
if (contact_id < 0) {
836845
return OperationStatus::failure(ErrorCode::invalid_argument, "touch contact id must not be negative");
837846
}
838847

839-
return with_device(device_, [contact_id](auto &device) {
840-
if (!device.open) {
841-
return OperationStatus::failure(ErrorCode::device_closed, "trackpad is closed");
842-
}
843-
844-
if (device.backend) {
845-
if (const auto status = device.backend->release_contact(contact_id); !status.ok()) {
846-
return status;
847-
}
848+
return submit_touch_event(
849+
device_,
850+
"trackpad is closed",
851+
[contact_id](auto &backend) {
852+
return backend.release_contact(contact_id);
853+
},
854+
[contact_id](auto &device) {
855+
device.last_contact.id = contact_id;
848856
}
849-
850-
device.last_contact.id = contact_id;
851-
++device.submitted_events;
852-
return OperationStatus::success();
853-
});
857+
);
854858
}
855859

856860
OperationStatus Trackpad::button(bool pressed) {

src/platform/linux/uhid_backend.cpp

Lines changed: 57 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,57 @@ namespace lvh::detail {
713713
return static_cast<KeyboardKeyCode>(0x41 + (digit - 'A'));
714714
}
715715

716+
template<std::size_t Count, class SubmitKeyEvent>
717+
OperationStatus submit_keyboard_events(const std::array<KeyboardEvent, Count> &events, SubmitKeyEvent &submit_key_event) {
718+
for (const auto &event : events) {
719+
if (const auto status = submit_key_event(event); !status.ok()) {
720+
return status;
721+
}
722+
}
723+
return OperationStatus::success();
724+
}
725+
726+
template<class SubmitKeyEvent>
727+
OperationStatus type_text_with_unicode_hex(std::string_view text, SubmitKeyEvent submit_key_event) {
728+
static constexpr std::array<KeyboardEvent, 6> unicode_hex_prefix {{
729+
{.key_code = 0xA2, .pressed = true},
730+
{.key_code = 0xA0, .pressed = true},
731+
{.key_code = 0x55, .pressed = true},
732+
{.key_code = 0x55, .pressed = false},
733+
{.key_code = 0xA0, .pressed = false},
734+
{.key_code = 0xA2, .pressed = false},
735+
}};
736+
static constexpr std::array<KeyboardEvent, 2> unicode_hex_suffix {{
737+
{.key_code = 0x0D, .pressed = true},
738+
{.key_code = 0x0D, .pressed = false},
739+
}};
740+
741+
for (const auto codepoint : decode_utf8(text)) {
742+
const auto hex = uppercase_hex(codepoint);
743+
744+
if (const auto status = submit_keyboard_events(unicode_hex_prefix, submit_key_event); !status.ok()) {
745+
return status;
746+
}
747+
748+
for (const auto digit : hex) {
749+
const auto key_code = hex_digit_key_code(digit);
750+
const std::array<KeyboardEvent, 2> digit_events {{
751+
{.key_code = key_code, .pressed = true},
752+
{.key_code = key_code, .pressed = false},
753+
}};
754+
if (const auto status = submit_keyboard_events(digit_events, submit_key_event); !status.ok()) {
755+
return status;
756+
}
757+
}
758+
759+
if (const auto status = submit_keyboard_events(unicode_hex_suffix, submit_key_event); !status.ok()) {
760+
return status;
761+
}
762+
}
763+
764+
return OperationStatus::success();
765+
}
766+
716767
[[maybe_unused]] int legacy_scroll_steps(std::int32_t distance) {
717768
if (distance == 0) {
718769
return 0;
@@ -1158,47 +1209,9 @@ namespace lvh::detail {
11581209
}
11591210

11601211
OperationStatus type_text(const KeyboardTextEvent &event) override {
1161-
for (const auto codepoint : decode_utf8(event.text)) {
1162-
const auto hex = uppercase_hex(codepoint);
1163-
1164-
if (const auto status = submit({.key_code = 0xA2, .pressed = true}); !status.ok()) {
1165-
return status;
1166-
}
1167-
if (const auto status = submit({.key_code = 0xA0, .pressed = true}); !status.ok()) {
1168-
return status;
1169-
}
1170-
if (const auto status = submit({.key_code = 0x55, .pressed = true}); !status.ok()) {
1171-
return status;
1172-
}
1173-
if (const auto status = submit({.key_code = 0x55, .pressed = false}); !status.ok()) {
1174-
return status;
1175-
}
1176-
if (const auto status = submit({.key_code = 0xA0, .pressed = false}); !status.ok()) {
1177-
return status;
1178-
}
1179-
if (const auto status = submit({.key_code = 0xA2, .pressed = false}); !status.ok()) {
1180-
return status;
1181-
}
1182-
1183-
for (const auto digit : hex) {
1184-
const auto key_code = hex_digit_key_code(digit);
1185-
if (const auto status = submit({.key_code = key_code, .pressed = true}); !status.ok()) {
1186-
return status;
1187-
}
1188-
if (const auto status = submit({.key_code = key_code, .pressed = false}); !status.ok()) {
1189-
return status;
1190-
}
1191-
}
1192-
1193-
if (const auto status = submit({.key_code = 0x0D, .pressed = true}); !status.ok()) {
1194-
return status;
1195-
}
1196-
if (const auto status = submit({.key_code = 0x0D, .pressed = false}); !status.ok()) {
1197-
return status;
1198-
}
1199-
}
1200-
1201-
return OperationStatus::success();
1212+
return type_text_with_unicode_hex(event.text, [this](const KeyboardEvent &key_event) {
1213+
return submit(key_event);
1214+
});
12021215
}
12031216

12041217
OperationStatus close() override {
@@ -1952,47 +1965,9 @@ namespace lvh::detail {
19521965
}
19531966

19541967
OperationStatus type_text(const KeyboardTextEvent &event) override {
1955-
for (const auto codepoint : decode_utf8(event.text)) {
1956-
const auto hex = uppercase_hex(codepoint);
1957-
1958-
if (const auto status = submit({.key_code = 0xA2, .pressed = true}); !status.ok()) {
1959-
return status;
1960-
}
1961-
if (const auto status = submit({.key_code = 0xA0, .pressed = true}); !status.ok()) {
1962-
return status;
1963-
}
1964-
if (const auto status = submit({.key_code = 0x55, .pressed = true}); !status.ok()) {
1965-
return status;
1966-
}
1967-
if (const auto status = submit({.key_code = 0x55, .pressed = false}); !status.ok()) {
1968-
return status;
1969-
}
1970-
if (const auto status = submit({.key_code = 0xA0, .pressed = false}); !status.ok()) {
1971-
return status;
1972-
}
1973-
if (const auto status = submit({.key_code = 0xA2, .pressed = false}); !status.ok()) {
1974-
return status;
1975-
}
1976-
1977-
for (const auto digit : hex) {
1978-
const auto key_code = hex_digit_key_code(digit);
1979-
if (const auto status = submit({.key_code = key_code, .pressed = true}); !status.ok()) {
1980-
return status;
1981-
}
1982-
if (const auto status = submit({.key_code = key_code, .pressed = false}); !status.ok()) {
1983-
return status;
1984-
}
1985-
}
1986-
1987-
if (const auto status = submit({.key_code = 0x0D, .pressed = true}); !status.ok()) {
1988-
return status;
1989-
}
1990-
if (const auto status = submit({.key_code = 0x0D, .pressed = false}); !status.ok()) {
1991-
return status;
1992-
}
1993-
}
1994-
1995-
return OperationStatus::success();
1968+
return type_text_with_unicode_hex(event.text, [this](const KeyboardEvent &key_event) {
1969+
return submit(key_event);
1970+
});
19961971
}
19971972

19981973
OperationStatus close() override {

0 commit comments

Comments
 (0)