]>
Commit | Line | Data |
---|---|---|
6105683d LV |
1 | /* |
2 | * SPDX-License-Identifier: GPL-2.0-or-later | |
3 | * | |
4 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
5 | * See the COPYING file in the top-level directory. | |
399a0477 PM |
6 | * |
7 | * TODO: | |
8 | * | |
9 | * - Enable SSL | |
10 | * - Manage SetOptions/ResetOptions commands | |
6105683d LV |
11 | */ |
12 | ||
13 | #include "qemu/osdep.h" | |
14 | #include "sysemu/sysemu.h" | |
15 | #include "qemu/main-loop.h" | |
16 | #include "qemu/sockets.h" | |
17 | #include "qapi/error.h" | |
18 | #include "qom/object_interfaces.h" | |
19 | #include "io/channel-socket.h" | |
20 | #include "ui/input.h" | |
db1015e9 | 21 | #include "qom/object.h" |
6105683d LV |
22 | #include "ui/vnc_keysym.h" /* use name2keysym from VNC as we use X11 values */ |
23 | #include "qemu/cutils.h" | |
24 | #include "qapi/qmp/qerror.h" | |
25 | #include "input-barrier.h" | |
26 | ||
27 | #define TYPE_INPUT_BARRIER "input-barrier" | |
30b5707c | 28 | OBJECT_DECLARE_SIMPLE_TYPE(InputBarrier, |
c734cd40 | 29 | INPUT_BARRIER) |
6105683d | 30 | |
6105683d LV |
31 | |
32 | #define MAX_HELLO_LENGTH 1024 | |
33 | ||
34 | struct InputBarrier { | |
35 | Object parent; | |
36 | ||
37 | QIOChannelSocket *sioc; | |
38 | guint ioc_tag; | |
39 | ||
40 | /* display properties */ | |
41 | gchar *name; | |
42 | int16_t x_origin, y_origin; | |
43 | int16_t width, height; | |
44 | ||
45 | /* keyboard/mouse server */ | |
46 | ||
47 | SocketAddress saddr; | |
48 | ||
49 | char buffer[MAX_HELLO_LENGTH]; | |
50 | }; | |
51 | ||
6105683d LV |
52 | |
53 | static const char *cmd_names[] = { | |
54 | [barrierCmdCNoop] = "CNOP", | |
55 | [barrierCmdCClose] = "CBYE", | |
56 | [barrierCmdCEnter] = "CINN", | |
57 | [barrierCmdCLeave] = "COUT", | |
58 | [barrierCmdCClipboard] = "CCLP", | |
59 | [barrierCmdCScreenSaver] = "CSEC", | |
60 | [barrierCmdCResetOptions] = "CROP", | |
61 | [barrierCmdCInfoAck] = "CIAK", | |
62 | [barrierCmdCKeepAlive] = "CALV", | |
63 | [barrierCmdDKeyDown] = "DKDN", | |
64 | [barrierCmdDKeyRepeat] = "DKRP", | |
65 | [barrierCmdDKeyUp] = "DKUP", | |
66 | [barrierCmdDMouseDown] = "DMDN", | |
67 | [barrierCmdDMouseUp] = "DMUP", | |
68 | [barrierCmdDMouseMove] = "DMMV", | |
69 | [barrierCmdDMouseRelMove] = "DMRM", | |
70 | [barrierCmdDMouseWheel] = "DMWM", | |
71 | [barrierCmdDClipboard] = "DCLP", | |
72 | [barrierCmdDInfo] = "DINF", | |
73 | [barrierCmdDSetOptions] = "DSOP", | |
74 | [barrierCmdDFileTransfer] = "DFTR", | |
75 | [barrierCmdDDragInfo] = "DDRG", | |
76 | [barrierCmdQInfo] = "QINF", | |
77 | [barrierCmdEIncompatible] = "EICV", | |
78 | [barrierCmdEBusy] = "EBSY", | |
79 | [barrierCmdEUnknown] = "EUNK", | |
80 | [barrierCmdEBad] = "EBAD", | |
81 | [barrierCmdHello] = "Barrier", | |
82 | [barrierCmdHelloBack] = "Barrier", | |
83 | }; | |
84 | ||
85 | static kbd_layout_t *kbd_layout; | |
86 | ||
87 | static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode) | |
88 | { | |
89 | /* keycode is optional, if it is not provided use keyid */ | |
90 | if (keycode && keycode <= qemu_input_map_xorgkbd_to_qcode_len) { | |
91 | return qemu_input_map_xorgkbd_to_qcode[keycode]; | |
92 | } | |
93 | ||
94 | if (keyid >= 0xE000 && keyid <= 0xEFFF) { | |
95 | keyid += 0x1000; | |
96 | } | |
97 | ||
98 | /* keyid is the X11 key id */ | |
99 | if (kbd_layout) { | |
100 | keycode = keysym2scancode(kbd_layout, keyid, NULL, false); | |
101 | ||
102 | return qemu_input_key_number_to_qcode(keycode); | |
103 | } | |
104 | ||
105 | return qemu_input_map_x11_to_qcode[keyid]; | |
106 | } | |
107 | ||
108 | static int input_barrier_to_mouse(uint8_t buttonid) | |
109 | { | |
110 | switch (buttonid) { | |
111 | case barrierButtonLeft: | |
112 | return INPUT_BUTTON_LEFT; | |
113 | case barrierButtonMiddle: | |
114 | return INPUT_BUTTON_MIDDLE; | |
115 | case barrierButtonRight: | |
116 | return INPUT_BUTTON_RIGHT; | |
117 | case barrierButtonExtra0: | |
118 | return INPUT_BUTTON_SIDE; | |
119 | } | |
120 | return buttonid; | |
121 | } | |
122 | ||
123 | #define read_char(x, p, l) \ | |
124 | do { \ | |
125 | int size = sizeof(char); \ | |
126 | if (l < size) { \ | |
127 | return G_SOURCE_REMOVE; \ | |
128 | } \ | |
129 | x = *(char *)p; \ | |
130 | p += size; \ | |
131 | l -= size; \ | |
132 | } while (0) | |
133 | ||
134 | #define read_short(x, p, l) \ | |
135 | do { \ | |
136 | int size = sizeof(short); \ | |
137 | if (l < size) { \ | |
138 | return G_SOURCE_REMOVE; \ | |
139 | } \ | |
140 | x = ntohs(*(short *)p); \ | |
141 | p += size; \ | |
142 | l -= size; \ | |
143 | } while (0) | |
144 | ||
145 | #define write_short(p, x, l) \ | |
146 | do { \ | |
147 | int size = sizeof(short); \ | |
148 | if (l < size) { \ | |
149 | return G_SOURCE_REMOVE; \ | |
150 | } \ | |
151 | *(short *)p = htons(x); \ | |
152 | p += size; \ | |
153 | l -= size; \ | |
154 | } while (0) | |
155 | ||
156 | #define read_int(x, p, l) \ | |
157 | do { \ | |
158 | int size = sizeof(int); \ | |
159 | if (l < size) { \ | |
160 | return G_SOURCE_REMOVE; \ | |
161 | } \ | |
162 | x = ntohl(*(int *)p); \ | |
163 | p += size; \ | |
164 | l -= size; \ | |
165 | } while (0) | |
166 | ||
167 | #define write_int(p, x, l) \ | |
168 | do { \ | |
169 | int size = sizeof(int); \ | |
170 | if (l < size) { \ | |
171 | return G_SOURCE_REMOVE; \ | |
172 | } \ | |
173 | *(int *)p = htonl(x); \ | |
174 | p += size; \ | |
175 | l -= size; \ | |
176 | } while (0) | |
177 | ||
178 | #define write_cmd(p, c, l) \ | |
179 | do { \ | |
180 | int size = strlen(cmd_names[c]); \ | |
181 | if (l < size) { \ | |
182 | return G_SOURCE_REMOVE; \ | |
183 | } \ | |
184 | memcpy(p, cmd_names[c], size); \ | |
185 | p += size; \ | |
186 | l -= size; \ | |
187 | } while (0) | |
188 | ||
189 | #define write_string(p, s, l) \ | |
190 | do { \ | |
191 | int size = strlen(s); \ | |
192 | if (l < size + sizeof(int)) { \ | |
193 | return G_SOURCE_REMOVE; \ | |
194 | } \ | |
195 | *(int *)p = htonl(size); \ | |
196 | p += sizeof(size); \ | |
197 | l -= sizeof(size); \ | |
198 | memcpy(p, s, size); \ | |
199 | p += size; \ | |
200 | l -= size; \ | |
201 | } while (0) | |
202 | ||
203 | static gboolean readcmd(InputBarrier *ib, struct barrierMsg *msg) | |
204 | { | |
205 | int ret, len, i; | |
206 | enum barrierCmd cmd; | |
207 | char *p; | |
208 | ||
209 | ret = qio_channel_read(QIO_CHANNEL(ib->sioc), (char *)&len, sizeof(len), | |
210 | NULL); | |
211 | if (ret < 0) { | |
212 | return G_SOURCE_REMOVE; | |
213 | } | |
214 | ||
215 | len = ntohl(len); | |
216 | if (len > MAX_HELLO_LENGTH) { | |
217 | return G_SOURCE_REMOVE; | |
218 | } | |
219 | ||
220 | ret = qio_channel_read(QIO_CHANNEL(ib->sioc), ib->buffer, len, NULL); | |
221 | if (ret < 0) { | |
222 | return G_SOURCE_REMOVE; | |
223 | } | |
224 | ||
225 | p = ib->buffer; | |
226 | if (len >= strlen(cmd_names[barrierCmdHello]) && | |
227 | memcmp(p, cmd_names[barrierCmdHello], | |
228 | strlen(cmd_names[barrierCmdHello])) == 0) { | |
229 | cmd = barrierCmdHello; | |
230 | p += strlen(cmd_names[barrierCmdHello]); | |
231 | len -= strlen(cmd_names[barrierCmdHello]); | |
232 | } else { | |
233 | for (cmd = 0; cmd < barrierCmdHello; cmd++) { | |
234 | if (memcmp(ib->buffer, cmd_names[cmd], 4) == 0) { | |
235 | break; | |
236 | } | |
237 | } | |
238 | ||
239 | if (cmd == barrierCmdHello) { | |
240 | return G_SOURCE_REMOVE; | |
241 | } | |
242 | p += 4; | |
243 | len -= 4; | |
244 | } | |
245 | ||
246 | msg->cmd = cmd; | |
247 | switch (cmd) { | |
248 | /* connection */ | |
249 | case barrierCmdHello: | |
250 | read_short(msg->version.major, p, len); | |
251 | read_short(msg->version.minor, p, len); | |
252 | break; | |
253 | case barrierCmdDSetOptions: | |
254 | read_int(msg->set.nb, p, len); | |
255 | msg->set.nb /= 2; | |
256 | if (msg->set.nb > BARRIER_MAX_OPTIONS) { | |
257 | msg->set.nb = BARRIER_MAX_OPTIONS; | |
258 | } | |
259 | i = 0; | |
260 | while (len && i < msg->set.nb) { | |
261 | read_int(msg->set.option[i].id, p, len); | |
262 | /* it's a string, restore endianness */ | |
263 | msg->set.option[i].id = htonl(msg->set.option[i].id); | |
264 | msg->set.option[i].nul = 0; | |
265 | read_int(msg->set.option[i].value, p, len); | |
266 | i++; | |
267 | } | |
268 | break; | |
269 | case barrierCmdQInfo: | |
270 | break; | |
271 | ||
272 | /* mouse */ | |
273 | case barrierCmdDMouseMove: | |
274 | case barrierCmdDMouseRelMove: | |
275 | read_short(msg->mousepos.x, p, len); | |
276 | read_short(msg->mousepos.y, p, len); | |
277 | break; | |
278 | case barrierCmdDMouseDown: | |
279 | case barrierCmdDMouseUp: | |
280 | read_char(msg->mousebutton.buttonid, p, len); | |
281 | break; | |
282 | case barrierCmdDMouseWheel: | |
283 | read_short(msg->mousepos.y, p, len); | |
284 | msg->mousepos.x = 0; | |
285 | if (len) { | |
286 | msg->mousepos.x = msg->mousepos.y; | |
287 | read_short(msg->mousepos.y, p, len); | |
288 | } | |
289 | break; | |
290 | ||
291 | /* keyboard */ | |
292 | case barrierCmdDKeyDown: | |
293 | case barrierCmdDKeyUp: | |
294 | read_short(msg->key.keyid, p, len); | |
295 | read_short(msg->key.modifier, p, len); | |
296 | msg->key.button = 0; | |
297 | if (len) { | |
298 | read_short(msg->key.button, p, len); | |
299 | } | |
300 | break; | |
301 | case barrierCmdDKeyRepeat: | |
302 | read_short(msg->repeat.keyid, p, len); | |
303 | read_short(msg->repeat.modifier, p, len); | |
304 | read_short(msg->repeat.repeat, p, len); | |
305 | msg->repeat.button = 0; | |
306 | if (len) { | |
307 | read_short(msg->repeat.button, p, len); | |
308 | } | |
309 | break; | |
310 | case barrierCmdCInfoAck: | |
311 | case barrierCmdCResetOptions: | |
312 | case barrierCmdCEnter: | |
313 | case barrierCmdDClipboard: | |
314 | case barrierCmdCKeepAlive: | |
315 | case barrierCmdCLeave: | |
316 | case barrierCmdCClose: | |
317 | break; | |
318 | ||
319 | /* Invalid from the server */ | |
320 | case barrierCmdHelloBack: | |
321 | case barrierCmdCNoop: | |
322 | case barrierCmdDInfo: | |
323 | break; | |
324 | ||
325 | /* Error codes */ | |
326 | case barrierCmdEIncompatible: | |
327 | read_short(msg->version.major, p, len); | |
328 | read_short(msg->version.minor, p, len); | |
329 | break; | |
330 | case barrierCmdEBusy: | |
331 | case barrierCmdEUnknown: | |
332 | case barrierCmdEBad: | |
333 | break; | |
334 | default: | |
335 | return G_SOURCE_REMOVE; | |
336 | } | |
337 | ||
338 | return G_SOURCE_CONTINUE; | |
339 | } | |
340 | ||
341 | static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg) | |
342 | { | |
343 | char *p; | |
344 | int ret, i; | |
345 | int avail, len; | |
346 | ||
347 | p = ib->buffer; | |
348 | avail = MAX_HELLO_LENGTH; | |
349 | ||
350 | /* reserve space to store the length */ | |
351 | p += sizeof(int); | |
352 | avail -= sizeof(int); | |
353 | ||
354 | switch (msg->cmd) { | |
355 | case barrierCmdHello: | |
356 | if (msg->version.major < BARRIER_VERSION_MAJOR || | |
357 | (msg->version.major == BARRIER_VERSION_MAJOR && | |
358 | msg->version.minor < BARRIER_VERSION_MINOR)) { | |
359 | ib->ioc_tag = 0; | |
360 | return G_SOURCE_REMOVE; | |
361 | } | |
362 | write_cmd(p, barrierCmdHelloBack, avail); | |
363 | write_short(p, BARRIER_VERSION_MAJOR, avail); | |
364 | write_short(p, BARRIER_VERSION_MINOR, avail); | |
365 | write_string(p, ib->name, avail); | |
366 | break; | |
367 | case barrierCmdCClose: | |
368 | ib->ioc_tag = 0; | |
369 | return G_SOURCE_REMOVE; | |
370 | case barrierCmdQInfo: | |
371 | write_cmd(p, barrierCmdDInfo, avail); | |
372 | write_short(p, ib->x_origin, avail); | |
373 | write_short(p, ib->y_origin, avail); | |
374 | write_short(p, ib->width, avail); | |
375 | write_short(p, ib->height, avail); | |
376 | write_short(p, 0, avail); /* warpsize (obsolete) */ | |
377 | write_short(p, 0, avail); /* mouse x */ | |
378 | write_short(p, 0, avail); /* mouse y */ | |
379 | break; | |
380 | case barrierCmdCInfoAck: | |
381 | break; | |
382 | case barrierCmdCResetOptions: | |
383 | /* TODO: reset options */ | |
384 | break; | |
385 | case barrierCmdDSetOptions: | |
386 | /* TODO: set options */ | |
387 | break; | |
388 | case barrierCmdCEnter: | |
389 | break; | |
390 | case barrierCmdDClipboard: | |
391 | break; | |
392 | case barrierCmdCKeepAlive: | |
393 | write_cmd(p, barrierCmdCKeepAlive, avail); | |
394 | break; | |
395 | case barrierCmdCLeave: | |
396 | break; | |
397 | ||
398 | /* mouse */ | |
399 | case barrierCmdDMouseMove: | |
400 | qemu_input_queue_abs(NULL, INPUT_AXIS_X, msg->mousepos.x, | |
401 | ib->x_origin, ib->width); | |
402 | qemu_input_queue_abs(NULL, INPUT_AXIS_Y, msg->mousepos.y, | |
403 | ib->y_origin, ib->height); | |
404 | qemu_input_event_sync(); | |
405 | break; | |
406 | case barrierCmdDMouseRelMove: | |
407 | qemu_input_queue_rel(NULL, INPUT_AXIS_X, msg->mousepos.x); | |
408 | qemu_input_queue_rel(NULL, INPUT_AXIS_Y, msg->mousepos.y); | |
409 | qemu_input_event_sync(); | |
410 | break; | |
411 | case barrierCmdDMouseDown: | |
412 | qemu_input_queue_btn(NULL, | |
413 | input_barrier_to_mouse(msg->mousebutton.buttonid), | |
414 | true); | |
415 | qemu_input_event_sync(); | |
416 | break; | |
417 | case barrierCmdDMouseUp: | |
418 | qemu_input_queue_btn(NULL, | |
419 | input_barrier_to_mouse(msg->mousebutton.buttonid), | |
420 | false); | |
421 | qemu_input_event_sync(); | |
422 | break; | |
423 | case barrierCmdDMouseWheel: | |
424 | qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP | |
425 | : INPUT_BUTTON_WHEEL_DOWN, true); | |
426 | qemu_input_event_sync(); | |
427 | qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP | |
428 | : INPUT_BUTTON_WHEEL_DOWN, false); | |
429 | qemu_input_event_sync(); | |
430 | break; | |
431 | ||
432 | /* keyboard */ | |
433 | case barrierCmdDKeyDown: | |
434 | qemu_input_event_send_key_qcode(NULL, | |
435 | input_barrier_to_qcode(msg->key.keyid, msg->key.button), | |
436 | true); | |
437 | break; | |
438 | case barrierCmdDKeyRepeat: | |
439 | for (i = 0; i < msg->repeat.repeat; i++) { | |
440 | qemu_input_event_send_key_qcode(NULL, | |
441 | input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button), | |
442 | false); | |
443 | qemu_input_event_send_key_qcode(NULL, | |
444 | input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button), | |
445 | true); | |
446 | } | |
447 | break; | |
448 | case barrierCmdDKeyUp: | |
449 | qemu_input_event_send_key_qcode(NULL, | |
450 | input_barrier_to_qcode(msg->key.keyid, msg->key.button), | |
451 | false); | |
452 | break; | |
453 | default: | |
454 | write_cmd(p, barrierCmdEUnknown, avail); | |
29453426 | 455 | break; |
6105683d LV |
456 | } |
457 | ||
458 | len = MAX_HELLO_LENGTH - avail - sizeof(int); | |
459 | if (len) { | |
460 | p = ib->buffer; | |
461 | avail = sizeof(len); | |
462 | write_int(p, len, avail); | |
463 | ret = qio_channel_write(QIO_CHANNEL(ib->sioc), ib->buffer, | |
464 | len + sizeof(len), NULL); | |
465 | if (ret < 0) { | |
466 | ib->ioc_tag = 0; | |
467 | return G_SOURCE_REMOVE; | |
468 | } | |
469 | } | |
470 | ||
471 | return G_SOURCE_CONTINUE; | |
472 | } | |
473 | ||
474 | static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED, | |
475 | GIOCondition condition, void *opaque) | |
476 | { | |
477 | InputBarrier *ib = opaque; | |
478 | int ret; | |
479 | struct barrierMsg msg; | |
480 | ||
481 | ret = readcmd(ib, &msg); | |
482 | if (ret == G_SOURCE_REMOVE) { | |
483 | ib->ioc_tag = 0; | |
484 | return G_SOURCE_REMOVE; | |
485 | } | |
486 | ||
487 | return writecmd(ib, &msg); | |
488 | } | |
489 | ||
490 | static void input_barrier_complete(UserCreatable *uc, Error **errp) | |
491 | { | |
492 | InputBarrier *ib = INPUT_BARRIER(uc); | |
493 | Error *local_err = NULL; | |
494 | ||
495 | if (!ib->name) { | |
496 | error_setg(errp, QERR_MISSING_PARAMETER, "name"); | |
497 | return; | |
498 | } | |
499 | ||
500 | /* | |
501 | * Connect to the primary | |
502 | * Primary is the server where the keyboard and the mouse | |
503 | * are connected and forwarded to the secondary (the client) | |
504 | */ | |
505 | ||
506 | ib->sioc = qio_channel_socket_new(); | |
507 | qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client"); | |
508 | ||
509 | qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err); | |
510 | if (local_err) { | |
511 | error_propagate(errp, local_err); | |
512 | return; | |
513 | } | |
514 | ||
515 | qio_channel_set_delay(QIO_CHANNEL(ib->sioc), false); | |
516 | ||
517 | ib->ioc_tag = qio_channel_add_watch(QIO_CHANNEL(ib->sioc), G_IO_IN, | |
518 | input_barrier_event, ib, NULL); | |
519 | } | |
520 | ||
521 | static void input_barrier_instance_finalize(Object *obj) | |
522 | { | |
523 | InputBarrier *ib = INPUT_BARRIER(obj); | |
524 | ||
525 | if (ib->ioc_tag) { | |
526 | g_source_remove(ib->ioc_tag); | |
527 | ib->ioc_tag = 0; | |
528 | } | |
529 | ||
530 | if (ib->sioc) { | |
531 | qio_channel_close(QIO_CHANNEL(ib->sioc), NULL); | |
532 | object_unref(OBJECT(ib->sioc)); | |
533 | } | |
534 | g_free(ib->name); | |
535 | g_free(ib->saddr.u.inet.host); | |
536 | g_free(ib->saddr.u.inet.port); | |
537 | } | |
538 | ||
539 | static char *input_barrier_get_name(Object *obj, Error **errp) | |
540 | { | |
541 | InputBarrier *ib = INPUT_BARRIER(obj); | |
542 | ||
543 | return g_strdup(ib->name); | |
544 | } | |
545 | ||
546 | static void input_barrier_set_name(Object *obj, const char *value, | |
547 | Error **errp) | |
548 | { | |
549 | InputBarrier *ib = INPUT_BARRIER(obj); | |
550 | ||
551 | if (ib->name) { | |
552 | error_setg(errp, "name property already set"); | |
553 | return; | |
554 | } | |
555 | ib->name = g_strdup(value); | |
556 | } | |
557 | ||
558 | static char *input_barrier_get_server(Object *obj, Error **errp) | |
559 | { | |
560 | InputBarrier *ib = INPUT_BARRIER(obj); | |
561 | ||
562 | return g_strdup(ib->saddr.u.inet.host); | |
563 | } | |
564 | ||
565 | static void input_barrier_set_server(Object *obj, const char *value, | |
566 | Error **errp) | |
567 | { | |
568 | InputBarrier *ib = INPUT_BARRIER(obj); | |
569 | ||
570 | g_free(ib->saddr.u.inet.host); | |
571 | ib->saddr.u.inet.host = g_strdup(value); | |
572 | } | |
573 | ||
574 | static char *input_barrier_get_port(Object *obj, Error **errp) | |
575 | { | |
576 | InputBarrier *ib = INPUT_BARRIER(obj); | |
577 | ||
578 | return g_strdup(ib->saddr.u.inet.port); | |
579 | } | |
580 | ||
581 | static void input_barrier_set_port(Object *obj, const char *value, | |
582 | Error **errp) | |
583 | { | |
584 | InputBarrier *ib = INPUT_BARRIER(obj); | |
585 | ||
586 | g_free(ib->saddr.u.inet.port); | |
587 | ib->saddr.u.inet.port = g_strdup(value); | |
588 | } | |
589 | ||
590 | static void input_barrier_set_x_origin(Object *obj, const char *value, | |
591 | Error **errp) | |
592 | { | |
593 | InputBarrier *ib = INPUT_BARRIER(obj); | |
594 | int result, err; | |
595 | ||
596 | err = qemu_strtoi(value, NULL, 0, &result); | |
597 | if (err < 0 || result < 0 || result > SHRT_MAX) { | |
598 | error_setg(errp, | |
599 | "x-origin property must be in the range [0..%d]", SHRT_MAX); | |
600 | return; | |
601 | } | |
602 | ib->x_origin = result; | |
603 | } | |
604 | ||
605 | static char *input_barrier_get_x_origin(Object *obj, Error **errp) | |
606 | { | |
607 | InputBarrier *ib = INPUT_BARRIER(obj); | |
608 | ||
609 | return g_strdup_printf("%d", ib->x_origin); | |
610 | } | |
611 | ||
612 | static void input_barrier_set_y_origin(Object *obj, const char *value, | |
613 | Error **errp) | |
614 | { | |
615 | InputBarrier *ib = INPUT_BARRIER(obj); | |
616 | int result, err; | |
617 | ||
618 | err = qemu_strtoi(value, NULL, 0, &result); | |
619 | if (err < 0 || result < 0 || result > SHRT_MAX) { | |
620 | error_setg(errp, | |
621 | "y-origin property must be in the range [0..%d]", SHRT_MAX); | |
622 | return; | |
623 | } | |
624 | ib->y_origin = result; | |
625 | } | |
626 | ||
627 | static char *input_barrier_get_y_origin(Object *obj, Error **errp) | |
628 | { | |
629 | InputBarrier *ib = INPUT_BARRIER(obj); | |
630 | ||
631 | return g_strdup_printf("%d", ib->y_origin); | |
632 | } | |
633 | ||
634 | static void input_barrier_set_width(Object *obj, const char *value, | |
635 | Error **errp) | |
636 | { | |
637 | InputBarrier *ib = INPUT_BARRIER(obj); | |
638 | int result, err; | |
639 | ||
640 | err = qemu_strtoi(value, NULL, 0, &result); | |
641 | if (err < 0 || result < 0 || result > SHRT_MAX) { | |
642 | error_setg(errp, | |
643 | "width property must be in the range [0..%d]", SHRT_MAX); | |
644 | return; | |
645 | } | |
646 | ib->width = result; | |
647 | } | |
648 | ||
649 | static char *input_barrier_get_width(Object *obj, Error **errp) | |
650 | { | |
651 | InputBarrier *ib = INPUT_BARRIER(obj); | |
652 | ||
653 | return g_strdup_printf("%d", ib->width); | |
654 | } | |
655 | ||
656 | static void input_barrier_set_height(Object *obj, const char *value, | |
657 | Error **errp) | |
658 | { | |
659 | InputBarrier *ib = INPUT_BARRIER(obj); | |
660 | int result, err; | |
661 | ||
662 | err = qemu_strtoi(value, NULL, 0, &result); | |
663 | if (err < 0 || result < 0 || result > SHRT_MAX) { | |
664 | error_setg(errp, | |
665 | "height property must be in the range [0..%d]", SHRT_MAX); | |
666 | return; | |
667 | } | |
668 | ib->height = result; | |
669 | } | |
670 | ||
671 | static char *input_barrier_get_height(Object *obj, Error **errp) | |
672 | { | |
673 | InputBarrier *ib = INPUT_BARRIER(obj); | |
674 | ||
675 | return g_strdup_printf("%d", ib->height); | |
676 | } | |
677 | ||
678 | static void input_barrier_instance_init(Object *obj) | |
679 | { | |
680 | InputBarrier *ib = INPUT_BARRIER(obj); | |
681 | ||
707f7507 LV |
682 | /* always use generic keymaps */ |
683 | if (keyboard_layout && !kbd_layout) { | |
684 | /* We use X11 key id, so use VNC name2keysym */ | |
685 | kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout, | |
686 | &error_fatal); | |
687 | } | |
688 | ||
6105683d LV |
689 | ib->saddr.type = SOCKET_ADDRESS_TYPE_INET; |
690 | ib->saddr.u.inet.host = g_strdup("localhost"); | |
691 | ib->saddr.u.inet.port = g_strdup("24800"); | |
692 | ||
693 | ib->x_origin = 0; | |
694 | ib->y_origin = 0; | |
695 | ib->width = 1920; | |
696 | ib->height = 1080; | |
6105683d LV |
697 | } |
698 | ||
699 | static void input_barrier_class_init(ObjectClass *oc, void *data) | |
700 | { | |
701 | UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); | |
702 | ||
703 | ucc->complete = input_barrier_complete; | |
d85855b8 EH |
704 | |
705 | object_class_property_add_str(oc, "name", | |
706 | input_barrier_get_name, | |
707 | input_barrier_set_name); | |
708 | object_class_property_add_str(oc, "server", | |
709 | input_barrier_get_server, | |
710 | input_barrier_set_server); | |
711 | object_class_property_add_str(oc, "port", | |
712 | input_barrier_get_port, | |
713 | input_barrier_set_port); | |
714 | object_class_property_add_str(oc, "x-origin", | |
715 | input_barrier_get_x_origin, | |
716 | input_barrier_set_x_origin); | |
717 | object_class_property_add_str(oc, "y-origin", | |
718 | input_barrier_get_y_origin, | |
719 | input_barrier_set_y_origin); | |
720 | object_class_property_add_str(oc, "width", | |
721 | input_barrier_get_width, | |
722 | input_barrier_set_width); | |
723 | object_class_property_add_str(oc, "height", | |
724 | input_barrier_get_height, | |
725 | input_barrier_set_height); | |
6105683d LV |
726 | } |
727 | ||
728 | static const TypeInfo input_barrier_info = { | |
729 | .name = TYPE_INPUT_BARRIER, | |
730 | .parent = TYPE_OBJECT, | |
6105683d LV |
731 | .class_init = input_barrier_class_init, |
732 | .instance_size = sizeof(InputBarrier), | |
733 | .instance_init = input_barrier_instance_init, | |
734 | .instance_finalize = input_barrier_instance_finalize, | |
735 | .interfaces = (InterfaceInfo[]) { | |
736 | { TYPE_USER_CREATABLE }, | |
737 | { } | |
738 | } | |
739 | }; | |
740 | ||
741 | static void register_types(void) | |
742 | { | |
743 | type_register_static(&input_barrier_info); | |
744 | } | |
745 | ||
746 | type_init(register_types); |