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