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_SIMPLE_TYPE(InputBarrier
,
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
];
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) {
93 /* keyid is the X11 key id */
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
)
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
;
118 #define read_char(x, p, l) \
120 int size = sizeof(char); \
122 return G_SOURCE_REMOVE; \
129 #define read_short(x, p, l) \
131 int size = sizeof(short); \
133 return G_SOURCE_REMOVE; \
135 x = ntohs(*(short *)p); \
140 #define write_short(p, x, l) \
142 int size = sizeof(short); \
144 return G_SOURCE_REMOVE; \
146 *(short *)p = htons(x); \
151 #define read_int(x, p, l) \
153 int size = sizeof(int); \
155 return G_SOURCE_REMOVE; \
157 x = ntohl(*(int *)p); \
162 #define write_int(p, x, l) \
164 int size = sizeof(int); \
166 return G_SOURCE_REMOVE; \
168 *(int *)p = htonl(x); \
173 #define write_cmd(p, c, l) \
175 int size = strlen(cmd_names[c]); \
177 return G_SOURCE_REMOVE; \
179 memcpy(p, cmd_names[c], size); \
184 #define write_string(p, s, l) \
186 int size = strlen(s); \
187 if (l < size + sizeof(int)) { \
188 return G_SOURCE_REMOVE; \
190 *(int *)p = htonl(size); \
193 memcpy(p, s, size); \
198 static gboolean
readcmd(InputBarrier
*ib
, struct barrierMsg
*msg
)
204 ret
= qio_channel_read(QIO_CHANNEL(ib
->sioc
), (char *)&len
, sizeof(len
),
207 return G_SOURCE_REMOVE
;
211 if (len
> MAX_HELLO_LENGTH
) {
212 return G_SOURCE_REMOVE
;
215 ret
= qio_channel_read(QIO_CHANNEL(ib
->sioc
), ib
->buffer
, len
, NULL
);
217 return G_SOURCE_REMOVE
;
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
]);
228 for (cmd
= 0; cmd
< barrierCmdHello
; cmd
++) {
229 if (memcmp(ib
->buffer
, cmd_names
[cmd
], 4) == 0) {
234 if (cmd
== barrierCmdHello
) {
235 return G_SOURCE_REMOVE
;
244 case barrierCmdHello
:
245 read_short(msg
->version
.major
, p
, len
);
246 read_short(msg
->version
.minor
, p
, len
);
248 case barrierCmdDSetOptions
:
249 read_int(msg
->set
.nb
, p
, len
);
251 if (msg
->set
.nb
> BARRIER_MAX_OPTIONS
) {
252 msg
->set
.nb
= BARRIER_MAX_OPTIONS
;
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
);
264 case barrierCmdQInfo
:
268 case barrierCmdDMouseMove
:
269 case barrierCmdDMouseRelMove
:
270 read_short(msg
->mousepos
.x
, p
, len
);
271 read_short(msg
->mousepos
.y
, p
, len
);
273 case barrierCmdDMouseDown
:
274 case barrierCmdDMouseUp
:
275 read_char(msg
->mousebutton
.buttonid
, p
, len
);
277 case barrierCmdDMouseWheel
:
278 read_short(msg
->mousepos
.y
, p
, len
);
281 msg
->mousepos
.x
= msg
->mousepos
.y
;
282 read_short(msg
->mousepos
.y
, p
, len
);
287 case barrierCmdDKeyDown
:
288 case barrierCmdDKeyUp
:
289 read_short(msg
->key
.keyid
, p
, len
);
290 read_short(msg
->key
.modifier
, p
, len
);
293 read_short(msg
->key
.button
, p
, len
);
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;
302 read_short(msg
->repeat
.button
, p
, len
);
305 case barrierCmdCInfoAck
:
306 case barrierCmdCResetOptions
:
307 case barrierCmdCEnter
:
308 case barrierCmdDClipboard
:
309 case barrierCmdCKeepAlive
:
310 case barrierCmdCLeave
:
311 case barrierCmdCClose
:
314 /* Invalid from the server */
315 case barrierCmdHelloBack
:
316 case barrierCmdCNoop
:
317 case barrierCmdDInfo
:
321 case barrierCmdEIncompatible
:
322 read_short(msg
->version
.major
, p
, len
);
323 read_short(msg
->version
.minor
, p
, len
);
325 case barrierCmdEBusy
:
326 case barrierCmdEUnknown
:
330 return G_SOURCE_REMOVE
;
333 return G_SOURCE_CONTINUE
;
336 static gboolean
writecmd(InputBarrier
*ib
, struct barrierMsg
*msg
)
343 avail
= MAX_HELLO_LENGTH
;
345 /* reserve space to store the length */
347 avail
-= sizeof(int);
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
)) {
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
);
362 case barrierCmdCClose
:
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 */
375 case barrierCmdCInfoAck
:
377 case barrierCmdCResetOptions
:
378 /* TODO: reset options */
380 case barrierCmdDSetOptions
:
381 /* TODO: set options */
383 case barrierCmdCEnter
:
385 case barrierCmdDClipboard
:
387 case barrierCmdCKeepAlive
:
388 write_cmd(p
, barrierCmdCKeepAlive
, avail
);
390 case barrierCmdCLeave
:
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();
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();
406 case barrierCmdDMouseDown
:
407 qemu_input_queue_btn(NULL
,
408 input_barrier_to_mouse(msg
->mousebutton
.buttonid
),
410 qemu_input_event_sync();
412 case barrierCmdDMouseUp
:
413 qemu_input_queue_btn(NULL
,
414 input_barrier_to_mouse(msg
->mousebutton
.buttonid
),
416 qemu_input_event_sync();
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();
428 case barrierCmdDKeyDown
:
429 qemu_input_event_send_key_qcode(NULL
,
430 input_barrier_to_qcode(msg
->key
.keyid
, msg
->key
.button
),
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
),
438 qemu_input_event_send_key_qcode(NULL
,
439 input_barrier_to_qcode(msg
->repeat
.keyid
, msg
->repeat
.button
),
443 case barrierCmdDKeyUp
:
444 qemu_input_event_send_key_qcode(NULL
,
445 input_barrier_to_qcode(msg
->key
.keyid
, msg
->key
.button
),
449 write_cmd(p
, barrierCmdEUnknown
, avail
);
453 len
= MAX_HELLO_LENGTH
- avail
- sizeof(int);
457 write_int(p
, len
, avail
);
458 ret
= qio_channel_write(QIO_CHANNEL(ib
->sioc
), ib
->buffer
,
459 len
+ sizeof(len
), NULL
);
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
;
474 struct barrierMsg msg
;
476 ret
= readcmd(ib
, &msg
);
477 if (ret
== G_SOURCE_REMOVE
) {
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
;
491 error_setg(errp
, QERR_MISSING_PARAMETER
, "name");
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
);
506 error_propagate(errp
, local_err
);
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
);
521 g_source_remove(ib
->ioc_tag
);
526 qio_channel_close(QIO_CHANNEL(ib
->sioc
), NULL
);
527 object_unref(OBJECT(ib
->sioc
));
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
,
544 InputBarrier
*ib
= INPUT_BARRIER(obj
);
547 error_setg(errp
, "name property already set");
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
,
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
,
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
,
588 InputBarrier
*ib
= INPUT_BARRIER(obj
);
591 err
= qemu_strtoi(value
, NULL
, 0, &result
);
592 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
594 "x-origin property must be in the range [0..%d]", SHRT_MAX
);
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
,
610 InputBarrier
*ib
= INPUT_BARRIER(obj
);
613 err
= qemu_strtoi(value
, NULL
, 0, &result
);
614 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
616 "y-origin property must be in the range [0..%d]", SHRT_MAX
);
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
,
632 InputBarrier
*ib
= INPUT_BARRIER(obj
);
635 err
= qemu_strtoi(value
, NULL
, 0, &result
);
636 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
638 "width property must be in the range [0..%d]", SHRT_MAX
);
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
,
654 InputBarrier
*ib
= INPUT_BARRIER(obj
);
657 err
= qemu_strtoi(value
, NULL
, 0, &result
);
658 if (err
< 0 || result
< 0 || result
> SHRT_MAX
) {
660 "height property must be in the range [0..%d]", SHRT_MAX
);
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
,
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");
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
);