xserver: Branch 'master'

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Mon Dec 14 01:28:41 UTC 2020


 configure.ac                                                 |    3 
 hw/xfree86/drivers/Makefile.am                               |    3 
 hw/xfree86/drivers/inputtest/Makefile.am                     |   48 
 hw/xfree86/drivers/inputtest/inputtestdrv.man                |  107 +
 hw/xfree86/drivers/inputtest/meson.build                     |   24 
 hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h |  144 +
 hw/xfree86/drivers/inputtest/xf86-input-inputtest.c          | 1027 +++++++++++
 hw/xfree86/meson.build                                       |    3 
 meson_options.txt                                            |    2 
 9 files changed, 1361 insertions(+)

New commits:
commit 8c0afc9eb26a0866301072dec30717885fd14305
Author: Povilas Kanapickas <povilas at radix.lt>
Date:   Sat Oct 10 02:09:05 2020 +0300

    xfree86: Implement a test input driver
    
    Signed-off-by: Povilas Kanapickas <povilas at radix.lt>

diff --git a/configure.ac b/configure.ac
index 14f936c62..2c9e5dbf6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -585,6 +585,7 @@ AC_ARG_WITH(xwayland-path, AS_HELP_STRING([--with-xwayland-path=PATH], [Director
 AC_ARG_ENABLE(standalone-xpbproxy, AS_HELP_STRING([--enable-standalone-xpbproxy], [Build a standalone xpbproxy (in addition to the one integrated into Xquartz as a separate thread) (default: no)]), [STANDALONE_XPBPROXY=$enableval], [STANDALONE_XPBPROXY=no])
 AC_ARG_ENABLE(xwin,    	      AS_HELP_STRING([--enable-xwin], [Build XWin server (default: auto)]), [XWIN=$enableval], [XWIN=auto])
 AC_ARG_ENABLE(glamor,         AS_HELP_STRING([--enable-glamor], [Build glamor dix module (default: auto)]), [GLAMOR=$enableval], [GLAMOR=auto])
+AC_ARG_ENABLE(xf86-input-inputtest, AS_HELP_STRING([--enable-xf86-input-inputtest], [Build Xorg test input driver (default: yes)]), [XORG_DRIVER_INPUT_INPUTTEST=$enableval], [XORG_DRIVER_INPUT_INPUTTEST=yes])
 dnl kdrive and its subsystems
 AC_ARG_ENABLE(kdrive,         AS_HELP_STRING([--enable-kdrive], [Build kdrive servers (default: no)]), [KDRIVE=$enableval], [KDRIVE=no])
 AC_ARG_ENABLE(xephyr,         AS_HELP_STRING([--enable-xephyr], [Build the kdrive Xephyr server (default: auto)]), [XEPHYR=$enableval], [XEPHYR=auto])
@@ -2019,6 +2020,7 @@ AM_CONDITIONAL([SOLARIS_VT], [test "x$solaris_vt" = xyes])
 AM_CONDITIONAL([DGA], [test "x$DGA" = xyes])
 AM_CONDITIONAL([XORG_BUS_PLATFORM], [test "x$CONFIG_UDEV_KMS" = xyes])
 AM_CONDITIONAL([XORG_DRIVER_MODESETTING], [test "x$XORG_DRIVER_MODESETTING" = xyes])
+AM_CONDITIONAL([XORG_DRIVER_INPUT_INPUTTEST], [test "x$XORG_DRIVER_INPUT_INPUTTEST" = xyes])
 
 dnl glamor
 if test "x$GLAMOR" = xauto; then
@@ -2458,6 +2460,7 @@ hw/xfree86/dri/Makefile
 hw/xfree86/dri2/Makefile
 hw/xfree86/dri2/pci_ids/Makefile
 hw/xfree86/drivers/Makefile
+hw/xfree86/drivers/inputtest/Makefile
 hw/xfree86/drivers/modesetting/Makefile
 hw/xfree86/exa/Makefile
 hw/xfree86/exa/man/Makefile
diff --git a/hw/xfree86/drivers/Makefile.am b/hw/xfree86/drivers/Makefile.am
index 04d787f52..954c9f1d4 100644
--- a/hw/xfree86/drivers/Makefile.am
+++ b/hw/xfree86/drivers/Makefile.am
@@ -3,3 +3,6 @@ SUBDIRS =
 if XORG_DRIVER_MODESETTING
 SUBDIRS += modesetting
 endif
+if XORG_DRIVER_INPUT_INPUTTEST
+SUBDIRS += inputtest
+endif
diff --git a/hw/xfree86/drivers/inputtest/Makefile.am b/hw/xfree86/drivers/inputtest/Makefile.am
new file mode 100644
index 000000000..8affc2535
--- /dev/null
+++ b/hw/xfree86/drivers/inputtest/Makefile.am
@@ -0,0 +1,48 @@
+#  Copyright 2005 Adam Jackson.
+#
+#  Permission is hereby granted, free of charge, to any person obtaining a
+#  copy of this software and associated documentation files (the "Software"),
+#  to deal in the Software without restriction, including without limitation
+#  on the rights to use, copy, modify, merge, publish, distribute, sub
+#  license, and/or sell copies of the Software, and to permit persons to whom
+#  the Software is furnished to do so, subject to the following conditions:
+#
+#  The above copyright notice and this permission notice (including the next
+#  paragraph) shall be included in all copies or substantial portions of the
+#  Software.
+#
+#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#  FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+#  ADAM JACKSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+#  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+#  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# this is obnoxious:
+# -module lets us name the module exactly how we want
+# -avoid-version prevents gratuitous .0.0.0 version numbers on the end
+# _ladir passes a dummy rpath to libtool so the thing will actually link
+# TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc.
+
+include $(top_srcdir)/manpages.am
+
+AM_CFLAGS = $(DIX_CFLAGS) $(XORG_CFLAGS) $(CWARNFLAGS)
+
+AM_CPPFLAGS = $(XORG_INCS)
+
+inputtest_drv_la_LTLIBRARIES = inputtest_drv.la
+inputtest_drv_la_LDFLAGS = -module -avoid-version
+inputtest_drv_ladir = @moduledir@/input
+
+inputtest_drv_la_SOURCES = xf86-input-inputtest.c xf86-input-inputtest-protocol.h
+
+sdk_HEADERS = xf86-input-inputtest-protocol.h
+
+drivermandir = $(DRIVER_MAN_DIR)
+driverman_PRE = inputtestdrv.man
+driverman_DATA = $(driverman_PRE:man=@DRIVER_MAN_SUFFIX@)
+
+EXTRA_DIST = inputtestdrv.man
+
+CLEANFILES = $(driverman_DATA)
+
diff --git a/hw/xfree86/drivers/inputtest/inputtestdrv.man b/hw/xfree86/drivers/inputtest/inputtestdrv.man
new file mode 100644
index 000000000..4ec872c6d
--- /dev/null
+++ b/hw/xfree86/drivers/inputtest/inputtestdrv.man
@@ -0,0 +1,107 @@
+.\" shorthand for double quote that works everywhere.
+.ds q \N'34'
+.TH INPUTTEST __drivermansuffix__ __vendorversion__
+.SH NAME
+inputtest \- An X.Org input driver for testing
+.SH SYNOPSIS
+.nf
+.B "Section \*qInputDevice\*q"
+.BI "  Identifier \*q" devname \*q
+.B  "  Driver \*qinputtest\*q"
+.BI "  Option \*qSocketPath\*q   \*q" path \*q
+\ \ ...
+.B EndSection
+.fi
+
+.SH DESCRIPTION
+.B inputtest
+is an Xorg input driver that passes events received over a socket on to the
+server as input events. It supports the full set of the xf86 driver APIs
+exposed by Xorg. The primary use cases of this input driver are various
+integration tests that need to interface with the input subsystem.
+
+.SH CONFIGURATION DETAILS
+Please refer to __xconfigfile__(__filemansuffix__) for general configuration
+details and for options that can be used with all input drivers.  This
+section only covers configuration details specific to this driver.
+.PP
+External process can communicate with the input driver via a named socket that
+is created after the driver is initialized. The paths to the socket is passed
+via input driver options.
+.PP
+The following driver
+.B Options
+are supported:
+.TP 7
+.BI "Option \*qSocketPath\*q \*q" string \*q
+Sets the path where the driver will create a named socket. Any existing file
+at that location will be removed.
+.TP 7
+.BI "Option \*qDeviceType\*q \*q" string \*q
+Sets the type of the device to be emulated.
+.IP
+.BI Keyboard
+Initializes a keyboard device.
+.IP
+.BI Pointer
+Initializes a relative-mode pointer device. It will have four valuators -
+a "Rel X" valuator at axis 0 and a "Rel Y" valuator at axis 1.
+A horizontal scroll valuator will be set up at axis 2.
+A vertical scroll valuator will be set up at axis 3.
+.IP
+.BI PointerAbsolute
+Initializes an absolute-mode pointer device. It will have four valuators -
+an "Abs X" valuator at axis 0 and an "Abs Y" valuator at axis 1.
+A horizontal scroll valuator will be set up at axis 2.
+A vertical scroll valuator will be set up at axis 3.
+.IP
+.BI PointerAbsoluteProximity
+Initializes an absolute-mode pointer device with proximity support.
+The valuators are initialized in the same way as for \fBPointerAbsolute\fR type.
+.IP
+.BI Touch
+Initializes a touch device.
+It will have 3 valuators: an "Abs MT Position X" at axis 0,
+an "Abs MT Position Y" valuator at axis 1 and an "Abs MT Pressure" valuator
+at axis 2.
+.TP 7
+.BI "Option \*qTouchCount\*q \*q" int \*q
+Sets the maximum number of simultaneous touches for touch devices.
+.TP 7
+.BI "Option \*qPointerButtonCount\*q \*q" int \*q
+Sets the maximum number of buttons in pointer devices.
+
+.SH INTERFACE WITH THE DRIVER
+The communication with the driver is a binary protocol defined in
+include/xf86-input-inputtest-protocol.h
+.PP
+At the beginning, the client process that communicates with the driver must
+connect to the socket that is created by the driver at SocketPath.
+Once the connection is established, it must write a xf86ITEventClientVersion
+event and read a xf86ITResponseServerVersion response where the driver
+specifies the protocol version supported by it. If this version is lower than
+requested by the client, then the driver will disconnect.
+.PP
+After receiving xf86ITResponseServerVersion message the client may send events
+to the driver. Each event is an instance of one of the
+.BI xf86ITEvent*
+structs. The length field defines the full length of the struct in bytes and
+the event field defines the type of the struct.
+.PP
+The responses from the server follow the same structure. Each response is an
+instance of one of the
+.BI xf86ITResponse*
+structs. The length field defines the full length of the struct in bytes and
+the event field defines the type of the struct.
+.PP
+The synchronization with Xorg is performed via
+.BI xf86ITEventWaitForSync
+event. After sending such event, the client must read of a
+.BI xf86ITResponseSyncFinished event from the socket without sending additional
+events. The completion of the read operation indicates that Xorg has fully
+processed all input events sent to it so far.
+
+.SH AUTHORS
+Povilas Kanapickas <povilas at radix.lt>
+.SH "SEE ALSO"
+__xservername__(__appmansuffix__), __xconfigfile__(__filemansuffix__), Xserver(__appmansuffix__), X(__miscmansuffix__)
diff --git a/hw/xfree86/drivers/inputtest/meson.build b/hw/xfree86/drivers/inputtest/meson.build
new file mode 100644
index 000000000..42b6ed197
--- /dev/null
+++ b/hw/xfree86/drivers/inputtest/meson.build
@@ -0,0 +1,24 @@
+inputtestdrv_srcs = [
+    'xf86-input-inputtest.c',
+]
+
+shared_module(
+    'inputtest_drv',
+    inputtestdrv_srcs,
+    name_prefix: '',
+
+    include_directories: [inc, xorg_inc],
+    c_args: xorg_c_args,
+    dependencies: [common_dep],
+
+    install: true,
+    install_dir: join_paths(module_dir, 'input'),
+)
+
+install_man(configure_file(
+    input: 'inputtestdrv.man',
+    output: 'inputtestdrv.4',
+    configuration: manpage_config,
+))
+
+install_data('xf86-input-inputtest-protocol.h', install_dir: xorgsdkdir)
diff --git a/hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h b/hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h
new file mode 100644
index 000000000..267532348
--- /dev/null
+++ b/hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright © 2020 Povilas Kanapickas <povilas at radix.lt>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Red Hat
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.  Red
+ * Hat makes no representations about the suitability of this software
+ * for any purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef XF86_INPUT_INPUTTEST_PROTOCOL_H_
+#define XF86_INPUT_INPUTTEST_PROTOCOL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define XF86IT_PROTOCOL_VERSION_MAJOR 1
+#define XF86IT_PROTOCOL_VERSION_MINOR 0
+
+enum xf86ITResponseType {
+    XF86IT_RESPONSE_SERVER_VERSION,
+    XF86IT_RESPONSE_SYNC_FINISHED,
+};
+
+typedef struct {
+    uint32_t length; /* length of the whole event in bytes, including the header */
+    enum xf86ITResponseType type;
+} xf86ITResponseHeader;
+
+typedef struct {
+    xf86ITResponseHeader header;
+    uint16_t major;
+    uint16_t minor;
+} xf86ITResponseServerVersion;
+
+typedef struct {
+    xf86ITResponseHeader header;
+} xf86ITResponseSyncFinished;
+
+typedef union {
+    xf86ITResponseHeader header;
+    xf86ITResponseServerVersion version;
+} xf86ITResponseAny;
+
+/* We care more about preserving the binary input driver protocol more than the
+   size of the messages, so hardcode a larger valuator count than the server has */
+#define XF86IT_MAX_VALUATORS 64
+
+enum xf86ITEventType {
+    XF86IT_EVENT_CLIENT_VERSION,
+    XF86IT_EVENT_WAIT_FOR_SYNC,
+    XF86IT_EVENT_MOTION,
+    XF86IT_EVENT_PROXIMITY,
+    XF86IT_EVENT_BUTTON,
+    XF86IT_EVENT_KEY,
+    XF86IT_EVENT_TOUCH,
+};
+
+typedef struct {
+    uint32_t length; /* length of the whole event in bytes, including the header */
+    enum xf86ITEventType type;
+} xf86ITEventHeader;
+
+typedef struct {
+    uint32_t has_unaccelerated;
+    uint8_t mask[(XF86IT_MAX_VALUATORS + 7) / 8];
+    double valuators[XF86IT_MAX_VALUATORS];
+    double unaccelerated[XF86IT_MAX_VALUATORS];
+} xf86ITValuatorData;
+
+typedef struct {
+    xf86ITEventHeader header;
+    uint16_t major;
+    uint16_t minor;
+} xf86ITEventClientVersion;
+
+typedef struct {
+    xf86ITEventHeader header;
+} xf86ITEventWaitForSync;
+
+typedef struct {
+    xf86ITEventHeader header;
+    uint32_t is_absolute;
+    xf86ITValuatorData valuators;
+} xf86ITEventMotion;
+
+typedef struct {
+    xf86ITEventHeader header;
+    uint32_t is_prox_in;
+    xf86ITValuatorData valuators;
+} xf86ITEventProximity;
+
+typedef struct {
+    xf86ITEventHeader header;
+    int32_t is_absolute;
+    int32_t button;
+    uint32_t is_press;
+    xf86ITValuatorData valuators;
+} xf86ITEventButton;
+
+typedef struct {
+    xf86ITEventHeader header;
+    int32_t key_code;
+    uint32_t is_press;
+} xf86ITEventKey;
+
+typedef struct {
+    xf86ITEventHeader header;
+    uint32_t touchid;
+    uint32_t touch_type;
+    xf86ITValuatorData valuators;
+} xf86ITEventTouch;
+
+typedef union {
+    xf86ITEventHeader header;
+    xf86ITEventClientVersion version;
+    xf86ITEventMotion motion;
+    xf86ITEventProximity proximity;
+    xf86ITEventButton button;
+    xf86ITEventKey key;
+    xf86ITEventTouch touch;
+} xf86ITEventAny;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* XF86_INPUT_INPUTTEST_PROTOCOL_H_ */
diff --git a/hw/xfree86/drivers/inputtest/xf86-input-inputtest.c b/hw/xfree86/drivers/inputtest/xf86-input-inputtest.c
new file mode 100644
index 000000000..0859c0768
--- /dev/null
+++ b/hw/xfree86/drivers/inputtest/xf86-input-inputtest.c
@@ -0,0 +1,1027 @@
+/*
+ * Copyright © 2013-2017 Red Hat, Inc.
+ * Copyright © 2020 Povilas Kanapickas <povilas at radix.lt>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Red Hat
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.  Red
+ * Hat makes no representations about the suitability of this software
+ * for any purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <exevents.h>
+#include <input.h>
+#include <xkbsrv.h>
+#include <xf86.h>
+#include <xf86Xinput.h>
+#include "xorgVersion.h"
+#include <xserver-properties.h>
+#include <os.h>
+#include <X11/Xatom.h>
+
+#include <linux/input.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <stdbool.h>
+
+#include "xf86-input-inputtest-protocol.h"
+
+#define POINTER_NUM_AXES 4 /* x, y, hscroll, vscroll */
+#define TOUCH_NUM_AXES 4 /* x, y, hscroll, vscroll */
+#define TOUCH_MAX_SLOTS 15
+
+#define TOUCH_AXIS_MAX 0xffff
+#define TABLET_PRESSURE_AXIS_MAX 2047
+
+#define EVENT_BUFFER_SIZE 4096
+
+enum xf86ITDeviceType {
+    DEVICE_KEYBOARD = 1,
+    DEVICE_POINTER,
+    DEVICE_POINTER_ABS,
+    DEVICE_POINTER_ABS_PROXIMITY,
+    DEVICE_TOUCH,
+};
+
+enum xf86ITClientState {
+    CLIENT_STATE_NOT_CONNECTED = 0,
+
+    /* connection_fd is valid */
+    CLIENT_STATE_NEW,
+
+    /* connection_fd is valid and client_protocol.{major,minor} are set */
+    CLIENT_STATE_READY,
+};
+
+typedef struct {
+    InputInfoPtr pInfo;
+
+    int socket_fd;  /* for accepting new clients */
+    int connection_fd; /* current client connection */
+
+    char *socket_path;
+
+    enum xf86ITClientState client_state;
+    struct {
+        int major, minor;
+    } client_protocol;
+
+    struct {
+        char data[EVENT_BUFFER_SIZE];
+        int valid_length;
+    } buffer;
+
+    uint32_t device_type;
+
+    /*  last_processed_event_num == last_event_num and waiting_for_drain != 0 must never be true
+        both at the same time. This would mean that we are waiting for the input queue to be
+        processed, yet all events have already been processed, i.e. a deadlock.
+
+        waiting_for_drain_mutex protects concurrent access to waiting_for_drain variable which
+        may be modified from multiple threads.
+    */
+    pthread_mutex_t waiting_for_drain_mutex;
+    bool waiting_for_drain;
+    int last_processed_event_num;
+    int last_event_num;
+
+    ValuatorMask *valuators;
+    ValuatorMask *valuators_unaccelerated;
+} xf86ITDevice, *xf86ITDevicePtr;
+
+static void
+read_input_from_connection(InputInfoPtr pInfo);
+
+static Bool
+notify_sync_finished(ClientPtr ptr, void *closure)
+{
+    int fd = (int)(intptr_t) closure;
+    xf86ITResponseSyncFinished response;
+    response.header.length = sizeof(response);
+    response.header.type = XF86IT_RESPONSE_SYNC_FINISHED;
+
+    input_lock();
+    /*  we don't really care whether the write succeeds. It may fail if the device is
+        already shut down and the descriptor is closed.
+    */
+    if (write(fd, &response, response.header.length) != response.header.length) {
+        LogMessageVerbSigSafe(X_ERROR, 0,
+                              "inputtest: Failed to write sync response: %s\n",
+                              strerror(errno));
+    }
+    input_unlock();
+    return TRUE;
+}
+
+static void
+input_drain_callback(CallbackListPtr *callback, void *data, void *call_data)
+{
+    void *drain_write_closure;
+    InputInfoPtr pInfo = data;
+    xf86ITDevicePtr driver_data = pInfo->private;
+    bool notify_synchronization = false;
+
+    pthread_mutex_lock(&driver_data->waiting_for_drain_mutex);
+    driver_data->last_processed_event_num = driver_data->last_event_num;
+    if (driver_data->waiting_for_drain) {
+        driver_data->waiting_for_drain = false;
+        notify_synchronization = true;
+    }
+    pthread_mutex_unlock(&driver_data->waiting_for_drain_mutex);
+
+    if (notify_synchronization) {
+        drain_write_closure = (void*)(intptr_t) driver_data->connection_fd;
+        /* One input event may result in additional sets of events being submitted to the
+           input queue from the input processing code itself. This results in
+           input_drain_callback being called multiple times.
+
+           We therefore schedule a WorkProc (to be run when the server is no longer busy)
+           to notify the client when all current events have been processed.
+         */
+        xf86IDrvMsg(pInfo, X_DEBUG, "Synchronization finished\n");
+        QueueWorkProc(notify_sync_finished, NULL, drain_write_closure);
+    }
+}
+
+static void
+read_events(int fd, int ready, void *data)
+{
+    DeviceIntPtr dev = (DeviceIntPtr) data;
+    InputInfoPtr pInfo = dev->public.devicePrivate;
+    read_input_from_connection(pInfo);
+}
+
+static void
+try_accept_connection(int fd, int ready, void *data)
+{
+    DeviceIntPtr dev = (DeviceIntPtr) data;
+    InputInfoPtr pInfo = dev->public.devicePrivate;
+    xf86ITDevicePtr driver_data = pInfo->private;
+    int connection_fd;
+    int flags;
+
+    if (driver_data->connection_fd >= 0)
+        return;
+
+    connection_fd = accept(driver_data->socket_fd, NULL, NULL);
+    if (connection_fd < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK)
+            return;
+        xf86IDrvMsg(pInfo, X_ERROR, "Failed to accept a connection\n");
+        return;
+    }
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Accepted input control connection\n");
+
+    flags = fcntl(connection_fd, F_GETFL, 0);
+    fcntl(connection_fd, F_SETFL, flags | O_NONBLOCK);
+
+    driver_data->connection_fd = connection_fd;
+    xf86AddInputEventDrainCallback(input_drain_callback, pInfo);
+    SetNotifyFd(driver_data->connection_fd, read_events, X_NOTIFY_READ, dev);
+
+    driver_data->client_state = CLIENT_STATE_NEW;
+}
+
+static int
+device_on(DeviceIntPtr dev)
+{
+    InputInfoPtr pInfo = dev->public.devicePrivate;
+    xf86ITDevicePtr driver_data = pInfo->private;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Device turned on\n");
+
+    xf86AddEnabledDevice(pInfo);
+    dev->public.on = TRUE;
+    driver_data->buffer.valid_length = 0;
+
+    try_accept_connection(-1, 0, dev);
+    if (driver_data->connection_fd < 0)
+        SetNotifyFd(driver_data->socket_fd, try_accept_connection, X_NOTIFY_READ, dev);
+
+    return Success;
+}
+
+static void
+teardown_client_connection(InputInfoPtr pInfo)
+{
+    xf86ITDevicePtr driver_data = pInfo->private;
+    if (driver_data->client_state != CLIENT_STATE_NOT_CONNECTED) {
+        RemoveNotifyFd(driver_data->connection_fd);
+        xf86RemoveInputEventDrainCallback(input_drain_callback, pInfo);
+
+        close(driver_data->connection_fd);
+        driver_data->connection_fd = -1;
+    }
+    RemoveNotifyFd(driver_data->socket_fd);
+    driver_data->client_state = CLIENT_STATE_NOT_CONNECTED;
+}
+
+static int
+device_off(DeviceIntPtr dev)
+{
+    InputInfoPtr pInfo = dev->public.devicePrivate;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Device turned off\n");
+
+    if (dev->public.on) {
+        teardown_client_connection(pInfo);
+        xf86RemoveEnabledDevice(pInfo);
+    }
+    dev->public.on = FALSE;
+    return Success;
+}
+
+static void
+ptr_ctl(DeviceIntPtr dev, PtrCtrl *ctl)
+{
+}
+
+static void
+init_button_map(unsigned char *btnmap, size_t size)
+{
+    int i;
+
+    memset(btnmap, 0, size);
+    for (i = 0; i < size; i++)
+        btnmap[i] = i;
+}
+
+static void
+init_button_labels(Atom *labels, size_t size)
+{
+    assert(size > 10);
+
+    memset(labels, 0, size * sizeof(Atom));
+    labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
+    labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
+    labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
+    labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
+    labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
+    labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT);
+    labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);
+    labels[7] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_SIDE);
+    labels[8] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_EXTRA);
+    labels[9] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_FORWARD);
+    labels[10] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_BACK);
+}
+
+static void
+init_axis_labels(Atom *labels, size_t size)
+{
+    memset(labels, 0, size * sizeof(Atom));
+    labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
+    labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
+    labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_HSCROLL);
+    labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_VSCROLL);
+}
+
+static void
+init_pointer(InputInfoPtr pInfo)
+{
+    DeviceIntPtr dev= pInfo->dev;
+    int min, max, res;
+    int nbuttons = 7;
+
+    unsigned char btnmap[MAX_BUTTONS + 1];
+    Atom btnlabels[MAX_BUTTONS];
+    Atom axislabels[POINTER_NUM_AXES];
+
+    nbuttons = xf86SetIntOption(pInfo->options, "PointerButtonCount", 7);
+
+    init_button_map(btnmap, ARRAY_SIZE(btnmap));
+    init_button_labels(btnlabels, ARRAY_SIZE(btnlabels));
+    init_axis_labels(axislabels, ARRAY_SIZE(axislabels));
+
+    InitPointerDeviceStruct((DevicePtr)dev,
+                            btnmap,
+                            nbuttons,
+                            btnlabels,
+                            ptr_ctl,
+                            GetMotionHistorySize(),
+                            POINTER_NUM_AXES,
+                            axislabels);
+    min = -1;
+    max = -1;
+    res = 0;
+
+    xf86InitValuatorAxisStruct(dev, 0, XIGetKnownProperty(AXIS_LABEL_PROP_REL_X),
+                               min, max, res * 1000, 0, res * 1000, Relative);
+    xf86InitValuatorAxisStruct(dev, 1, XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y),
+                               min, max, res * 1000, 0, res * 1000, Relative);
+
+    SetScrollValuator(dev, 2, SCROLL_TYPE_HORIZONTAL, 15, 0);
+    SetScrollValuator(dev, 3, SCROLL_TYPE_VERTICAL, 15, 0);
+}
+
+static void
+init_pointer_absolute(InputInfoPtr pInfo)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    int min, max, res;
+    int nbuttons = 7;
+
+    unsigned char btnmap[MAX_BUTTONS + 1];
+    Atom btnlabels[MAX_BUTTONS];
+    Atom axislabels[POINTER_NUM_AXES];
+
+    nbuttons = xf86SetIntOption(pInfo->options, "PointerButtonCount", 7);
+
+    init_button_map(btnmap, ARRAY_SIZE(btnmap));
+    init_button_labels(btnlabels, ARRAY_SIZE(btnlabels));
+    init_axis_labels(axislabels, ARRAY_SIZE(axislabels));
+
+    InitPointerDeviceStruct((DevicePtr)dev,
+                            btnmap,
+                            nbuttons,
+                            btnlabels,
+                            ptr_ctl,
+                            GetMotionHistorySize(),
+                            POINTER_NUM_AXES,
+                            axislabels);
+    min = 0;
+    max = TOUCH_AXIS_MAX;
+    res = 0;
+
+    xf86InitValuatorAxisStruct(dev, 0, XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X),
+                               min, max, res * 1000, 0, res * 1000, Absolute);
+    xf86InitValuatorAxisStruct(dev, 1, XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y),
+                               min, max, res * 1000, 0, res * 1000, Absolute);
+
+    SetScrollValuator(dev, 2, SCROLL_TYPE_HORIZONTAL, 15, 0);
+    SetScrollValuator(dev, 3, SCROLL_TYPE_VERTICAL, 15, 0);
+
+}
+
+static void
+init_proximity(InputInfoPtr pInfo)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    InitProximityClassDeviceStruct(dev);
+}
+
+static void
+init_keyboard(InputInfoPtr pInfo)
+{
+    DeviceIntPtr dev= pInfo->dev;
+    XkbRMLVOSet rmlvo = {0};
+    XkbRMLVOSet defaults = {0};
+
+    XkbGetRulesDflts(&defaults);
+
+    rmlvo.rules = xf86SetStrOption(pInfo->options, "xkb_rules", defaults.rules);
+    rmlvo.model = xf86SetStrOption(pInfo->options, "xkb_model", defaults.model);
+    rmlvo.layout = xf86SetStrOption(pInfo->options, "xkb_layout", defaults.layout);
+    rmlvo.variant = xf86SetStrOption(pInfo->options, "xkb_variant", defaults.variant);
+    rmlvo.options = xf86SetStrOption(pInfo->options, "xkb_options", defaults.options);
+
+    InitKeyboardDeviceStruct(dev, &rmlvo, NULL, NULL);
+    XkbFreeRMLVOSet(&rmlvo, FALSE);
+    XkbFreeRMLVOSet(&defaults, FALSE);
+}
+
+static void
+init_touch(InputInfoPtr pInfo)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    int min, max, res;
+    unsigned char btnmap[MAX_BUTTONS + 1];
+    Atom btnlabels[MAX_BUTTONS];
+    Atom axislabels[TOUCH_NUM_AXES];
+    int nbuttons = 7;
+    int ntouches = TOUCH_MAX_SLOTS;
+
+    init_button_map(btnmap, ARRAY_SIZE(btnmap));
+    init_button_labels(btnlabels, ARRAY_SIZE(btnlabels));
+    init_axis_labels(axislabels, ARRAY_SIZE(axislabels));
+
+    InitPointerDeviceStruct((DevicePtr)dev,
+                            btnmap,
+                            nbuttons,
+                            btnlabels,
+                            ptr_ctl,
+                            GetMotionHistorySize(),
+                            TOUCH_NUM_AXES,
+                            axislabels);
+    min = 0;
+    max = TOUCH_AXIS_MAX;
+    res = 0;
+
+    xf86InitValuatorAxisStruct(dev, 0,
+                               XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_X),
+                               min, max, res * 1000, 0, res * 1000, Absolute);
+    xf86InitValuatorAxisStruct(dev, 1,
+                               XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_Y),
+                               min, max, res * 1000, 0, res * 1000, Absolute);
+    xf86InitValuatorAxisStruct(dev, 2,
+                               XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_PRESSURE),
+                               min, TABLET_PRESSURE_AXIS_MAX, res * 1000, 0, res * 1000, Absolute);
+
+    ntouches = xf86SetIntOption(pInfo->options, "TouchCount", TOUCH_MAX_SLOTS);
+    if (ntouches == 0) /* unknown */
+        ntouches = TOUCH_MAX_SLOTS;
+    InitTouchClassDeviceStruct(dev, ntouches, XIDirectTouch, 2);
+}
+
+static void
+device_init(DeviceIntPtr dev)
+{
+    InputInfoPtr pInfo = dev->public.devicePrivate;
+    xf86ITDevicePtr driver_data = pInfo->private;
+
+    dev->public.on = FALSE;
+
+    switch (driver_data->device_type) {
+        case DEVICE_KEYBOARD:
+            init_keyboard(pInfo);
+            break;
+        case DEVICE_POINTER:
+            init_pointer(pInfo);
+            break;
+        case DEVICE_POINTER_ABS:
+            init_pointer_absolute(pInfo);
+            break;
+        case DEVICE_POINTER_ABS_PROXIMITY:
+            init_pointer_absolute(pInfo);
+            init_proximity(pInfo);
+            break;
+        case DEVICE_TOUCH:
+            init_touch(pInfo);
+            break;
+    }
+}
+
+static void
+device_destroy(DeviceIntPtr dev)
+{
+    InputInfoPtr pInfo = dev->public.devicePrivate;
+    xf86IDrvMsg(pInfo, X_INFO, "Close\n");
+}
+
+static int
+device_control(DeviceIntPtr dev, int mode)
+{
+    switch (mode) {
+        case DEVICE_INIT:
+            device_init(dev);
+            break;
+        case DEVICE_ON:
+            device_on(dev);
+            break;
+        case DEVICE_OFF:
+            device_off(dev);
+            break;
+        case DEVICE_CLOSE:
+            device_destroy(dev);
+            break;
+    }
+
+    return Success;
+}
+
+static void
+convert_to_valuator_mask(xf86ITValuatorData *event, ValuatorMask *mask)
+{
+    valuator_mask_zero(mask);
+    for (int i = 0; i < min(XF86IT_MAX_VALUATORS, MAX_VALUATORS); ++i) {
+        if (BitIsOn(event->mask, i)) {
+            if (event->has_unaccelerated) {
+                valuator_mask_set_unaccelerated(mask, i, event->valuators[i],
+                                                event->unaccelerated[i]);
+            } else {
+                valuator_mask_set_double(mask, i, event->valuators[i]);
+            }
+        }
+    }
+}
+
+static void
+handle_client_version(InputInfoPtr pInfo, xf86ITEventClientVersion *event)
+{
+    xf86ITDevicePtr driver_data = pInfo->private;
+    xf86ITResponseServerVersion response;
+
+    response.header.length = sizeof(response);
+    response.header.type = XF86IT_RESPONSE_SERVER_VERSION;
+    response.major = XF86IT_PROTOCOL_VERSION_MAJOR;
+    response.minor = XF86IT_PROTOCOL_VERSION_MINOR;
+
+    if (write(driver_data->connection_fd, &response, response.header.length) != response.header.length) {
+        xf86IDrvMsg(pInfo, X_ERROR, "Error writing driver version: %s\n", strerror(errno));
+        teardown_client_connection(pInfo);
+        return;
+    }
+
+    if (event->major != XF86IT_PROTOCOL_VERSION_MAJOR ||
+        event->minor > XF86IT_PROTOCOL_VERSION_MINOR)
+    {
+        xf86IDrvMsg(pInfo, X_ERROR, "Unsupported protocol version: %d.%d (current %d.%d)\n",
+                    event->major, event->minor,
+                    XF86IT_PROTOCOL_VERSION_MAJOR,
+                    XF86IT_PROTOCOL_VERSION_MINOR);
+        teardown_client_connection(pInfo);
+        return;
+    }
+
+    driver_data->client_protocol.major = event->major;
+    driver_data->client_protocol.minor = event->minor;
+
+    driver_data->client_state = CLIENT_STATE_READY;
+}
+
+static void
+handle_wait_for_sync(InputInfoPtr pInfo)
+{
+    xf86ITDevicePtr driver_data = pInfo->private;
+    bool notify_synchronization = false;
+    void *drain_write_closure;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Handling sync event\n");
+
+    pthread_mutex_lock(&driver_data->waiting_for_drain_mutex);
+    if (driver_data->last_processed_event_num == driver_data->last_event_num) {
+        notify_synchronization = true;
+    } else {
+        driver_data->waiting_for_drain = true;
+    }
+    pthread_mutex_unlock(&driver_data->waiting_for_drain_mutex);
+
+    if (notify_synchronization) {
+        drain_write_closure = (void*)(intptr_t) driver_data->connection_fd;
+        xf86IDrvMsg(pInfo, X_DEBUG, "Synchronization finished\n");
+        notify_sync_finished(NULL, drain_write_closure);
+    }
+}
+
+static void
+handle_motion(InputInfoPtr pInfo, xf86ITEventMotion *event)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    xf86ITDevicePtr driver_data = pInfo->private;
+    ValuatorMask *mask = driver_data->valuators;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Handling motion event\n");
+
+    driver_data->last_event_num++;
+
+    convert_to_valuator_mask(&event->valuators, mask);
+    xf86PostMotionEventM(dev, event->is_absolute ? Absolute : Relative, mask);
+}
+
+static void
+handle_proximity(InputInfoPtr pInfo, xf86ITEventProximity *event)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    xf86ITDevicePtr driver_data = pInfo->private;
+    ValuatorMask *mask = driver_data->valuators;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Handling proximity event\n");
+
+    driver_data->last_event_num++;
+
+    convert_to_valuator_mask(&event->valuators, mask);
+    xf86PostProximityEventM(dev, event->is_prox_in, mask);
+}
+
+static void
+handle_button(InputInfoPtr pInfo, xf86ITEventButton *event)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    xf86ITDevicePtr driver_data = pInfo->private;
+    ValuatorMask *mask = driver_data->valuators;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Handling button event\n");
+
+    driver_data->last_event_num++;
+
+    convert_to_valuator_mask(&event->valuators, mask);
+    xf86PostButtonEventM(dev, event->is_absolute ? Absolute : Relative, event->button,
+                         event->is_press, mask);
+}
+
+static void
+handle_key(InputInfoPtr pInfo, xf86ITEventKey *event)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    xf86ITDevicePtr driver_data = pInfo->private;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Handling key event\n");
+
+    driver_data->last_event_num++;
+
+    xf86PostKeyboardEvent(dev, event->key_code, event->is_press);
+}
+
+static void
+handle_touch(InputInfoPtr pInfo, xf86ITEventTouch *event)
+{
+    DeviceIntPtr dev = pInfo->dev;
+    xf86ITDevicePtr driver_data = pInfo->private;
+    ValuatorMask *mask = driver_data->valuators;
+
+    xf86IDrvMsg(pInfo, X_DEBUG, "Handling touch event\n");
+
+    driver_data->last_event_num++;
+
+    convert_to_valuator_mask(&event->valuators, mask);
+    xf86PostTouchEvent(dev, event->touchid, event->touch_type, 0, mask);
+}
+
+static void
+client_new_handle_event(InputInfoPtr pInfo, xf86ITEventAny *event)
+{
+    switch (event->header.type) {
+        case XF86IT_EVENT_CLIENT_VERSION:
+            handle_client_version(pInfo, &event->version);
+            break;
+        default:
+            xf86IDrvMsg(pInfo, X_ERROR, "Event before client is ready: event type %d\n",
+                        event->header.type);
+            teardown_client_connection(pInfo);
+            break;
+    }
+}
+
+static void
+client_ready_handle_event(InputInfoPtr pInfo, xf86ITEventAny *event)
+{
+    switch (event->header.type) {
+        case XF86IT_EVENT_WAIT_FOR_SYNC:
+            handle_wait_for_sync(pInfo);
+            break;
+        case XF86IT_EVENT_MOTION:
+            handle_motion(pInfo, &event->motion);
+            break;
+        case XF86IT_EVENT_PROXIMITY:
+            handle_proximity(pInfo, &event->proximity);
+            break;
+        case XF86IT_EVENT_BUTTON:
+            handle_button(pInfo, &event->button);
+            break;
+        case XF86IT_EVENT_KEY:
+            handle_key(pInfo, &event->key);
+            break;
+        case XF86IT_EVENT_TOUCH:
+            handle_touch(pInfo, &event->touch);
+            break;
+        case XF86IT_EVENT_CLIENT_VERSION:
+            xf86IDrvMsg(pInfo, X_ERROR, "Only single ClientVersion event is allowed\n");
+            teardown_client_connection(pInfo);
+            break;
+        default:
+            xf86IDrvMsg(pInfo, X_ERROR, "Invalid event when client is ready %d\n",
+                        event->header.type);
+            teardown_client_connection(pInfo);
+            break;
+    }
+}
+
+static void
+handle_event(InputInfoPtr pInfo, xf86ITEventAny *event)
+{
+    xf86ITDevicePtr driver_data = pInfo->private;
+
+    if (!pInfo->dev->public.on)
+        return;
+
+    switch (driver_data->client_state) {
+        case CLIENT_STATE_NOT_CONNECTED:
+            xf86IDrvMsg(pInfo, X_ERROR, "Got event when client is not connected\n");
+            break;
+        case CLIENT_STATE_NEW:
+            client_new_handle_event(pInfo, event);
+            break;
+        case CLIENT_STATE_READY:
+            client_ready_handle_event(pInfo, event);
+            break;
+    }
+}
+
+static bool
+is_supported_event(enum xf86ITEventType type)
+{
+    switch (type) {
+        case XF86IT_EVENT_CLIENT_VERSION:
+        case XF86IT_EVENT_WAIT_FOR_SYNC:
+        case XF86IT_EVENT_MOTION:
+        case XF86IT_EVENT_PROXIMITY:
+        case XF86IT_EVENT_BUTTON:
+        case XF86IT_EVENT_KEY:
+        case XF86IT_EVENT_TOUCH:
+            return true;
+    }
+    return false;
+}
+
+static int
+get_event_size(enum xf86ITEventType type)
+{
+    switch (type) {
+        case XF86IT_EVENT_CLIENT_VERSION: return sizeof(xf86ITEventClientVersion);
+        case XF86IT_EVENT_WAIT_FOR_SYNC: return sizeof(xf86ITEventWaitForSync);
+        case XF86IT_EVENT_MOTION: return sizeof(xf86ITEventMotion);
+        case XF86IT_EVENT_PROXIMITY: return sizeof(xf86ITEventProximity);
+        case XF86IT_EVENT_BUTTON: return sizeof(xf86ITEventButton);
+        case XF86IT_EVENT_KEY: return sizeof(xf86ITEventKey);
+        case XF86IT_EVENT_TOUCH: return sizeof(xf86ITEventTouch);
+    }
+    abort();
+}
+
+static void
+read_input_from_connection(InputInfoPtr pInfo)
+{
+    xf86ITDevicePtr driver_data = pInfo->private;
+
+    while (1) {
+        int processed_size = 0;
+        int read_size = read(driver_data->connection_fd,
+                             driver_data->buffer.data + driver_data->buffer.valid_length,
+                             EVENT_BUFFER_SIZE - driver_data->buffer.valid_length);
+
+        if (read_size < 0) {
+            if (errno == EAGAIN || errno == EWOULDBLOCK)
+                return;
+
+            xf86IDrvMsg(pInfo, X_ERROR, "Error reading events: %s\n", strerror(errno));
+            teardown_client_connection(pInfo);
+            return;
+        }
+
+        driver_data->buffer.valid_length += read_size;
+
+        while (1) {
+            xf86ITEventHeader *event_header;
+            char *event_begin = driver_data->buffer.data + processed_size;
+
+            if (driver_data->buffer.valid_length - processed_size < sizeof(xf86ITEventHeader))
+                break;
+
+            event_header = (xf86ITEventHeader*) event_begin;
+
+            if (event_header->length >= EVENT_BUFFER_SIZE) {
+                xf86IDrvMsg(pInfo, X_ERROR, "Received event with too long length: %d\n",
+                            event_header->length);
+                teardown_client_connection(pInfo);
+                return;
+            }
+
+            if (driver_data->buffer.valid_length - processed_size < event_header->length)
+                break;
+
+            if (is_supported_event(event_header->type)) {
+                int expected_event_size = get_event_size(event_header->type);
+
+                if (event_header->length != expected_event_size) {
+                    xf86IDrvMsg(pInfo, X_ERROR, "Unexpected event length: was %d bytes, "
+                                "expected %d (event type: %d)\n",
+                                event_header->length, expected_event_size,
+                                (int) event_header->type);
+                    teardown_client_connection(pInfo);
+                    return;
+                }
+
+                handle_event(pInfo, (xf86ITEventAny*) event_begin);
+            }
+            processed_size += event_header->length;
+        }
+
+        if (processed_size > 0) {
+            memmove(driver_data->buffer.data,
+                    driver_data->buffer.data + processed_size,
+                    driver_data->buffer.valid_length - processed_size);
+            driver_data->buffer.valid_length -= processed_size;
+        }
+
+        if (read_size == 0)
+            break;
+    }
+}
+
+static void
+read_input(InputInfoPtr pInfo)
+{
+    /* The test input driver does not set up the pInfo->fd and use the regular
+       read_input callback because we want to only accept the connection to
+       the controlling socket after the device is turned on.
+    */
+}
+
+static const char*
+get_type_name(InputInfoPtr pInfo, xf86ITDevicePtr driver_data)
+{
+    switch (driver_data->device_type) {
+        case DEVICE_TOUCH: return XI_TOUCHSCREEN;
+        case DEVICE_POINTER: return XI_MOUSE;
+        case DEVICE_POINTER_ABS: return XI_MOUSE;
+        case DEVICE_POINTER_ABS_PROXIMITY: return XI_TABLET;
+        case DEVICE_KEYBOARD: return XI_KEYBOARD;
+    }
+    xf86IDrvMsg(pInfo, X_ERROR, "Unexpected device type %d\n",
+                driver_data->device_type);
+    return XI_KEYBOARD;
+}
+
+static xf86ITDevicePtr
+device_alloc(void)
+{
+    xf86ITDevicePtr driver_data = calloc(sizeof(xf86ITDevice), 1);
+
+    if (!driver_data)
+        return NULL;
+
+    driver_data->socket_fd = -1;
+    driver_data->connection_fd = -1;
+
+    return driver_data;
+}
+
+static void
+free_driver_data(xf86ITDevicePtr driver_data)
+{
+    if (driver_data) {
+        close(driver_data->connection_fd);
+        close(driver_data->socket_fd);
+        if (driver_data->socket_path)
+            unlink(driver_data->socket_path);
+        free(driver_data->socket_path);
+        pthread_mutex_destroy(&driver_data->waiting_for_drain_mutex);
+
+        if (driver_data->valuators)
+            valuator_mask_free(&driver_data->valuators);
+        if (driver_data->valuators_unaccelerated)
+            valuator_mask_free(&driver_data->valuators_unaccelerated);
+    }
+    free(driver_data);
+}
+
+static int
+pre_init(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
+{
+    xf86ITDevicePtr driver_data = NULL;
+    char *device_type_option;
+    struct sockaddr_un addr;
+
+    pInfo->type_name = 0;
+    pInfo->device_control = device_control;
+    pInfo->read_input = read_input;
+    pInfo->control_proc = NULL;
+    pInfo->switch_mode = NULL;
+
+    driver_data = device_alloc();
+    if (!driver_data)
+        goto fail;
+
+    driver_data->client_state = CLIENT_STATE_NOT_CONNECTED;
+    driver_data->last_event_num = 1;
+    driver_data->last_processed_event_num = 0;
+    driver_data->waiting_for_drain = false;
+    pthread_mutex_init(&driver_data->waiting_for_drain_mutex, NULL);
+
+    driver_data->valuators = valuator_mask_new(6);
+    if (!driver_data->valuators)
+        goto fail;
+
+    driver_data->valuators_unaccelerated = valuator_mask_new(2);
+    if (!driver_data->valuators_unaccelerated)
+        goto fail;
+
+    driver_data->socket_path = xf86SetStrOption(pInfo->options, "SocketPath", NULL);
+    if (!driver_data->socket_path){
+        xf86IDrvMsg(pInfo, X_ERROR, "SocketPath must be specified\n");
+        goto fail;
+    }
+
+    if (strlen(driver_data->socket_path) >= sizeof(addr.sun_path)) {
+        xf86IDrvMsg(pInfo, X_ERROR, "SocketPath is too long\n");
+        goto fail;
+    }
+
+    unlink(driver_data->socket_path);
+
+    driver_data->socket_fd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
+    if (driver_data->socket_fd < 0) {
+        xf86IDrvMsg(pInfo, X_ERROR, "Failed to create a socket for communication: %s\n",
+                    strerror(errno));
+        goto fail;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, driver_data->socket_path, sizeof(addr.sun_path) - 1);
+
+    if (bind(driver_data->socket_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
+        xf86IDrvMsg(pInfo, X_ERROR, "Failed to assign address to the socket\n");
+        goto fail;
+    }
+
+    if (chmod(driver_data->socket_path, 0777) != 0) {
+        xf86IDrvMsg(pInfo, X_ERROR, "Failed to chmod the socket path\n");
+        goto fail;
+    }
+
+    if (listen(driver_data->socket_fd, 1) != 0) {
+        xf86IDrvMsg(pInfo, X_ERROR, "Failed to listen on the socket\n");
+        goto fail;
+    }
+
+    device_type_option = xf86SetStrOption(pInfo->options, "DeviceType", NULL);
+    if (device_type_option == NULL) {
+        xf86IDrvMsg(pInfo, X_ERROR, "DeviceType option must be specified\n");
+        goto fail;
+    }
+
+    if (strcmp(device_type_option, "Keyboard") == 0) {
+        driver_data->device_type = DEVICE_KEYBOARD;
+    } else if (strcmp(device_type_option, "Pointer") == 0) {
+        driver_data->device_type = DEVICE_POINTER;
+    } else if (strcmp(device_type_option, "PointerAbsolute") == 0) {
+        driver_data->device_type = DEVICE_POINTER_ABS;
+    } else if (strcmp(device_type_option, "PointerAbsoluteProximity") == 0) {
+        driver_data->device_type = DEVICE_POINTER_ABS_PROXIMITY;
+    } else if (strcmp(device_type_option, "Touch") == 0) {
+        driver_data->device_type = DEVICE_TOUCH;
+    } else {
+        xf86IDrvMsg(pInfo, X_ERROR, "Unsupported DeviceType option.\n");
+        goto fail;
+    }
+    free(device_type_option);
+
+    pInfo->private = driver_data;
+    driver_data->pInfo = pInfo;
+
+    pInfo->type_name = get_type_name(pInfo, driver_data);
+
+    return Success;
+fail:
+    free_driver_data(driver_data);
+    return BadValue;
+}
+
+static void
+uninit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
+{
+    xf86ITDevicePtr driver_data = pInfo->private;
+    free_driver_data(driver_data);
+    pInfo->private = NULL;
+    xf86DeleteInput(pInfo, flags);
+}
+
+InputDriverRec driver = {
+    .driverVersion = 1,
+    .driverName = "inputtest",
+    .PreInit = pre_init,
+    .UnInit = uninit,
+    .module = NULL,
+    .default_options = NULL,
+    .capabilities = 0
+};
+
+static XF86ModuleVersionInfo version_info = {
+    "inputtest",
+    MODULEVENDORSTRING,
+    MODINFOSTRING1,
+    MODINFOSTRING2,
+    XORG_VERSION_CURRENT,
+    XORG_VERSION_MAJOR,
+    XORG_VERSION_MINOR,
+    XORG_VERSION_PATCH,
+    ABI_CLASS_XINPUT,
+    ABI_XINPUT_VERSION,
+    MOD_CLASS_XINPUT,
+    {0, 0, 0, 0}
+};
+
+static void*
+setup_proc(void *module, void *options, int *errmaj, int *errmin)
+{
+    xf86AddInputDriver(&driver, module, 0);
+    return module;
+}
+
+_X_EXPORT XF86ModuleData inputtestModuleData = {
+    .vers = &version_info,
+    .setup = &setup_proc,
+    .teardown = NULL
+};
diff --git a/hw/xfree86/meson.build b/hw/xfree86/meson.build
index cf1f1ba78..8fed4bf63 100644
--- a/hw/xfree86/meson.build
+++ b/hw/xfree86/meson.build
@@ -133,6 +133,9 @@ endif
 if build_modesetting
    subdir('drivers/modesetting')
 endif
+if get_option('xf86-input-inputtest')
+    subdir('drivers/inputtest')
+endif
 
 if get_option('suid_wrapper')
     executable('Xorg.wrap',
diff --git a/meson_options.txt b/meson_options.txt
index 1a07745e0..2a8b7e373 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -107,6 +107,8 @@ option('agp', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto',
        description: 'AGP support')
 option('sha1', type: 'combo', choices: ['libc', 'CommonCrypto', 'CryptoAPI', 'libmd', 'libsha1', 'libnettle', 'libgcrypt', 'libcrypto', 'auto'], value: 'auto',
        description: 'SHA1 implementation')
+option('xf86-input-inputtest', type: 'boolean', value: true,
+       description: 'Test input driver support on Xorg')
 
 option('dri1', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto', description: 'Build DRI1 extension (default: auto)')
 option('dri2', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto', description: 'Build DRI2 extension (default: auto)')


More information about the xorg-commit mailing list