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 "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
37 QIOChannelSocket
*sioc
;
40 /* display properties */
42 int16_t x_origin
, y_origin
;
43 int16_t width
, height
;
45 /* keyboard/mouse server */
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) {
101 /* keyid is the X11 key id */
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
)
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
;
126 #define read_char(x, p, l) \
128 int size = sizeof(char); \
130 return G_SOURCE_REMOVE; \
137 #define read_short(x, p, l) \
139 int size = sizeof(short); \
141 return G_SOURCE_REMOVE; \
143 x = ntohs(*(short *)p); \
148 #define write_short(p, x, l) \
150 int size = sizeof(short); \
152 return G_SOURCE_REMOVE; \
154 *(short *)p = htons(x); \
159 #define read_int(x, p, l) \
161 int size = sizeof(int); \
163 return G_SOURCE_REMOVE; \
165 x = ntohl(*(int *)p); \
170 #define write_int(p, x, l) \
172 int size = sizeof(int); \
174 return G_SOURCE_REMOVE; \
176 *(int *)p = htonl(x); \
181 #define write_cmd(p, c, l) \
183 int size = strlen(cmd_names[c]); \
185 return G_SOURCE_REMOVE; \
187 memcpy(p, cmd_names[c], size); \
192 #define write_string(p, s, l) \
194 int size = strlen(s); \
195 if (l < size + sizeof(int)) { \
196 return G_SOURCE_REMOVE; \
198 *(int *)p = htonl(size); \
201 memcpy(p, s, size); \
206 static gboolean
readcmd(InputBarrier
*ib
, struct barrierMsg
*msg
)
212 ret
= qio_channel_read(QIO_CHANNEL(ib
->sioc
), (char *)&len
, sizeof(len
),
215 return G_SOURCE_REMOVE
;
219 if (len
> MAX_HELLO_LENGTH
) {
220 return G_SOURCE_REMOVE
;
223 ret
= qio_channel_read(QIO_CHANNEL(ib
->sioc
), ib
->buffer
, len
, NULL
);
225 return G_SOURCE_REMOVE
;
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
]);
236 for (cmd
= 0; cmd
< barrierCmdHello
; cmd
++) {
237 if (memcmp(ib
->buffer
, cmd_names
[cmd
], 4) == 0) {
242 if (cmd
== barrierCmdHello
) {
243 return G_SOURCE_REMOVE
;
252 case barrierCmdHello
:
253 read_short(msg
->version
.major
, p
, len
);
254 read_short(msg
->version
.minor
, p
, len
);
256 case barrierCmdDSetOptions
:
257 read_int(msg
->set
.nb
, p
, len
);
259 if (msg
->set
.nb
> BARRIER_MAX_OPTIONS
) {
260 msg
->set
.nb
= BARRIER_MAX_OPTIONS
;
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
);
272 case barrierCmdQInfo
:
276 case barrierCmdDMouseMove
:
277 case barrierCmdDMouseRelMove
:
278 read_short(msg
->mousepos
.x
, p
, len
);
279 read_short(msg
->mousepos
.y
, p
, len
);
281 case barrierCmdDMouseDown
:
282 case barrierCmdDMouseUp
:
283 read_char(msg
->mousebutton
.buttonid
, p
, len
);
285 case barrierCmdDMouseWheel
:
286 read_short(msg
->mousepos
.y
, p
, len
);
289 msg
->mousepos
.x
= msg
->mousepos
.y
;
290 read_short(msg
->mousepos
.y
, p
, len
);
295 case barrierCmdDKeyDown
:
296 case barrierCmdDKeyUp
:
297 read_short(msg
->key
.keyid
, p
, len
);
298 read_short(msg
->key
.modifier
, p
, len
);
301 read_short(msg
->key
.button
, p
, len
);
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;
310 read_short(msg
->repeat
.button
, p
, len
);
313 case barrierCmdCInfoAck
:
314 case barrierCmdCResetOptions
:
315 case barrierCmdCEnter
:
316 case barrierCmdDClipboard
:
317 case barrierCmdCKeepAlive
:
318 case barrierCmdCLeave
:
319 case barrierCmdCClose
:
322 /* Invalid from the server */
323 case barrierCmdHelloBack
:
324 case barrierCmdCNoop
:
325 case barrierCmdDInfo
:
329 case barrierCmdEIncompatible
:
330 read_short(msg
->version
.major
, p
, len
);
331 read_short(msg
->version
.minor
, p
, len
);
333 case barrierCmdEBusy
:
334 case barrierCmdEUnknown
:
338 return G_SOURCE_REMOVE
;
341 return G_SOURCE_CONTINUE
;
344 static gboolean
writecmd(InputBarrier
*ib
, struct barrierMsg
*msg
)
351 avail
= MAX_HELLO_LENGTH
;
353 /* reserve space to store the length */
355 avail
-= sizeof(int);
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
)) {
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
);
370 case barrierCmdCClose
:
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 */
383 case barrierCmdCInfoAck
:
385 case barrierCmdCResetOptions
:
386 /* TODO: reset options */
388 case barrierCmdDSetOptions
:
389 /* TODO: set options */
391 case barrierCmdCEnter
:
393 case barrierCmdDClipboard
:
395 case barrierCmdCKeepAlive
:
396 write_cmd(p
, barrierCmdCKeepAlive
, avail
);
398 case barrierCmdCLeave
:
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();
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();
414 case barrierCmdDMouseDown
:
415 qemu_input_queue_btn(NULL
,
416 input_barrier_to_mouse(msg
->mousebutton
.buttonid
),
418 qemu_input_event_sync();
420 case barrierCmdDMouseUp
:
421 qemu_input_queue_btn(NULL
,
422 input_barrier_to_mouse(msg
->mousebutton
.buttonid
),
424 qemu_input_event_sync();
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();
436 case barrierCmdDKeyDown
:
437 qemu_input_event_send_key_qcode(NULL
,
438 input_barrier_to_qcode(msg
->key
.keyid
, msg
->key
.button
),
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
),
446 qemu_input_event_send_key_qcode(NULL
,
447 input_barrier_to_qcode(msg
->repeat
.keyid
, msg
->repeat
.button
),
451 case barrierCmdDKeyUp
:
452 qemu_input_event_send_key_qcode(NULL
,
453 input_barrier_to_qcode(msg
->key
.keyid
, msg
->key
.button
),
457 write_cmd(p
, barrierCmdEUnknown
, avail
);
461 len
= MAX_HELLO_LENGTH
- avail
- sizeof(int);
465 write_int(p
, len
, avail
);
466 ret
= qio_channel_write(QIO_CHANNEL(ib
->sioc
), ib
->buffer
,
467 len
+ sizeof(len
), NULL
);
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
;
482 struct barrierMsg msg
;
484 ret
= readcmd(ib
, &msg
);
485 if (ret
== G_SOURCE_REMOVE
) {
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
;
499 error_setg(errp
, QERR_MISSING_PARAMETER
, "name");
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
);
514 error_propagate(errp
, local_err
);
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
);
529 g_source_remove(ib
->ioc_tag
);
534 qio_channel_close(QIO_CHANNEL(ib
->sioc
), NULL
);
535 object_unref(OBJECT(ib
->sioc
));
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
,
552 InputBarrier
*ib
= INPUT_BARRIER(obj
);
555 error_setg(errp
, "name property already set");
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
,
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
,
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
,
596 InputBarrier
*ib
= INPUT_BARRIER(obj
);
599 err
= qemu_strtoi(value
, NULL
, 0, &result
);
600 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
602 "x-origin property must be in the range [0..%d]", SHRT_MAX
);
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
,
618 InputBarrier
*ib
= INPUT_BARRIER(obj
);
621 err
= qemu_strtoi(value
, NULL
, 0, &result
);
622 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
624 "y-origin property must be in the range [0..%d]", SHRT_MAX
);
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
,
640 InputBarrier
*ib
= INPUT_BARRIER(obj
);
643 err
= qemu_strtoi(value
, NULL
, 0, &result
);
644 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
646 "width property must be in the range [0..%d]", SHRT_MAX
);
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
,
662 InputBarrier
*ib
= INPUT_BARRIER(obj
);
665 err
= qemu_strtoi(value
, NULL
, 0, &result
);
666 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
668 "height property must be in the range [0..%d]", SHRT_MAX
);
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
,
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");
701 object_property_add_str(obj
, "name",
702 input_barrier_get_name
,
703 input_barrier_set_name
);
704 object_property_add_str(obj
, "server",
705 input_barrier_get_server
,
706 input_barrier_set_server
);
707 object_property_add_str(obj
, "port",
708 input_barrier_get_port
,
709 input_barrier_set_port
);
710 object_property_add_str(obj
, "x-origin",
711 input_barrier_get_x_origin
,
712 input_barrier_set_x_origin
);
713 object_property_add_str(obj
, "y-origin",
714 input_barrier_get_y_origin
,
715 input_barrier_set_y_origin
);
716 object_property_add_str(obj
, "width",
717 input_barrier_get_width
,
718 input_barrier_set_width
);
719 object_property_add_str(obj
, "height",
720 input_barrier_get_height
,
721 input_barrier_set_height
);
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
);