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.
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"
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_TYPE(InputBarrier
, InputBarrierClass
,
24 input_barrier
, INPUT_BARRIER
)
27 #define MAX_HELLO_LENGTH 1024
32 QIOChannelSocket
*sioc
;
35 /* display properties */
37 int16_t x_origin
, y_origin
;
38 int16_t width
, height
;
40 /* keyboard/mouse server */
44 char buffer
[MAX_HELLO_LENGTH
];
47 struct InputBarrierClass
{
48 ObjectClass parent_class
;
51 static const char *cmd_names
[] = {
52 [barrierCmdCNoop
] = "CNOP",
53 [barrierCmdCClose
] = "CBYE",
54 [barrierCmdCEnter
] = "CINN",
55 [barrierCmdCLeave
] = "COUT",
56 [barrierCmdCClipboard
] = "CCLP",
57 [barrierCmdCScreenSaver
] = "CSEC",
58 [barrierCmdCResetOptions
] = "CROP",
59 [barrierCmdCInfoAck
] = "CIAK",
60 [barrierCmdCKeepAlive
] = "CALV",
61 [barrierCmdDKeyDown
] = "DKDN",
62 [barrierCmdDKeyRepeat
] = "DKRP",
63 [barrierCmdDKeyUp
] = "DKUP",
64 [barrierCmdDMouseDown
] = "DMDN",
65 [barrierCmdDMouseUp
] = "DMUP",
66 [barrierCmdDMouseMove
] = "DMMV",
67 [barrierCmdDMouseRelMove
] = "DMRM",
68 [barrierCmdDMouseWheel
] = "DMWM",
69 [barrierCmdDClipboard
] = "DCLP",
70 [barrierCmdDInfo
] = "DINF",
71 [barrierCmdDSetOptions
] = "DSOP",
72 [barrierCmdDFileTransfer
] = "DFTR",
73 [barrierCmdDDragInfo
] = "DDRG",
74 [barrierCmdQInfo
] = "QINF",
75 [barrierCmdEIncompatible
] = "EICV",
76 [barrierCmdEBusy
] = "EBSY",
77 [barrierCmdEUnknown
] = "EUNK",
78 [barrierCmdEBad
] = "EBAD",
79 [barrierCmdHello
] = "Barrier",
80 [barrierCmdHelloBack
] = "Barrier",
83 static kbd_layout_t
*kbd_layout
;
85 static int input_barrier_to_qcode(uint16_t keyid
, uint16_t keycode
)
87 /* keycode is optional, if it is not provided use keyid */
88 if (keycode
&& keycode
<= qemu_input_map_xorgkbd_to_qcode_len
) {
89 return qemu_input_map_xorgkbd_to_qcode
[keycode
];
92 if (keyid
>= 0xE000 && keyid
<= 0xEFFF) {
96 /* keyid is the X11 key id */
98 keycode
= keysym2scancode(kbd_layout
, keyid
, NULL
, false);
100 return qemu_input_key_number_to_qcode(keycode
);
103 return qemu_input_map_x11_to_qcode
[keyid
];
106 static int input_barrier_to_mouse(uint8_t buttonid
)
109 case barrierButtonLeft
:
110 return INPUT_BUTTON_LEFT
;
111 case barrierButtonMiddle
:
112 return INPUT_BUTTON_MIDDLE
;
113 case barrierButtonRight
:
114 return INPUT_BUTTON_RIGHT
;
115 case barrierButtonExtra0
:
116 return INPUT_BUTTON_SIDE
;
121 #define read_char(x, p, l) \
123 int size = sizeof(char); \
125 return G_SOURCE_REMOVE; \
132 #define read_short(x, p, l) \
134 int size = sizeof(short); \
136 return G_SOURCE_REMOVE; \
138 x = ntohs(*(short *)p); \
143 #define write_short(p, x, l) \
145 int size = sizeof(short); \
147 return G_SOURCE_REMOVE; \
149 *(short *)p = htons(x); \
154 #define read_int(x, p, l) \
156 int size = sizeof(int); \
158 return G_SOURCE_REMOVE; \
160 x = ntohl(*(int *)p); \
165 #define write_int(p, x, l) \
167 int size = sizeof(int); \
169 return G_SOURCE_REMOVE; \
171 *(int *)p = htonl(x); \
176 #define write_cmd(p, c, l) \
178 int size = strlen(cmd_names[c]); \
180 return G_SOURCE_REMOVE; \
182 memcpy(p, cmd_names[c], size); \
187 #define write_string(p, s, l) \
189 int size = strlen(s); \
190 if (l < size + sizeof(int)) { \
191 return G_SOURCE_REMOVE; \
193 *(int *)p = htonl(size); \
196 memcpy(p, s, size); \
201 static gboolean
readcmd(InputBarrier
*ib
, struct barrierMsg
*msg
)
207 ret
= qio_channel_read(QIO_CHANNEL(ib
->sioc
), (char *)&len
, sizeof(len
),
210 return G_SOURCE_REMOVE
;
214 if (len
> MAX_HELLO_LENGTH
) {
215 return G_SOURCE_REMOVE
;
218 ret
= qio_channel_read(QIO_CHANNEL(ib
->sioc
), ib
->buffer
, len
, NULL
);
220 return G_SOURCE_REMOVE
;
224 if (len
>= strlen(cmd_names
[barrierCmdHello
]) &&
225 memcmp(p
, cmd_names
[barrierCmdHello
],
226 strlen(cmd_names
[barrierCmdHello
])) == 0) {
227 cmd
= barrierCmdHello
;
228 p
+= strlen(cmd_names
[barrierCmdHello
]);
229 len
-= strlen(cmd_names
[barrierCmdHello
]);
231 for (cmd
= 0; cmd
< barrierCmdHello
; cmd
++) {
232 if (memcmp(ib
->buffer
, cmd_names
[cmd
], 4) == 0) {
237 if (cmd
== barrierCmdHello
) {
238 return G_SOURCE_REMOVE
;
247 case barrierCmdHello
:
248 read_short(msg
->version
.major
, p
, len
);
249 read_short(msg
->version
.minor
, p
, len
);
251 case barrierCmdDSetOptions
:
252 read_int(msg
->set
.nb
, p
, len
);
254 if (msg
->set
.nb
> BARRIER_MAX_OPTIONS
) {
255 msg
->set
.nb
= BARRIER_MAX_OPTIONS
;
258 while (len
&& i
< msg
->set
.nb
) {
259 read_int(msg
->set
.option
[i
].id
, p
, len
);
260 /* it's a string, restore endianness */
261 msg
->set
.option
[i
].id
= htonl(msg
->set
.option
[i
].id
);
262 msg
->set
.option
[i
].nul
= 0;
263 read_int(msg
->set
.option
[i
].value
, p
, len
);
267 case barrierCmdQInfo
:
271 case barrierCmdDMouseMove
:
272 case barrierCmdDMouseRelMove
:
273 read_short(msg
->mousepos
.x
, p
, len
);
274 read_short(msg
->mousepos
.y
, p
, len
);
276 case barrierCmdDMouseDown
:
277 case barrierCmdDMouseUp
:
278 read_char(msg
->mousebutton
.buttonid
, p
, len
);
280 case barrierCmdDMouseWheel
:
281 read_short(msg
->mousepos
.y
, p
, len
);
284 msg
->mousepos
.x
= msg
->mousepos
.y
;
285 read_short(msg
->mousepos
.y
, p
, len
);
290 case barrierCmdDKeyDown
:
291 case barrierCmdDKeyUp
:
292 read_short(msg
->key
.keyid
, p
, len
);
293 read_short(msg
->key
.modifier
, p
, len
);
296 read_short(msg
->key
.button
, p
, len
);
299 case barrierCmdDKeyRepeat
:
300 read_short(msg
->repeat
.keyid
, p
, len
);
301 read_short(msg
->repeat
.modifier
, p
, len
);
302 read_short(msg
->repeat
.repeat
, p
, len
);
303 msg
->repeat
.button
= 0;
305 read_short(msg
->repeat
.button
, p
, len
);
308 case barrierCmdCInfoAck
:
309 case barrierCmdCResetOptions
:
310 case barrierCmdCEnter
:
311 case barrierCmdDClipboard
:
312 case barrierCmdCKeepAlive
:
313 case barrierCmdCLeave
:
314 case barrierCmdCClose
:
317 /* Invalid from the server */
318 case barrierCmdHelloBack
:
319 case barrierCmdCNoop
:
320 case barrierCmdDInfo
:
324 case barrierCmdEIncompatible
:
325 read_short(msg
->version
.major
, p
, len
);
326 read_short(msg
->version
.minor
, p
, len
);
328 case barrierCmdEBusy
:
329 case barrierCmdEUnknown
:
333 return G_SOURCE_REMOVE
;
336 return G_SOURCE_CONTINUE
;
339 static gboolean
writecmd(InputBarrier
*ib
, struct barrierMsg
*msg
)
346 avail
= MAX_HELLO_LENGTH
;
348 /* reserve space to store the length */
350 avail
-= sizeof(int);
353 case barrierCmdHello
:
354 if (msg
->version
.major
< BARRIER_VERSION_MAJOR
||
355 (msg
->version
.major
== BARRIER_VERSION_MAJOR
&&
356 msg
->version
.minor
< BARRIER_VERSION_MINOR
)) {
358 return G_SOURCE_REMOVE
;
360 write_cmd(p
, barrierCmdHelloBack
, avail
);
361 write_short(p
, BARRIER_VERSION_MAJOR
, avail
);
362 write_short(p
, BARRIER_VERSION_MINOR
, avail
);
363 write_string(p
, ib
->name
, avail
);
365 case barrierCmdCClose
:
367 return G_SOURCE_REMOVE
;
368 case barrierCmdQInfo
:
369 write_cmd(p
, barrierCmdDInfo
, avail
);
370 write_short(p
, ib
->x_origin
, avail
);
371 write_short(p
, ib
->y_origin
, avail
);
372 write_short(p
, ib
->width
, avail
);
373 write_short(p
, ib
->height
, avail
);
374 write_short(p
, 0, avail
); /* warpsize (obsolete) */
375 write_short(p
, 0, avail
); /* mouse x */
376 write_short(p
, 0, avail
); /* mouse y */
378 case barrierCmdCInfoAck
:
380 case barrierCmdCResetOptions
:
381 /* TODO: reset options */
383 case barrierCmdDSetOptions
:
384 /* TODO: set options */
386 case barrierCmdCEnter
:
388 case barrierCmdDClipboard
:
390 case barrierCmdCKeepAlive
:
391 write_cmd(p
, barrierCmdCKeepAlive
, avail
);
393 case barrierCmdCLeave
:
397 case barrierCmdDMouseMove
:
398 qemu_input_queue_abs(NULL
, INPUT_AXIS_X
, msg
->mousepos
.x
,
399 ib
->x_origin
, ib
->width
);
400 qemu_input_queue_abs(NULL
, INPUT_AXIS_Y
, msg
->mousepos
.y
,
401 ib
->y_origin
, ib
->height
);
402 qemu_input_event_sync();
404 case barrierCmdDMouseRelMove
:
405 qemu_input_queue_rel(NULL
, INPUT_AXIS_X
, msg
->mousepos
.x
);
406 qemu_input_queue_rel(NULL
, INPUT_AXIS_Y
, msg
->mousepos
.y
);
407 qemu_input_event_sync();
409 case barrierCmdDMouseDown
:
410 qemu_input_queue_btn(NULL
,
411 input_barrier_to_mouse(msg
->mousebutton
.buttonid
),
413 qemu_input_event_sync();
415 case barrierCmdDMouseUp
:
416 qemu_input_queue_btn(NULL
,
417 input_barrier_to_mouse(msg
->mousebutton
.buttonid
),
419 qemu_input_event_sync();
421 case barrierCmdDMouseWheel
:
422 qemu_input_queue_btn(NULL
, (msg
->mousepos
.y
> 0) ? INPUT_BUTTON_WHEEL_UP
423 : INPUT_BUTTON_WHEEL_DOWN
, true);
424 qemu_input_event_sync();
425 qemu_input_queue_btn(NULL
, (msg
->mousepos
.y
> 0) ? INPUT_BUTTON_WHEEL_UP
426 : INPUT_BUTTON_WHEEL_DOWN
, false);
427 qemu_input_event_sync();
431 case barrierCmdDKeyDown
:
432 qemu_input_event_send_key_qcode(NULL
,
433 input_barrier_to_qcode(msg
->key
.keyid
, msg
->key
.button
),
436 case barrierCmdDKeyRepeat
:
437 for (i
= 0; i
< msg
->repeat
.repeat
; i
++) {
438 qemu_input_event_send_key_qcode(NULL
,
439 input_barrier_to_qcode(msg
->repeat
.keyid
, msg
->repeat
.button
),
441 qemu_input_event_send_key_qcode(NULL
,
442 input_barrier_to_qcode(msg
->repeat
.keyid
, msg
->repeat
.button
),
446 case barrierCmdDKeyUp
:
447 qemu_input_event_send_key_qcode(NULL
,
448 input_barrier_to_qcode(msg
->key
.keyid
, msg
->key
.button
),
452 write_cmd(p
, barrierCmdEUnknown
, avail
);
456 len
= MAX_HELLO_LENGTH
- avail
- sizeof(int);
460 write_int(p
, len
, avail
);
461 ret
= qio_channel_write(QIO_CHANNEL(ib
->sioc
), ib
->buffer
,
462 len
+ sizeof(len
), NULL
);
465 return G_SOURCE_REMOVE
;
469 return G_SOURCE_CONTINUE
;
472 static gboolean
input_barrier_event(QIOChannel
*ioc G_GNUC_UNUSED
,
473 GIOCondition condition
, void *opaque
)
475 InputBarrier
*ib
= opaque
;
477 struct barrierMsg msg
;
479 ret
= readcmd(ib
, &msg
);
480 if (ret
== G_SOURCE_REMOVE
) {
482 return G_SOURCE_REMOVE
;
485 return writecmd(ib
, &msg
);
488 static void input_barrier_complete(UserCreatable
*uc
, Error
**errp
)
490 InputBarrier
*ib
= INPUT_BARRIER(uc
);
491 Error
*local_err
= NULL
;
494 error_setg(errp
, QERR_MISSING_PARAMETER
, "name");
499 * Connect to the primary
500 * Primary is the server where the keyboard and the mouse
501 * are connected and forwarded to the secondary (the client)
504 ib
->sioc
= qio_channel_socket_new();
505 qio_channel_set_name(QIO_CHANNEL(ib
->sioc
), "barrier-client");
507 qio_channel_socket_connect_sync(ib
->sioc
, &ib
->saddr
, &local_err
);
509 error_propagate(errp
, local_err
);
513 qio_channel_set_delay(QIO_CHANNEL(ib
->sioc
), false);
515 ib
->ioc_tag
= qio_channel_add_watch(QIO_CHANNEL(ib
->sioc
), G_IO_IN
,
516 input_barrier_event
, ib
, NULL
);
519 static void input_barrier_instance_finalize(Object
*obj
)
521 InputBarrier
*ib
= INPUT_BARRIER(obj
);
524 g_source_remove(ib
->ioc_tag
);
529 qio_channel_close(QIO_CHANNEL(ib
->sioc
), NULL
);
530 object_unref(OBJECT(ib
->sioc
));
533 g_free(ib
->saddr
.u
.inet
.host
);
534 g_free(ib
->saddr
.u
.inet
.port
);
537 static char *input_barrier_get_name(Object
*obj
, Error
**errp
)
539 InputBarrier
*ib
= INPUT_BARRIER(obj
);
541 return g_strdup(ib
->name
);
544 static void input_barrier_set_name(Object
*obj
, const char *value
,
547 InputBarrier
*ib
= INPUT_BARRIER(obj
);
550 error_setg(errp
, "name property already set");
553 ib
->name
= g_strdup(value
);
556 static char *input_barrier_get_server(Object
*obj
, Error
**errp
)
558 InputBarrier
*ib
= INPUT_BARRIER(obj
);
560 return g_strdup(ib
->saddr
.u
.inet
.host
);
563 static void input_barrier_set_server(Object
*obj
, const char *value
,
566 InputBarrier
*ib
= INPUT_BARRIER(obj
);
568 g_free(ib
->saddr
.u
.inet
.host
);
569 ib
->saddr
.u
.inet
.host
= g_strdup(value
);
572 static char *input_barrier_get_port(Object
*obj
, Error
**errp
)
574 InputBarrier
*ib
= INPUT_BARRIER(obj
);
576 return g_strdup(ib
->saddr
.u
.inet
.port
);
579 static void input_barrier_set_port(Object
*obj
, const char *value
,
582 InputBarrier
*ib
= INPUT_BARRIER(obj
);
584 g_free(ib
->saddr
.u
.inet
.port
);
585 ib
->saddr
.u
.inet
.port
= g_strdup(value
);
588 static void input_barrier_set_x_origin(Object
*obj
, const char *value
,
591 InputBarrier
*ib
= INPUT_BARRIER(obj
);
594 err
= qemu_strtoi(value
, NULL
, 0, &result
);
595 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
597 "x-origin property must be in the range [0..%d]", SHRT_MAX
);
600 ib
->x_origin
= result
;
603 static char *input_barrier_get_x_origin(Object
*obj
, Error
**errp
)
605 InputBarrier
*ib
= INPUT_BARRIER(obj
);
607 return g_strdup_printf("%d", ib
->x_origin
);
610 static void input_barrier_set_y_origin(Object
*obj
, const char *value
,
613 InputBarrier
*ib
= INPUT_BARRIER(obj
);
616 err
= qemu_strtoi(value
, NULL
, 0, &result
);
617 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
619 "y-origin property must be in the range [0..%d]", SHRT_MAX
);
622 ib
->y_origin
= result
;
625 static char *input_barrier_get_y_origin(Object
*obj
, Error
**errp
)
627 InputBarrier
*ib
= INPUT_BARRIER(obj
);
629 return g_strdup_printf("%d", ib
->y_origin
);
632 static void input_barrier_set_width(Object
*obj
, const char *value
,
635 InputBarrier
*ib
= INPUT_BARRIER(obj
);
638 err
= qemu_strtoi(value
, NULL
, 0, &result
);
639 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
641 "width property must be in the range [0..%d]", SHRT_MAX
);
647 static char *input_barrier_get_width(Object
*obj
, Error
**errp
)
649 InputBarrier
*ib
= INPUT_BARRIER(obj
);
651 return g_strdup_printf("%d", ib
->width
);
654 static void input_barrier_set_height(Object
*obj
, const char *value
,
657 InputBarrier
*ib
= INPUT_BARRIER(obj
);
660 err
= qemu_strtoi(value
, NULL
, 0, &result
);
661 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
663 "height property must be in the range [0..%d]", SHRT_MAX
);
669 static char *input_barrier_get_height(Object
*obj
, Error
**errp
)
671 InputBarrier
*ib
= INPUT_BARRIER(obj
);
673 return g_strdup_printf("%d", ib
->height
);
676 static void input_barrier_instance_init(Object
*obj
)
678 InputBarrier
*ib
= INPUT_BARRIER(obj
);
680 /* always use generic keymaps */
681 if (keyboard_layout
&& !kbd_layout
) {
682 /* We use X11 key id, so use VNC name2keysym */
683 kbd_layout
= init_keyboard_layout(name2keysym
, keyboard_layout
,
687 ib
->saddr
.type
= SOCKET_ADDRESS_TYPE_INET
;
688 ib
->saddr
.u
.inet
.host
= g_strdup("localhost");
689 ib
->saddr
.u
.inet
.port
= g_strdup("24800");
696 object_property_add_str(obj
, "name",
697 input_barrier_get_name
,
698 input_barrier_set_name
);
699 object_property_add_str(obj
, "server",
700 input_barrier_get_server
,
701 input_barrier_set_server
);
702 object_property_add_str(obj
, "port",
703 input_barrier_get_port
,
704 input_barrier_set_port
);
705 object_property_add_str(obj
, "x-origin",
706 input_barrier_get_x_origin
,
707 input_barrier_set_x_origin
);
708 object_property_add_str(obj
, "y-origin",
709 input_barrier_get_y_origin
,
710 input_barrier_set_y_origin
);
711 object_property_add_str(obj
, "width",
712 input_barrier_get_width
,
713 input_barrier_set_width
);
714 object_property_add_str(obj
, "height",
715 input_barrier_get_height
,
716 input_barrier_set_height
);
719 static void input_barrier_class_init(ObjectClass
*oc
, void *data
)
721 UserCreatableClass
*ucc
= USER_CREATABLE_CLASS(oc
);
723 ucc
->complete
= input_barrier_complete
;
726 static const TypeInfo input_barrier_info
= {
727 .name
= TYPE_INPUT_BARRIER
,
728 .parent
= TYPE_OBJECT
,
729 .class_size
= sizeof(InputBarrierClass
),
730 .class_init
= input_barrier_class_init
,
731 .instance_size
= sizeof(InputBarrier
),
732 .instance_init
= input_barrier_instance_init
,
733 .instance_finalize
= input_barrier_instance_finalize
,
734 .interfaces
= (InterfaceInfo
[]) {
735 { TYPE_USER_CREATABLE
},
740 static void register_types(void)
742 type_register_static(&input_barrier_info
);
745 type_init(register_types
);