399a04775e
docs/barrier.txt has a couple of TODO notes about things to be implemented in this device; move them into a comment in the source code. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Laurent Vivier <laurent@vivier.eu> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Message-id: 20210727204112.12579-3-peter.maydell@linaro.org
747 lines
22 KiB
C
747 lines
22 KiB
C
/*
|
|
* 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 "sysemu/sysemu.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);
|