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