]> git.proxmox.com Git - mirror_qemu.git/blob - ui/input-barrier.c
Use OBJECT_DECLARE_TYPE where possible
[mirror_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_TYPE(InputBarrier, InputBarrierClass,
24 input_barrier, 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 struct InputBarrierClass {
48 ObjectClass parent_class;
49 };
50
51 static const char *cmd_names[] = {
52 [barrierCmdCNoop] = "CNOP",
53 [barrierCmdCClose] = "CBYE",
54 [barrierCmdCEnter] = "CINN",
55 [barrierCmdCLeave] = "COUT",
56 [barrierCmdCClipboard] = "CCLP",
57 [barrierCmdCScreenSaver] = "CSEC",
58 [barrierCmdCResetOptions] = "CROP",
59 [barrierCmdCInfoAck] = "CIAK",
60 [barrierCmdCKeepAlive] = "CALV",
61 [barrierCmdDKeyDown] = "DKDN",
62 [barrierCmdDKeyRepeat] = "DKRP",
63 [barrierCmdDKeyUp] = "DKUP",
64 [barrierCmdDMouseDown] = "DMDN",
65 [barrierCmdDMouseUp] = "DMUP",
66 [barrierCmdDMouseMove] = "DMMV",
67 [barrierCmdDMouseRelMove] = "DMRM",
68 [barrierCmdDMouseWheel] = "DMWM",
69 [barrierCmdDClipboard] = "DCLP",
70 [barrierCmdDInfo] = "DINF",
71 [barrierCmdDSetOptions] = "DSOP",
72 [barrierCmdDFileTransfer] = "DFTR",
73 [barrierCmdDDragInfo] = "DDRG",
74 [barrierCmdQInfo] = "QINF",
75 [barrierCmdEIncompatible] = "EICV",
76 [barrierCmdEBusy] = "EBSY",
77 [barrierCmdEUnknown] = "EUNK",
78 [barrierCmdEBad] = "EBAD",
79 [barrierCmdHello] = "Barrier",
80 [barrierCmdHelloBack] = "Barrier",
81 };
82
83 static kbd_layout_t *kbd_layout;
84
85 static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode)
86 {
87 /* keycode is optional, if it is not provided use keyid */
88 if (keycode && keycode <= qemu_input_map_xorgkbd_to_qcode_len) {
89 return qemu_input_map_xorgkbd_to_qcode[keycode];
90 }
91
92 if (keyid >= 0xE000 && keyid <= 0xEFFF) {
93 keyid += 0x1000;
94 }
95
96 /* keyid is the X11 key id */
97 if (kbd_layout) {
98 keycode = keysym2scancode(kbd_layout, keyid, NULL, false);
99
100 return qemu_input_key_number_to_qcode(keycode);
101 }
102
103 return qemu_input_map_x11_to_qcode[keyid];
104 }
105
106 static int input_barrier_to_mouse(uint8_t buttonid)
107 {
108 switch (buttonid) {
109 case barrierButtonLeft:
110 return INPUT_BUTTON_LEFT;
111 case barrierButtonMiddle:
112 return INPUT_BUTTON_MIDDLE;
113 case barrierButtonRight:
114 return INPUT_BUTTON_RIGHT;
115 case barrierButtonExtra0:
116 return INPUT_BUTTON_SIDE;
117 }
118 return buttonid;
119 }
120
121 #define read_char(x, p, l) \
122 do { \
123 int size = sizeof(char); \
124 if (l < size) { \
125 return G_SOURCE_REMOVE; \
126 } \
127 x = *(char *)p; \
128 p += size; \
129 l -= size; \
130 } while (0)
131
132 #define read_short(x, p, l) \
133 do { \
134 int size = sizeof(short); \
135 if (l < size) { \
136 return G_SOURCE_REMOVE; \
137 } \
138 x = ntohs(*(short *)p); \
139 p += size; \
140 l -= size; \
141 } while (0)
142
143 #define write_short(p, x, l) \
144 do { \
145 int size = sizeof(short); \
146 if (l < size) { \
147 return G_SOURCE_REMOVE; \
148 } \
149 *(short *)p = htons(x); \
150 p += size; \
151 l -= size; \
152 } while (0)
153
154 #define read_int(x, p, l) \
155 do { \
156 int size = sizeof(int); \
157 if (l < size) { \
158 return G_SOURCE_REMOVE; \
159 } \
160 x = ntohl(*(int *)p); \
161 p += size; \
162 l -= size; \
163 } while (0)
164
165 #define write_int(p, x, l) \
166 do { \
167 int size = sizeof(int); \
168 if (l < size) { \
169 return G_SOURCE_REMOVE; \
170 } \
171 *(int *)p = htonl(x); \
172 p += size; \
173 l -= size; \
174 } while (0)
175
176 #define write_cmd(p, c, l) \
177 do { \
178 int size = strlen(cmd_names[c]); \
179 if (l < size) { \
180 return G_SOURCE_REMOVE; \
181 } \
182 memcpy(p, cmd_names[c], size); \
183 p += size; \
184 l -= size; \
185 } while (0)
186
187 #define write_string(p, s, l) \
188 do { \
189 int size = strlen(s); \
190 if (l < size + sizeof(int)) { \
191 return G_SOURCE_REMOVE; \
192 } \
193 *(int *)p = htonl(size); \
194 p += sizeof(size); \
195 l -= sizeof(size); \
196 memcpy(p, s, size); \
197 p += size; \
198 l -= size; \
199 } while (0)
200
201 static gboolean readcmd(InputBarrier *ib, struct barrierMsg *msg)
202 {
203 int ret, len, i;
204 enum barrierCmd cmd;
205 char *p;
206
207 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), (char *)&len, sizeof(len),
208 NULL);
209 if (ret < 0) {
210 return G_SOURCE_REMOVE;
211 }
212
213 len = ntohl(len);
214 if (len > MAX_HELLO_LENGTH) {
215 return G_SOURCE_REMOVE;
216 }
217
218 ret = qio_channel_read(QIO_CHANNEL(ib->sioc), ib->buffer, len, NULL);
219 if (ret < 0) {
220 return G_SOURCE_REMOVE;
221 }
222
223 p = ib->buffer;
224 if (len >= strlen(cmd_names[barrierCmdHello]) &&
225 memcmp(p, cmd_names[barrierCmdHello],
226 strlen(cmd_names[barrierCmdHello])) == 0) {
227 cmd = barrierCmdHello;
228 p += strlen(cmd_names[barrierCmdHello]);
229 len -= strlen(cmd_names[barrierCmdHello]);
230 } else {
231 for (cmd = 0; cmd < barrierCmdHello; cmd++) {
232 if (memcmp(ib->buffer, cmd_names[cmd], 4) == 0) {
233 break;
234 }
235 }
236
237 if (cmd == barrierCmdHello) {
238 return G_SOURCE_REMOVE;
239 }
240 p += 4;
241 len -= 4;
242 }
243
244 msg->cmd = cmd;
245 switch (cmd) {
246 /* connection */
247 case barrierCmdHello:
248 read_short(msg->version.major, p, len);
249 read_short(msg->version.minor, p, len);
250 break;
251 case barrierCmdDSetOptions:
252 read_int(msg->set.nb, p, len);
253 msg->set.nb /= 2;
254 if (msg->set.nb > BARRIER_MAX_OPTIONS) {
255 msg->set.nb = BARRIER_MAX_OPTIONS;
256 }
257 i = 0;
258 while (len && i < msg->set.nb) {
259 read_int(msg->set.option[i].id, p, len);
260 /* it's a string, restore endianness */
261 msg->set.option[i].id = htonl(msg->set.option[i].id);
262 msg->set.option[i].nul = 0;
263 read_int(msg->set.option[i].value, p, len);
264 i++;
265 }
266 break;
267 case barrierCmdQInfo:
268 break;
269
270 /* mouse */
271 case barrierCmdDMouseMove:
272 case barrierCmdDMouseRelMove:
273 read_short(msg->mousepos.x, p, len);
274 read_short(msg->mousepos.y, p, len);
275 break;
276 case barrierCmdDMouseDown:
277 case barrierCmdDMouseUp:
278 read_char(msg->mousebutton.buttonid, p, len);
279 break;
280 case barrierCmdDMouseWheel:
281 read_short(msg->mousepos.y, p, len);
282 msg->mousepos.x = 0;
283 if (len) {
284 msg->mousepos.x = msg->mousepos.y;
285 read_short(msg->mousepos.y, p, len);
286 }
287 break;
288
289 /* keyboard */
290 case barrierCmdDKeyDown:
291 case barrierCmdDKeyUp:
292 read_short(msg->key.keyid, p, len);
293 read_short(msg->key.modifier, p, len);
294 msg->key.button = 0;
295 if (len) {
296 read_short(msg->key.button, p, len);
297 }
298 break;
299 case barrierCmdDKeyRepeat:
300 read_short(msg->repeat.keyid, p, len);
301 read_short(msg->repeat.modifier, p, len);
302 read_short(msg->repeat.repeat, p, len);
303 msg->repeat.button = 0;
304 if (len) {
305 read_short(msg->repeat.button, p, len);
306 }
307 break;
308 case barrierCmdCInfoAck:
309 case barrierCmdCResetOptions:
310 case barrierCmdCEnter:
311 case barrierCmdDClipboard:
312 case barrierCmdCKeepAlive:
313 case barrierCmdCLeave:
314 case barrierCmdCClose:
315 break;
316
317 /* Invalid from the server */
318 case barrierCmdHelloBack:
319 case barrierCmdCNoop:
320 case barrierCmdDInfo:
321 break;
322
323 /* Error codes */
324 case barrierCmdEIncompatible:
325 read_short(msg->version.major, p, len);
326 read_short(msg->version.minor, p, len);
327 break;
328 case barrierCmdEBusy:
329 case barrierCmdEUnknown:
330 case barrierCmdEBad:
331 break;
332 default:
333 return G_SOURCE_REMOVE;
334 }
335
336 return G_SOURCE_CONTINUE;
337 }
338
339 static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg)
340 {
341 char *p;
342 int ret, i;
343 int avail, len;
344
345 p = ib->buffer;
346 avail = MAX_HELLO_LENGTH;
347
348 /* reserve space to store the length */
349 p += sizeof(int);
350 avail -= sizeof(int);
351
352 switch (msg->cmd) {
353 case barrierCmdHello:
354 if (msg->version.major < BARRIER_VERSION_MAJOR ||
355 (msg->version.major == BARRIER_VERSION_MAJOR &&
356 msg->version.minor < BARRIER_VERSION_MINOR)) {
357 ib->ioc_tag = 0;
358 return G_SOURCE_REMOVE;
359 }
360 write_cmd(p, barrierCmdHelloBack, avail);
361 write_short(p, BARRIER_VERSION_MAJOR, avail);
362 write_short(p, BARRIER_VERSION_MINOR, avail);
363 write_string(p, ib->name, avail);
364 break;
365 case barrierCmdCClose:
366 ib->ioc_tag = 0;
367 return G_SOURCE_REMOVE;
368 case barrierCmdQInfo:
369 write_cmd(p, barrierCmdDInfo, avail);
370 write_short(p, ib->x_origin, avail);
371 write_short(p, ib->y_origin, avail);
372 write_short(p, ib->width, avail);
373 write_short(p, ib->height, avail);
374 write_short(p, 0, avail); /* warpsize (obsolete) */
375 write_short(p, 0, avail); /* mouse x */
376 write_short(p, 0, avail); /* mouse y */
377 break;
378 case barrierCmdCInfoAck:
379 break;
380 case barrierCmdCResetOptions:
381 /* TODO: reset options */
382 break;
383 case barrierCmdDSetOptions:
384 /* TODO: set options */
385 break;
386 case barrierCmdCEnter:
387 break;
388 case barrierCmdDClipboard:
389 break;
390 case barrierCmdCKeepAlive:
391 write_cmd(p, barrierCmdCKeepAlive, avail);
392 break;
393 case barrierCmdCLeave:
394 break;
395
396 /* mouse */
397 case barrierCmdDMouseMove:
398 qemu_input_queue_abs(NULL, INPUT_AXIS_X, msg->mousepos.x,
399 ib->x_origin, ib->width);
400 qemu_input_queue_abs(NULL, INPUT_AXIS_Y, msg->mousepos.y,
401 ib->y_origin, ib->height);
402 qemu_input_event_sync();
403 break;
404 case barrierCmdDMouseRelMove:
405 qemu_input_queue_rel(NULL, INPUT_AXIS_X, msg->mousepos.x);
406 qemu_input_queue_rel(NULL, INPUT_AXIS_Y, msg->mousepos.y);
407 qemu_input_event_sync();
408 break;
409 case barrierCmdDMouseDown:
410 qemu_input_queue_btn(NULL,
411 input_barrier_to_mouse(msg->mousebutton.buttonid),
412 true);
413 qemu_input_event_sync();
414 break;
415 case barrierCmdDMouseUp:
416 qemu_input_queue_btn(NULL,
417 input_barrier_to_mouse(msg->mousebutton.buttonid),
418 false);
419 qemu_input_event_sync();
420 break;
421 case barrierCmdDMouseWheel:
422 qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP
423 : INPUT_BUTTON_WHEEL_DOWN, true);
424 qemu_input_event_sync();
425 qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP
426 : INPUT_BUTTON_WHEEL_DOWN, false);
427 qemu_input_event_sync();
428 break;
429
430 /* keyboard */
431 case barrierCmdDKeyDown:
432 qemu_input_event_send_key_qcode(NULL,
433 input_barrier_to_qcode(msg->key.keyid, msg->key.button),
434 true);
435 break;
436 case barrierCmdDKeyRepeat:
437 for (i = 0; i < msg->repeat.repeat; i++) {
438 qemu_input_event_send_key_qcode(NULL,
439 input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button),
440 false);
441 qemu_input_event_send_key_qcode(NULL,
442 input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button),
443 true);
444 }
445 break;
446 case barrierCmdDKeyUp:
447 qemu_input_event_send_key_qcode(NULL,
448 input_barrier_to_qcode(msg->key.keyid, msg->key.button),
449 false);
450 break;
451 default:
452 write_cmd(p, barrierCmdEUnknown, avail);
453 break;
454 }
455
456 len = MAX_HELLO_LENGTH - avail - sizeof(int);
457 if (len) {
458 p = ib->buffer;
459 avail = sizeof(len);
460 write_int(p, len, avail);
461 ret = qio_channel_write(QIO_CHANNEL(ib->sioc), ib->buffer,
462 len + sizeof(len), NULL);
463 if (ret < 0) {
464 ib->ioc_tag = 0;
465 return G_SOURCE_REMOVE;
466 }
467 }
468
469 return G_SOURCE_CONTINUE;
470 }
471
472 static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED,
473 GIOCondition condition, void *opaque)
474 {
475 InputBarrier *ib = opaque;
476 int ret;
477 struct barrierMsg msg;
478
479 ret = readcmd(ib, &msg);
480 if (ret == G_SOURCE_REMOVE) {
481 ib->ioc_tag = 0;
482 return G_SOURCE_REMOVE;
483 }
484
485 return writecmd(ib, &msg);
486 }
487
488 static void input_barrier_complete(UserCreatable *uc, Error **errp)
489 {
490 InputBarrier *ib = INPUT_BARRIER(uc);
491 Error *local_err = NULL;
492
493 if (!ib->name) {
494 error_setg(errp, QERR_MISSING_PARAMETER, "name");
495 return;
496 }
497
498 /*
499 * Connect to the primary
500 * Primary is the server where the keyboard and the mouse
501 * are connected and forwarded to the secondary (the client)
502 */
503
504 ib->sioc = qio_channel_socket_new();
505 qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client");
506
507 qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err);
508 if (local_err) {
509 error_propagate(errp, local_err);
510 return;
511 }
512
513 qio_channel_set_delay(QIO_CHANNEL(ib->sioc), false);
514
515 ib->ioc_tag = qio_channel_add_watch(QIO_CHANNEL(ib->sioc), G_IO_IN,
516 input_barrier_event, ib, NULL);
517 }
518
519 static void input_barrier_instance_finalize(Object *obj)
520 {
521 InputBarrier *ib = INPUT_BARRIER(obj);
522
523 if (ib->ioc_tag) {
524 g_source_remove(ib->ioc_tag);
525 ib->ioc_tag = 0;
526 }
527
528 if (ib->sioc) {
529 qio_channel_close(QIO_CHANNEL(ib->sioc), NULL);
530 object_unref(OBJECT(ib->sioc));
531 }
532 g_free(ib->name);
533 g_free(ib->saddr.u.inet.host);
534 g_free(ib->saddr.u.inet.port);
535 }
536
537 static char *input_barrier_get_name(Object *obj, Error **errp)
538 {
539 InputBarrier *ib = INPUT_BARRIER(obj);
540
541 return g_strdup(ib->name);
542 }
543
544 static void input_barrier_set_name(Object *obj, const char *value,
545 Error **errp)
546 {
547 InputBarrier *ib = INPUT_BARRIER(obj);
548
549 if (ib->name) {
550 error_setg(errp, "name property already set");
551 return;
552 }
553 ib->name = g_strdup(value);
554 }
555
556 static char *input_barrier_get_server(Object *obj, Error **errp)
557 {
558 InputBarrier *ib = INPUT_BARRIER(obj);
559
560 return g_strdup(ib->saddr.u.inet.host);
561 }
562
563 static void input_barrier_set_server(Object *obj, const char *value,
564 Error **errp)
565 {
566 InputBarrier *ib = INPUT_BARRIER(obj);
567
568 g_free(ib->saddr.u.inet.host);
569 ib->saddr.u.inet.host = g_strdup(value);
570 }
571
572 static char *input_barrier_get_port(Object *obj, Error **errp)
573 {
574 InputBarrier *ib = INPUT_BARRIER(obj);
575
576 return g_strdup(ib->saddr.u.inet.port);
577 }
578
579 static void input_barrier_set_port(Object *obj, const char *value,
580 Error **errp)
581 {
582 InputBarrier *ib = INPUT_BARRIER(obj);
583
584 g_free(ib->saddr.u.inet.port);
585 ib->saddr.u.inet.port = g_strdup(value);
586 }
587
588 static void input_barrier_set_x_origin(Object *obj, const char *value,
589 Error **errp)
590 {
591 InputBarrier *ib = INPUT_BARRIER(obj);
592 int result, err;
593
594 err = qemu_strtoi(value, NULL, 0, &result);
595 if (err < 0 || result < 0 || result > SHRT_MAX) {
596 error_setg(errp,
597 "x-origin property must be in the range [0..%d]", SHRT_MAX);
598 return;
599 }
600 ib->x_origin = result;
601 }
602
603 static char *input_barrier_get_x_origin(Object *obj, Error **errp)
604 {
605 InputBarrier *ib = INPUT_BARRIER(obj);
606
607 return g_strdup_printf("%d", ib->x_origin);
608 }
609
610 static void input_barrier_set_y_origin(Object *obj, const char *value,
611 Error **errp)
612 {
613 InputBarrier *ib = INPUT_BARRIER(obj);
614 int result, err;
615
616 err = qemu_strtoi(value, NULL, 0, &result);
617 if (err < 0 || result < 0 || result > SHRT_MAX) {
618 error_setg(errp,
619 "y-origin property must be in the range [0..%d]", SHRT_MAX);
620 return;
621 }
622 ib->y_origin = result;
623 }
624
625 static char *input_barrier_get_y_origin(Object *obj, Error **errp)
626 {
627 InputBarrier *ib = INPUT_BARRIER(obj);
628
629 return g_strdup_printf("%d", ib->y_origin);
630 }
631
632 static void input_barrier_set_width(Object *obj, const char *value,
633 Error **errp)
634 {
635 InputBarrier *ib = INPUT_BARRIER(obj);
636 int result, err;
637
638 err = qemu_strtoi(value, NULL, 0, &result);
639 if (err < 0 || result < 0 || result > SHRT_MAX) {
640 error_setg(errp,
641 "width property must be in the range [0..%d]", SHRT_MAX);
642 return;
643 }
644 ib->width = result;
645 }
646
647 static char *input_barrier_get_width(Object *obj, Error **errp)
648 {
649 InputBarrier *ib = INPUT_BARRIER(obj);
650
651 return g_strdup_printf("%d", ib->width);
652 }
653
654 static void input_barrier_set_height(Object *obj, const char *value,
655 Error **errp)
656 {
657 InputBarrier *ib = INPUT_BARRIER(obj);
658 int result, err;
659
660 err = qemu_strtoi(value, NULL, 0, &result);
661 if (err < 0 || result < 0 || result > SHRT_MAX) {
662 error_setg(errp,
663 "height property must be in the range [0..%d]", SHRT_MAX);
664 return;
665 }
666 ib->height = result;
667 }
668
669 static char *input_barrier_get_height(Object *obj, Error **errp)
670 {
671 InputBarrier *ib = INPUT_BARRIER(obj);
672
673 return g_strdup_printf("%d", ib->height);
674 }
675
676 static void input_barrier_instance_init(Object *obj)
677 {
678 InputBarrier *ib = INPUT_BARRIER(obj);
679
680 /* always use generic keymaps */
681 if (keyboard_layout && !kbd_layout) {
682 /* We use X11 key id, so use VNC name2keysym */
683 kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
684 &error_fatal);
685 }
686
687 ib->saddr.type = SOCKET_ADDRESS_TYPE_INET;
688 ib->saddr.u.inet.host = g_strdup("localhost");
689 ib->saddr.u.inet.port = g_strdup("24800");
690
691 ib->x_origin = 0;
692 ib->y_origin = 0;
693 ib->width = 1920;
694 ib->height = 1080;
695
696 object_property_add_str(obj, "name",
697 input_barrier_get_name,
698 input_barrier_set_name);
699 object_property_add_str(obj, "server",
700 input_barrier_get_server,
701 input_barrier_set_server);
702 object_property_add_str(obj, "port",
703 input_barrier_get_port,
704 input_barrier_set_port);
705 object_property_add_str(obj, "x-origin",
706 input_barrier_get_x_origin,
707 input_barrier_set_x_origin);
708 object_property_add_str(obj, "y-origin",
709 input_barrier_get_y_origin,
710 input_barrier_set_y_origin);
711 object_property_add_str(obj, "width",
712 input_barrier_get_width,
713 input_barrier_set_width);
714 object_property_add_str(obj, "height",
715 input_barrier_get_height,
716 input_barrier_set_height);
717 }
718
719 static void input_barrier_class_init(ObjectClass *oc, void *data)
720 {
721 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
722
723 ucc->complete = input_barrier_complete;
724 }
725
726 static const TypeInfo input_barrier_info = {
727 .name = TYPE_INPUT_BARRIER,
728 .parent = TYPE_OBJECT,
729 .class_size = sizeof(InputBarrierClass),
730 .class_init = input_barrier_class_init,
731 .instance_size = sizeof(InputBarrier),
732 .instance_init = input_barrier_instance_init,
733 .instance_finalize = input_barrier_instance_finalize,
734 .interfaces = (InterfaceInfo[]) {
735 { TYPE_USER_CREATABLE },
736 { }
737 }
738 };
739
740 static void register_types(void)
741 {
742 type_register_static(&input_barrier_info);
743 }
744
745 type_init(register_types);