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