qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v3] ui: add an embedded Barrier client


From: Laurent Vivier
Subject: Re: [Qemu-devel] [PATCH v3] ui: add an embedded Barrier client
Date: Mon, 16 Sep 2019 14:14:57 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.8.0


Ping?


Le 06/09/2019 à 10:38, Laurent Vivier a écrit :
> This allows to receive mouse and keyboard events from
> a Barrier server.
> 
> This is enabled by adding the following parameter on the
> command line
> 
>     ... -object input-barrier,id=$id,name=$name ...
> 
> Where $name is the name declared in the screens section of barrier.conf
> 
> The barrier server (barriers) must be configured and must run on the
> local host.
> 
> For instance:
> 
>   section: screens
>       localhost:
>           ...
>       VM-1:
>           ...
>       end
> 
>   section: links
>       localhost:
>           right = VM-1
>       VM-1:
>           left = localhost
>   end
> 
> Then on the QEMU command line:
> 
>     ... -object input-barrier,id=barrie0,name=VM-1 ...
> 
> When the mouse will move out of the screen of the local host on
> the right, the mouse and the keyboard will be grabbed and all
> related events will be send to the guest OS.
> 
> This is usefull when qemu is configured without emulated graphic card
> but with a VFIO attached graphic card.
> 
> More information about Barrier can be found at:
> 
>   https://github.com/debauchee/barrier
> 
> This avoids to install the Barrier server in the guest OS,
> for instance when it is not supported or during the installation.
> 
> Signed-off-by: Laurent Vivier <address@hidden>
> ---
> 
> Notes:
>     v3: remove typo in barrier.txt
>     
>     v2: use qemu_input_map_xorgkbd_to_qcode[] to convert button field
>         use keyboard_layout to convert keys if button field is not available
>         fix some command in the protocol
>         add ui/input-barrier.h
>         rework read/write functions
>         add server/port properties
>         add display properties
>         rework protocol
>         allow to reconnect (object_del + object_add)
>         add documentation
> 
>  docs/barrier.txt   | 370 ++++++++++++++++++++++
>  ui/Makefile.objs   |   1 +
>  ui/input-barrier.c | 750 +++++++++++++++++++++++++++++++++++++++++++++
>  ui/input-barrier.h | 112 +++++++
>  4 files changed, 1233 insertions(+)
>  create mode 100644 docs/barrier.txt
>  create mode 100644 ui/input-barrier.c
>  create mode 100644 ui/input-barrier.h
> 
> diff --git a/docs/barrier.txt b/docs/barrier.txt
> new file mode 100644
> index 000000000000..b21d15015d96
> --- /dev/null
> +++ b/docs/barrier.txt
> @@ -0,0 +1,370 @@
> +                                QEMU Barrier Client
> +
> +
> +* About
> +
> +    Barrier is a KVM (Keyboard-Video-Mouse) software forked from Symless's
> +    synergy 1.9 codebase.
> +
> +    See https://github.com/debauchee/barrier
> +
> +* QEMU usage
> +
> +    Generally, mouse and keyboard are grabbed through the QEMU video
> +    interface emulation.
> +
> +    But when we want to use a video graphic adapter via a PCI passthrough
> +    there is no way to provide the keyboard and mouse inputs to the VM
> +    except by plugging a second set of mouse and keyboard to the host
> +    or by installing a KVM software in the guest OS.
> +
> +    The QEMU Barrier client avoids this by implementing directly the Barrier
> +    protocol into QEMU.
> +
> +    This protocol is enabled by adding an input-barrier object to QEMU.
> +
> +    Syntax: input-barrier,id=<object-id>,name=<guest display name>
> +            [,server=<barrier server address>][,port=<barrier server port>]
> +            [,x-origin=<x-origin>][,y-origin=<y-origin>]
> +            [,width=<width>][,height=<height>]
> +
> +    The object can be added on the QEMU command line, for instance with:
> +
> +        ... -object input-barrier,id=barrier0,name=VM-1 ...
> +
> +    where VM-1 is the name the display configured int the Barrier server
> +    on the host providing the mouse and the keyboard events.
> +
> +    by default <barrier server address> is "localhost", port is 24800,
> +    <x-origin> and <y-origin> are set to 0, <width> and <height> to
> +    1920 and 1080.
> +
> +    If Barrier server is stopped QEMU needs to be reconnected manually,
> +    by removing and re-adding the input-barrier object, for instance
> +    with the help of the HMP monitor:
> +
> +        (qemu) object_del barrier0
> +        (qemu) object_add input-barrier,id=barrier0,name=VM-1
> +
> +* Message format
> +
> +    Message format between the server and client is in two parts:
> +
> +        1- the payload length is a 32bit integer in network endianness,
> +        2- the payload
> +
> +    The payload starts with a 4byte string (without NUL) which is the
> +    command. The first command between the server and the client
> +    is the only command not encoded on 4 bytes ("Barrier").
> +    The remaining part of the payload is decoded according to the command.
> +
> +* Protocol Description (from barrier/src/lib/barrier/protocol_types.h)
> +
> +    - barrierCmdHello          "Barrier"
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t minor, int16_t major }
> +      Description:
> +
> +          Say hello to client
> +          minor = protocol major version number supported by server
> +          major = protocol minor version number supported by server
> +
> +    - barrierCmdHelloBack      "Barrier"
> +
> +      Direction:  client ->server
> +      Parameters: { int16_t minor, int16_t major, char *name}
> +      Description:
> +
> +          Respond to hello from server
> +          minor = protocol major version number supported by client
> +          major = protocol minor version number supported by client
> +          name  = client name
> +
> +    - barrierCmdDInfo          "DINF"
> +
> +      Direction:  client ->server
> +      Parameters: { int16_t x_origin, int16_t y_origin, int16_t width, 
> int16_t height, int16_t x, int16_t y}
> +      Description:
> +
> +          The client screen must send this message in response to the
> +          barrierCmdQInfo message.  It must also send this message when the
> +          screen's resolution changes.  In this case, the client screen 
> should
> +          ignore any barrierCmdDMouseMove messages until it receives a
> +          barrierCmdCInfoAck in order to prevent attempts to move the mouse 
> off
> +          the new screen area.
> +
> +    - barrierCmdCNoop          "CNOP"
> +
> +      Direction:  client -> server
> +      Parameters: None
> +      Description:
> +
> +          No operation
> +
> +    - barrierCmdCClose         "CBYE"
> +
> +      Direction:  server -> client
> +      Parameters: None
> +      Description:
> +
> +          Close connection
> +
> +    - barrierCmdCEnter         "CINN"
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t x, int16_t y, int32_t seq, int16_t modifier }
> +      Description:
> +
> +          Enter screen.
> +          x,y      = entering screen absolute coordinates
> +          seq      = sequence number, which is used to order messages between
> +                     screens.  the secondary screen must return this number
> +                     with some messages
> +          modifier = modifier key mask.  this will have bits set for each
> +                     toggle modifier key that is activated on entry to the
> +                     screen.  the secondary screen should adjust its toggle
> +                     modifiers to reflect that state.
> +
> +    - barrierCmdCLeave         "COUT"
> +
> +      Direction:  server -> client
> +      Parameters: None
> +      Description:
> +
> +          Leaving screen.  the secondary screen should send clipboard data in
> +          response to this message for those clipboards that it has grabbed
> +          (i.e. has sent a barrierCmdCClipboard for and has not received a
> +          barrierCmdCClipboard for with a greater sequence number) and that
> +          were grabbed or have changed since the last leave.
> +
> +    - barrierCmdCClipboard     "CCLP"
> +
> +      Direction:  server -> client
> +      Parameters: { int8_t id, int32_t seq }
> +      Description:
> +
> +          Grab clipboard. Sent by screen when some other app on that screen
> +          grabs a clipboard.
> +          id  = the clipboard identifier
> +          seq = sequence number. Client must use the sequence number passed 
> in
> +                the most recent barrierCmdCEnter.  the server always sends 0.
> +
> +    - barrierCmdCScreenSaver   "CSEC"
> +
> +      Direction:  server -> client
> +      Parameters: { int8_t started }
> +      Description:
> +
> +          Screensaver change.
> +          started = Screensaver on primary has started (1) or closed (0)
> +
> +    - barrierCmdCResetOptions  "CROP"
> +
> +      Direction:  server -> client
> +      Parameters: None
> +      Description:
> +
> +          Reset options. Client should reset all of its options to their
> +          defaults.
> +
> +    - barrierCmdCInfoAck       "CIAK"
> +
> +      Direction:  server -> client
> +      Parameters: None
> +      Description:
> +
> +          Resolution change acknowledgment. Sent by server in response to a
> +          client screen's barrierCmdDInfo. This is sent for every
> +          barrierCmdDInfo, whether or not the server had sent a 
> barrierCmdQInfo.
> +
> +    - barrierCmdCKeepAlive     "CALV"
> +
> +      Direction:  server -> client
> +      Parameters: None
> +      Description:
> +
> +          Keep connection alive. Sent by the server periodically to verify
> +          that connections are still up and running.  clients must reply in
> +          kind on receipt.  if the server gets an error sending the message 
> or
> +          does not receive a reply within a reasonable time then the server
> +          disconnects the client.  if the client doesn't receive these (or 
> any
> +          message) periodically then it should disconnect from the server.  
> the
> +          appropriate interval is defined by an option.
> +
> +    - barrierCmdDKeyDown       "DKDN"
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t keyid, int16_t modifier [,int16_t button] }
> +      Description:
> +
> +          Key pressed.
> +          keyid    = X11 key id
> +          modified = modified mask
> +          button   = X11 Xkb keycode (optional)
> +
> +    - barrierCmdDKeyRepeat     "DKRP"
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t keyid, int16_t modifier, int16_t repeat 
> [,int16_t button] }
> +      Description:
> +
> +          Key auto-repeat.
> +          keyid    = X11 key id
> +          modified = modified mask
> +          repeat   = number of repeats
> +          button   = X11 Xkb keycode (optional)
> +
> +    - barrierCmdDKeyUp         "DKUP"
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t keyid, int16_t modifier [,int16_t button] }
> +      Description:
> +
> +          Key released.
> +          keyid    = X11 key id
> +          modified = modified mask
> +          button   = X11 Xkb keycode (optional)
> +
> +    - barrierCmdDMouseDown     "DMDN"
> +
> +      Direction:  server -> client
> +      Parameters: { int8_t button }
> +      Description:
> +
> +          Mouse button pressed.
> +          button = button id
> +
> +    - barrierCmdDMouseUp       "DMUP"
> +
> +      Direction:  server -> client
> +      Parameters: { int8_t button }
> +      Description:
> +
> +          Mouse button release.
> +          button = button id
> +
> +    - barrierCmdDMouseMove     "DMMV"
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t x, int16_t y }
> +      Description:
> +
> +          Absolute mouse moved.
> +          x,y = absolute screen coordinates
> +
> +    - barrierCmdDMouseRelMove  "DMRM"
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t x, int16_t y }
> +      Description:
> +
> +          Relative mouse moved.
> +          x,y = r relative screen coordinates
> +
> +    - barrierCmdDMouseWheel    "DMWM"
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t x , int16_t y } or { int16_t y }
> +      Description:
> +
> +          Mouse scroll. The delta should be +120 for one tick forward (away
> +          from the user) or right and -120 for one tick backward (toward the
> +          user) or left.
> +          x = x delta
> +          y = y delta
> +
> +    - barrierCmdDClipboard     "DCLP"
> +
> +      Direction:  server -> client
> +      Parameters: { int8_t id, int32_t seq, int8_t mark, char *data }
> +      Description:
> +
> +          Clipboard data.
> +          id  = clipboard id
> +          seq = sequence number. The sequence number is 0 when sent by the
> +                server.  Client screens should use the/ sequence number from
> +                the most recent barrierCmdCEnter.
> +
> +    - barrierCmdDSetOptions    "DSOP"
> +
> +      Direction:  server -> client
> +      Parameters: { int32 t nb, { int32_t id, int32_t val }[] }
> +      Description:
> +
> +          Set options. Client should set the given option/value pairs.
> +          nb  = numbers of { id, val } entries
> +          id  = option id
> +          val = option new value
> +
> +    - barrierCmdDFileTransfer  "DFTR"
> +
> +      Direction:  server -> client
> +      Parameters: { int8_t mark, char *content }
> +      Description:
> +
> +          Transfer file data.
> +          mark = 0 means the content followed is the file size
> +                 1 means the content followed is the chunk data
> +                 2 means the file transfer is finished
> +
> +    - barrierCmdDDragInfo      "DDRG" int16_t char *
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t nb, char *content }
> +      Description:
> +
> +          Drag information.
> +          nb      = number of dragging objects
> +          content = object's directory
> +
> +    - barrierCmdQInfo          "QINF"
> +
> +      Direction:  server -> client
> +      Parameters: None
> +      Description:
> +
> +          Query screen info
> +          Client should reply with a barrierCmdDInfo
> +
> +    - barrierCmdEIncompatible  "EICV"
> +
> +      Direction:  server -> client
> +      Parameters: { int16_t nb, major *minor }
> +      Description:
> +
> +          Incompatible version.
> +          major = major version
> +          minor = minor version
> +
> +    - barrierCmdEBusy          "EBSY"
> +
> +      Direction:  server -> client
> +      Parameters: None
> +      Description:
> +
> +          Name provided when connecting is already in use.
> +
> +    - barrierCmdEUnknown       "EUNK"
> +
> +      Direction:  server -> client
> +      Parameters: None
> +      Description:
> +
> +          Unknown client. Name provided when connecting is not in primary's
> +           screen configuration map.
> +
> +    - barrierCmdEBad           "EBAD"
> +
> +      Direction:  server -> client
> +      Parameters: None
> +      Description:
> +
> +          Protocol violation. Server should disconnect after sending this
> +          message.
> +
> +* TO DO
> +
> +    - Enable SSL
> +    - Manage SetOptions/ResetOptions commands
> +
> diff --git a/ui/Makefile.objs b/ui/Makefile.objs
> index ba39080edb2b..e6da6ff047fd 100644
> --- a/ui/Makefile.objs
> +++ b/ui/Makefile.objs
> @@ -9,6 +9,7 @@ vnc-obj-y += vnc-jobs.o
>  
>  common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
>  common-obj-y += input.o input-keymap.o input-legacy.o kbd-state.o
> +common-obj-y += input-barrier.o
>  common-obj-$(CONFIG_LINUX) += input-linux.o
>  common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
>  common-obj-$(CONFIG_COCOA) += cocoa.o
> diff --git a/ui/input-barrier.c b/ui/input-barrier.c
> new file mode 100644
> index 000000000000..a2c961f285a4
> --- /dev/null
> +++ b/ui/input-barrier.c
> @@ -0,0 +1,750 @@
> +/*
> + * 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.
> + */
> +
> +#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 "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"
> +#define INPUT_BARRIER(obj) \
> +    OBJECT_CHECK(InputBarrier, (obj), TYPE_INPUT_BARRIER)
> +#define INPUT_BARRIER_GET_CLASS(obj) \
> +    OBJECT_GET_CLASS(InputBarrierClass, (obj), TYPE_INPUT_BARRIER)
> +#define INPUT_BARRIER_CLASS(klass) \
> +    OBJECT_CLASS_CHECK(InputBarrierClass, (klass), TYPE_INPUT_BARRIER)
> +
> +typedef struct InputBarrier InputBarrier;
> +typedef struct InputBarrierClass InputBarrierClass;
> +
> +#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];
> +};
> +
> +struct InputBarrierClass {
> +    ObjectClass parent_class;
> +};
> +
> +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);
> +
> +    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;
> +
> +    object_property_add_str(obj, "name",
> +                            input_barrier_get_name,
> +                            input_barrier_set_name, NULL);
> +    object_property_add_str(obj, "server",
> +                            input_barrier_get_server,
> +                            input_barrier_set_server, NULL);
> +    object_property_add_str(obj, "port",
> +                            input_barrier_get_port,
> +                            input_barrier_set_port, NULL);
> +    object_property_add_str(obj, "x-origin",
> +                            input_barrier_get_x_origin,
> +                            input_barrier_set_x_origin, NULL);
> +    object_property_add_str(obj, "y-origin",
> +                            input_barrier_get_y_origin,
> +                            input_barrier_set_y_origin, NULL);
> +    object_property_add_str(obj, "width",
> +                            input_barrier_get_width,
> +                            input_barrier_set_width, NULL);
> +    object_property_add_str(obj, "height",
> +                            input_barrier_get_height,
> +                            input_barrier_set_height, NULL);
> +}
> +
> +static void input_barrier_class_init(ObjectClass *oc, void *data)
> +{
> +    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
> +
> +    ucc->complete = input_barrier_complete;
> +
> +    /* always use generic keymaps */
> +    if (keyboard_layout) {
> +        /* We use X11 key id, so use VNC name2keysym */
> +        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout,
> +                                          &error_fatal);
> +    }
> +}
> +
> +static const TypeInfo input_barrier_info = {
> +    .name = TYPE_INPUT_BARRIER,
> +    .parent = TYPE_OBJECT,
> +    .class_size = sizeof(InputBarrierClass),
> +    .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);
> diff --git a/ui/input-barrier.h b/ui/input-barrier.h
> new file mode 100644
> index 000000000000..e5b090590ad5
> --- /dev/null
> +++ b/ui/input-barrier.h
> @@ -0,0 +1,112 @@
> +/*
> + * 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.
> + */
> +
> +#ifndef UI_INPUT_BARRIER_H
> +#define UI_INPUT_BARRIER_H
> +
> +/* Barrier protocol */
> +#define BARRIER_VERSION_MAJOR 1
> +#define BARRIER_VERSION_MINOR 6
> +
> +enum barrierCmd {
> +    barrierCmdCNoop,
> +    barrierCmdCClose,
> +    barrierCmdCEnter,
> +    barrierCmdCLeave,
> +    barrierCmdCClipboard,
> +    barrierCmdCScreenSaver,
> +    barrierCmdCResetOptions,
> +    barrierCmdCInfoAck,
> +    barrierCmdCKeepAlive,
> +    barrierCmdDKeyDown,
> +    barrierCmdDKeyRepeat,
> +    barrierCmdDKeyUp,
> +    barrierCmdDMouseDown,
> +    barrierCmdDMouseUp,
> +    barrierCmdDMouseMove,
> +    barrierCmdDMouseRelMove,
> +    barrierCmdDMouseWheel,
> +    barrierCmdDClipboard,
> +    barrierCmdDInfo,
> +    barrierCmdDSetOptions,
> +    barrierCmdDFileTransfer,
> +    barrierCmdDDragInfo,
> +    barrierCmdQInfo,
> +    barrierCmdEIncompatible,
> +    barrierCmdEBusy,
> +    barrierCmdEUnknown,
> +    barrierCmdEBad,
> +    /* connection sequence */
> +    barrierCmdHello,
> +    barrierCmdHelloBack,
> +};
> +
> +enum {
> +   barrierButtonNone,
> +   barrierButtonLeft,
> +   barrierButtonMiddle,
> +   barrierButtonRight,
> +   barrierButtonExtra0
> +};
> +
> +struct barrierVersion {
> +    int16_t major;
> +    int16_t minor;
> +};
> +
> +struct barrierMouseButton {
> +    int8_t buttonid;
> +};
> +
> +struct barrierEnter {
> +    int16_t x;
> +    int16_t y;
> +    int32_t seqn;
> +    int16_t modifier;
> +};
> +
> +struct barrierMousePos {
> +    int16_t x;
> +    int16_t y;
> +};
> +
> +struct barrierKey {
> +    int16_t keyid;
> +    int16_t modifier;
> +    int16_t button;
> +};
> +
> +struct barrierRepeat {
> +    int16_t keyid;
> +    int16_t modifier;
> +    int16_t repeat;
> +    int16_t button;
> +};
> +
> +#define BARRIER_MAX_OPTIONS 32
> +struct barrierSet {
> +    int nb;
> +    struct {
> +        int id;
> +        char nul;
> +        int value;
> +    } option[BARRIER_MAX_OPTIONS];
> +};
> +
> +struct barrierMsg {
> +    enum barrierCmd cmd;
> +    union {
> +        struct barrierVersion version;
> +        struct barrierMouseButton mousebutton;
> +        struct barrierMousePos mousepos;
> +        struct barrierEnter enter;
> +        struct barrierKey key;
> +        struct barrierRepeat repeat;
> +        struct barrierSet set;
> +    };
> +};
> +#endif
> 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]