@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,198 @@
1
+ """Tests for pentablet.py."""
2
+
3
+ import math
4
+ import time
5
+ from unittest.mock import MagicMock, patch
6
+
7
+ import pytest
8
+
9
+ from inputtino._core import PenTablet as CorePenTablet
10
+ from inputtino.pentablet import (
11
+ DEFAULT_PEN_TABLET,
12
+ DeviceDefinition,
13
+ PenButtonType,
14
+ PenTablet,
15
+ PenToolType,
16
+ )
17
+ from tests.helpers import mark_practical
18
+
19
+
20
+ @pytest.fixture
21
+ def mock_core_tablet():
22
+ """Create a mock for CorePenTablet with basic functionality."""
23
+ with patch("inputtino._core.PenTablet") as mock_tablet_cls:
24
+ mock_instance = MagicMock(spec=CorePenTablet)
25
+ mock_tablet_cls.create.return_value = mock_instance
26
+ yield mock_instance
27
+
28
+
29
+ def test_pen_tablet_creation_with_default_device():
30
+ """Test creating a pen tablet with default device definition."""
31
+ with patch("inputtino._core.PenTablet") as mock_tablet_cls:
32
+ PenTablet()
33
+
34
+ mock_tablet_cls.create.assert_called_once()
35
+ created_def = mock_tablet_cls.create.call_args[0][0]
36
+ assert created_def.name == DEFAULT_PEN_TABLET.name
37
+ assert created_def.vendor_id == DEFAULT_PEN_TABLET.vendor_id
38
+ assert created_def.product_id == DEFAULT_PEN_TABLET.product_id
39
+ assert created_def.version == DEFAULT_PEN_TABLET.version
40
+
41
+
42
+ def test_pen_tablet_creation_with_custom_device():
43
+ """Test creating a pen tablet with custom device definition."""
44
+ custom_def = DeviceDefinition(
45
+ name="Custom Pen Tablet",
46
+ vendor_id=0x1234,
47
+ product_id=0x5678,
48
+ version=0x0001,
49
+ device_phys="/dev/input/test",
50
+ device_uniq="unique_id",
51
+ )
52
+
53
+ with patch("inputtino._core.PenTablet") as mock_tablet_cls:
54
+ PenTablet(custom_def)
55
+
56
+ mock_tablet_cls.create.assert_called_once()
57
+ created_def = mock_tablet_cls.create.call_args[0][0]
58
+ assert created_def.name == custom_def.name
59
+ assert created_def.vendor_id == custom_def.vendor_id
60
+ assert created_def.product_id == custom_def.product_id
61
+ assert created_def.version == custom_def.version
62
+ assert created_def.device_phys == custom_def.device_phys
63
+ assert created_def.device_uniq == custom_def.device_uniq
64
+
65
+
66
+ def test_pen_tablet_place_tool(mock_core_tablet):
67
+ """Test placing a tool on the tablet surface."""
68
+ tablet = PenTablet()
69
+ tablet.place_tool(
70
+ PenToolType.PEN,
71
+ x=0.5,
72
+ y=0.5,
73
+ pressure=1.0,
74
+ distance=0.0,
75
+ tilt_x=45.0,
76
+ tilt_y=-45.0,
77
+ )
78
+
79
+ mock_core_tablet.place_tool.assert_called_once_with(
80
+ PenToolType.PEN, 0.5, 0.5, 1.0, 0.0, 45.0, -45.0
81
+ )
82
+
83
+
84
+ def test_pen_tablet_set_button(mock_core_tablet):
85
+ """Test setting pen button states."""
86
+ tablet = PenTablet()
87
+
88
+ for button in [
89
+ PenButtonType.PRIMARY,
90
+ PenButtonType.SECONDARY,
91
+ PenButtonType.TERTIARY,
92
+ ]:
93
+ for pressed in (True, False):
94
+ tablet.set_button(button, pressed)
95
+ mock_core_tablet.set_btn.assert_called_with(button, pressed)
96
+
97
+
98
+ def test_pen_tablet_nodes(mock_core_tablet):
99
+ """Test getting pen tablet device nodes."""
100
+ expected_nodes = ["/dev/input/event0", "/dev/input/pen0"]
101
+ mock_core_tablet.get_nodes.return_value = expected_nodes
102
+
103
+ tablet = PenTablet()
104
+ assert tablet.nodes == expected_nodes
105
+ mock_core_tablet.get_nodes.assert_called_once()
106
+
107
+
108
+ def test_pen_tablet_creation_failure():
109
+ """Test handling of pen tablet creation failure."""
110
+ with patch("inputtino._core.PenTablet") as mock_tablet_cls:
111
+ mock_tablet_cls.create.side_effect = RuntimeError(
112
+ "Failed to create pen tablet device"
113
+ )
114
+
115
+ with pytest.raises(RuntimeError, match="Failed to create pen tablet device"):
116
+ PenTablet()
117
+
118
+
119
+ @mark_practical
120
+ def test_practical_pen_draw_circle():
121
+ """Test drawing a circle pattern with the pen tablet.
122
+
123
+ This practical test demonstrates:
124
+ 1. Drawing a circle using the pen tool
125
+ 2. Varying pressure based on position
126
+ 3. Adding tilt effects
127
+
128
+ Note:
129
+ This test requires actual device access (/dev/uinput)
130
+ and may fail if run without proper permissions.
131
+ """
132
+ tablet = PenTablet()
133
+
134
+ # Ensure we have valid device nodes
135
+ assert len(tablet.nodes) > 0, "No pen tablet device nodes created"
136
+
137
+ # Give system time to register the device
138
+ time.sleep(0.1)
139
+
140
+ # Draw circle pattern
141
+ radius = 0.3 # 30% of tablet width
142
+ center_x = 0.5
143
+ center_y = 0.5
144
+ steps = 50
145
+ try:
146
+ for i in range(steps + 1):
147
+ angle = 2 * math.pi * i / steps
148
+ x = center_x + radius * math.cos(angle)
149
+ y = center_y + radius * math.sin(angle)
150
+
151
+ # Vary pressure based on position (higher on right side)
152
+ pressure = 0.5 + 0.5 * math.cos(angle)
153
+
154
+ # Add tilt effect (tilt towards center)
155
+ tilt_x = -30 * math.cos(angle) # -30 to 30 degrees
156
+ tilt_y = -30 * math.sin(angle) # -30 to 30 degrees
157
+
158
+ tablet.place_tool(
159
+ PenToolType.PEN,
160
+ x=x,
161
+ y=y,
162
+ pressure=pressure,
163
+ distance=0.0, # Pen is touching surface
164
+ tilt_x=tilt_x,
165
+ tilt_y=tilt_y,
166
+ )
167
+ time.sleep(0.02) # Small delay for smooth movement
168
+
169
+ # Test button presses at cardinal points
170
+ for angle in [0, math.pi / 2, math.pi, 3 * math.pi / 2]:
171
+ x = center_x + radius * math.cos(angle)
172
+ y = center_y + radius * math.sin(angle)
173
+
174
+ tablet.place_tool(
175
+ PenToolType.PEN,
176
+ x=x,
177
+ y=y,
178
+ pressure=1.0,
179
+ distance=0.0,
180
+ tilt_x=0,
181
+ tilt_y=0,
182
+ )
183
+ tablet.set_button(PenButtonType.PRIMARY, True)
184
+ time.sleep(0.1)
185
+ tablet.set_button(PenButtonType.PRIMARY, False)
186
+ time.sleep(0.1)
187
+
188
+ finally:
189
+ # Place tool at distance to indicate release
190
+ tablet.place_tool(
191
+ PenToolType.SAME_AS_BEFORE,
192
+ x=center_x,
193
+ y=center_y,
194
+ pressure=0.0,
195
+ distance=1.0, # Pen is far from surface
196
+ tilt_x=0,
197
+ tilt_y=0,
198
+ )
@@ -0,0 +1,144 @@
1
+ """Tests for touchscreen.py."""
2
+
3
+ import time
4
+ from unittest.mock import MagicMock, patch
5
+
6
+ import pytest
7
+
8
+ from inputtino._core import TouchScreen as CoreTouchScreen
9
+ from inputtino.touchscreen import DeviceDefinition, TouchScreen
10
+ from tests.helpers import mark_practical
11
+
12
+
13
+ @pytest.fixture
14
+ def mock_core_touchscreen():
15
+ """Create a mock for CoreTouchScreen with basic functionality."""
16
+ with patch("inputtino._core.TouchScreen") as mock_touchscreen_cls:
17
+ mock_instance = MagicMock(spec=CoreTouchScreen)
18
+ mock_touchscreen_cls.create.return_value = mock_instance
19
+ yield mock_instance
20
+
21
+
22
+ def test_touchscreen_creation_with_default_device():
23
+ """Test creating a touchscreen with default device definition."""
24
+ with patch("inputtino._core.TouchScreen") as mock_touchscreen_cls:
25
+ TouchScreen()
26
+
27
+ mock_touchscreen_cls.create.assert_called_once()
28
+ created_def = mock_touchscreen_cls.create.call_args[0][0]
29
+ assert created_def.name == "Wolf (virtual) touchscreen"
30
+ assert created_def.vendor_id == 0xAB00
31
+ assert created_def.product_id == 0xAB03
32
+ assert created_def.version == 0xAB00
33
+
34
+
35
+ def test_touchscreen_creation_with_custom_device():
36
+ """Test creating a touchscreen with custom device definition."""
37
+ custom_def = DeviceDefinition(
38
+ name="Custom TouchScreen",
39
+ vendor_id=0x1234,
40
+ product_id=0x5678,
41
+ version=0x0001,
42
+ device_phys="/dev/input/test",
43
+ device_uniq="unique_id",
44
+ )
45
+
46
+ with patch("inputtino._core.TouchScreen") as mock_touchscreen_cls:
47
+ TouchScreen(custom_def)
48
+
49
+ mock_touchscreen_cls.create.assert_called_once()
50
+ created_def = mock_touchscreen_cls.create.call_args[0][0]
51
+ assert created_def.name == custom_def.name
52
+ assert created_def.vendor_id == custom_def.vendor_id
53
+ assert created_def.product_id == custom_def.product_id
54
+ assert created_def.version == custom_def.version
55
+ assert created_def.device_phys == custom_def.device_phys
56
+ assert created_def.device_uniq == custom_def.device_uniq
57
+
58
+
59
+ def test_touchscreen_finger_placement(mock_core_touchscreen):
60
+ """Test finger placement on touchscreen."""
61
+ touchscreen = TouchScreen()
62
+
63
+ # Test placing a finger with default pressure and orientation
64
+ touchscreen.place_finger(0, 0.5, 0.5)
65
+ mock_core_touchscreen.place_finger.assert_called_with(0, 0.5, 0.5, 1.0, 0)
66
+
67
+ # Test placing a finger with custom pressure and orientation
68
+ touchscreen.place_finger(1, 0.75, 0.25, 0.8, 45)
69
+ mock_core_touchscreen.place_finger.assert_called_with(1, 0.75, 0.25, 0.8, 45)
70
+
71
+
72
+ def test_touchscreen_finger_release(mock_core_touchscreen):
73
+ """Test finger release from touchscreen."""
74
+ touchscreen = TouchScreen()
75
+ touchscreen.release_finger(0)
76
+ mock_core_touchscreen.release_finger.assert_called_with(0)
77
+
78
+
79
+ def test_touchscreen_nodes(mock_core_touchscreen):
80
+ """Test getting touchscreen device nodes."""
81
+ expected_nodes = ["/dev/input/event0"]
82
+ mock_core_touchscreen.get_nodes.return_value = expected_nodes
83
+
84
+ touchscreen = TouchScreen()
85
+ assert touchscreen.nodes == expected_nodes
86
+ mock_core_touchscreen.get_nodes.assert_called_once()
87
+
88
+
89
+ def test_touchscreen_creation_failure():
90
+ """Test handling of touchscreen creation failure."""
91
+ with patch("inputtino._core.TouchScreen") as mock_touchscreen_cls:
92
+ mock_touchscreen_cls.create.side_effect = RuntimeError(
93
+ "Failed to create touchscreen device"
94
+ )
95
+
96
+ with pytest.raises(RuntimeError, match="Failed to create touchscreen device"):
97
+ TouchScreen()
98
+
99
+
100
+ @mark_practical
101
+ def test_practical_touchscreen_draw_circle():
102
+ """Test drawing a circle pattern with touch input.
103
+
104
+ This practical test demonstrates drawing a circle by:
105
+ 1. Moving a single touch point in a circular pattern
106
+ 2. Using normalized coordinates
107
+ 3. Creating a smooth touch movement pattern
108
+
109
+ Note:
110
+ This test requires actual device access (/dev/uinput)
111
+ and may fail if run without proper permissions.
112
+ """
113
+ touchscreen = TouchScreen()
114
+
115
+ # Ensure we have valid device nodes
116
+ assert len(touchscreen.nodes) > 0, "No touchscreen device nodes created"
117
+
118
+ # Give system time to register the device
119
+ time.sleep(0.1)
120
+
121
+ # Draw a circle using touch inputs
122
+ from math import cos, pi, sin
123
+
124
+ center_x, center_y = 0.5, 0.5 # Center of the screen
125
+ radius = 0.2 # Circle radius in normalized coordinates
126
+ steps = 36 # Number of points to create the circle
127
+
128
+ try:
129
+ # Draw circle pattern
130
+ for i in range(steps + 1):
131
+ angle = 2 * pi * i / steps
132
+ x = center_x + radius * cos(angle)
133
+ y = center_y + radius * sin(angle)
134
+
135
+ # Ensure coordinates stay within bounds
136
+ x = max(0.0, min(1.0, x))
137
+ y = max(0.0, min(1.0, y))
138
+
139
+ touchscreen.place_finger(0, x, y)
140
+ time.sleep(0.02) # Small delay for smooth movement
141
+
142
+ finally:
143
+ # Ensure we always release the finger
144
+ touchscreen.release_finger(0)
@@ -0,0 +1,146 @@
1
+ """Tests for trackpad.py."""
2
+
3
+ import time
4
+ from unittest.mock import MagicMock, patch
5
+
6
+ import pytest
7
+
8
+ from inputtino._core import Trackpad as CoreTrackpad
9
+ from inputtino.trackpad import DeviceDefinition, Trackpad
10
+ from tests.helpers import mark_practical
11
+
12
+
13
+ @pytest.fixture
14
+ def mock_core_trackpad():
15
+ """Create a mock for CoreTrackpad with basic functionality."""
16
+ with patch("inputtino._core.Trackpad") as mock_trackpad_cls:
17
+ mock_instance = MagicMock(spec=CoreTrackpad)
18
+ mock_trackpad_cls.create.return_value = mock_instance
19
+ yield mock_instance
20
+
21
+
22
+ def test_trackpad_creation_with_default_device():
23
+ """Test creating a trackpad with default device definition."""
24
+ with patch("inputtino._core.Trackpad") as mock_trackpad_cls:
25
+ Trackpad()
26
+
27
+ mock_trackpad_cls.create.assert_called_once()
28
+ created_def = mock_trackpad_cls.create.call_args[0][0]
29
+ assert created_def.name == "Wolf (virtual) touchpad"
30
+ assert created_def.vendor_id == 0xAB00
31
+ assert created_def.product_id == 0xAB02
32
+ assert created_def.version == 0xAB00
33
+
34
+
35
+ def test_trackpad_creation_with_custom_device():
36
+ """Test creating a trackpad with custom device definition."""
37
+ custom_def = DeviceDefinition(
38
+ name="Custom Trackpad",
39
+ vendor_id=0x1234,
40
+ product_id=0x5678,
41
+ version=0x0001,
42
+ device_phys="/dev/input/test",
43
+ device_uniq="unique_id",
44
+ )
45
+
46
+ with patch("inputtino._core.Trackpad") as mock_trackpad_cls:
47
+ Trackpad(custom_def)
48
+
49
+ mock_trackpad_cls.create.assert_called_once()
50
+ created_def = mock_trackpad_cls.create.call_args[0][0]
51
+ assert created_def.name == custom_def.name
52
+ assert created_def.vendor_id == custom_def.vendor_id
53
+ assert created_def.product_id == custom_def.product_id
54
+ assert created_def.version == custom_def.version
55
+ assert created_def.device_phys == custom_def.device_phys
56
+ assert created_def.device_uniq == custom_def.device_uniq
57
+
58
+
59
+ def test_trackpad_finger_operations(mock_core_trackpad):
60
+ """Test trackpad finger placement and release operations."""
61
+ trackpad = Trackpad()
62
+
63
+ # Test finger placement
64
+ trackpad.place_finger(0, 0.5, 0.5, 0.8, 45)
65
+ mock_core_trackpad.place_finger.assert_called_once_with(0, 0.5, 0.5, 0.8, 45)
66
+
67
+ # Test finger release
68
+ trackpad.release_finger(0)
69
+ mock_core_trackpad.release_finger.assert_called_once_with(0)
70
+
71
+
72
+ def test_trackpad_button_operations(mock_core_trackpad):
73
+ """Test trackpad button operations."""
74
+ trackpad = Trackpad()
75
+
76
+ # Test button press
77
+ trackpad.set_left_button(True)
78
+ mock_core_trackpad.set_left_btn.assert_called_once_with(True)
79
+
80
+ # Test button release
81
+ mock_core_trackpad.set_left_btn.reset_mock()
82
+ trackpad.set_left_button(False)
83
+ mock_core_trackpad.set_left_btn.assert_called_once_with(False)
84
+
85
+
86
+ def test_trackpad_nodes(mock_core_trackpad):
87
+ """Test getting trackpad device nodes."""
88
+ expected_nodes = ["/dev/input/event1"]
89
+ mock_core_trackpad.get_nodes.return_value = expected_nodes
90
+
91
+ trackpad = Trackpad()
92
+ assert trackpad.nodes == expected_nodes
93
+ mock_core_trackpad.get_nodes.assert_called_once()
94
+
95
+
96
+ def test_trackpad_creation_failure():
97
+ """Test handling of trackpad creation failure."""
98
+ with patch("inputtino._core.Trackpad") as mock_trackpad_cls:
99
+ mock_trackpad_cls.create.side_effect = RuntimeError(
100
+ "Failed to create trackpad device"
101
+ )
102
+
103
+ with pytest.raises(RuntimeError, match="Failed to create trackpad device"):
104
+ Trackpad()
105
+
106
+
107
+ @mark_practical
108
+ def test_practical_trackpad_gestures():
109
+ """Test practical trackpad gestures.
110
+
111
+ This practical test demonstrates basic trackpad functionality by:
112
+ 1. Simulating single finger tap
113
+ 2. Simulating two-finger scroll
114
+ 3. Testing button click
115
+
116
+ Note:
117
+ This test requires actual device access (/dev/uinput)
118
+ and may fail if run without proper permissions.
119
+ """
120
+ trackpad = Trackpad()
121
+
122
+ # Ensure we have valid device nodes
123
+ assert len(trackpad.nodes) > 0, "No trackpad device nodes created"
124
+
125
+ # Give system time to register the device
126
+ time.sleep(0.1)
127
+
128
+ # Single finger tap
129
+ trackpad.place_finger(0, 0.5, 0.5, 1.0, 0)
130
+ time.sleep(0.1)
131
+ trackpad.release_finger(0)
132
+
133
+ # Two-finger scroll (vertical)
134
+ trackpad.place_finger(0, 0.5, 0.3, 1.0, 0)
135
+ trackpad.place_finger(1, 0.5, 0.4, 1.0, 0)
136
+ for y in range(3, 8):
137
+ trackpad.place_finger(0, 0.5, y / 10, 1.0, 0)
138
+ trackpad.place_finger(1, 0.5, (y + 1) / 10, 1.0, 0)
139
+ time.sleep(0.05)
140
+ trackpad.release_finger(0)
141
+ trackpad.release_finger(1)
142
+
143
+ # Button click
144
+ trackpad.set_left_button(True)
145
+ time.sleep(0.1)
146
+ trackpad.set_left_button(False)