qtest: add tulip test case
[qemu/kevin.git] / ui / input-barrier.c
blob527c75e130ee96d078930ee5884215525927b6f8
1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
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.
6 */
8 #include "qemu/osdep.h"
9 #include "sysemu/sysemu.h"
10 #include "qemu/main-loop.h"
11 #include "qemu/sockets.h"
12 #include "qapi/error.h"
13 #include "qom/object_interfaces.h"
14 #include "io/channel-socket.h"
15 #include "ui/input.h"
16 #include "ui/vnc_keysym.h" /* use name2keysym from VNC as we use X11 values */
17 #include "qemu/cutils.h"
18 #include "qapi/qmp/qerror.h"
19 #include "input-barrier.h"
21 #define TYPE_INPUT_BARRIER "input-barrier"
22 #define INPUT_BARRIER(obj) \
23 OBJECT_CHECK(InputBarrier, (obj), TYPE_INPUT_BARRIER)
24 #define INPUT_BARRIER_GET_CLASS(obj) \
25 OBJECT_GET_CLASS(InputBarrierClass, (obj), TYPE_INPUT_BARRIER)
26 #define INPUT_BARRIER_CLASS(klass) \
27 OBJECT_CLASS_CHECK(InputBarrierClass, (klass), TYPE_INPUT_BARRIER)
29 typedef struct InputBarrier InputBarrier;
30 typedef struct InputBarrierClass InputBarrierClass;
32 #define MAX_HELLO_LENGTH 1024
34 struct InputBarrier {
35 Object parent;
37 QIOChannelSocket *sioc;
38 guint ioc_tag;
40 /* display properties */
41 gchar *name;
42 int16_t x_origin, y_origin;
43 int16_t width, height;
45 /* keyboard/mouse server */
47 SocketAddress saddr;
49 char buffer[MAX_HELLO_LENGTH];
52 struct InputBarrierClass {
53 ObjectClass parent_class;
56 static const char *cmd_names[] = {
57 [barrierCmdCNoop] = "CNOP",
58 [barrierCmdCClose] = "CBYE",
59 [barrierCmdCEnter] = "CINN",
60 [barrierCmdCLeave] = "COUT",
61 [barrierCmdCClipboard] = "CCLP",
62 [barrierCmdCScreenSaver] = "CSEC",
63 [barrierCmdCResetOptions] = "CROP",
64 [barrierCmdCInfoAck] = "CIAK",
65 [barrierCmdCKeepAlive] = "CALV",
66 [barrierCmdDKeyDown] = "DKDN",
67 [barrierCmdDKeyRepeat] = "DKRP",
68 [barrierCmdDKeyUp] = "DKUP",
69 [barrierCmdDMouseDown] = "DMDN",
70 [barrierCmdDMouseUp] = "DMUP",
71 [barrierCmdDMouseMove] = "DMMV",
72 [barrierCmdDMouseRelMove] = "DMRM",
73 [barrierCmdDMouseWheel] = "DMWM",
74 [barrierCmdDClipboard] = "DCLP",
75 [barrierCmdDInfo] = "DINF",
76 [barrierCmdDSetOptions] = "DSOP",
77 [barrierCmdDFileTransfer] = "DFTR",
78 [barrierCmdDDragInfo] = "DDRG",
79 [barrierCmdQInfo] = "QINF",
80 [barrierCmdEIncompatible] = "EICV",
81 [barrierCmdEBusy] = "EBSY",
82 [barrierCmdEUnknown] = "EUNK",
83 [barrierCmdEBad] = "EBAD",
84 [barrierCmdHello] = "Barrier",
85 [barrierCmdHelloBack] = "Barrier",
88 static kbd_layout_t *kbd_layout;
90 static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode)
92 /* keycode is optional, if it is not provided use keyid */
93 if (keycode && keycode <= qemu_input_map_xorgkbd_to_qcode_len) {
94 return qemu_input_map_xorgkbd_to_qcode[keycode];
97 if (keyid >= 0xE000 && keyid <= 0xEFFF) {
98 keyid += 0x1000;
101 /* keyid is the X11 key id */
102 if (kbd_layout) {
103 keycode = keysym2scancode(kbd_layout, keyid, NULL, false);
105 return qemu_input_key_number_to_qcode(keycode);
108 return qemu_input_map_x11_to_qcode[keyid];
111 static int input_barrier_to_mouse(uint8_t buttonid)
113 switch (buttonid) {
114 case barrierButtonLeft:
115 return INPUT_BUTTON_LEFT;
116 case barrierButtonMiddle:
117 return INPUT_BUTTON_MIDDLE;
118 case barrierButtonRight:
119 return INPUT_BUTTON_RIGHT;
120 case barrierButtonExtra0:
121 return INPUT_BUTTON_SIDE;
123 return buttonid;
126 #define read_char(x, p, l) \
127 do { \
128 int size = sizeof(char); \
129 if (l < size) { \
130 return G_SOURCE_REMOVE; \
132 x = *(char *)p; \
133 p += size; \
134 l -= size; \
135 } while (0)
137 #define read_short(x, p, l) \
138 do { \
139 int size = sizeof(short); \
140 if (l < size) { \
141 return G_SOURCE_REMOVE; \
143 x = ntohs(*(short *)p); \
144 p += size; \
145 l -= size; \
146 } while (0)
148 #define write_short(p, x, l) \
149 do { \
150 int size = sizeof(short); \
151 if (l < size) { \
152 return G_SOURCE_REMOVE; \
154 *(short *)p = htons(x); \
155 p += size; \
156 l -= size; \
157 } while (0)
159 #define read_int(x, p, l) \
160 do { \
161 int size = sizeof(int); \
162 if (l < size) { \
163 return G_SOURCE_REMOVE; \
165 x = ntohl(*(int *)p); \
166 p += size; \
167 l -= size; \
168 } while (0)
170 #define write_int(p, x, l) \
171 do { \
172 int size = sizeof(int); \
173 if (l < size) { \
174 return G_SOURCE_REMOVE; \
176 *(int *)p = htonl(x); \
177 p += size; \
178 l -= size; \
179 } while (0)
181 #define write_cmd(p, c, l) \
182 do { \
183 int size = strlen(cmd_names[c]); \
184 if (l < size) { \
185 return G_SOURCE_REMOVE; \
187 memcpy(p, cmd_names[c], size); \
188 p += size; \
189 l -= size; \
190 } while (0)
192 #define write_string(p, s, l) \
193 do { \
194 int size = strlen(s); \
195 if (l < size + sizeof(int)) { \
196 return G_SOURCE_REMOVE; \
198 *(int *)p = htonl(size); \
199 p += sizeof(size); \
200 l -= sizeof(size); \
201 memcpy(p, s, size); \
202 p += size; \
203 l -= size; \
204 } while (0)
206 static gboolean readcmd(InputBarrier *ib, struct barrierMsg *msg)
208 int ret, len, i;
209 enum barrierCmd cmd;
210 char *p;
212 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), (char *)&len, sizeof(len),
213 NULL);
214 if (ret < 0) {
215 return G_SOURCE_REMOVE;
218 len = ntohl(len);
219 if (len > MAX_HELLO_LENGTH) {
220 return G_SOURCE_REMOVE;
223 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), ib->buffer, len, NULL);
224 if (ret < 0) {
225 return G_SOURCE_REMOVE;
228 p = ib->buffer;
229 if (len >= strlen(cmd_names[barrierCmdHello]) &&
230 memcmp(p, cmd_names[barrierCmdHello],
231 strlen(cmd_names[barrierCmdHello])) == 0) {
232 cmd = barrierCmdHello;
233 p += strlen(cmd_names[barrierCmdHello]);
234 len -= strlen(cmd_names[barrierCmdHello]);
235 } else {
236 for (cmd = 0; cmd < barrierCmdHello; cmd++) {
237 if (memcmp(ib->buffer, cmd_names[cmd], 4) == 0) {
238 break;
242 if (cmd == barrierCmdHello) {
243 return G_SOURCE_REMOVE;
245 p += 4;
246 len -= 4;
249 msg->cmd = cmd;
250 switch (cmd) {
251 /* connection */
252 case barrierCmdHello:
253 read_short(msg->version.major, p, len);
254 read_short(msg->version.minor, p, len);
255 break;
256 case barrierCmdDSetOptions:
257 read_int(msg->set.nb, p, len);
258 msg->set.nb /= 2;
259 if (msg->set.nb > BARRIER_MAX_OPTIONS) {
260 msg->set.nb = BARRIER_MAX_OPTIONS;
262 i = 0;
263 while (len && i < msg->set.nb) {
264 read_int(msg->set.option[i].id, p, len);
265 /* it's a string, restore endianness */
266 msg->set.option[i].id = htonl(msg->set.option[i].id);
267 msg->set.option[i].nul = 0;
268 read_int(msg->set.option[i].value, p, len);
269 i++;
271 break;
272 case barrierCmdQInfo:
273 break;
275 /* mouse */
276 case barrierCmdDMouseMove:
277 case barrierCmdDMouseRelMove:
278 read_short(msg->mousepos.x, p, len);
279 read_short(msg->mousepos.y, p, len);
280 break;
281 case barrierCmdDMouseDown:
282 case barrierCmdDMouseUp:
283 read_char(msg->mousebutton.buttonid, p, len);
284 break;
285 case barrierCmdDMouseWheel:
286 read_short(msg->mousepos.y, p, len);
287 msg->mousepos.x = 0;
288 if (len) {
289 msg->mousepos.x = msg->mousepos.y;
290 read_short(msg->mousepos.y, p, len);
292 break;
294 /* keyboard */
295 case barrierCmdDKeyDown:
296 case barrierCmdDKeyUp:
297 read_short(msg->key.keyid, p, len);
298 read_short(msg->key.modifier, p, len);
299 msg->key.button = 0;
300 if (len) {
301 read_short(msg->key.button, p, len);
303 break;
304 case barrierCmdDKeyRepeat:
305 read_short(msg->repeat.keyid, p, len);
306 read_short(msg->repeat.modifier, p, len);
307 read_short(msg->repeat.repeat, p, len);
308 msg->repeat.button = 0;
309 if (len) {
310 read_short(msg->repeat.button, p, len);
312 break;
313 case barrierCmdCInfoAck:
314 case barrierCmdCResetOptions:
315 case barrierCmdCEnter:
316 case barrierCmdDClipboard:
317 case barrierCmdCKeepAlive:
318 case barrierCmdCLeave:
319 case barrierCmdCClose:
320 break;
322 /* Invalid from the server */
323 case barrierCmdHelloBack:
324 case barrierCmdCNoop:
325 case barrierCmdDInfo:
326 break;
328 /* Error codes */
329 case barrierCmdEIncompatible:
330 read_short(msg->version.major, p, len);
331 read_short(msg->version.minor, p, len);
332 break;
333 case barrierCmdEBusy:
334 case barrierCmdEUnknown:
335 case barrierCmdEBad:
336 break;
337 default:
338 return G_SOURCE_REMOVE;
341 return G_SOURCE_CONTINUE;
344 static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg)
346 char *p;
347 int ret, i;
348 int avail, len;
350 p = ib->buffer;
351 avail = MAX_HELLO_LENGTH;
353 /* reserve space to store the length */
354 p += sizeof(int);
355 avail -= sizeof(int);
357 switch (msg->cmd) {
358 case barrierCmdHello:
359 if (msg->version.major < BARRIER_VERSION_MAJOR ||
360 (msg->version.major == BARRIER_VERSION_MAJOR &&
361 msg->version.minor < BARRIER_VERSION_MINOR)) {
362 ib->ioc_tag = 0;
363 return G_SOURCE_REMOVE;
365 write_cmd(p, barrierCmdHelloBack, avail);
366 write_short(p, BARRIER_VERSION_MAJOR, avail);
367 write_short(p, BARRIER_VERSION_MINOR, avail);
368 write_string(p, ib->name, avail);
369 break;
370 case barrierCmdCClose:
371 ib->ioc_tag = 0;
372 return G_SOURCE_REMOVE;
373 case barrierCmdQInfo:
374 write_cmd(p, barrierCmdDInfo, avail);
375 write_short(p, ib->x_origin, avail);
376 write_short(p, ib->y_origin, avail);
377 write_short(p, ib->width, avail);
378 write_short(p, ib->height, avail);
379 write_short(p, 0, avail); /* warpsize (obsolete) */
380 write_short(p, 0, avail); /* mouse x */
381 write_short(p, 0, avail); /* mouse y */
382 break;
383 case barrierCmdCInfoAck:
384 break;
385 case barrierCmdCResetOptions:
386 /* TODO: reset options */
387 break;
388 case barrierCmdDSetOptions:
389 /* TODO: set options */
390 break;
391 case barrierCmdCEnter:
392 break;
393 case barrierCmdDClipboard:
394 break;
395 case barrierCmdCKeepAlive:
396 write_cmd(p, barrierCmdCKeepAlive, avail);
397 break;
398 case barrierCmdCLeave:
399 break;
401 /* mouse */
402 case barrierCmdDMouseMove:
403 qemu_input_queue_abs(NULL, INPUT_AXIS_X, msg->mousepos.x,
404 ib->x_origin, ib->width);
405 qemu_input_queue_abs(NULL, INPUT_AXIS_Y, msg->mousepos.y,
406 ib->y_origin, ib->height);
407 qemu_input_event_sync();
408 break;
409 case barrierCmdDMouseRelMove:
410 qemu_input_queue_rel(NULL, INPUT_AXIS_X, msg->mousepos.x);
411 qemu_input_queue_rel(NULL, INPUT_AXIS_Y, msg->mousepos.y);
412 qemu_input_event_sync();
413 break;
414 case barrierCmdDMouseDown:
415 qemu_input_queue_btn(NULL,
416 input_barrier_to_mouse(msg->mousebutton.buttonid),
417 true);
418 qemu_input_event_sync();
419 break;
420 case barrierCmdDMouseUp:
421 qemu_input_queue_btn(NULL,
422 input_barrier_to_mouse(msg->mousebutton.buttonid),
423 false);
424 qemu_input_event_sync();
425 break;
426 case barrierCmdDMouseWheel:
427 qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP
428 : INPUT_BUTTON_WHEEL_DOWN, true);
429 qemu_input_event_sync();
430 qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP
431 : INPUT_BUTTON_WHEEL_DOWN, false);
432 qemu_input_event_sync();
433 break;
435 /* keyboard */
436 case barrierCmdDKeyDown:
437 qemu_input_event_send_key_qcode(NULL,
438 input_barrier_to_qcode(msg->key.keyid, msg->key.button),
439 true);
440 break;
441 case barrierCmdDKeyRepeat:
442 for (i = 0; i < msg->repeat.repeat; i++) {
443 qemu_input_event_send_key_qcode(NULL,
444 input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button),
445 false);
446 qemu_input_event_send_key_qcode(NULL,
447 input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button),
448 true);
450 break;
451 case barrierCmdDKeyUp:
452 qemu_input_event_send_key_qcode(NULL,
453 input_barrier_to_qcode(msg->key.keyid, msg->key.button),
454 false);
455 break;
456 default:
457 write_cmd(p, barrierCmdEUnknown, avail);
458 break;
461 len = MAX_HELLO_LENGTH - avail - sizeof(int);
462 if (len) {
463 p = ib->buffer;
464 avail = sizeof(len);
465 write_int(p, len, avail);
466 ret = qio_channel_write(QIO_CHANNEL(ib->sioc), ib->buffer,
467 len + sizeof(len), NULL);
468 if (ret < 0) {
469 ib->ioc_tag = 0;
470 return G_SOURCE_REMOVE;
474 return G_SOURCE_CONTINUE;
477 static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED,
478 GIOCondition condition, void *opaque)
480 InputBarrier *ib = opaque;
481 int ret;
482 struct barrierMsg msg;
484 ret = readcmd(ib, &msg);
485 if (ret == G_SOURCE_REMOVE) {
486 ib->ioc_tag = 0;
487 return G_SOURCE_REMOVE;
490 return writecmd(ib, &msg);
493 static void input_barrier_complete(UserCreatable *uc, Error **errp)
495 InputBarrier *ib = INPUT_BARRIER(uc);
496 Error *local_err = NULL;
498 if (!ib->name) {
499 error_setg(errp, QERR_MISSING_PARAMETER, "name");
500 return;
504 * Connect to the primary
505 * Primary is the server where the keyboard and the mouse
506 * are connected and forwarded to the secondary (the client)
509 ib->sioc = qio_channel_socket_new();
510 qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client");
512 qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err);
513 if (local_err) {
514 error_propagate(errp, local_err);
515 return;
518 qio_channel_set_delay(QIO_CHANNEL(ib->sioc), false);
520 ib->ioc_tag = qio_channel_add_watch(QIO_CHANNEL(ib->sioc), G_IO_IN,
521 input_barrier_event, ib, NULL);
524 static void input_barrier_instance_finalize(Object *obj)
526 InputBarrier *ib = INPUT_BARRIER(obj);
528 if (ib->ioc_tag) {
529 g_source_remove(ib->ioc_tag);
530 ib->ioc_tag = 0;
533 if (ib->sioc) {
534 qio_channel_close(QIO_CHANNEL(ib->sioc), NULL);
535 object_unref(OBJECT(ib->sioc));
537 g_free(ib->name);
538 g_free(ib->saddr.u.inet.host);
539 g_free(ib->saddr.u.inet.port);
542 static char *input_barrier_get_name(Object *obj, Error **errp)
544 InputBarrier *ib = INPUT_BARRIER(obj);
546 return g_strdup(ib->name);
549 static void input_barrier_set_name(Object *obj, const char *value,
550 Error **errp)
552 InputBarrier *ib = INPUT_BARRIER(obj);
554 if (ib->name) {
555 error_setg(errp, "name property already set");
556 return;
558 ib->name = g_strdup(value);
561 static char *input_barrier_get_server(Object *obj, Error **errp)
563 InputBarrier *ib = INPUT_BARRIER(obj);
565 return g_strdup(ib->saddr.u.inet.host);
568 static void input_barrier_set_server(Object *obj, const char *value,
569 Error **errp)
571 InputBarrier *ib = INPUT_BARRIER(obj);
573 g_free(ib->saddr.u.inet.host);
574 ib->saddr.u.inet.host = g_strdup(value);
577 static char *input_barrier_get_port(Object *obj, Error **errp)
579 InputBarrier *ib = INPUT_BARRIER(obj);
581 return g_strdup(ib->saddr.u.inet.port);
584 static void input_barrier_set_port(Object *obj, const char *value,
585 Error **errp)
587 InputBarrier *ib = INPUT_BARRIER(obj);
589 g_free(ib->saddr.u.inet.port);
590 ib->saddr.u.inet.port = g_strdup(value);
593 static void input_barrier_set_x_origin(Object *obj, const char *value,
594 Error **errp)
596 InputBarrier *ib = INPUT_BARRIER(obj);
597 int result, err;
599 err = qemu_strtoi(value, NULL, 0, &result);
600 if (err < 0 || result < 0 || result > SHRT_MAX) {
601 error_setg(errp,
602 "x-origin property must be in the range [0..%d]", SHRT_MAX);
603 return;
605 ib->x_origin = result;
608 static char *input_barrier_get_x_origin(Object *obj, Error **errp)
610 InputBarrier *ib = INPUT_BARRIER(obj);
612 return g_strdup_printf("%d", ib->x_origin);
615 static void input_barrier_set_y_origin(Object *obj, const char *value,
616 Error **errp)
618 InputBarrier *ib = INPUT_BARRIER(obj);
619 int result, err;
621 err = qemu_strtoi(value, NULL, 0, &result);
622 if (err < 0 || result < 0 || result > SHRT_MAX) {
623 error_setg(errp,
624 "y-origin property must be in the range [0..%d]", SHRT_MAX);
625 return;
627 ib->y_origin = result;
630 static char *input_barrier_get_y_origin(Object *obj, Error **errp)
632 InputBarrier *ib = INPUT_BARRIER(obj);
634 return g_strdup_printf("%d", ib->y_origin);
637 static void input_barrier_set_width(Object *obj, const char *value,
638 Error **errp)
640 InputBarrier *ib = INPUT_BARRIER(obj);
641 int result, err;
643 err = qemu_strtoi(value, NULL, 0, &result);
644 if (err < 0 || result < 0 || result > SHRT_MAX) {
645 error_setg(errp,
646 "width property must be in the range [0..%d]", SHRT_MAX);
647 return;
649 ib->width = result;
652 static char *input_barrier_get_width(Object *obj, Error **errp)
654 InputBarrier *ib = INPUT_BARRIER(obj);
656 return g_strdup_printf("%d", ib->width);
659 static void input_barrier_set_height(Object *obj, const char *value,
660 Error **errp)
662 InputBarrier *ib = INPUT_BARRIER(obj);
663 int result, err;
665 err = qemu_strtoi(value, NULL, 0, &result);
666 if (err < 0 || result < 0 || result > SHRT_MAX) {
667 error_setg(errp,
668 "height property must be in the range [0..%d]", SHRT_MAX);
669 return;
671 ib->height = result;
674 static char *input_barrier_get_height(Object *obj, Error **errp)
676 InputBarrier *ib = INPUT_BARRIER(obj);
678 return g_strdup_printf("%d", ib->height);
681 static void input_barrier_instance_init(Object *obj)
683 InputBarrier *ib = INPUT_BARRIER(obj);
685 /* always use generic keymaps */
686 if (keyboard_layout && !kbd_layout) {
687 /* We use X11 key id, so use VNC name2keysym */
688 kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
689 &error_fatal);
692 ib->saddr.type = SOCKET_ADDRESS_TYPE_INET;
693 ib->saddr.u.inet.host = g_strdup("localhost");
694 ib->saddr.u.inet.port = g_strdup("24800");
696 ib->x_origin = 0;
697 ib->y_origin = 0;
698 ib->width = 1920;
699 ib->height = 1080;
701 object_property_add_str(obj, "name",
702 input_barrier_get_name,
703 input_barrier_set_name, NULL);
704 object_property_add_str(obj, "server",
705 input_barrier_get_server,
706 input_barrier_set_server, NULL);
707 object_property_add_str(obj, "port",
708 input_barrier_get_port,
709 input_barrier_set_port, NULL);
710 object_property_add_str(obj, "x-origin",
711 input_barrier_get_x_origin,
712 input_barrier_set_x_origin, NULL);
713 object_property_add_str(obj, "y-origin",
714 input_barrier_get_y_origin,
715 input_barrier_set_y_origin, NULL);
716 object_property_add_str(obj, "width",
717 input_barrier_get_width,
718 input_barrier_set_width, NULL);
719 object_property_add_str(obj, "height",
720 input_barrier_get_height,
721 input_barrier_set_height, NULL);
724 static void input_barrier_class_init(ObjectClass *oc, void *data)
726 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
728 ucc->complete = input_barrier_complete;
731 static const TypeInfo input_barrier_info = {
732 .name = TYPE_INPUT_BARRIER,
733 .parent = TYPE_OBJECT,
734 .class_size = sizeof(InputBarrierClass),
735 .class_init = input_barrier_class_init,
736 .instance_size = sizeof(InputBarrier),
737 .instance_init = input_barrier_instance_init,
738 .instance_finalize = input_barrier_instance_finalize,
739 .interfaces = (InterfaceInfo[]) {
740 { TYPE_USER_CREATABLE },
745 static void register_types(void)
747 type_register_static(&input_barrier_info);
750 type_init(register_types);