configure: add --without-default-features
[qemu.git] / ui / input-barrier.c
blob81b8d04ec8dae63f8da2e70917c94b2a6869499f
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 "qom/object.h"
17 #include "ui/vnc_keysym.h" /* use name2keysym from VNC as we use X11 values */
18 #include "qemu/cutils.h"
19 #include "qapi/qmp/qerror.h"
20 #include "input-barrier.h"
22 #define TYPE_INPUT_BARRIER "input-barrier"
23 OBJECT_DECLARE_SIMPLE_TYPE(InputBarrier,
24 INPUT_BARRIER)
27 #define MAX_HELLO_LENGTH 1024
29 struct InputBarrier {
30 Object parent;
32 QIOChannelSocket *sioc;
33 guint ioc_tag;
35 /* display properties */
36 gchar *name;
37 int16_t x_origin, y_origin;
38 int16_t width, height;
40 /* keyboard/mouse server */
42 SocketAddress saddr;
44 char buffer[MAX_HELLO_LENGTH];
48 static const char *cmd_names[] = {
49 [barrierCmdCNoop] = "CNOP",
50 [barrierCmdCClose] = "CBYE",
51 [barrierCmdCEnter] = "CINN",
52 [barrierCmdCLeave] = "COUT",
53 [barrierCmdCClipboard] = "CCLP",
54 [barrierCmdCScreenSaver] = "CSEC",
55 [barrierCmdCResetOptions] = "CROP",
56 [barrierCmdCInfoAck] = "CIAK",
57 [barrierCmdCKeepAlive] = "CALV",
58 [barrierCmdDKeyDown] = "DKDN",
59 [barrierCmdDKeyRepeat] = "DKRP",
60 [barrierCmdDKeyUp] = "DKUP",
61 [barrierCmdDMouseDown] = "DMDN",
62 [barrierCmdDMouseUp] = "DMUP",
63 [barrierCmdDMouseMove] = "DMMV",
64 [barrierCmdDMouseRelMove] = "DMRM",
65 [barrierCmdDMouseWheel] = "DMWM",
66 [barrierCmdDClipboard] = "DCLP",
67 [barrierCmdDInfo] = "DINF",
68 [barrierCmdDSetOptions] = "DSOP",
69 [barrierCmdDFileTransfer] = "DFTR",
70 [barrierCmdDDragInfo] = "DDRG",
71 [barrierCmdQInfo] = "QINF",
72 [barrierCmdEIncompatible] = "EICV",
73 [barrierCmdEBusy] = "EBSY",
74 [barrierCmdEUnknown] = "EUNK",
75 [barrierCmdEBad] = "EBAD",
76 [barrierCmdHello] = "Barrier",
77 [barrierCmdHelloBack] = "Barrier",
80 static kbd_layout_t *kbd_layout;
82 static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode)
84 /* keycode is optional, if it is not provided use keyid */
85 if (keycode && keycode <= qemu_input_map_xorgkbd_to_qcode_len) {
86 return qemu_input_map_xorgkbd_to_qcode[keycode];
89 if (keyid >= 0xE000 && keyid <= 0xEFFF) {
90 keyid += 0x1000;
93 /* keyid is the X11 key id */
94 if (kbd_layout) {
95 keycode = keysym2scancode(kbd_layout, keyid, NULL, false);
97 return qemu_input_key_number_to_qcode(keycode);
100 return qemu_input_map_x11_to_qcode[keyid];
103 static int input_barrier_to_mouse(uint8_t buttonid)
105 switch (buttonid) {
106 case barrierButtonLeft:
107 return INPUT_BUTTON_LEFT;
108 case barrierButtonMiddle:
109 return INPUT_BUTTON_MIDDLE;
110 case barrierButtonRight:
111 return INPUT_BUTTON_RIGHT;
112 case barrierButtonExtra0:
113 return INPUT_BUTTON_SIDE;
115 return buttonid;
118 #define read_char(x, p, l) \
119 do { \
120 int size = sizeof(char); \
121 if (l < size) { \
122 return G_SOURCE_REMOVE; \
124 x = *(char *)p; \
125 p += size; \
126 l -= size; \
127 } while (0)
129 #define read_short(x, p, l) \
130 do { \
131 int size = sizeof(short); \
132 if (l < size) { \
133 return G_SOURCE_REMOVE; \
135 x = ntohs(*(short *)p); \
136 p += size; \
137 l -= size; \
138 } while (0)
140 #define write_short(p, x, l) \
141 do { \
142 int size = sizeof(short); \
143 if (l < size) { \
144 return G_SOURCE_REMOVE; \
146 *(short *)p = htons(x); \
147 p += size; \
148 l -= size; \
149 } while (0)
151 #define read_int(x, p, l) \
152 do { \
153 int size = sizeof(int); \
154 if (l < size) { \
155 return G_SOURCE_REMOVE; \
157 x = ntohl(*(int *)p); \
158 p += size; \
159 l -= size; \
160 } while (0)
162 #define write_int(p, x, l) \
163 do { \
164 int size = sizeof(int); \
165 if (l < size) { \
166 return G_SOURCE_REMOVE; \
168 *(int *)p = htonl(x); \
169 p += size; \
170 l -= size; \
171 } while (0)
173 #define write_cmd(p, c, l) \
174 do { \
175 int size = strlen(cmd_names[c]); \
176 if (l < size) { \
177 return G_SOURCE_REMOVE; \
179 memcpy(p, cmd_names[c], size); \
180 p += size; \
181 l -= size; \
182 } while (0)
184 #define write_string(p, s, l) \
185 do { \
186 int size = strlen(s); \
187 if (l < size + sizeof(int)) { \
188 return G_SOURCE_REMOVE; \
190 *(int *)p = htonl(size); \
191 p += sizeof(size); \
192 l -= sizeof(size); \
193 memcpy(p, s, size); \
194 p += size; \
195 l -= size; \
196 } while (0)
198 static gboolean readcmd(InputBarrier *ib, struct barrierMsg *msg)
200 int ret, len, i;
201 enum barrierCmd cmd;
202 char *p;
204 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), (char *)&len, sizeof(len),
205 NULL);
206 if (ret < 0) {
207 return G_SOURCE_REMOVE;
210 len = ntohl(len);
211 if (len > MAX_HELLO_LENGTH) {
212 return G_SOURCE_REMOVE;
215 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), ib->buffer, len, NULL);
216 if (ret < 0) {
217 return G_SOURCE_REMOVE;
220 p = ib->buffer;
221 if (len >= strlen(cmd_names[barrierCmdHello]) &&
222 memcmp(p, cmd_names[barrierCmdHello],
223 strlen(cmd_names[barrierCmdHello])) == 0) {
224 cmd = barrierCmdHello;
225 p += strlen(cmd_names[barrierCmdHello]);
226 len -= strlen(cmd_names[barrierCmdHello]);
227 } else {
228 for (cmd = 0; cmd < barrierCmdHello; cmd++) {
229 if (memcmp(ib->buffer, cmd_names[cmd], 4) == 0) {
230 break;
234 if (cmd == barrierCmdHello) {
235 return G_SOURCE_REMOVE;
237 p += 4;
238 len -= 4;
241 msg->cmd = cmd;
242 switch (cmd) {
243 /* connection */
244 case barrierCmdHello:
245 read_short(msg->version.major, p, len);
246 read_short(msg->version.minor, p, len);
247 break;
248 case barrierCmdDSetOptions:
249 read_int(msg->set.nb, p, len);
250 msg->set.nb /= 2;
251 if (msg->set.nb > BARRIER_MAX_OPTIONS) {
252 msg->set.nb = BARRIER_MAX_OPTIONS;
254 i = 0;
255 while (len && i < msg->set.nb) {
256 read_int(msg->set.option[i].id, p, len);
257 /* it's a string, restore endianness */
258 msg->set.option[i].id = htonl(msg->set.option[i].id);
259 msg->set.option[i].nul = 0;
260 read_int(msg->set.option[i].value, p, len);
261 i++;
263 break;
264 case barrierCmdQInfo:
265 break;
267 /* mouse */
268 case barrierCmdDMouseMove:
269 case barrierCmdDMouseRelMove:
270 read_short(msg->mousepos.x, p, len);
271 read_short(msg->mousepos.y, p, len);
272 break;
273 case barrierCmdDMouseDown:
274 case barrierCmdDMouseUp:
275 read_char(msg->mousebutton.buttonid, p, len);
276 break;
277 case barrierCmdDMouseWheel:
278 read_short(msg->mousepos.y, p, len);
279 msg->mousepos.x = 0;
280 if (len) {
281 msg->mousepos.x = msg->mousepos.y;
282 read_short(msg->mousepos.y, p, len);
284 break;
286 /* keyboard */
287 case barrierCmdDKeyDown:
288 case barrierCmdDKeyUp:
289 read_short(msg->key.keyid, p, len);
290 read_short(msg->key.modifier, p, len);
291 msg->key.button = 0;
292 if (len) {
293 read_short(msg->key.button, p, len);
295 break;
296 case barrierCmdDKeyRepeat:
297 read_short(msg->repeat.keyid, p, len);
298 read_short(msg->repeat.modifier, p, len);
299 read_short(msg->repeat.repeat, p, len);
300 msg->repeat.button = 0;
301 if (len) {
302 read_short(msg->repeat.button, p, len);
304 break;
305 case barrierCmdCInfoAck:
306 case barrierCmdCResetOptions:
307 case barrierCmdCEnter:
308 case barrierCmdDClipboard:
309 case barrierCmdCKeepAlive:
310 case barrierCmdCLeave:
311 case barrierCmdCClose:
312 break;
314 /* Invalid from the server */
315 case barrierCmdHelloBack:
316 case barrierCmdCNoop:
317 case barrierCmdDInfo:
318 break;
320 /* Error codes */
321 case barrierCmdEIncompatible:
322 read_short(msg->version.major, p, len);
323 read_short(msg->version.minor, p, len);
324 break;
325 case barrierCmdEBusy:
326 case barrierCmdEUnknown:
327 case barrierCmdEBad:
328 break;
329 default:
330 return G_SOURCE_REMOVE;
333 return G_SOURCE_CONTINUE;
336 static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg)
338 char *p;
339 int ret, i;
340 int avail, len;
342 p = ib->buffer;
343 avail = MAX_HELLO_LENGTH;
345 /* reserve space to store the length */
346 p += sizeof(int);
347 avail -= sizeof(int);
349 switch (msg->cmd) {
350 case barrierCmdHello:
351 if (msg->version.major < BARRIER_VERSION_MAJOR ||
352 (msg->version.major == BARRIER_VERSION_MAJOR &&
353 msg->version.minor < BARRIER_VERSION_MINOR)) {
354 ib->ioc_tag = 0;
355 return G_SOURCE_REMOVE;
357 write_cmd(p, barrierCmdHelloBack, avail);
358 write_short(p, BARRIER_VERSION_MAJOR, avail);
359 write_short(p, BARRIER_VERSION_MINOR, avail);
360 write_string(p, ib->name, avail);
361 break;
362 case barrierCmdCClose:
363 ib->ioc_tag = 0;
364 return G_SOURCE_REMOVE;
365 case barrierCmdQInfo:
366 write_cmd(p, barrierCmdDInfo, avail);
367 write_short(p, ib->x_origin, avail);
368 write_short(p, ib->y_origin, avail);
369 write_short(p, ib->width, avail);
370 write_short(p, ib->height, avail);
371 write_short(p, 0, avail); /* warpsize (obsolete) */
372 write_short(p, 0, avail); /* mouse x */
373 write_short(p, 0, avail); /* mouse y */
374 break;
375 case barrierCmdCInfoAck:
376 break;
377 case barrierCmdCResetOptions:
378 /* TODO: reset options */
379 break;
380 case barrierCmdDSetOptions:
381 /* TODO: set options */
382 break;
383 case barrierCmdCEnter:
384 break;
385 case barrierCmdDClipboard:
386 break;
387 case barrierCmdCKeepAlive:
388 write_cmd(p, barrierCmdCKeepAlive, avail);
389 break;
390 case barrierCmdCLeave:
391 break;
393 /* mouse */
394 case barrierCmdDMouseMove:
395 qemu_input_queue_abs(NULL, INPUT_AXIS_X, msg->mousepos.x,
396 ib->x_origin, ib->width);
397 qemu_input_queue_abs(NULL, INPUT_AXIS_Y, msg->mousepos.y,
398 ib->y_origin, ib->height);
399 qemu_input_event_sync();
400 break;
401 case barrierCmdDMouseRelMove:
402 qemu_input_queue_rel(NULL, INPUT_AXIS_X, msg->mousepos.x);
403 qemu_input_queue_rel(NULL, INPUT_AXIS_Y, msg->mousepos.y);
404 qemu_input_event_sync();
405 break;
406 case barrierCmdDMouseDown:
407 qemu_input_queue_btn(NULL,
408 input_barrier_to_mouse(msg->mousebutton.buttonid),
409 true);
410 qemu_input_event_sync();
411 break;
412 case barrierCmdDMouseUp:
413 qemu_input_queue_btn(NULL,
414 input_barrier_to_mouse(msg->mousebutton.buttonid),
415 false);
416 qemu_input_event_sync();
417 break;
418 case barrierCmdDMouseWheel:
419 qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP
420 : INPUT_BUTTON_WHEEL_DOWN, true);
421 qemu_input_event_sync();
422 qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP
423 : INPUT_BUTTON_WHEEL_DOWN, false);
424 qemu_input_event_sync();
425 break;
427 /* keyboard */
428 case barrierCmdDKeyDown:
429 qemu_input_event_send_key_qcode(NULL,
430 input_barrier_to_qcode(msg->key.keyid, msg->key.button),
431 true);
432 break;
433 case barrierCmdDKeyRepeat:
434 for (i = 0; i < msg->repeat.repeat; i++) {
435 qemu_input_event_send_key_qcode(NULL,
436 input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button),
437 false);
438 qemu_input_event_send_key_qcode(NULL,
439 input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button),
440 true);
442 break;
443 case barrierCmdDKeyUp:
444 qemu_input_event_send_key_qcode(NULL,
445 input_barrier_to_qcode(msg->key.keyid, msg->key.button),
446 false);
447 break;
448 default:
449 write_cmd(p, barrierCmdEUnknown, avail);
450 break;
453 len = MAX_HELLO_LENGTH - avail - sizeof(int);
454 if (len) {
455 p = ib->buffer;
456 avail = sizeof(len);
457 write_int(p, len, avail);
458 ret = qio_channel_write(QIO_CHANNEL(ib->sioc), ib->buffer,
459 len + sizeof(len), NULL);
460 if (ret < 0) {
461 ib->ioc_tag = 0;
462 return G_SOURCE_REMOVE;
466 return G_SOURCE_CONTINUE;
469 static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED,
470 GIOCondition condition, void *opaque)
472 InputBarrier *ib = opaque;
473 int ret;
474 struct barrierMsg msg;
476 ret = readcmd(ib, &msg);
477 if (ret == G_SOURCE_REMOVE) {
478 ib->ioc_tag = 0;
479 return G_SOURCE_REMOVE;
482 return writecmd(ib, &msg);
485 static void input_barrier_complete(UserCreatable *uc, Error **errp)
487 InputBarrier *ib = INPUT_BARRIER(uc);
488 Error *local_err = NULL;
490 if (!ib->name) {
491 error_setg(errp, QERR_MISSING_PARAMETER, "name");
492 return;
496 * Connect to the primary
497 * Primary is the server where the keyboard and the mouse
498 * are connected and forwarded to the secondary (the client)
501 ib->sioc = qio_channel_socket_new();
502 qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client");
504 qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err);
505 if (local_err) {
506 error_propagate(errp, local_err);
507 return;
510 qio_channel_set_delay(QIO_CHANNEL(ib->sioc), false);
512 ib->ioc_tag = qio_channel_add_watch(QIO_CHANNEL(ib->sioc), G_IO_IN,
513 input_barrier_event, ib, NULL);
516 static void input_barrier_instance_finalize(Object *obj)
518 InputBarrier *ib = INPUT_BARRIER(obj);
520 if (ib->ioc_tag) {
521 g_source_remove(ib->ioc_tag);
522 ib->ioc_tag = 0;
525 if (ib->sioc) {
526 qio_channel_close(QIO_CHANNEL(ib->sioc), NULL);
527 object_unref(OBJECT(ib->sioc));
529 g_free(ib->name);
530 g_free(ib->saddr.u.inet.host);
531 g_free(ib->saddr.u.inet.port);
534 static char *input_barrier_get_name(Object *obj, Error **errp)
536 InputBarrier *ib = INPUT_BARRIER(obj);
538 return g_strdup(ib->name);
541 static void input_barrier_set_name(Object *obj, const char *value,
542 Error **errp)
544 InputBarrier *ib = INPUT_BARRIER(obj);
546 if (ib->name) {
547 error_setg(errp, "name property already set");
548 return;
550 ib->name = g_strdup(value);
553 static char *input_barrier_get_server(Object *obj, Error **errp)
555 InputBarrier *ib = INPUT_BARRIER(obj);
557 return g_strdup(ib->saddr.u.inet.host);
560 static void input_barrier_set_server(Object *obj, const char *value,
561 Error **errp)
563 InputBarrier *ib = INPUT_BARRIER(obj);
565 g_free(ib->saddr.u.inet.host);
566 ib->saddr.u.inet.host = g_strdup(value);
569 static char *input_barrier_get_port(Object *obj, Error **errp)
571 InputBarrier *ib = INPUT_BARRIER(obj);
573 return g_strdup(ib->saddr.u.inet.port);
576 static void input_barrier_set_port(Object *obj, const char *value,
577 Error **errp)
579 InputBarrier *ib = INPUT_BARRIER(obj);
581 g_free(ib->saddr.u.inet.port);
582 ib->saddr.u.inet.port = g_strdup(value);
585 static void input_barrier_set_x_origin(Object *obj, const char *value,
586 Error **errp)
588 InputBarrier *ib = INPUT_BARRIER(obj);
589 int result, err;
591 err = qemu_strtoi(value, NULL, 0, &result);
592 if (err < 0 || result < 0 || result > SHRT_MAX) {
593 error_setg(errp,
594 "x-origin property must be in the range [0..%d]", SHRT_MAX);
595 return;
597 ib->x_origin = result;
600 static char *input_barrier_get_x_origin(Object *obj, Error **errp)
602 InputBarrier *ib = INPUT_BARRIER(obj);
604 return g_strdup_printf("%d", ib->x_origin);
607 static void input_barrier_set_y_origin(Object *obj, const char *value,
608 Error **errp)
610 InputBarrier *ib = INPUT_BARRIER(obj);
611 int result, err;
613 err = qemu_strtoi(value, NULL, 0, &result);
614 if (err < 0 || result < 0 || result > SHRT_MAX) {
615 error_setg(errp,
616 "y-origin property must be in the range [0..%d]", SHRT_MAX);
617 return;
619 ib->y_origin = result;
622 static char *input_barrier_get_y_origin(Object *obj, Error **errp)
624 InputBarrier *ib = INPUT_BARRIER(obj);
626 return g_strdup_printf("%d", ib->y_origin);
629 static void input_barrier_set_width(Object *obj, const char *value,
630 Error **errp)
632 InputBarrier *ib = INPUT_BARRIER(obj);
633 int result, err;
635 err = qemu_strtoi(value, NULL, 0, &result);
636 if (err < 0 || result < 0 || result > SHRT_MAX) {
637 error_setg(errp,
638 "width property must be in the range [0..%d]", SHRT_MAX);
639 return;
641 ib->width = result;
644 static char *input_barrier_get_width(Object *obj, Error **errp)
646 InputBarrier *ib = INPUT_BARRIER(obj);
648 return g_strdup_printf("%d", ib->width);
651 static void input_barrier_set_height(Object *obj, const char *value,
652 Error **errp)
654 InputBarrier *ib = INPUT_BARRIER(obj);
655 int result, err;
657 err = qemu_strtoi(value, NULL, 0, &result);
658 if (err < 0 || result < 0 || result > SHRT_MAX) {
659 error_setg(errp,
660 "height property must be in the range [0..%d]", SHRT_MAX);
661 return;
663 ib->height = result;
666 static char *input_barrier_get_height(Object *obj, Error **errp)
668 InputBarrier *ib = INPUT_BARRIER(obj);
670 return g_strdup_printf("%d", ib->height);
673 static void input_barrier_instance_init(Object *obj)
675 InputBarrier *ib = INPUT_BARRIER(obj);
677 /* always use generic keymaps */
678 if (keyboard_layout && !kbd_layout) {
679 /* We use X11 key id, so use VNC name2keysym */
680 kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
681 &error_fatal);
684 ib->saddr.type = SOCKET_ADDRESS_TYPE_INET;
685 ib->saddr.u.inet.host = g_strdup("localhost");
686 ib->saddr.u.inet.port = g_strdup("24800");
688 ib->x_origin = 0;
689 ib->y_origin = 0;
690 ib->width = 1920;
691 ib->height = 1080;
694 static void input_barrier_class_init(ObjectClass *oc, void *data)
696 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
698 ucc->complete = input_barrier_complete;
700 object_class_property_add_str(oc, "name",
701 input_barrier_get_name,
702 input_barrier_set_name);
703 object_class_property_add_str(oc, "server",
704 input_barrier_get_server,
705 input_barrier_set_server);
706 object_class_property_add_str(oc, "port",
707 input_barrier_get_port,
708 input_barrier_set_port);
709 object_class_property_add_str(oc, "x-origin",
710 input_barrier_get_x_origin,
711 input_barrier_set_x_origin);
712 object_class_property_add_str(oc, "y-origin",
713 input_barrier_get_y_origin,
714 input_barrier_set_y_origin);
715 object_class_property_add_str(oc, "width",
716 input_barrier_get_width,
717 input_barrier_set_width);
718 object_class_property_add_str(oc, "height",
719 input_barrier_get_height,
720 input_barrier_set_height);
723 static const TypeInfo input_barrier_info = {
724 .name = TYPE_INPUT_BARRIER,
725 .parent = TYPE_OBJECT,
726 .class_init = input_barrier_class_init,
727 .instance_size = sizeof(InputBarrier),
728 .instance_init = input_barrier_instance_init,
729 .instance_finalize = input_barrier_instance_finalize,
730 .interfaces = (InterfaceInfo[]) {
731 { TYPE_USER_CREATABLE },
736 static void register_types(void)
738 type_register_static(&input_barrier_info);
741 type_init(register_types);