qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

input-barrier.c (22180B)


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