@desklink/host 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +44 -0
  3. package/README.md +266 -0
  4. package/bin/desklink-host.mjs +113 -0
  5. package/dist/bridge.d.ts +57 -0
  6. package/dist/bridge.d.ts.map +1 -0
  7. package/dist/bridge.js +208 -0
  8. package/dist/bridge.js.map +1 -0
  9. package/dist/engineProcess.d.ts +82 -0
  10. package/dist/engineProcess.d.ts.map +1 -0
  11. package/dist/engineProcess.js +243 -0
  12. package/dist/engineProcess.js.map +1 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +12 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/protocol.d.ts +169 -0
  18. package/dist/protocol.d.ts.map +1 -0
  19. package/dist/protocol.js +18 -0
  20. package/dist/protocol.js.map +1 -0
  21. package/dist/resolveEngine.d.ts +34 -0
  22. package/dist/resolveEngine.d.ts.map +1 -0
  23. package/dist/resolveEngine.js +103 -0
  24. package/dist/resolveEngine.js.map +1 -0
  25. package/docs/BOUNDARY.md +145 -0
  26. package/docs/PROTOCOL.md +363 -0
  27. package/engine/Cargo.lock +3077 -0
  28. package/engine/Cargo.toml +37 -0
  29. package/engine/build.rs +107 -0
  30. package/engine/native/inputtino_shim.cpp +11 -0
  31. package/engine/native/vpx_shim.c +184 -0
  32. package/engine/native/wrapper.h +7 -0
  33. package/engine/src/capture.rs +510 -0
  34. package/engine/src/clipboard.rs +132 -0
  35. package/engine/src/convert.rs +299 -0
  36. package/engine/src/encoder.rs +204 -0
  37. package/engine/src/input.rs +355 -0
  38. package/engine/src/keymap.rs +338 -0
  39. package/engine/src/main.rs +614 -0
  40. package/engine/src/peer.rs +775 -0
  41. package/engine/src/portal.rs +115 -0
  42. package/engine/src/protocol.rs +331 -0
  43. package/engine/src/session.rs +2159 -0
  44. package/engine/src/x11.rs +244 -0
  45. package/engine/vendor/inputtino/.clang-format +166 -0
  46. package/engine/vendor/inputtino/.clang-tidy +30 -0
  47. package/engine/vendor/inputtino/.dockerignore +11 -0
  48. package/engine/vendor/inputtino/.github/workflows/mirror-repo.yml +14 -0
  49. package/engine/vendor/inputtino/.github/workflows/python-lint-format-test-typecheck.yml +124 -0
  50. package/engine/vendor/inputtino/.github/workflows/python-test-install.yaml +49 -0
  51. package/engine/vendor/inputtino/.github/workflows/rust.yml +40 -0
  52. package/engine/vendor/inputtino/CMakeLists.txt +187 -0
  53. package/engine/vendor/inputtino/LICENSE +21 -0
  54. package/engine/vendor/inputtino/README.md +110 -0
  55. package/engine/vendor/inputtino/bindings/python/.devcontainer/Dockerfile +23 -0
  56. package/engine/vendor/inputtino/bindings/python/.devcontainer/devcontainer.json +28 -0
  57. package/engine/vendor/inputtino/bindings/python/.python-version +1 -0
  58. package/engine/vendor/inputtino/bindings/python/CMakeLists.txt +23 -0
  59. package/engine/vendor/inputtino/bindings/python/CPPLINT.cfg +2 -0
  60. package/engine/vendor/inputtino/bindings/python/Makefile +49 -0
  61. package/engine/vendor/inputtino/bindings/python/README.md +215 -0
  62. package/engine/vendor/inputtino/bindings/python/pyproject.toml +178 -0
  63. package/engine/vendor/inputtino/bindings/python/src/inputtino/__init__.py +43 -0
  64. package/engine/vendor/inputtino/bindings/python/src/inputtino/_core.pyi +533 -0
  65. package/engine/vendor/inputtino/bindings/python/src/inputtino/base.py +63 -0
  66. package/engine/vendor/inputtino/bindings/python/src/inputtino/joypad.py +248 -0
  67. package/engine/vendor/inputtino/bindings/python/src/inputtino/keyboard.py +256 -0
  68. package/engine/vendor/inputtino/bindings/python/src/inputtino/mouse.py +106 -0
  69. package/engine/vendor/inputtino/bindings/python/src/inputtino/pentablet.py +73 -0
  70. package/engine/vendor/inputtino/bindings/python/src/inputtino/py.typed +0 -0
  71. package/engine/vendor/inputtino/bindings/python/src/inputtino/touchscreen.py +61 -0
  72. package/engine/vendor/inputtino/bindings/python/src/inputtino/trackpad.py +74 -0
  73. package/engine/vendor/inputtino/bindings/python/src/main.cpp +217 -0
  74. package/engine/vendor/inputtino/bindings/python/tests/__init__.py +0 -0
  75. package/engine/vendor/inputtino/bindings/python/tests/helpers.py +3 -0
  76. package/engine/vendor/inputtino/bindings/python/tests/test_base.py +60 -0
  77. package/engine/vendor/inputtino/bindings/python/tests/test_inputtino.py +5 -0
  78. package/engine/vendor/inputtino/bindings/python/tests/test_joypad.py +332 -0
  79. package/engine/vendor/inputtino/bindings/python/tests/test_keyboard.py +164 -0
  80. package/engine/vendor/inputtino/bindings/python/tests/test_mouse.py +185 -0
  81. package/engine/vendor/inputtino/bindings/python/tests/test_pentablet.py +198 -0
  82. package/engine/vendor/inputtino/bindings/python/tests/test_touchscreen.py +144 -0
  83. package/engine/vendor/inputtino/bindings/python/tests/test_trackpad.py +146 -0
  84. package/engine/vendor/inputtino/bindings/python/uv.lock +390 -0
  85. package/engine/vendor/inputtino/bindings/rust/Cargo.toml +6 -0
  86. package/engine/vendor/inputtino/bindings/rust/inputtino/.dockerignore +1 -0
  87. package/engine/vendor/inputtino/bindings/rust/inputtino/Cargo.toml +18 -0
  88. package/engine/vendor/inputtino/bindings/rust/inputtino/examples/keyboard.rs +33 -0
  89. package/engine/vendor/inputtino/bindings/rust/inputtino/examples/mouse.rs +17 -0
  90. package/engine/vendor/inputtino/bindings/rust/inputtino/src/common.rs +116 -0
  91. package/engine/vendor/inputtino/bindings/rust/inputtino/src/error.rs +17 -0
  92. package/engine/vendor/inputtino/bindings/rust/inputtino/src/joypad.rs +74 -0
  93. package/engine/vendor/inputtino/bindings/rust/inputtino/src/joypad_nintendo.rs +135 -0
  94. package/engine/vendor/inputtino/bindings/rust/inputtino/src/joypad_ps5.rs +280 -0
  95. package/engine/vendor/inputtino/bindings/rust/inputtino/src/joypad_xbox.rs +134 -0
  96. package/engine/vendor/inputtino/bindings/rust/inputtino/src/keyboard.rs +76 -0
  97. package/engine/vendor/inputtino/bindings/rust/inputtino/src/lib.rs +32 -0
  98. package/engine/vendor/inputtino/bindings/rust/inputtino/src/mouse.rs +178 -0
  99. package/engine/vendor/inputtino/bindings/rust/inputtino/tests/common.rs +38 -0
  100. package/engine/vendor/inputtino/bindings/rust/inputtino/tests/joypads.rs +150 -0
  101. package/engine/vendor/inputtino/bindings/rust/inputtino/tests/keyboard.rs +73 -0
  102. package/engine/vendor/inputtino/bindings/rust/inputtino/tests/mouse.rs +139 -0
  103. package/engine/vendor/inputtino/bindings/rust/inputtino-sys/.dockerignore +1 -0
  104. package/engine/vendor/inputtino/bindings/rust/inputtino-sys/Cargo.toml +12 -0
  105. package/engine/vendor/inputtino/bindings/rust/inputtino-sys/build.rs +48 -0
  106. package/engine/vendor/inputtino/bindings/rust/inputtino-sys/src/lib.rs +5 -0
  107. package/engine/vendor/inputtino/bindings/rust/inputtino-sys/wrapper.h +1 -0
  108. package/engine/vendor/inputtino/cmake/utils.cmake +9 -0
  109. package/engine/vendor/inputtino/docker/Dockerfile +70 -0
  110. package/engine/vendor/inputtino/include/inputtino/input.h +324 -0
  111. package/engine/vendor/inputtino/include/inputtino/input.hpp +466 -0
  112. package/engine/vendor/inputtino/include/inputtino/result.hpp +37 -0
  113. package/engine/vendor/inputtino/share/pkgconfig/libinputtino.pc.in +17 -0
  114. package/engine/vendor/inputtino/src/c-bindings/helpers.hpp +19 -0
  115. package/engine/vendor/inputtino/src/c-bindings/joypad_ps5.cpp +115 -0
  116. package/engine/vendor/inputtino/src/c-bindings/joypad_switch.cpp +63 -0
  117. package/engine/vendor/inputtino/src/c-bindings/joypad_xone.cpp +62 -0
  118. package/engine/vendor/inputtino/src/c-bindings/keyboard.cpp +42 -0
  119. package/engine/vendor/inputtino/src/c-bindings/mouse.cpp +67 -0
  120. package/engine/vendor/inputtino/src/c-bindings/pen_tablet.cpp +50 -0
  121. package/engine/vendor/inputtino/src/c-bindings/touchscreen.cpp +43 -0
  122. package/engine/vendor/inputtino/src/c-bindings/trackpad.cpp +49 -0
  123. package/engine/vendor/inputtino/src/uhid/README.adoc +63 -0
  124. package/engine/vendor/inputtino/src/uhid/include/crc32.hpp +38 -0
  125. package/engine/vendor/inputtino/src/uhid/include/uhid/protected_types.hpp +36 -0
  126. package/engine/vendor/inputtino/src/uhid/include/uhid/ps5.hpp +550 -0
  127. package/engine/vendor/inputtino/src/uhid/include/uhid/uhid.hpp +168 -0
  128. package/engine/vendor/inputtino/src/uhid/joypad_ps5.cpp +580 -0
  129. package/engine/vendor/inputtino/src/uinput/include/inputtino/keyboard.hpp +84 -0
  130. package/engine/vendor/inputtino/src/uinput/include/inputtino/protected_ps5_types.hpp +7 -0
  131. package/engine/vendor/inputtino/src/uinput/include/inputtino/protected_types.hpp +110 -0
  132. package/engine/vendor/inputtino/src/uinput/joypad_nintendo.cpp +190 -0
  133. package/engine/vendor/inputtino/src/uinput/joypad_ps.cpp +203 -0
  134. package/engine/vendor/inputtino/src/uinput/joypad_utils.hpp +246 -0
  135. package/engine/vendor/inputtino/src/uinput/joypad_xbox.cpp +190 -0
  136. package/engine/vendor/inputtino/src/uinput/keyboard.cpp +120 -0
  137. package/engine/vendor/inputtino/src/uinput/mouse.cpp +199 -0
  138. package/engine/vendor/inputtino/src/uinput/pentablet.cpp +162 -0
  139. package/engine/vendor/inputtino/src/uinput/touchscreen.cpp +140 -0
  140. package/engine/vendor/inputtino/src/uinput/trackpad.cpp +192 -0
  141. package/examples/reference-client.html +428 -0
  142. package/package.json +38 -0
@@ -0,0 +1,246 @@
1
+ #pragma once
2
+ #include <algorithm>
3
+ #include <chrono>
4
+ #include <cstring>
5
+ #include <fcntl.h>
6
+ #include <filesystem>
7
+ #include <inputtino/input.hpp>
8
+ #include <inputtino/protected_types.hpp>
9
+ #include <iostream>
10
+ #include <linux/input.h>
11
+ #include <linux/uinput.h>
12
+ #include <optional>
13
+ #include <poll.h>
14
+ #include <thread>
15
+
16
+ namespace inputtino {
17
+
18
+ using namespace std::chrono_literals;
19
+
20
+ constexpr long MAX_GAIN = 0xFFFF;
21
+ constexpr int RUMBLE_POLL_TIMEOUT = 500; // ms
22
+
23
+ /**
24
+ * Joypads will also have one `/dev/input/js*` device as child, we want to expose that as well
25
+ */
26
+ static std::vector<std::string> get_child_dev_nodes(libevdev_uinput *device) {
27
+ std::vector<std::string> result;
28
+
29
+ auto dev_path = libevdev_uinput_get_devnode(device);
30
+ if (dev_path) {
31
+ result.push_back(dev_path);
32
+ }
33
+
34
+ auto sys_path = libevdev_uinput_get_syspath(device);
35
+ if (sys_path) {
36
+ for (const auto &entry : std::filesystem::directory_iterator(sys_path)) {
37
+ if (entry.is_directory() && entry.path().filename().string().rfind("js", 0) == 0) { // starts with "js"?
38
+ result.push_back("/dev/input/" + entry.path().filename().string());
39
+ }
40
+ }
41
+ }
42
+
43
+ return result;
44
+ }
45
+
46
+ struct ActiveRumbleEffect {
47
+ std::chrono::steady_clock::time_point start_point;
48
+ std::chrono::steady_clock::time_point end_point;
49
+ std::chrono::milliseconds length;
50
+ std::chrono::milliseconds delay;
51
+ ff_envelope envelope;
52
+
53
+ struct {
54
+ long weak, strong;
55
+ } start;
56
+
57
+ struct {
58
+ long weak, strong;
59
+ } end;
60
+ };
61
+
62
+ static long rumble_magnitude(std::chrono::milliseconds time_left,
63
+ std::uint32_t start,
64
+ std::uint32_t end,
65
+ std::chrono::milliseconds length) {
66
+ auto rel = end - start;
67
+ return start + (rel * time_left.count() / length.count());
68
+ }
69
+
70
+ static std::pair<long, long> simulate_rumble(const ActiveRumbleEffect &effect,
71
+ const std::chrono::steady_clock::time_point &now) {
72
+ if (effect.end_point < now || now < effect.start_point) {
73
+ return {0, 0};
74
+ }
75
+
76
+ auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(effect.end_point - now);
77
+ auto t = effect.length - time_left;
78
+
79
+ auto weak = rumble_magnitude(t, effect.start.weak, effect.end.weak, effect.length);
80
+ auto strong = rumble_magnitude(t, effect.start.strong, effect.end.strong, effect.length);
81
+
82
+ if (t.count() < effect.envelope.attack_length) {
83
+ weak = (effect.envelope.attack_level * t.count() + weak * (effect.envelope.attack_length - t.count())) /
84
+ effect.envelope.attack_length;
85
+ strong = (effect.envelope.attack_level * t.count() + strong * (effect.envelope.attack_length - t.count())) /
86
+ effect.envelope.attack_length;
87
+ } else if (time_left.count() < effect.envelope.fade_length) {
88
+ auto dt = (t - effect.length).count() + effect.envelope.fade_length;
89
+
90
+ weak = (effect.envelope.fade_level * dt + weak * (effect.envelope.fade_length - dt)) / effect.envelope.fade_length;
91
+ strong = (effect.envelope.fade_level * dt + strong * (effect.envelope.fade_length - dt)) /
92
+ effect.envelope.fade_length;
93
+ }
94
+
95
+ return {weak, strong};
96
+ }
97
+
98
+ static ActiveRumbleEffect create_rumble_effect(const ff_effect &effect) {
99
+ // All duration values are expressed in ms. Values above 32767 ms (0x7fff) should not be used
100
+ ActiveRumbleEffect r_effect{
101
+ .start_point = std::chrono::steady_clock::time_point::min(),
102
+ .end_point = std::chrono::steady_clock::time_point::min(),
103
+ .length = std::chrono::milliseconds{std::clamp(effect.replay.length, (std::uint16_t)0, (std::uint16_t)32767)},
104
+ .delay = std::chrono::milliseconds{std::clamp(effect.replay.delay, (std::uint16_t)0, (std::uint16_t)32767)},
105
+ .envelope = {}};
106
+ switch (effect.type) {
107
+ case FF_CONSTANT:
108
+ r_effect.start.weak = effect.u.constant.level;
109
+ r_effect.start.strong = effect.u.constant.level;
110
+ r_effect.end.weak = effect.u.constant.level;
111
+ r_effect.end.strong = effect.u.constant.level;
112
+ r_effect.envelope = effect.u.constant.envelope;
113
+ break;
114
+ case FF_PERIODIC:
115
+ r_effect.start.weak = effect.u.periodic.magnitude;
116
+ r_effect.start.strong = effect.u.periodic.magnitude;
117
+ r_effect.end.weak = effect.u.periodic.magnitude;
118
+ r_effect.end.strong = effect.u.periodic.magnitude;
119
+ r_effect.envelope = effect.u.periodic.envelope;
120
+ break;
121
+ case FF_RAMP:
122
+ r_effect.start.weak = effect.u.ramp.start_level;
123
+ r_effect.start.strong = effect.u.ramp.start_level;
124
+ r_effect.end.weak = effect.u.ramp.end_level;
125
+ r_effect.end.strong = effect.u.ramp.end_level;
126
+ r_effect.envelope = effect.u.ramp.envelope;
127
+ break;
128
+ case FF_RUMBLE:
129
+ r_effect.start.weak = effect.u.rumble.weak_magnitude;
130
+ r_effect.start.strong = effect.u.rumble.strong_magnitude;
131
+ r_effect.end.weak = effect.u.rumble.weak_magnitude;
132
+ r_effect.end.strong = effect.u.rumble.strong_magnitude;
133
+ break;
134
+ }
135
+ return r_effect;
136
+ }
137
+
138
+ /**
139
+ * Here we listen for events from the device and call the corresponding callback functions
140
+ *
141
+ * Rumble:
142
+ * First of, this is called force feedback (FF) in linux,
143
+ * you can see some docs here: https://www.kernel.org/doc/html/latest/input/ff.html
144
+ * In uinput this works as a two step process:
145
+ * - you first upload the FF effect with a given request ID
146
+ * - later on when the rumble has been activated you'll receive an EV_FF in your /dev/input/event**
147
+ * where the value is the request ID
148
+ * You can test the virtual devices that we create by simply using the utility `fftest`
149
+ */
150
+ static void event_listener(const std::shared_ptr<BaseJoypadState> &state) {
151
+ std::this_thread::sleep_for(100ms); // We have to sleep in order to be able to read from the newly created device
152
+
153
+ auto uinput_fd = libevdev_uinput_get_fd(state->joy.get());
154
+ if (uinput_fd < 0) {
155
+ std::cerr << "Unable to open uinput device, additional events will be disabled.";
156
+ return;
157
+ }
158
+
159
+ // We have to add 0_NONBLOCK to the flags in order to be able to read the events
160
+ int flags = fcntl(uinput_fd, F_GETFL, 0);
161
+ fcntl(uinput_fd, F_SETFL, flags | O_NONBLOCK);
162
+
163
+ /* Local copy of all the uploaded ff effects */
164
+ std::map<int, ActiveRumbleEffect> ff_effects = {};
165
+ std::pair<std::uint32_t, std::uint32_t> prev_rumble = {0, 0};
166
+
167
+ /* This can only be set globally when receiving FF_GAIN */
168
+ unsigned int current_gain = MAX_GAIN;
169
+
170
+ std::array<pollfd, 1> pfds = {pollfd{.fd = uinput_fd, .events = POLLIN}};
171
+ int poll_rs = 0;
172
+
173
+ while (!state->stop_listening_events) {
174
+ poll_rs = poll(pfds.data(), pfds.size(), RUMBLE_POLL_TIMEOUT);
175
+ if (poll_rs < 0) {
176
+ std::cerr << "Failed polling uinput fd; ret=" << strerror(errno);
177
+ return;
178
+ }
179
+
180
+ auto events = fetch_events(uinput_fd);
181
+ for (auto ev : events) {
182
+ if (ev->type == EV_UINPUT && ev->code == UI_FF_UPLOAD) { // Upload a new FF effect
183
+ uinput_ff_upload upload{};
184
+ upload.request_id = ev->value;
185
+
186
+ ioctl(uinput_fd, UI_BEGIN_FF_UPLOAD, &upload); // retrieve the effect
187
+
188
+ auto new_effect = create_rumble_effect(upload.effect);
189
+ if (ff_effects.find(upload.effect.id) != ff_effects.end()) {
190
+ // We have to keep the original start and end points of the effect
191
+ new_effect.start_point = ff_effects[upload.effect.id].start_point;
192
+ new_effect.end_point = ff_effects[upload.effect.id].end_point;
193
+ }
194
+ ff_effects.insert_or_assign(upload.effect.id, create_rumble_effect(upload.effect));
195
+ upload.retval = 0;
196
+
197
+ ioctl(uinput_fd, UI_END_FF_UPLOAD, &upload);
198
+ } else if (ev->type == EV_UINPUT && ev->code == UI_FF_ERASE) { // Remove an uploaded FF effect
199
+ uinput_ff_erase erase{};
200
+ erase.request_id = ev->value;
201
+
202
+ ioctl(uinput_fd, UI_BEGIN_FF_ERASE, &erase); // retrieve ff_erase
203
+
204
+ ff_effects.erase(erase.effect_id);
205
+ erase.retval = 0;
206
+
207
+ ioctl(uinput_fd, UI_END_FF_ERASE, &erase);
208
+ } else if (ev->type == EV_FF && ev->code == FF_GAIN) { // Force feedback set gain
209
+ current_gain = std::clamp((long)ev->value, 0l, MAX_GAIN);
210
+ } else if (ev->type == EV_FF) { // Force feedback effect
211
+ auto effect_id = ev->code;
212
+ if (auto effect = ff_effects.find(effect_id); effect != ff_effects.end()) {
213
+ if (ev->value) { // Activate
214
+ auto now = std::chrono::steady_clock::now();
215
+ effect->second.start_point = now + effect->second.delay;
216
+ effect->second.end_point = now + effect->second.delay + effect->second.length;
217
+ } else { // Deactivate
218
+ effect->second.end_point = std::chrono::steady_clock::time_point::min();
219
+ }
220
+ }
221
+ }
222
+ }
223
+
224
+ auto now = std::chrono::steady_clock::now();
225
+
226
+ // Accumulate all rumble effects
227
+ std::pair<long, long> current_rumble = {0, 0};
228
+ for (auto &[_id, effect] : ff_effects) {
229
+ auto [weak, strong] = simulate_rumble(effect, now);
230
+ current_rumble.first += weak;
231
+ current_rumble.second += strong;
232
+ }
233
+ // Avoid sending too many events
234
+ if (prev_rumble.first != current_rumble.first || prev_rumble.second != current_rumble.second) {
235
+ prev_rumble.first = current_rumble.first;
236
+ prev_rumble.second = current_rumble.second;
237
+
238
+ if (auto callback = state->on_rumble) {
239
+ callback.value()(static_cast<int>((current_rumble.second * current_gain / MAX_GAIN)),
240
+ static_cast<int>((current_rumble.first * current_gain / MAX_GAIN)));
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ } // namespace inputtino
@@ -0,0 +1,190 @@
1
+ #include "joypad_utils.hpp"
2
+ #include <cstring>
3
+ #include <inputtino/input.hpp>
4
+ #include <inputtino/protected_types.hpp>
5
+ #include <iostream>
6
+ #include <linux/input.h>
7
+ #include <optional>
8
+ #include <thread>
9
+
10
+ namespace inputtino {
11
+
12
+ std::vector<std::string> XboxOneJoypad::get_nodes() const {
13
+ std::vector<std::string> nodes;
14
+
15
+ if (auto joy = _state->joy.get()) {
16
+ auto additional_nodes = get_child_dev_nodes(joy);
17
+ nodes.insert(nodes.end(), additional_nodes.begin(), additional_nodes.end());
18
+ }
19
+
20
+ return nodes;
21
+ }
22
+
23
+ Result<libevdev_uinput_ptr> create_xbox_controller(const DeviceDefinition &device) {
24
+ libevdev *dev = libevdev_new();
25
+ libevdev_uinput *uidev;
26
+
27
+ libevdev_set_name(dev, device.name.c_str());
28
+ libevdev_set_id_vendor(dev, device.vendor_id);
29
+ libevdev_set_id_product(dev, device.product_id);
30
+ libevdev_set_id_version(dev, device.version);
31
+ libevdev_set_id_bustype(dev, BUS_USB);
32
+
33
+ libevdev_enable_event_type(dev, EV_KEY);
34
+ libevdev_enable_event_code(dev, EV_KEY, BTN_WEST, nullptr);
35
+ libevdev_enable_event_code(dev, EV_KEY, BTN_EAST, nullptr);
36
+ libevdev_enable_event_code(dev, EV_KEY, BTN_NORTH, nullptr);
37
+ libevdev_enable_event_code(dev, EV_KEY, BTN_SOUTH, nullptr);
38
+ libevdev_enable_event_code(dev, EV_KEY, BTN_THUMBL, nullptr);
39
+ libevdev_enable_event_code(dev, EV_KEY, BTN_THUMBR, nullptr);
40
+ libevdev_enable_event_code(dev, EV_KEY, BTN_TR, nullptr);
41
+ libevdev_enable_event_code(dev, EV_KEY, BTN_TL, nullptr);
42
+ libevdev_enable_event_code(dev, EV_KEY, BTN_SELECT, nullptr);
43
+ libevdev_enable_event_code(dev, EV_KEY, BTN_MODE, nullptr);
44
+ libevdev_enable_event_code(dev, EV_KEY, BTN_START, nullptr);
45
+
46
+ libevdev_enable_event_type(dev, EV_ABS);
47
+
48
+ input_absinfo dpad{0, -1, 1, 0, 0, 0};
49
+ libevdev_enable_event_code(dev, EV_ABS, ABS_HAT0Y, &dpad);
50
+ libevdev_enable_event_code(dev, EV_ABS, ABS_HAT0X, &dpad);
51
+
52
+ input_absinfo stick{0, -32768, 32767, 16, 128, 0};
53
+ libevdev_enable_event_code(dev, EV_ABS, ABS_X, &stick);
54
+ libevdev_enable_event_code(dev, EV_ABS, ABS_RX, &stick);
55
+ libevdev_enable_event_code(dev, EV_ABS, ABS_Y, &stick);
56
+ libevdev_enable_event_code(dev, EV_ABS, ABS_RY, &stick);
57
+
58
+ input_absinfo trigger{0, 0, 255, 0, 0, 0};
59
+ libevdev_enable_event_code(dev, EV_ABS, ABS_Z, &trigger);
60
+ libevdev_enable_event_code(dev, EV_ABS, ABS_RZ, &trigger);
61
+
62
+ libevdev_enable_event_type(dev, EV_FF);
63
+ libevdev_enable_event_code(dev, EV_FF, FF_RUMBLE, nullptr);
64
+ libevdev_enable_event_code(dev, EV_FF, FF_CONSTANT, nullptr);
65
+ libevdev_enable_event_code(dev, EV_FF, FF_PERIODIC, nullptr);
66
+ libevdev_enable_event_code(dev, EV_FF, FF_SINE, nullptr);
67
+ libevdev_enable_event_code(dev, EV_FF, FF_RAMP, nullptr);
68
+ libevdev_enable_event_code(dev, EV_FF, FF_GAIN, nullptr);
69
+
70
+ auto err = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev);
71
+ libevdev_free(dev);
72
+ if (err != 0) {
73
+ return Error(strerror(-err));
74
+ }
75
+
76
+ return libevdev_uinput_ptr{uidev, ::libevdev_uinput_destroy};
77
+ }
78
+
79
+ XboxOneJoypad::XboxOneJoypad() : _state(std::make_shared<XboxOneJoypadState>()) {}
80
+
81
+ XboxOneJoypad::~XboxOneJoypad() {
82
+ if (_state) {
83
+ _state->stop_listening_events = true;
84
+ if (_state->joy.get() != nullptr && _state->events_thread.joinable()) {
85
+ _state->events_thread.join();
86
+ }
87
+ }
88
+ }
89
+
90
+ Result<XboxOneJoypad> XboxOneJoypad::create(const DeviceDefinition &device) {
91
+ auto joy_el = create_xbox_controller(device);
92
+ if (!joy_el) {
93
+ return Error(joy_el.getErrorMessage());
94
+ }
95
+
96
+ XboxOneJoypad joypad;
97
+ joypad._state->joy = std::move(*joy_el);
98
+
99
+ auto event_thread = std::thread(event_listener, joypad._state);
100
+ joypad._state->events_thread = std::move(event_thread);
101
+ joypad._state->events_thread.detach();
102
+ return joypad;
103
+ }
104
+
105
+ void XboxOneJoypad::set_pressed_buttons(unsigned int newly_pressed) {
106
+ // Button flags that have been changed between current and prev
107
+ auto bf_changed = newly_pressed ^ this->_state->currently_pressed_btns;
108
+ // Button flags that are only part of the new packet
109
+ auto bf_new = newly_pressed;
110
+ if (auto controller = this->_state->joy.get()) {
111
+
112
+ if (bf_changed) {
113
+ if ((DPAD_UP | DPAD_DOWN) & bf_changed) {
114
+ int button_state = bf_new & DPAD_UP ? -1 : (bf_new & DPAD_DOWN ? 1 : 0);
115
+
116
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_HAT0Y, button_state);
117
+ }
118
+
119
+ if ((DPAD_LEFT | DPAD_RIGHT) & bf_changed) {
120
+ int button_state = bf_new & DPAD_LEFT ? -1 : (bf_new & DPAD_RIGHT ? 1 : 0);
121
+
122
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_HAT0X, button_state);
123
+ }
124
+
125
+ if (START & bf_changed)
126
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_START, bf_new & START ? 1 : 0);
127
+ if (BACK & bf_changed)
128
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_SELECT, bf_new & BACK ? 1 : 0);
129
+ if (LEFT_STICK & bf_changed)
130
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_THUMBL, bf_new & LEFT_STICK ? 1 : 0);
131
+ if (RIGHT_STICK & bf_changed)
132
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_THUMBR, bf_new & RIGHT_STICK ? 1 : 0);
133
+ if (LEFT_BUTTON & bf_changed)
134
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_TL, bf_new & LEFT_BUTTON ? 1 : 0);
135
+ if (RIGHT_BUTTON & bf_changed)
136
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_TR, bf_new & RIGHT_BUTTON ? 1 : 0);
137
+ if (HOME & bf_changed)
138
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_MODE, bf_new & HOME ? 1 : 0);
139
+ if (A & bf_changed)
140
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_SOUTH, bf_new & A ? 1 : 0);
141
+ if (B & bf_changed)
142
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_EAST, bf_new & B ? 1 : 0);
143
+ if (X & bf_changed)
144
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_NORTH, bf_new & X ? 1 : 0);
145
+ if (Y & bf_changed)
146
+ libevdev_uinput_write_event(controller, EV_KEY, BTN_WEST, bf_new & Y ? 1 : 0);
147
+ }
148
+
149
+ libevdev_uinput_write_event(controller, EV_SYN, SYN_REPORT, 0);
150
+ }
151
+ this->_state->currently_pressed_btns = bf_new;
152
+ }
153
+
154
+ void XboxOneJoypad::set_stick(STICK_POSITION stick_type, short x, short y) {
155
+ if (auto controller = this->_state->joy.get()) {
156
+ if (stick_type == LS) {
157
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_X, x);
158
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_Y, -y);
159
+ } else {
160
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_RX, x);
161
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_RY, -y);
162
+ }
163
+
164
+ libevdev_uinput_write_event(controller, EV_SYN, SYN_REPORT, 0);
165
+ }
166
+ }
167
+
168
+ void XboxOneJoypad::set_triggers(int16_t left, int16_t right) {
169
+ if (auto controller = this->_state->joy.get()) {
170
+ if (left > 0) {
171
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_Z, left);
172
+ } else {
173
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_Z, left);
174
+ }
175
+
176
+ if (right > 0) {
177
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_RZ, right);
178
+ } else {
179
+ libevdev_uinput_write_event(controller, EV_ABS, ABS_RZ, right);
180
+ }
181
+
182
+ libevdev_uinput_write_event(controller, EV_SYN, SYN_REPORT, 0);
183
+ }
184
+ }
185
+
186
+ void XboxOneJoypad::set_on_rumble(const std::function<void(int, int)> &callback) {
187
+ this->_state->on_rumble = callback;
188
+ }
189
+
190
+ } // namespace inputtino
@@ -0,0 +1,120 @@
1
+ #include "inputtino/input.hpp"
2
+
3
+ #include <algorithm>
4
+ #include <cstring>
5
+ #include <inputtino/protected_types.hpp>
6
+ #include <inputtino/keyboard.hpp>
7
+ #include <thread>
8
+
9
+ namespace inputtino {
10
+
11
+ using namespace std::string_literals;
12
+
13
+ std::vector<std::string> Keyboard::get_nodes() const {
14
+ std::vector<std::string> nodes;
15
+
16
+ if (auto kb = _state->kb.get()) {
17
+ nodes.emplace_back(libevdev_uinput_get_devnode(kb));
18
+ }
19
+
20
+ return nodes;
21
+ }
22
+
23
+ Result<libevdev_uinput_ptr> create_keyboard(const DeviceDefinition &device) {
24
+ auto dev = libevdev_new();
25
+ libevdev_uinput *uidev;
26
+
27
+ libevdev_set_name(dev, device.name.c_str());
28
+ libevdev_set_id_vendor(dev, device.vendor_id);
29
+ libevdev_set_id_product(dev, device.product_id);
30
+ libevdev_set_id_version(dev, device.version);
31
+ libevdev_set_id_bustype(dev, BUS_USB);
32
+
33
+ libevdev_enable_event_type(dev, EV_KEY);
34
+ libevdev_enable_event_code(dev, EV_KEY, KEY_BACKSPACE, nullptr);
35
+
36
+ for (auto ev : keyboard::key_mappings) {
37
+ libevdev_enable_event_code(dev, EV_KEY, ev.second.linux_code, nullptr);
38
+ }
39
+
40
+ auto err = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev);
41
+ libevdev_free(dev);
42
+ if (err != 0) {
43
+ return Error(strerror(-err));
44
+ }
45
+
46
+ return libevdev_uinput_ptr{uidev, ::libevdev_uinput_destroy};
47
+ }
48
+
49
+ static std::optional<keyboard::KEY_MAP> press_btn(libevdev_uinput *kb, short key_code) {
50
+ auto search_key = keyboard::key_mappings.find(key_code);
51
+ if (search_key != keyboard::key_mappings.end()) {
52
+ auto mapped_key = search_key->second;
53
+
54
+ libevdev_uinput_write_event(kb, EV_MSC, MSC_SCAN, mapped_key.scan_code);
55
+ libevdev_uinput_write_event(kb, EV_KEY, mapped_key.linux_code, 1);
56
+ libevdev_uinput_write_event(kb, EV_SYN, SYN_REPORT, 0);
57
+ return mapped_key;
58
+ }
59
+ return {};
60
+ }
61
+
62
+ Keyboard::Keyboard() : _state(std::make_shared<KeyboardState>()) {}
63
+
64
+ Keyboard::~Keyboard() {
65
+ if (_state) {
66
+ _state->stop_repeat_thread = true;
67
+ if (_state->repeat_press_t.joinable()) {
68
+ _state->repeat_press_t.join();
69
+ }
70
+ }
71
+ }
72
+
73
+ Result<Keyboard> Keyboard::create(const DeviceDefinition &device, int millis_repress_key) {
74
+ auto kb_el = create_keyboard(device);
75
+ if (kb_el) {
76
+ Keyboard kb;
77
+ kb._state->kb = std::move(*kb_el);
78
+ auto repeat_thread = std::thread([state = kb._state, millis_repress_key]() {
79
+ while (!state->stop_repeat_thread) {
80
+ std::this_thread::sleep_for(std::chrono::milliseconds(millis_repress_key));
81
+ for (auto key : state->cur_press_keys) {
82
+ if (auto keyboard = state->kb.get()) {
83
+ press_btn(keyboard, key);
84
+ }
85
+ }
86
+ }
87
+ });
88
+ kb._state->repeat_press_t = std::move(repeat_thread);
89
+ kb._state->repeat_press_t.detach();
90
+ return kb;
91
+ } else {
92
+ return Error(kb_el.getErrorMessage());
93
+ }
94
+ }
95
+
96
+ void Keyboard::press(short key_code) {
97
+ if (auto keyboard = _state->kb.get()) {
98
+ if (auto key = press_btn(keyboard, key_code)) {
99
+ _state->cur_press_keys.push_back(key_code);
100
+ }
101
+ }
102
+ }
103
+
104
+ void Keyboard::release(short key_code) {
105
+ auto search_key = keyboard::key_mappings.find(key_code);
106
+ if (search_key != keyboard::key_mappings.end()) {
107
+ if (auto keyboard = _state->kb.get()) {
108
+ auto mapped_key = search_key->second;
109
+ this->_state->cur_press_keys.erase(
110
+ std::remove(this->_state->cur_press_keys.begin(), this->_state->cur_press_keys.end(), key_code),
111
+ this->_state->cur_press_keys.end());
112
+
113
+ libevdev_uinput_write_event(keyboard, EV_MSC, MSC_SCAN, mapped_key.scan_code);
114
+ libevdev_uinput_write_event(keyboard, EV_KEY, mapped_key.linux_code, 0);
115
+ libevdev_uinput_write_event(keyboard, EV_SYN, SYN_REPORT, 0);
116
+ }
117
+ }
118
+ }
119
+
120
+ } // namespace inputtino