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