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.
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"
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
,
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
];
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) {
98 /* keyid is the X11 key id */
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
)
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
;
123 #define read_char(x, p, l) \
125 int size = sizeof(char); \
127 return G_SOURCE_REMOVE; \
134 #define read_short(x, p, l) \
136 int size = sizeof(short); \
138 return G_SOURCE_REMOVE; \
140 x = ntohs(*(short *)p); \
145 #define write_short(p, x, l) \
147 int size = sizeof(short); \
149 return G_SOURCE_REMOVE; \
151 *(short *)p = htons(x); \
156 #define read_int(x, p, l) \
158 int size = sizeof(int); \
160 return G_SOURCE_REMOVE; \
162 x = ntohl(*(int *)p); \
167 #define write_int(p, x, l) \
169 int size = sizeof(int); \
171 return G_SOURCE_REMOVE; \
173 *(int *)p = htonl(x); \
178 #define write_cmd(p, c, l) \
180 int size = strlen(cmd_names[c]); \
182 return G_SOURCE_REMOVE; \
184 memcpy(p, cmd_names[c], size); \
189 #define write_string(p, s, l) \
191 int size = strlen(s); \
192 if (l < size + sizeof(int)) { \
193 return G_SOURCE_REMOVE; \
195 *(int *)p = htonl(size); \
198 memcpy(p, s, size); \
203 static gboolean
readcmd(InputBarrier
*ib
, struct barrierMsg
*msg
)
209 ret
= qio_channel_read(QIO_CHANNEL(ib
->sioc
), (char *)&len
, sizeof(len
),
212 return G_SOURCE_REMOVE
;
216 if (len
> MAX_HELLO_LENGTH
) {
217 return G_SOURCE_REMOVE
;
220 ret
= qio_channel_read(QIO_CHANNEL(ib
->sioc
), ib
->buffer
, len
, NULL
);
222 return G_SOURCE_REMOVE
;
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
]);
233 for (cmd
= 0; cmd
< barrierCmdHello
; cmd
++) {
234 if (memcmp(ib
->buffer
, cmd_names
[cmd
], 4) == 0) {
239 if (cmd
== barrierCmdHello
) {
240 return G_SOURCE_REMOVE
;
249 case barrierCmdHello
:
250 read_short(msg
->version
.major
, p
, len
);
251 read_short(msg
->version
.minor
, p
, len
);
253 case barrierCmdDSetOptions
:
254 read_int(msg
->set
.nb
, p
, len
);
256 if (msg
->set
.nb
> BARRIER_MAX_OPTIONS
) {
257 msg
->set
.nb
= BARRIER_MAX_OPTIONS
;
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
);
269 case barrierCmdQInfo
:
273 case barrierCmdDMouseMove
:
274 case barrierCmdDMouseRelMove
:
275 read_short(msg
->mousepos
.x
, p
, len
);
276 read_short(msg
->mousepos
.y
, p
, len
);
278 case barrierCmdDMouseDown
:
279 case barrierCmdDMouseUp
:
280 read_char(msg
->mousebutton
.buttonid
, p
, len
);
282 case barrierCmdDMouseWheel
:
283 read_short(msg
->mousepos
.y
, p
, len
);
286 msg
->mousepos
.x
= msg
->mousepos
.y
;
287 read_short(msg
->mousepos
.y
, p
, len
);
292 case barrierCmdDKeyDown
:
293 case barrierCmdDKeyUp
:
294 read_short(msg
->key
.keyid
, p
, len
);
295 read_short(msg
->key
.modifier
, p
, len
);
298 read_short(msg
->key
.button
, p
, len
);
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;
307 read_short(msg
->repeat
.button
, p
, len
);
310 case barrierCmdCInfoAck
:
311 case barrierCmdCResetOptions
:
312 case barrierCmdCEnter
:
313 case barrierCmdDClipboard
:
314 case barrierCmdCKeepAlive
:
315 case barrierCmdCLeave
:
316 case barrierCmdCClose
:
319 /* Invalid from the server */
320 case barrierCmdHelloBack
:
321 case barrierCmdCNoop
:
322 case barrierCmdDInfo
:
326 case barrierCmdEIncompatible
:
327 read_short(msg
->version
.major
, p
, len
);
328 read_short(msg
->version
.minor
, p
, len
);
330 case barrierCmdEBusy
:
331 case barrierCmdEUnknown
:
335 return G_SOURCE_REMOVE
;
338 return G_SOURCE_CONTINUE
;
341 static gboolean
writecmd(InputBarrier
*ib
, struct barrierMsg
*msg
)
348 avail
= MAX_HELLO_LENGTH
;
350 /* reserve space to store the length */
352 avail
-= sizeof(int);
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
)) {
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
);
367 case barrierCmdCClose
:
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 */
380 case barrierCmdCInfoAck
:
382 case barrierCmdCResetOptions
:
383 /* TODO: reset options */
385 case barrierCmdDSetOptions
:
386 /* TODO: set options */
388 case barrierCmdCEnter
:
390 case barrierCmdDClipboard
:
392 case barrierCmdCKeepAlive
:
393 write_cmd(p
, barrierCmdCKeepAlive
, avail
);
395 case barrierCmdCLeave
:
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();
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();
411 case barrierCmdDMouseDown
:
412 qemu_input_queue_btn(NULL
,
413 input_barrier_to_mouse(msg
->mousebutton
.buttonid
),
415 qemu_input_event_sync();
417 case barrierCmdDMouseUp
:
418 qemu_input_queue_btn(NULL
,
419 input_barrier_to_mouse(msg
->mousebutton
.buttonid
),
421 qemu_input_event_sync();
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();
433 case barrierCmdDKeyDown
:
434 qemu_input_event_send_key_qcode(NULL
,
435 input_barrier_to_qcode(msg
->key
.keyid
, msg
->key
.button
),
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
),
443 qemu_input_event_send_key_qcode(NULL
,
444 input_barrier_to_qcode(msg
->repeat
.keyid
, msg
->repeat
.button
),
448 case barrierCmdDKeyUp
:
449 qemu_input_event_send_key_qcode(NULL
,
450 input_barrier_to_qcode(msg
->key
.keyid
, msg
->key
.button
),
454 write_cmd(p
, barrierCmdEUnknown
, avail
);
458 len
= MAX_HELLO_LENGTH
- avail
- sizeof(int);
462 write_int(p
, len
, avail
);
463 ret
= qio_channel_write(QIO_CHANNEL(ib
->sioc
), ib
->buffer
,
464 len
+ sizeof(len
), NULL
);
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
;
479 struct barrierMsg msg
;
481 ret
= readcmd(ib
, &msg
);
482 if (ret
== G_SOURCE_REMOVE
) {
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
;
496 error_setg(errp
, QERR_MISSING_PARAMETER
, "name");
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
);
511 error_propagate(errp
, local_err
);
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
);
526 g_source_remove(ib
->ioc_tag
);
531 qio_channel_close(QIO_CHANNEL(ib
->sioc
), NULL
);
532 object_unref(OBJECT(ib
->sioc
));
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
,
549 InputBarrier
*ib
= INPUT_BARRIER(obj
);
552 error_setg(errp
, "name property already set");
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
,
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
,
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
,
593 InputBarrier
*ib
= INPUT_BARRIER(obj
);
596 err
= qemu_strtoi(value
, NULL
, 0, &result
);
597 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
599 "x-origin property must be in the range [0..%d]", SHRT_MAX
);
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
,
615 InputBarrier
*ib
= INPUT_BARRIER(obj
);
618 err
= qemu_strtoi(value
, NULL
, 0, &result
);
619 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
621 "y-origin property must be in the range [0..%d]", SHRT_MAX
);
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
,
637 InputBarrier
*ib
= INPUT_BARRIER(obj
);
640 err
= qemu_strtoi(value
, NULL
, 0, &result
);
641 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
643 "width property must be in the range [0..%d]", SHRT_MAX
);
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
,
659 InputBarrier
*ib
= INPUT_BARRIER(obj
);
662 err
= qemu_strtoi(value
, NULL
, 0, &result
);
663 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
665 "height property must be in the range [0..%d]", SHRT_MAX
);
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
,
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");
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
);