xref: /openbmc/qemu/ui/input-barrier.c (revision 30b6852c)
1 /*
2  * SPDX-License-Identifier: GPL-2.0-or-later
3  *
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.
6  *
7  * TODO:
8  *
9  *  - Enable SSL
10  *  - Manage SetOptions/ResetOptions commands
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" /* 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"
26 
27 #define TYPE_INPUT_BARRIER "input-barrier"
28 OBJECT_DECLARE_SIMPLE_TYPE(InputBarrier,
29                            INPUT_BARRIER)
30 
31 
32 #define MAX_HELLO_LENGTH 1024
33 
34 struct InputBarrier {
35     Object parent;
36 
37     QIOChannelSocket *sioc;
38     guint ioc_tag;
39 
40     /* display properties */
41     gchar *name;
42     int16_t x_origin, y_origin;
43     int16_t width, height;
44 
45     /* keyboard/mouse server */
46 
47     SocketAddress saddr;
48 
49     char buffer[MAX_HELLO_LENGTH];
50 };
51 
52 
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",
83 };
84 
85 static kbd_layout_t *kbd_layout;
86 
87 static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode)
88 {
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];
92     }
93 
94     if (keyid >= 0xE000 && keyid <= 0xEFFF) {
95         keyid += 0x1000;
96     }
97 
98     /* keyid is the X11 key id */
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 
108 static 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)           \
124 do {                                 \
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)          \
135 do {                                 \
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)         \
146 do {                                 \
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)            \
157 do {                                 \
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)           \
168 do {                                 \
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)           \
179 do {                                 \
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)        \
190 do {                                 \
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 
203 static 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     /* connection */
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             /* 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);
266             i++;
267         }
268         break;
269     case barrierCmdQInfo:
270         break;
271 
272     /* mouse */
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     /* keyboard */
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     /* Invalid from the server */
320     case barrierCmdHelloBack:
321     case barrierCmdCNoop:
322     case barrierCmdDInfo:
323         break;
324 
325     /* Error codes */
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 
341 static 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     /* reserve space to store the length */
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);    /* warpsize (obsolete) */
377         write_short(p, 0, avail);    /* mouse x */
378         write_short(p, 0, avail);    /* mouse y */
379         break;
380     case barrierCmdCInfoAck:
381         break;
382     case barrierCmdCResetOptions:
383         /* TODO: reset options */
384         break;
385     case barrierCmdDSetOptions:
386         /* TODO: set options */
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     /* mouse */
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     /* keyboard */
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 
474 static 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 
490 static 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      * 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)
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 
521 static 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 
539 static 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 
546 static 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 
558 static 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 
565 static 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 
574 static 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 
581 static 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 
590 static 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 
605 static 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 
612 static 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 
627 static 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 
634 static 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 
649 static 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 
656 static 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 
671 static 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 
678 static void input_barrier_instance_init(Object *obj)
679 {
680     InputBarrier *ib = INPUT_BARRIER(obj);
681 
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,
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 
699 static 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 
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 },
737         { }
738     }
739 };
740 
741 static void register_types(void)
742 {
743     type_register_static(&input_barrier_info);
744 }
745 
746 type_init(register_types);
747