From 8c0afc9eb26a0866301072dec30717885fd14305 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Sat, 10 Oct 2020 02:09:05 +0300 Subject: [PATCH] xfree86: Implement a test input driver Signed-off-by: Povilas Kanapickas --- 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 + .../inputtest/xf86-input-inputtest-protocol.h | 144 +++ .../drivers/inputtest/xf86-input-inputtest.c | 1027 +++++++++++++++++ hw/xfree86/meson.build | 3 + meson_options.txt | 2 + 9 files changed, 1361 insertions(+) create mode 100644 hw/xfree86/drivers/inputtest/Makefile.am create mode 100644 hw/xfree86/drivers/inputtest/inputtestdrv.man create mode 100644 hw/xfree86/drivers/inputtest/meson.build create mode 100644 hw/xfree86/drivers/inputtest/xf86-input-inputtest-protocol.h create mode 100644 hw/xfree86/drivers/inputtest/xf86-input-inputtest.c 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 +.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 + * + * 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 + +#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 + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "xorgVersion.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +#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)')