123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746 |
- /*
- * SPDX-License-Identifier: GPL-2.0-or-later
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- * TODO:
- *
- * - Enable SSL
- * - Manage SetOptions/ResetOptions commands
- */
- #include "qemu/osdep.h"
- #include "system/system.h"
- #include "qemu/main-loop.h"
- #include "qemu/sockets.h"
- #include "qapi/error.h"
- #include "qom/object_interfaces.h"
- #include "io/channel-socket.h"
- #include "ui/input.h"
- #include "qom/object.h"
- #include "ui/vnc_keysym.h" /* use name2keysym from VNC as we use X11 values */
- #include "qemu/cutils.h"
- #include "qapi/qmp/qerror.h"
- #include "input-barrier.h"
- #define TYPE_INPUT_BARRIER "input-barrier"
- OBJECT_DECLARE_SIMPLE_TYPE(InputBarrier,
- INPUT_BARRIER)
- #define MAX_HELLO_LENGTH 1024
- struct InputBarrier {
- Object parent;
- QIOChannelSocket *sioc;
- guint ioc_tag;
- /* display properties */
- gchar *name;
- int16_t x_origin, y_origin;
- int16_t width, height;
- /* keyboard/mouse server */
- SocketAddress saddr;
- char buffer[MAX_HELLO_LENGTH];
- };
- static const char *cmd_names[] = {
- [barrierCmdCNoop] = "CNOP",
- [barrierCmdCClose] = "CBYE",
- [barrierCmdCEnter] = "CINN",
- [barrierCmdCLeave] = "COUT",
- [barrierCmdCClipboard] = "CCLP",
- [barrierCmdCScreenSaver] = "CSEC",
- [barrierCmdCResetOptions] = "CROP",
- [barrierCmdCInfoAck] = "CIAK",
- [barrierCmdCKeepAlive] = "CALV",
- [barrierCmdDKeyDown] = "DKDN",
- [barrierCmdDKeyRepeat] = "DKRP",
- [barrierCmdDKeyUp] = "DKUP",
- [barrierCmdDMouseDown] = "DMDN",
- [barrierCmdDMouseUp] = "DMUP",
- [barrierCmdDMouseMove] = "DMMV",
- [barrierCmdDMouseRelMove] = "DMRM",
- [barrierCmdDMouseWheel] = "DMWM",
- [barrierCmdDClipboard] = "DCLP",
- [barrierCmdDInfo] = "DINF",
- [barrierCmdDSetOptions] = "DSOP",
- [barrierCmdDFileTransfer] = "DFTR",
- [barrierCmdDDragInfo] = "DDRG",
- [barrierCmdQInfo] = "QINF",
- [barrierCmdEIncompatible] = "EICV",
- [barrierCmdEBusy] = "EBSY",
- [barrierCmdEUnknown] = "EUNK",
- [barrierCmdEBad] = "EBAD",
- [barrierCmdHello] = "Barrier",
- [barrierCmdHelloBack] = "Barrier",
- };
- static kbd_layout_t *kbd_layout;
- static int input_barrier_to_qcode(uint16_t keyid, uint16_t keycode)
- {
- /* keycode is optional, if it is not provided use keyid */
- if (keycode && keycode <= qemu_input_map_xorgkbd_to_qcode_len) {
- return qemu_input_map_xorgkbd_to_qcode[keycode];
- }
- if (keyid >= 0xE000 && keyid <= 0xEFFF) {
- keyid += 0x1000;
- }
- /* keyid is the X11 key id */
- if (kbd_layout) {
- keycode = keysym2scancode(kbd_layout, keyid, NULL, false);
- return qemu_input_key_number_to_qcode(keycode);
- }
- return qemu_input_map_x11_to_qcode[keyid];
- }
- static int input_barrier_to_mouse(uint8_t buttonid)
- {
- switch (buttonid) {
- case barrierButtonLeft:
- return INPUT_BUTTON_LEFT;
- case barrierButtonMiddle:
- return INPUT_BUTTON_MIDDLE;
- case barrierButtonRight:
- return INPUT_BUTTON_RIGHT;
- case barrierButtonExtra0:
- return INPUT_BUTTON_SIDE;
- }
- return buttonid;
- }
- #define read_char(x, p, l) \
- do { \
- int size = sizeof(char); \
- if (l < size) { \
- return G_SOURCE_REMOVE; \
- } \
- x = *(char *)p; \
- p += size; \
- l -= size; \
- } while (0)
- #define read_short(x, p, l) \
- do { \
- int size = sizeof(short); \
- if (l < size) { \
- return G_SOURCE_REMOVE; \
- } \
- x = ntohs(*(short *)p); \
- p += size; \
- l -= size; \
- } while (0)
- #define write_short(p, x, l) \
- do { \
- int size = sizeof(short); \
- if (l < size) { \
- return G_SOURCE_REMOVE; \
- } \
- *(short *)p = htons(x); \
- p += size; \
- l -= size; \
- } while (0)
- #define read_int(x, p, l) \
- do { \
- int size = sizeof(int); \
- if (l < size) { \
- return G_SOURCE_REMOVE; \
- } \
- x = ntohl(*(int *)p); \
- p += size; \
- l -= size; \
- } while (0)
- #define write_int(p, x, l) \
- do { \
- int size = sizeof(int); \
- if (l < size) { \
- return G_SOURCE_REMOVE; \
- } \
- *(int *)p = htonl(x); \
- p += size; \
- l -= size; \
- } while (0)
- #define write_cmd(p, c, l) \
- do { \
- int size = strlen(cmd_names[c]); \
- if (l < size) { \
- return G_SOURCE_REMOVE; \
- } \
- memcpy(p, cmd_names[c], size); \
- p += size; \
- l -= size; \
- } while (0)
- #define write_string(p, s, l) \
- do { \
- int size = strlen(s); \
- if (l < size + sizeof(int)) { \
- return G_SOURCE_REMOVE; \
- } \
- *(int *)p = htonl(size); \
- p += sizeof(size); \
- l -= sizeof(size); \
- memcpy(p, s, size); \
- p += size; \
- l -= size; \
- } while (0)
- static gboolean readcmd(InputBarrier *ib, struct barrierMsg *msg)
- {
- int ret, len, i;
- enum barrierCmd cmd;
- char *p;
- ret = qio_channel_read(QIO_CHANNEL(ib->sioc), (char *)&len, sizeof(len),
- NULL);
- if (ret < 0) {
- return G_SOURCE_REMOVE;
- }
- len = ntohl(len);
- if (len > MAX_HELLO_LENGTH) {
- return G_SOURCE_REMOVE;
- }
- ret = qio_channel_read(QIO_CHANNEL(ib->sioc), ib->buffer, len, NULL);
- if (ret < 0) {
- return G_SOURCE_REMOVE;
- }
- p = ib->buffer;
- if (len >= strlen(cmd_names[barrierCmdHello]) &&
- memcmp(p, cmd_names[barrierCmdHello],
- strlen(cmd_names[barrierCmdHello])) == 0) {
- cmd = barrierCmdHello;
- p += strlen(cmd_names[barrierCmdHello]);
- len -= strlen(cmd_names[barrierCmdHello]);
- } else {
- for (cmd = 0; cmd < barrierCmdHello; cmd++) {
- if (memcmp(ib->buffer, cmd_names[cmd], 4) == 0) {
- break;
- }
- }
- if (cmd == barrierCmdHello) {
- return G_SOURCE_REMOVE;
- }
- p += 4;
- len -= 4;
- }
- msg->cmd = cmd;
- switch (cmd) {
- /* connection */
- case barrierCmdHello:
- read_short(msg->version.major, p, len);
- read_short(msg->version.minor, p, len);
- break;
- case barrierCmdDSetOptions:
- read_int(msg->set.nb, p, len);
- msg->set.nb /= 2;
- if (msg->set.nb > BARRIER_MAX_OPTIONS) {
- msg->set.nb = BARRIER_MAX_OPTIONS;
- }
- i = 0;
- while (len && i < msg->set.nb) {
- read_int(msg->set.option[i].id, p, len);
- /* it's a string, restore endianness */
- msg->set.option[i].id = htonl(msg->set.option[i].id);
- msg->set.option[i].nul = 0;
- read_int(msg->set.option[i].value, p, len);
- i++;
- }
- break;
- case barrierCmdQInfo:
- break;
- /* mouse */
- case barrierCmdDMouseMove:
- case barrierCmdDMouseRelMove:
- read_short(msg->mousepos.x, p, len);
- read_short(msg->mousepos.y, p, len);
- break;
- case barrierCmdDMouseDown:
- case barrierCmdDMouseUp:
- read_char(msg->mousebutton.buttonid, p, len);
- break;
- case barrierCmdDMouseWheel:
- read_short(msg->mousepos.y, p, len);
- msg->mousepos.x = 0;
- if (len) {
- msg->mousepos.x = msg->mousepos.y;
- read_short(msg->mousepos.y, p, len);
- }
- break;
- /* keyboard */
- case barrierCmdDKeyDown:
- case barrierCmdDKeyUp:
- read_short(msg->key.keyid, p, len);
- read_short(msg->key.modifier, p, len);
- msg->key.button = 0;
- if (len) {
- read_short(msg->key.button, p, len);
- }
- break;
- case barrierCmdDKeyRepeat:
- read_short(msg->repeat.keyid, p, len);
- read_short(msg->repeat.modifier, p, len);
- read_short(msg->repeat.repeat, p, len);
- msg->repeat.button = 0;
- if (len) {
- read_short(msg->repeat.button, p, len);
- }
- break;
- case barrierCmdCInfoAck:
- case barrierCmdCResetOptions:
- case barrierCmdCEnter:
- case barrierCmdDClipboard:
- case barrierCmdCKeepAlive:
- case barrierCmdCLeave:
- case barrierCmdCClose:
- break;
- /* Invalid from the server */
- case barrierCmdHelloBack:
- case barrierCmdCNoop:
- case barrierCmdDInfo:
- break;
- /* Error codes */
- case barrierCmdEIncompatible:
- read_short(msg->version.major, p, len);
- read_short(msg->version.minor, p, len);
- break;
- case barrierCmdEBusy:
- case barrierCmdEUnknown:
- case barrierCmdEBad:
- break;
- default:
- return G_SOURCE_REMOVE;
- }
- return G_SOURCE_CONTINUE;
- }
- static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg)
- {
- char *p;
- int ret, i;
- int avail, len;
- p = ib->buffer;
- avail = MAX_HELLO_LENGTH;
- /* reserve space to store the length */
- p += sizeof(int);
- avail -= sizeof(int);
- switch (msg->cmd) {
- case barrierCmdHello:
- if (msg->version.major < BARRIER_VERSION_MAJOR ||
- (msg->version.major == BARRIER_VERSION_MAJOR &&
- msg->version.minor < BARRIER_VERSION_MINOR)) {
- ib->ioc_tag = 0;
- return G_SOURCE_REMOVE;
- }
- write_cmd(p, barrierCmdHelloBack, avail);
- write_short(p, BARRIER_VERSION_MAJOR, avail);
- write_short(p, BARRIER_VERSION_MINOR, avail);
- write_string(p, ib->name, avail);
- break;
- case barrierCmdCClose:
- ib->ioc_tag = 0;
- return G_SOURCE_REMOVE;
- case barrierCmdQInfo:
- write_cmd(p, barrierCmdDInfo, avail);
- write_short(p, ib->x_origin, avail);
- write_short(p, ib->y_origin, avail);
- write_short(p, ib->width, avail);
- write_short(p, ib->height, avail);
- write_short(p, 0, avail); /* warpsize (obsolete) */
- write_short(p, 0, avail); /* mouse x */
- write_short(p, 0, avail); /* mouse y */
- break;
- case barrierCmdCInfoAck:
- break;
- case barrierCmdCResetOptions:
- /* TODO: reset options */
- break;
- case barrierCmdDSetOptions:
- /* TODO: set options */
- break;
- case barrierCmdCEnter:
- break;
- case barrierCmdDClipboard:
- break;
- case barrierCmdCKeepAlive:
- write_cmd(p, barrierCmdCKeepAlive, avail);
- break;
- case barrierCmdCLeave:
- break;
- /* mouse */
- case barrierCmdDMouseMove:
- qemu_input_queue_abs(NULL, INPUT_AXIS_X, msg->mousepos.x,
- ib->x_origin, ib->width);
- qemu_input_queue_abs(NULL, INPUT_AXIS_Y, msg->mousepos.y,
- ib->y_origin, ib->height);
- qemu_input_event_sync();
- break;
- case barrierCmdDMouseRelMove:
- qemu_input_queue_rel(NULL, INPUT_AXIS_X, msg->mousepos.x);
- qemu_input_queue_rel(NULL, INPUT_AXIS_Y, msg->mousepos.y);
- qemu_input_event_sync();
- break;
- case barrierCmdDMouseDown:
- qemu_input_queue_btn(NULL,
- input_barrier_to_mouse(msg->mousebutton.buttonid),
- true);
- qemu_input_event_sync();
- break;
- case barrierCmdDMouseUp:
- qemu_input_queue_btn(NULL,
- input_barrier_to_mouse(msg->mousebutton.buttonid),
- false);
- qemu_input_event_sync();
- break;
- case barrierCmdDMouseWheel:
- qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP
- : INPUT_BUTTON_WHEEL_DOWN, true);
- qemu_input_event_sync();
- qemu_input_queue_btn(NULL, (msg->mousepos.y > 0) ? INPUT_BUTTON_WHEEL_UP
- : INPUT_BUTTON_WHEEL_DOWN, false);
- qemu_input_event_sync();
- break;
- /* keyboard */
- case barrierCmdDKeyDown:
- qemu_input_event_send_key_qcode(NULL,
- input_barrier_to_qcode(msg->key.keyid, msg->key.button),
- true);
- break;
- case barrierCmdDKeyRepeat:
- for (i = 0; i < msg->repeat.repeat; i++) {
- qemu_input_event_send_key_qcode(NULL,
- input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button),
- false);
- qemu_input_event_send_key_qcode(NULL,
- input_barrier_to_qcode(msg->repeat.keyid, msg->repeat.button),
- true);
- }
- break;
- case barrierCmdDKeyUp:
- qemu_input_event_send_key_qcode(NULL,
- input_barrier_to_qcode(msg->key.keyid, msg->key.button),
- false);
- break;
- default:
- write_cmd(p, barrierCmdEUnknown, avail);
- break;
- }
- len = MAX_HELLO_LENGTH - avail - sizeof(int);
- if (len) {
- p = ib->buffer;
- avail = sizeof(len);
- write_int(p, len, avail);
- ret = qio_channel_write(QIO_CHANNEL(ib->sioc), ib->buffer,
- len + sizeof(len), NULL);
- if (ret < 0) {
- ib->ioc_tag = 0;
- return G_SOURCE_REMOVE;
- }
- }
- return G_SOURCE_CONTINUE;
- }
- static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED,
- GIOCondition condition, void *opaque)
- {
- InputBarrier *ib = opaque;
- int ret;
- struct barrierMsg msg;
- ret = readcmd(ib, &msg);
- if (ret == G_SOURCE_REMOVE) {
- ib->ioc_tag = 0;
- return G_SOURCE_REMOVE;
- }
- return writecmd(ib, &msg);
- }
- static void input_barrier_complete(UserCreatable *uc, Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(uc);
- Error *local_err = NULL;
- if (!ib->name) {
- error_setg(errp, QERR_MISSING_PARAMETER, "name");
- return;
- }
- /*
- * Connect to the primary
- * Primary is the server where the keyboard and the mouse
- * are connected and forwarded to the secondary (the client)
- */
- ib->sioc = qio_channel_socket_new();
- qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client");
- qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
- qio_channel_set_delay(QIO_CHANNEL(ib->sioc), false);
- ib->ioc_tag = qio_channel_add_watch(QIO_CHANNEL(ib->sioc), G_IO_IN,
- input_barrier_event, ib, NULL);
- }
- static void input_barrier_instance_finalize(Object *obj)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- if (ib->ioc_tag) {
- g_source_remove(ib->ioc_tag);
- ib->ioc_tag = 0;
- }
- if (ib->sioc) {
- qio_channel_close(QIO_CHANNEL(ib->sioc), NULL);
- object_unref(OBJECT(ib->sioc));
- }
- g_free(ib->name);
- g_free(ib->saddr.u.inet.host);
- g_free(ib->saddr.u.inet.port);
- }
- static char *input_barrier_get_name(Object *obj, Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- return g_strdup(ib->name);
- }
- static void input_barrier_set_name(Object *obj, const char *value,
- Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- if (ib->name) {
- error_setg(errp, "name property already set");
- return;
- }
- ib->name = g_strdup(value);
- }
- static char *input_barrier_get_server(Object *obj, Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- return g_strdup(ib->saddr.u.inet.host);
- }
- static void input_barrier_set_server(Object *obj, const char *value,
- Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- g_free(ib->saddr.u.inet.host);
- ib->saddr.u.inet.host = g_strdup(value);
- }
- static char *input_barrier_get_port(Object *obj, Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- return g_strdup(ib->saddr.u.inet.port);
- }
- static void input_barrier_set_port(Object *obj, const char *value,
- Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- g_free(ib->saddr.u.inet.port);
- ib->saddr.u.inet.port = g_strdup(value);
- }
- static void input_barrier_set_x_origin(Object *obj, const char *value,
- Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- int result, err;
- err = qemu_strtoi(value, NULL, 0, &result);
- if (err < 0 || result < 0 || result > SHRT_MAX) {
- error_setg(errp,
- "x-origin property must be in the range [0..%d]", SHRT_MAX);
- return;
- }
- ib->x_origin = result;
- }
- static char *input_barrier_get_x_origin(Object *obj, Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- return g_strdup_printf("%d", ib->x_origin);
- }
- static void input_barrier_set_y_origin(Object *obj, const char *value,
- Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- int result, err;
- err = qemu_strtoi(value, NULL, 0, &result);
- if (err < 0 || result < 0 || result > SHRT_MAX) {
- error_setg(errp,
- "y-origin property must be in the range [0..%d]", SHRT_MAX);
- return;
- }
- ib->y_origin = result;
- }
- static char *input_barrier_get_y_origin(Object *obj, Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- return g_strdup_printf("%d", ib->y_origin);
- }
- static void input_barrier_set_width(Object *obj, const char *value,
- Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- int result, err;
- err = qemu_strtoi(value, NULL, 0, &result);
- if (err < 0 || result < 0 || result > SHRT_MAX) {
- error_setg(errp,
- "width property must be in the range [0..%d]", SHRT_MAX);
- return;
- }
- ib->width = result;
- }
- static char *input_barrier_get_width(Object *obj, Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- return g_strdup_printf("%d", ib->width);
- }
- static void input_barrier_set_height(Object *obj, const char *value,
- Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- int result, err;
- err = qemu_strtoi(value, NULL, 0, &result);
- if (err < 0 || result < 0 || result > SHRT_MAX) {
- error_setg(errp,
- "height property must be in the range [0..%d]", SHRT_MAX);
- return;
- }
- ib->height = result;
- }
- static char *input_barrier_get_height(Object *obj, Error **errp)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- return g_strdup_printf("%d", ib->height);
- }
- static void input_barrier_instance_init(Object *obj)
- {
- InputBarrier *ib = INPUT_BARRIER(obj);
- /* always use generic keymaps */
- if (keyboard_layout && !kbd_layout) {
- /* We use X11 key id, so use VNC name2keysym */
- kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
- &error_fatal);
- }
- ib->saddr.type = SOCKET_ADDRESS_TYPE_INET;
- ib->saddr.u.inet.host = g_strdup("localhost");
- ib->saddr.u.inet.port = g_strdup("24800");
- ib->x_origin = 0;
- ib->y_origin = 0;
- ib->width = 1920;
- ib->height = 1080;
- }
- static void input_barrier_class_init(ObjectClass *oc, void *data)
- {
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
- ucc->complete = input_barrier_complete;
- object_class_property_add_str(oc, "name",
- input_barrier_get_name,
- input_barrier_set_name);
- object_class_property_add_str(oc, "server",
- input_barrier_get_server,
- input_barrier_set_server);
- object_class_property_add_str(oc, "port",
- input_barrier_get_port,
- input_barrier_set_port);
- object_class_property_add_str(oc, "x-origin",
- input_barrier_get_x_origin,
- input_barrier_set_x_origin);
- object_class_property_add_str(oc, "y-origin",
- input_barrier_get_y_origin,
- input_barrier_set_y_origin);
- object_class_property_add_str(oc, "width",
- input_barrier_get_width,
- input_barrier_set_width);
- object_class_property_add_str(oc, "height",
- input_barrier_get_height,
- input_barrier_set_height);
- }
- static const TypeInfo input_barrier_info = {
- .name = TYPE_INPUT_BARRIER,
- .parent = TYPE_OBJECT,
- .class_init = input_barrier_class_init,
- .instance_size = sizeof(InputBarrier),
- .instance_init = input_barrier_instance_init,
- .instance_finalize = input_barrier_instance_finalize,
- .interfaces = (InterfaceInfo[]) {
- { TYPE_USER_CREATABLE },
- { }
- }
- };
- static void register_types(void)
- {
- type_register_static(&input_barrier_info);
- }
- type_init(register_types);
|