| /* | |
| * 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, const 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 = (const InterfaceInfo[]) { | |
| { TYPE_USER_CREATABLE }, | |
| { } | |
| } | |
| }; | |
| static void register_types(void) | |
| { | |
| type_register_static(&input_barrier_info); | |
| } | |
| type_init(register_types); |