libvduse: Add support for reconnecting
[qemu.git] / ui / input-barrier.c
blob2d57ca70791b87943d925f045770e0ce48443031
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.
7 * TODO:
9 * - Enable SSL
10 * - Manage SetOptions/ResetOptions commands
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"
21 #include "qom/object.h"
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"
27 #define TYPE_INPUT_BARRIER "input-barrier"
28 OBJECT_DECLARE_SIMPLE_TYPE(InputBarrier,
29 INPUT_BARRIER)
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];
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",
85 static kbd_layout_t *kbd_layout;
87 static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode)
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];
94 if (keyid >= 0xE000 && keyid <= 0xEFFF) {
95 keyid += 0x1000;
98 /* keyid is the X11 key id */
99 if (kbd_layout) {
100 keycode = keysym2scancode(kbd_layout, keyid, NULL, false);
102 return qemu_input_key_number_to_qcode(keycode);
105 return qemu_input_map_x11_to_qcode[keyid];
108 static int input_barrier_to_mouse(uint8_t buttonid)
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;
120 return buttonid;
123 #define read_char(x, p, l) \
124 do { \
125 int size = sizeof(char); \
126 if (l < size) { \
127 return G_SOURCE_REMOVE; \
129 x = *(char *)p; \
130 p += size; \
131 l -= size; \
132 } while (0)
134 #define read_short(x, p, l) \
135 do { \
136 int size = sizeof(short); \
137 if (l < size) { \
138 return G_SOURCE_REMOVE; \
140 x = ntohs(*(short *)p); \
141 p += size; \
142 l -= size; \
143 } while (0)
145 #define write_short(p, x, l) \
146 do { \
147 int size = sizeof(short); \
148 if (l < size) { \
149 return G_SOURCE_REMOVE; \
151 *(short *)p = htons(x); \
152 p += size; \
153 l -= size; \
154 } while (0)
156 #define read_int(x, p, l) \
157 do { \
158 int size = sizeof(int); \
159 if (l < size) { \
160 return G_SOURCE_REMOVE; \
162 x = ntohl(*(int *)p); \
163 p += size; \
164 l -= size; \
165 } while (0)
167 #define write_int(p, x, l) \
168 do { \
169 int size = sizeof(int); \
170 if (l < size) { \
171 return G_SOURCE_REMOVE; \
173 *(int *)p = htonl(x); \
174 p += size; \
175 l -= size; \
176 } while (0)
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; \
184 memcpy(p, cmd_names[c], size); \
185 p += size; \
186 l -= size; \
187 } while (0)
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; \
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)
203 static gboolean readcmd(InputBarrier *ib, struct barrierMsg *msg)
205 int ret, len, i;
206 enum barrierCmd cmd;
207 char *p;
209 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), (char *)&len, sizeof(len),
210 NULL);
211 if (ret < 0) {
212 return G_SOURCE_REMOVE;
215 len = ntohl(len);
216 if (len > MAX_HELLO_LENGTH) {
217 return G_SOURCE_REMOVE;
220 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), ib->buffer, len, NULL);
221 if (ret < 0) {
222 return G_SOURCE_REMOVE;
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;
239 if (cmd == barrierCmdHello) {
240 return G_SOURCE_REMOVE;
242 p += 4;
243 len -= 4;
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;
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++;
268 break;
269 case barrierCmdQInfo:
270 break;
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);
289 break;
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);
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);
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;
319 /* Invalid from the server */
320 case barrierCmdHelloBack:
321 case barrierCmdCNoop:
322 case barrierCmdDInfo:
323 break;
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;
338 return G_SOURCE_CONTINUE;
341 static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg)
343 char *p;
344 int ret, i;
345 int avail, len;
347 p = ib->buffer;
348 avail = MAX_HELLO_LENGTH;
350 /* reserve space to store the length */
351 p += sizeof(int);
352 avail -= sizeof(int);
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;
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;
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;
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);
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);
455 break;
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;
471 return G_SOURCE_CONTINUE;
474 static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED,
475 GIOCondition condition, void *opaque)
477 InputBarrier *ib = opaque;
478 int ret;
479 struct barrierMsg msg;
481 ret = readcmd(ib, &msg);
482 if (ret == G_SOURCE_REMOVE) {
483 ib->ioc_tag = 0;
484 return G_SOURCE_REMOVE;
487 return writecmd(ib, &msg);
490 static void input_barrier_complete(UserCreatable *uc, Error **errp)
492 InputBarrier *ib = INPUT_BARRIER(uc);
493 Error *local_err = NULL;
495 if (!ib->name) {
496 error_setg(errp, QERR_MISSING_PARAMETER, "name");
497 return;
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)
506 ib->sioc = qio_channel_socket_new();
507 qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client");
509 qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err);
510 if (local_err) {
511 error_propagate(errp, local_err);
512 return;
515 qio_channel_set_delay(QIO_CHANNEL(ib->sioc), false);
517 ib->ioc_tag = qio_channel_add_watch(QIO_CHANNEL(ib->sioc), G_IO_IN,
518 input_barrier_event, ib, NULL);
521 static void input_barrier_instance_finalize(Object *obj)
523 InputBarrier *ib = INPUT_BARRIER(obj);
525 if (ib->ioc_tag) {
526 g_source_remove(ib->ioc_tag);
527 ib->ioc_tag = 0;
530 if (ib->sioc) {
531 qio_channel_close(QIO_CHANNEL(ib->sioc), NULL);
532 object_unref(OBJECT(ib->sioc));
534 g_free(ib->name);
535 g_free(ib->saddr.u.inet.host);
536 g_free(ib->saddr.u.inet.port);
539 static char *input_barrier_get_name(Object *obj, Error **errp)
541 InputBarrier *ib = INPUT_BARRIER(obj);
543 return g_strdup(ib->name);
546 static void input_barrier_set_name(Object *obj, const char *value,
547 Error **errp)
549 InputBarrier *ib = INPUT_BARRIER(obj);
551 if (ib->name) {
552 error_setg(errp, "name property already set");
553 return;
555 ib->name = g_strdup(value);
558 static char *input_barrier_get_server(Object *obj, Error **errp)
560 InputBarrier *ib = INPUT_BARRIER(obj);
562 return g_strdup(ib->saddr.u.inet.host);
565 static void input_barrier_set_server(Object *obj, const char *value,
566 Error **errp)
568 InputBarrier *ib = INPUT_BARRIER(obj);
570 g_free(ib->saddr.u.inet.host);
571 ib->saddr.u.inet.host = g_strdup(value);
574 static char *input_barrier_get_port(Object *obj, Error **errp)
576 InputBarrier *ib = INPUT_BARRIER(obj);
578 return g_strdup(ib->saddr.u.inet.port);
581 static void input_barrier_set_port(Object *obj, const char *value,
582 Error **errp)
584 InputBarrier *ib = INPUT_BARRIER(obj);
586 g_free(ib->saddr.u.inet.port);
587 ib->saddr.u.inet.port = g_strdup(value);
590 static void input_barrier_set_x_origin(Object *obj, const char *value,
591 Error **errp)
593 InputBarrier *ib = INPUT_BARRIER(obj);
594 int result, err;
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;
602 ib->x_origin = result;
605 static char *input_barrier_get_x_origin(Object *obj, Error **errp)
607 InputBarrier *ib = INPUT_BARRIER(obj);
609 return g_strdup_printf("%d", ib->x_origin);
612 static void input_barrier_set_y_origin(Object *obj, const char *value,
613 Error **errp)
615 InputBarrier *ib = INPUT_BARRIER(obj);
616 int result, err;
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;
624 ib->y_origin = result;
627 static char *input_barrier_get_y_origin(Object *obj, Error **errp)
629 InputBarrier *ib = INPUT_BARRIER(obj);
631 return g_strdup_printf("%d", ib->y_origin);
634 static void input_barrier_set_width(Object *obj, const char *value,
635 Error **errp)
637 InputBarrier *ib = INPUT_BARRIER(obj);
638 int result, err;
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;
646 ib->width = result;
649 static char *input_barrier_get_width(Object *obj, Error **errp)
651 InputBarrier *ib = INPUT_BARRIER(obj);
653 return g_strdup_printf("%d", ib->width);
656 static void input_barrier_set_height(Object *obj, const char *value,
657 Error **errp)
659 InputBarrier *ib = INPUT_BARRIER(obj);
660 int result, err;
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;
668 ib->height = result;
671 static char *input_barrier_get_height(Object *obj, Error **errp)
673 InputBarrier *ib = INPUT_BARRIER(obj);
675 return g_strdup_printf("%d", ib->height);
678 static void input_barrier_instance_init(Object *obj)
680 InputBarrier *ib = INPUT_BARRIER(obj);
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);
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");
693 ib->x_origin = 0;
694 ib->y_origin = 0;
695 ib->width = 1920;
696 ib->height = 1080;
699 static void input_barrier_class_init(ObjectClass *oc, void *data)
701 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
703 ucc->complete = input_barrier_complete;
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);
728 static const TypeInfo input_barrier_info = {
729 .name = TYPE_INPUT_BARRIER,
730 .parent = TYPE_OBJECT,
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 },
741 static void register_types(void)
743 type_register_static(&input_barrier_info);
746 type_init(register_types);