FreeRDP
Loading...
Searching...
No Matches
shadow_input.c
1
19#include <winpr/assert.h>
20#include <freerdp/config.h>
21
22#include <freerdp/log.h>
23#include "shadow.h"
24
25#define TAG SERVER_TAG("shadow.input")
26
27/* A key in rdpShadowClient::keysDown: the scan code with its extended flags */
28#define KEY_INDEX_EXTENDED 0x100
29#define KEY_INDEX_EXTENDED1 0x200
30#define KEY_INDEX_COUNT 0x400
31
32WINPR_ATTR_NODISCARD
33static size_t shadow_input_key_index(UINT16 flags, UINT8 code)
34{
35 size_t index = code;
36 if (flags & KBD_FLAGS_EXTENDED)
37 index |= KEY_INDEX_EXTENDED;
38 if (flags & KBD_FLAGS_EXTENDED1)
39 index |= KEY_INDEX_EXTENDED1;
40 return index;
41}
42
43WINPR_ATTR_NODISCARD
44static BOOL shadow_input_key_is_down(const rdpShadowClient* client, size_t index)
45{
46 WINPR_ASSERT(client);
47 WINPR_ASSERT(index < KEY_INDEX_COUNT);
48 return (client->keysDown[index / 8] & (1u << (index % 8))) != 0;
49}
50
51/* rdpShadowServer::keysHeld counts the clients holding each key, so that no client thread needs
52 * to walk server->clients: a capture thread may hold that list's lock while it waits for the
53 * client thread to take a frame. Guarded by server->lock, which nothing else takes. */
54
55/* Marks the key as pressed or released by this client. */
56static void shadow_input_key_mark(rdpShadowClient* client, size_t index, BOOL down)
57{
58 WINPR_ASSERT(client);
59 WINPR_ASSERT(index < KEY_INDEX_COUNT);
60 rdpShadowServer* server = client->server;
61 WINPR_ASSERT(server);
62
63 const BYTE bit = (BYTE)(1u << (index % 8));
64
65 EnterCriticalSection(&server->lock);
66 if (down && !shadow_input_key_is_down(client, index))
67 {
68 client->keysDown[index / 8] |= bit;
69 server->keysHeld[index]++;
70 }
71 else if (!down && shadow_input_key_is_down(client, index))
72 {
73 client->keysDown[index / 8] &= (BYTE)~bit;
74 WINPR_ASSERT(server->keysHeld[index] > 0);
75 server->keysHeld[index]--;
76 }
77 LeaveCriticalSection(&server->lock);
78}
79
80/* Marks the key as released by this client and returns whether another client sharing the
81 * desktop still holds it, in one step under the lock. */
82WINPR_ATTR_NODISCARD
83static BOOL shadow_input_key_release(rdpShadowClient* client, size_t index)
84{
85 WINPR_ASSERT(client);
86 rdpShadowServer* server = client->server;
87 WINPR_ASSERT(server);
88
89 EnterCriticalSection(&server->lock);
90 shadow_input_key_mark(client, index, FALSE);
91 const BOOL held = server->keysHeld[index] > 0;
92 LeaveCriticalSection(&server->lock);
93 return held;
94}
95
96BOOL shadow_input_release_keys(rdpShadowClient* client)
97{
98 WINPR_ASSERT(client);
99 WINPR_ASSERT(client->server);
100 rdpShadowSubsystem* subsystem = client->server->subsystem;
101 WINPR_ASSERT(subsystem);
102
103 BOOL rc = TRUE;
104 for (size_t index = 0; index < KEY_INDEX_COUNT; index++)
105 {
106 if (!shadow_input_key_is_down(client, index))
107 continue;
108 /* another client sharing the desktop still holds it: it stays down for that one */
109 if (shadow_input_key_release(client, index))
110 continue;
111
112 UINT16 flags = KBD_FLAGS_RELEASE;
113 if (index & KEY_INDEX_EXTENDED)
114 flags |= KBD_FLAGS_EXTENDED;
115 if (index & KEY_INDEX_EXTENDED1)
116 flags |= KBD_FLAGS_EXTENDED1;
117 const UINT8 code = (UINT8)(index & 0xFF);
118
119 WLog_DBG(TAG, "releasing key 0x%02" PRIx8 " flags=0x%04" PRIx16 " the client left down",
120 code, flags);
121 /* keep releasing the other keys, but report the failure */
122 if (!IFCALLRESULT(TRUE, subsystem->KeyboardEvent, subsystem, client, flags, code))
123 {
124 WLog_WARN(TAG, "releasing key 0x%02" PRIx8 " flags=0x%04" PRIx16 " failed", code,
125 flags);
126 rc = FALSE;
127 }
128 }
129 return rc;
130}
131
132WINPR_ATTR_NODISCARD
133static BOOL shadow_input_synchronize_event(rdpInput* input, UINT32 flags)
134{
135 WINPR_ASSERT(input);
136 rdpShadowClient* client = (rdpShadowClient*)input->context;
137 WINPR_ASSERT(client);
138 WINPR_ASSERT(client->server);
139 rdpShadowSubsystem* subsystem = client->server->subsystem;
140 WINPR_ASSERT(subsystem);
141
142 WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16, client->mayInteract ? "use" : "discard", flags);
143 if (!client->mayInteract)
144 return TRUE;
145
146 /* the client starts its keyboard state over (it sends this when it gets the focus back):
147 * what it held before is up now */
148 const BOOL released = shadow_input_release_keys(client);
149 const BOOL synced = IFCALLRESULT(TRUE, subsystem->SynchronizeEvent, subsystem, client, flags);
150 return released && synced;
151}
152
153WINPR_ATTR_NODISCARD
154static BOOL shadow_input_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
155{
156 WINPR_ASSERT(input);
157 rdpShadowClient* client = (rdpShadowClient*)input->context;
158 WINPR_ASSERT(client);
159 WINPR_ASSERT(client->server);
160 rdpShadowSubsystem* subsystem = client->server->subsystem;
161 WINPR_ASSERT(subsystem);
162
163 WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16, client->mayInteract ? "use" : "discard", flags);
164 if (!client->mayInteract)
165 return TRUE;
166
167 const BOOL rc = IFCALLRESULT(TRUE, subsystem->KeyboardEvent, subsystem, client, flags, code);
168 if (rc)
169 shadow_input_key_mark(client, shadow_input_key_index(flags, code),
170 (flags & KBD_FLAGS_RELEASE) == 0);
171 return rc;
172}
173
174WINPR_ATTR_NODISCARD
175static BOOL shadow_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
176{
177 WINPR_ASSERT(input);
178 rdpShadowClient* client = (rdpShadowClient*)input->context;
179 WINPR_ASSERT(client);
180 WINPR_ASSERT(client->server);
181 rdpShadowSubsystem* subsystem = client->server->subsystem;
182 WINPR_ASSERT(subsystem);
183
184 WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16, client->mayInteract ? "use" : "discard", flags);
185 if (!client->mayInteract)
186 return TRUE;
187
188 return IFCALLRESULT(TRUE, subsystem->UnicodeKeyboardEvent, subsystem, client, flags, code);
189}
190
191WINPR_ATTR_NODISCARD
192static BOOL shadow_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
193{
194 WINPR_ASSERT(input);
195 rdpShadowClient* client = (rdpShadowClient*)input->context;
196 WINPR_ASSERT(client);
197 WINPR_ASSERT(client->server);
198 rdpShadowSubsystem* subsystem = client->server->subsystem;
199 WINPR_ASSERT(subsystem);
200
201 if (client->server->shareSubRect)
202 {
203 x += client->server->subRect.left;
204 y += client->server->subRect.top;
205 }
206
207 if ((flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL | PTR_FLAGS_WHEEL_NEGATIVE)) == 0)
208 {
209 client->pointerX = x;
210 client->pointerY = y;
211
212 if ((client->pointerX == subsystem->pointerX) && (client->pointerY == subsystem->pointerY))
213 {
214 flags &= ~PTR_FLAGS_MOVE;
215
216 if (!(flags & (PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3)))
217 return TRUE;
218 }
219 }
220
221 WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16 ", x=%" PRIu16 ", y=%" PRIu16,
222 client->mayInteract ? "use" : "discard", flags, x, y);
223 if (!client->mayInteract)
224 return TRUE;
225
226 return IFCALLRESULT(TRUE, subsystem->MouseEvent, subsystem, client, flags, x, y);
227}
228
229WINPR_ATTR_NODISCARD
230static BOOL shadow_input_rel_mouse_event(rdpInput* input, UINT16 flags, INT16 xDelta, INT16 yDelta)
231{
232 WINPR_ASSERT(input);
233
234 rdpShadowClient* client = (rdpShadowClient*)input->context;
235 WINPR_ASSERT(client);
236 WINPR_ASSERT(client->server);
237
238 rdpShadowSubsystem* subsystem = client->server->subsystem;
239 WINPR_ASSERT(subsystem);
240
241 WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16 ", x=%" PRId16 ", y=%" PRId16,
242 client->mayInteract ? "use" : "discard", flags, xDelta, yDelta);
243 const uint16_t mask = PTR_FLAGS_MOVE | PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 |
244 PTR_FLAGS_BUTTON3 | PTR_XFLAGS_BUTTON1 | PTR_XFLAGS_BUTTON2;
245 if ((flags & mask) != 0)
246 {
247 WLog_WARN(TAG, "Unknown flags 0x%04x", WINPR_CXX_COMPAT_CAST(unsigned, flags & ~mask));
248 }
249 if (!client->mayInteract)
250 return TRUE;
251
252 return IFCALLRESULT(TRUE, subsystem->RelMouseEvent, subsystem, client, flags, xDelta, yDelta);
253}
254
255WINPR_ATTR_NODISCARD
256static BOOL shadow_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
257{
258 WINPR_ASSERT(input);
259 rdpShadowClient* client = (rdpShadowClient*)input->context;
260 WINPR_ASSERT(client);
261 WINPR_ASSERT(client->server);
262 rdpShadowSubsystem* subsystem = client->server->subsystem;
263 WINPR_ASSERT(subsystem);
264
265 if (client->server->shareSubRect)
266 {
267 x += client->server->subRect.left;
268 y += client->server->subRect.top;
269 }
270
271 client->pointerX = x;
272 client->pointerY = y;
273
274 WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16 ", x=%" PRIu16 ", y=%" PRIu16,
275 client->mayInteract ? "use" : "discard", flags, x, y);
276 if (!client->mayInteract)
277 return TRUE;
278
279 return IFCALLRESULT(TRUE, subsystem->ExtendedMouseEvent, subsystem, client, flags, x, y);
280}
281
282void shadow_input_register_callbacks(rdpInput* input)
283{
284 WINPR_ASSERT(input);
285 input->SynchronizeEvent = shadow_input_synchronize_event;
286 input->KeyboardEvent = shadow_input_keyboard_event;
287 input->UnicodeKeyboardEvent = shadow_input_unicode_keyboard_event;
288 input->MouseEvent = shadow_input_mouse_event;
289 input->ExtendedMouseEvent = shadow_input_extended_mouse_event;
290 input->RelMouseEvent = shadow_input_rel_mouse_event;
291}