1
2
3
4
5
6
7
8
9
10
11
12
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"
20#include "ui/input.h"
21#include "qom/object.h"
22#include "ui/vnc_keysym.h"
23#include "qemu/cutils.h"
24#include "qapi/qmp/qerror.h"
25#include "input-barrier.h"
26
27#define TYPE_INPUT_BARRIER "input-barrier"
28OBJECT_DECLARE_SIMPLE_TYPE(InputBarrier,
29 INPUT_BARRIER)
30
31
32#define MAX_HELLO_LENGTH 1024
33
34struct InputBarrier {
35 Object parent;
36
37 QIOChannelSocket *sioc;
38 guint ioc_tag;
39
40
41 gchar *name;
42 int16_t x_origin, y_origin;
43 int16_t width, height;
44
45
46
47 SocketAddress saddr;
48
49 char buffer[MAX_HELLO_LENGTH];
50};
51
52
53static 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",
83};
84
85static kbd_layout_t *kbd_layout;
86
87static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode)
88{
89
90 if (keycode && keycode <= qemu_input_map_xorgkbd_to_qcode_len) {
91 return qemu_input_map_xorgkbd_to_qcode[keycode];
92 }
93
94 if (keyid >= 0xE000 && keyid <= 0xEFFF) {
95 keyid += 0x1000;
96 }
97
98
99 if (kbd_layout) {
100 keycode = keysym2scancode(kbd_layout, keyid, NULL, false);
101
102 return qemu_input_key_number_to_qcode(keycode);
103 }
104
105 return qemu_input_map_x11_to_qcode[keyid];
106}
107
108static int input_barrier_to_mouse(uint8_t buttonid)
109{
110 switch (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;
119 }
120 return buttonid;
121}
122
123#define read_char(x, p, l) \
124do { \
125 int size = sizeof(char); \
126 if (l < size) { \
127 return G_SOURCE_REMOVE; \
128 } \
129 x = *(char *)p; \
130 p += size; \
131 l -= size; \
132} while (0)
133
134#define read_short(x, p, l) \
135do { \
136 int size = sizeof(short); \
137 if (l < size) { \
138 return G_SOURCE_REMOVE; \
139 } \
140 x = ntohs(*(short *)p); \
141 p += size; \
142 l -= size; \
143} while (0)
144
145#define write_short(p, x, l) \
146do { \
147 int size = sizeof(short); \
148 if (l < size) { \
149 return G_SOURCE_REMOVE; \
150 } \
151 *(short *)p = htons(x); \
152 p += size; \
153 l -= size; \
154} while (0)
155
156#define read_int(x, p, l) \
157do { \
158 int size = sizeof(int); \
159 if (l < size) { \
160 return G_SOURCE_REMOVE; \
161 } \
162 x = ntohl(*(int *)p); \
163 p += size; \
164 l -= size; \
165} while (0)
166
167#define write_int(p, x, l) \
168do { \
169 int size = sizeof(int); \
170 if (l < size) { \
171 return G_SOURCE_REMOVE; \
172 } \
173 *(int *)p = htonl(x); \
174 p += size; \
175 l -= size; \
176} while (0)
177
178#define write_cmd(p, c, l) \
179do { \
180 int size = strlen(cmd_names[c]); \
181 if (l < size) { \
182 return G_SOURCE_REMOVE; \
183 } \
184 memcpy(p, cmd_names[c], size); \
185 p += size; \
186 l -= size; \
187} while (0)
188
189#define write_string(p, s, l) \
190do { \
191 int size = strlen(s); \
192 if (l < size + sizeof(int)) { \
193 return G_SOURCE_REMOVE; \
194 } \
195 *(int *)p = htonl(size); \
196 p += sizeof(size); \
197 l -= sizeof(size); \
198 memcpy(p, s, size); \
199 p += size; \
200 l -= size; \
201} while (0)
202
203static gboolean readcmd(InputBarrier *ib, struct barrierMsg *msg)
204{
205 int ret, len, i;
206 enum barrierCmd cmd;
207 char *p;
208
209 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), (char *)&len, sizeof(len),
210 NULL);
211 if (ret < 0) {
212 return G_SOURCE_REMOVE;
213 }
214
215 len = ntohl(len);
216 if (len > MAX_HELLO_LENGTH) {
217 return G_SOURCE_REMOVE;
218 }
219
220 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), ib->buffer, len, NULL);
221 if (ret < 0) {
222 return G_SOURCE_REMOVE;
223 }
224
225 p = ib->buffer;
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]);
232 } else {
233 for (cmd = 0; cmd < barrierCmdHello; cmd++) {
234 if (memcmp(ib->buffer, cmd_names[cmd], 4) == 0) {
235 break;
236 }
237 }
238
239 if (cmd == barrierCmdHello) {
240 return G_SOURCE_REMOVE;
241 }
242 p += 4;
243 len -= 4;
244 }
245
246 msg->cmd = cmd;
247 switch (cmd) {
248
249 case barrierCmdHello:
250 read_short(msg->version.major, p, len);
251 read_short(msg->version.minor, p, len);
252 break;
253 case barrierCmdDSetOptions:
254 read_int(msg->set.nb, p, len);
255 msg->set.nb /= 2;
256 if (msg->set.nb > BARRIER_MAX_OPTIONS) {
257 msg->set.nb = BARRIER_MAX_OPTIONS;
258 }
259 i = 0;
260 while (len && i < msg->set.nb) {
261 read_int(msg->set.option[i].id, p, len);
262
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);
266 i++;
267 }
268 break;
269 case barrierCmdQInfo:
270 break;
271
272
273 case barrierCmdDMouseMove:
274 case barrierCmdDMouseRelMove:
275 read_short(msg->mousepos.x, p, len);
276 read_short(msg->mousepos.y, p, len);
277 break;
278 case barrierCmdDMouseDown:
279 case barrierCmdDMouseUp:
280 read_char(msg->mousebutton.buttonid, p, len);
281 break;
282 case barrierCmdDMouseWheel:
283 read_short(msg->mousepos.y, p, len);
284 msg->mousepos.x = 0;
285 if (len) {
286 msg->mousepos.x = msg->mousepos.y;
287 read_short(msg->mousepos.y, p, len);
288 }
289 break;
290
291
292 case barrierCmdDKeyDown:
293 case barrierCmdDKeyUp:
294 read_short(msg->key.keyid, p, len);
295 read_short(msg->key.modifier, p, len);
296 msg->key.button = 0;
297 if (len) {
298 read_short(msg->key.button, p, len);
299 }
300 break;
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;
306 if (len) {
307 read_short(msg->repeat.button, p, len);
308 }
309 break;
310 case barrierCmdCInfoAck:
311 case barrierCmdCResetOptions:
312 case barrierCmdCEnter:
313 case barrierCmdDClipboard:
314 case barrierCmdCKeepAlive:
315 case barrierCmdCLeave:
316 case barrierCmdCClose:
317 break;
318
319
320 case barrierCmdHelloBack:
321 case barrierCmdCNoop:
322 case barrierCmdDInfo:
323 break;
324
325
326 case barrierCmdEIncompatible:
327 read_short(msg->version.major, p, len);
328 read_short(msg->version.minor, p, len);
329 break;
330 case barrierCmdEBusy:
331 case barrierCmdEUnknown:
332 case barrierCmdEBad:
333 break;
334 default:
335 return G_SOURCE_REMOVE;
336 }
337
338 return G_SOURCE_CONTINUE;
339}
340
341static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg)
342{
343 char *p;
344 int ret, i;
345 int avail, len;
346
347 p = ib->buffer;
348 avail = MAX_HELLO_LENGTH;
349
350
351 p += sizeof(int);
352 avail -= sizeof(int);
353
354 switch (msg->cmd) {
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)) {
359 ib->ioc_tag = 0;
360 return G_SOURCE_REMOVE;
361 }
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);
366 break;
367 case barrierCmdCClose:
368 ib->ioc_tag = 0;
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);
377 write_short(p, 0, avail);
378 write_short(p, 0, avail);
379 break;
380 case barrierCmdCInfoAck:
381 break;
382 case barrierCmdCResetOptions:
383
384 break;
385 case barrierCmdDSetOptions:
386
387 break;
388 case barrierCmdCEnter:
389 break;
390 case barrierCmdDClipboard:
391 break;
392 case barrierCmdCKeepAlive:
393 write_cmd(p, barrierCmdCKeepAlive, avail);
394 break;
395 case barrierCmdCLeave:
396 break;
397
398
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();
405 break;
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();
410 break;
411 case barrierCmdDMouseDown:
412 qemu_input_queue_btn(NULL,
413 input_barrier_to_mouse(msg->mousebutton.buttonid),
414 true);
415 qemu_input_event_sync();
416 break;
417 case barrierCmdDMouseUp:
418 qemu_input_queue_btn(NULL,
419 input_barrier_to_mouse(msg->mousebutton.buttonid),
420 false);
421 qemu_input_event_sync();
422 break;
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();
430 break;
431
432
433 case barrierCmdDKeyDown:
434 qemu_input_event_send_key_qcode(NULL,
435 input_barrier_to_qcode(msg->key.keyid, msg->key.button),
436 true);
437 break;
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),
442 false);
443 qemu_input_event_send_key_qcode(NULL,
444 input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button),
445 true);
446 }
447 break;
448 case barrierCmdDKeyUp:
449 qemu_input_event_send_key_qcode(NULL,
450 input_barrier_to_qcode(msg->key.keyid, msg->key.button),
451 false);
452 break;
453 default:
454 write_cmd(p, barrierCmdEUnknown, avail);
455 break;
456 }
457
458 len = MAX_HELLO_LENGTH - avail - sizeof(int);
459 if (len) {
460 p = ib->buffer;
461 avail = sizeof(len);
462 write_int(p, len, avail);
463 ret = qio_channel_write(QIO_CHANNEL(ib->sioc), ib->buffer,
464 len + sizeof(len), NULL);
465 if (ret < 0) {
466 ib->ioc_tag = 0;
467 return G_SOURCE_REMOVE;
468 }
469 }
470
471 return G_SOURCE_CONTINUE;
472}
473
474static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED,
475 GIOCondition condition, void *opaque)
476{
477 InputBarrier *ib = opaque;
478 int ret;
479 struct barrierMsg msg;
480
481 ret = readcmd(ib, &msg);
482 if (ret == G_SOURCE_REMOVE) {
483 ib->ioc_tag = 0;
484 return G_SOURCE_REMOVE;
485 }
486
487 return writecmd(ib, &msg);
488}
489
490static void input_barrier_complete(UserCreatable *uc, Error **errp)
491{
492 InputBarrier *ib = INPUT_BARRIER(uc);
493 Error *local_err = NULL;
494
495 if (!ib->name) {
496 error_setg(errp, QERR_MISSING_PARAMETER, "name");
497 return;
498 }
499
500
501
502
503
504
505
506 ib->sioc = qio_channel_socket_new();
507 qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client");
508
509 qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err);
510 if (local_err) {
511 error_propagate(errp, local_err);
512 return;
513 }
514
515 qio_channel_set_delay(QIO_CHANNEL(ib->sioc), false);
516
517 ib->ioc_tag = qio_channel_add_watch(QIO_CHANNEL(ib->sioc), G_IO_IN,
518 input_barrier_event, ib, NULL);
519}
520
521static void input_barrier_instance_finalize(Object *obj)
522{
523 InputBarrier *ib = INPUT_BARRIER(obj);
524
525 if (ib->ioc_tag) {
526 g_source_remove(ib->ioc_tag);
527 ib->ioc_tag = 0;
528 }
529
530 if (ib->sioc) {
531 qio_channel_close(QIO_CHANNEL(ib->sioc), NULL);
532 object_unref(OBJECT(ib->sioc));
533 }
534 g_free(ib->name);
535 g_free(ib->saddr.u.inet.host);
536 g_free(ib->saddr.u.inet.port);
537}
538
539static char *input_barrier_get_name(Object *obj, Error **errp)
540{
541 InputBarrier *ib = INPUT_BARRIER(obj);
542
543 return g_strdup(ib->name);
544}
545
546static void input_barrier_set_name(Object *obj, const char *value,
547 Error **errp)
548{
549 InputBarrier *ib = INPUT_BARRIER(obj);
550
551 if (ib->name) {
552 error_setg(errp, "name property already set");
553 return;
554 }
555 ib->name = g_strdup(value);
556}
557
558static char *input_barrier_get_server(Object *obj, Error **errp)
559{
560 InputBarrier *ib = INPUT_BARRIER(obj);
561
562 return g_strdup(ib->saddr.u.inet.host);
563}
564
565static void input_barrier_set_server(Object *obj, const char *value,
566 Error **errp)
567{
568 InputBarrier *ib = INPUT_BARRIER(obj);
569
570 g_free(ib->saddr.u.inet.host);
571 ib->saddr.u.inet.host = g_strdup(value);
572}
573
574static char *input_barrier_get_port(Object *obj, Error **errp)
575{
576 InputBarrier *ib = INPUT_BARRIER(obj);
577
578 return g_strdup(ib->saddr.u.inet.port);
579}
580
581static void input_barrier_set_port(Object *obj, const char *value,
582 Error **errp)
583{
584 InputBarrier *ib = INPUT_BARRIER(obj);
585
586 g_free(ib->saddr.u.inet.port);
587 ib->saddr.u.inet.port = g_strdup(value);
588}
589
590static void input_barrier_set_x_origin(Object *obj, const char *value,
591 Error **errp)
592{
593 InputBarrier *ib = INPUT_BARRIER(obj);
594 int result, err;
595
596 err = qemu_strtoi(value, NULL, 0, &result);
597 if (err < 0 || result < 0 || result > SHRT_MAX) {
598 error_setg(errp,
599 "x-origin property must be in the range [0..%d]", SHRT_MAX);
600 return;
601 }
602 ib->x_origin = result;
603}
604
605static char *input_barrier_get_x_origin(Object *obj, Error **errp)
606{
607 InputBarrier *ib = INPUT_BARRIER(obj);
608
609 return g_strdup_printf("%d", ib->x_origin);
610}
611
612static void input_barrier_set_y_origin(Object *obj, const char *value,
613 Error **errp)
614{
615 InputBarrier *ib = INPUT_BARRIER(obj);
616 int result, err;
617
618 err = qemu_strtoi(value, NULL, 0, &result);
619 if (err < 0 || result < 0 || result > SHRT_MAX) {
620 error_setg(errp,
621 "y-origin property must be in the range [0..%d]", SHRT_MAX);
622 return;
623 }
624 ib->y_origin = result;
625}
626
627static char *input_barrier_get_y_origin(Object *obj, Error **errp)
628{
629 InputBarrier *ib = INPUT_BARRIER(obj);
630
631 return g_strdup_printf("%d", ib->y_origin);
632}
633
634static void input_barrier_set_width(Object *obj, const char *value,
635 Error **errp)
636{
637 InputBarrier *ib = INPUT_BARRIER(obj);
638 int result, err;
639
640 err = qemu_strtoi(value, NULL, 0, &result);
641 if (err < 0 || result < 0 || result > SHRT_MAX) {
642 error_setg(errp,
643 "width property must be in the range [0..%d]", SHRT_MAX);
644 return;
645 }
646 ib->width = result;
647}
648
649static char *input_barrier_get_width(Object *obj, Error **errp)
650{
651 InputBarrier *ib = INPUT_BARRIER(obj);
652
653 return g_strdup_printf("%d", ib->width);
654}
655
656static void input_barrier_set_height(Object *obj, const char *value,
657 Error **errp)
658{
659 InputBarrier *ib = INPUT_BARRIER(obj);
660 int result, err;
661
662 err = qemu_strtoi(value, NULL, 0, &result);
663 if (err < 0 || result < 0 || result > SHRT_MAX) {
664 error_setg(errp,
665 "height property must be in the range [0..%d]", SHRT_MAX);
666 return;
667 }
668 ib->height = result;
669}
670
671static char *input_barrier_get_height(Object *obj, Error **errp)
672{
673 InputBarrier *ib = INPUT_BARRIER(obj);
674
675 return g_strdup_printf("%d", ib->height);
676}
677
678static void input_barrier_instance_init(Object *obj)
679{
680 InputBarrier *ib = INPUT_BARRIER(obj);
681
682
683 if (keyboard_layout && !kbd_layout) {
684
685 kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
686 &error_fatal);
687 }
688
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");
692
693 ib->x_origin = 0;
694 ib->y_origin = 0;
695 ib->width = 1920;
696 ib->height = 1080;
697}
698
699static void input_barrier_class_init(ObjectClass *oc, void *data)
700{
701 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
702
703 ucc->complete = input_barrier_complete;
704
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);
726}
727
728static 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 },
737 { }
738 }
739};
740
741static void register_types(void)
742{
743 type_register_static(&input_barrier_info);
744}
745
746type_init(register_types);
747