Path: blob/master/utils/xorg-deps/xf86-input-neko/src/neko.c
1007 views
/*1* (c) 2017 Martin Kepplinger <[email protected]>2* (c) 2007 Clement Chauplannaz, Thales e-Transactions <[email protected]>3* (c) 2006 Sascha Hauer, Pengutronix <[email protected]>4* (c) 2023 Miroslav Sedivy5*6* derived from the xf86-input-void driver7* Copyright 1999 by Frederic Lepied, France. <[email protected]>8*9* Permission to use, copy, modify, distribute, and sell this software and its10* documentation for any purpose is hereby granted without fee, provided that11* the above copyright notice appear in all copies and that both that12* copyright notice and this permission notice appear in supporting13* documentation, and that the name of Frederic Lepied not be used in14* advertising or publicity pertaining to distribution of the software without15* specific, written prior permission. Frederic Lepied makes no16* representations about the suitability of this software for any purpose. It17* is provided "as is" without express or implied warranty.18*19* FREDERIC LEPIED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,20* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO21* EVENT SHALL FREDERIC LEPIED BE LIABLE FOR ANY SPECIAL, INDIRECT OR22* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,23* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER24* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR25* PERFORMANCE OF THIS SOFTWARE.26*27* SPDX-License-Identifier: MIT28* License-Filename: COPYING29*/3031/* neko input driver */32// https://www.x.org/releases/X11R7.7/doc/xorg-server/Xinput.html3334#ifdef HAVE_CONFIG_H35#include "config.h"36#endif3738#define DEF_SOCKET_NAME "/tmp/xf86-input-neko.sock"39#define BUFFER_SIZE 124041#include <stdio.h>42#include <stdio.h>43#include <sys/socket.h>44#include <sys/un.h>45#include <misc.h>46#include <xf86.h>47#if !defined(DGUX)48#include <xisb.h>49#endif50#include <xf86_OSproc.h>51#include <xf86Xinput.h>52#include <exevents.h> /* Needed for InitValuator/Proximity stuff */53#include <X11/keysym.h>54#include <mipointer.h>55#include <xserver-properties.h>56#include <pthread.h>5758#define MAX_USED_VALUATORS 3 /* x, y, pressure */59#define TOUCH_MAX_SLOTS 10 /* max number of simultaneous touches */6061struct neko_message62{63uint16_t type;64uint32_t touchId;65int32_t x;66int32_t y;67uint8_t pressure;68};6970struct neko_priv71{72pthread_t thread;73/* config */74int height;75int width;76int pmax;77ValuatorMask *valuators;78uint16_t slots;79/* socket */80struct sockaddr_un addr;81int listen_socket;82char *socket_name;83};8485// from binary representation to struct86static void87UnpackNekoMessage(struct neko_message *msg, unsigned char *buffer)88{89msg->type = buffer[0]; // TODO: use full 16bit type90msg->touchId = buffer[1] | (buffer[2] << 8); // TODO: use full 32bit touchId91msg->x = buffer[3] | (buffer[4] << 8) | (buffer[5] << 16) | (buffer[6] << 24);92msg->y = buffer[7] | (buffer[8] << 8) | (buffer[9] << 16) | (buffer[10] << 24);93msg->pressure = buffer[11];94}9596static void97ReadInput(InputInfoPtr pInfo)98{99struct neko_priv *priv = (struct neko_priv *) (pInfo->private);100struct neko_message msg;101int ret;102103int data_socket;104unsigned char buffer[BUFFER_SIZE];105106for (;;)107{108/* Wait for incoming connection. */109data_socket = accept(priv->listen_socket, NULL, NULL);110111/* Handle error conditions. */112if (data_socket == -1)113{114xf86IDrvMsg(pInfo, X_ERROR, "unable to accept connection\n");115break;116}117118xf86IDrvMsg(pInfo, X_INFO, "accepted connection\n");119120for(;;)121{122/* Wait for next data packet. */123ret = read(data_socket, buffer, BUFFER_SIZE);124125/* Handle error conditions. */126if (ret == -1)127{128xf86IDrvMsg(pInfo, X_ERROR, "unable to read data\n");129break;130}131132/* Connection closed by client. */133if (ret == 0)134{135xf86IDrvMsg(pInfo, X_INFO, "connection closed\n");136break;137}138139/* Ensure message is long enough. */140if (ret != BUFFER_SIZE)141{142xf86IDrvMsg(pInfo, X_ERROR, "invalid message size\n");143break;144}145146UnpackNekoMessage(&msg, buffer);147148ValuatorMask *m = priv->valuators;149valuator_mask_zero(m);150151// do not send valuators if x and y are -1152if (msg.x != -1 && msg.y != -1)153{154valuator_mask_set_double(m, 0, msg.x);155valuator_mask_set_double(m, 1, msg.y);156valuator_mask_set_double(m, 2, msg.pressure);157}158159// TODO: extend to other types, such as keyboard and mouse160xf86PostTouchEvent(pInfo->dev, msg.touchId, msg.type, 0, m);161}162163/* Close socket. */164close(data_socket);165166xf86IDrvMsg(pInfo, X_INFO, "closed connection\n");167}168}169170static void171PointerCtrl(__attribute__ ((unused)) DeviceIntPtr device,172__attribute__ ((unused)) PtrCtrl *ctrl)173{174}175176static int177InitTouch(InputInfoPtr pInfo)178{179// custom private data180struct neko_priv *priv = pInfo->private;181182const int nbtns = 11;183const int naxes = 3;184185unsigned char map[nbtns + 1];186Atom btn_labels[nbtns];187Atom axis_labels[naxes];188189// init button map190memset(map, 0, sizeof(map));191for (int i = 0; i < nbtns; i++)192{193map[i + 1] = i + 1;194}195196// init btn_labels197memset(btn_labels, 0, ARRAY_SIZE(btn_labels) * sizeof(Atom));198btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);199btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);200btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);201btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);202btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);203btn_labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT);204btn_labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);205btn_labels[7] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_SIDE);206btn_labels[8] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_EXTRA);207btn_labels[9] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_FORWARD);208btn_labels[10] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_BACK);209210// init axis labels211memset(axis_labels, 0, ARRAY_SIZE(axis_labels) * sizeof(Atom));212axis_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_X);213axis_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_Y);214axis_labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_PRESSURE);215216/* initialize mouse emulation valuators */217if (InitPointerDeviceStruct((DevicePtr)pInfo->dev,218map,219nbtns, btn_labels,220PointerCtrl,221GetMotionHistorySize(),222naxes, axis_labels) == FALSE)223{224xf86IDrvMsg(pInfo, X_ERROR,225"unable to allocate PointerDeviceStruct\n");226return !Success;227}228229/*230This function is provided to initialize an XAxisInfoRec, and should be231called for core and extension devices that have valuators. The space232for the XAxisInfoRec is allocated by the InitValuatorClassDeviceStruct233function, but is not initialized.234235InitValuatorAxisStruct should be called once for each axis of motion236reported by the device. Each invocation should be passed the axis237number (starting with 0), the minimum value for that axis, the maximum238value for that axis, and the resolution of the device in counts per meter.239If the device reports relative motion, 0 should be reported as the240minimum and maximum values.241242InitValuatorAxisStruct(dev, axnum, minval, maxval, resolution)243DeviceIntPtr dev;244int axnum;245int minval;246int maxval;247int resolution;248*/249xf86InitValuatorAxisStruct(pInfo->dev, 0,250XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_X),2510, /* min val */252priv->width - 1, /* max val */253priv->width, /* resolution */2540, /* min_res */255priv->width, /* max_res */256Absolute);257258xf86InitValuatorAxisStruct(pInfo->dev, 1,259XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_Y),2600, /* min val */261priv->height - 1, /* max val */262priv->height, /* resolution */2630, /* min_res */264priv->height, /* max_res */265Absolute);266267xf86InitValuatorAxisStruct(pInfo->dev, 2,268XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_PRESSURE),2690, /* min val */270priv->pmax, /* max val */271priv->pmax + 1, /* resolution */2720, /* min_res */273priv->pmax + 1, /* max_res */274Absolute);275276/*277The mode field is either XIDirectTouch for direct−input touch devices278such as touchscreens or XIDependentTouch for indirect input devices such279as touchpads. For XIDirectTouch devices, touch events are sent to window280at the position the touch occured. For XIDependentTouch devices, touch281events are sent to the window at the position of the device's sprite.282283The num_touches field defines the maximum number of simultaneous touches284the device supports. A num_touches of 0 means the maximum number of285simultaneous touches is undefined or unspecified. This field should be286used as a guide only, devices will lie about their capabilities.287*/288if (InitTouchClassDeviceStruct(pInfo->dev,289priv->slots,290XIDirectTouch,291naxes) == FALSE)292{293xf86IDrvMsg(pInfo, X_ERROR,294"unable to allocate TouchClassDeviceStruct\n");295return !Success;296}297298return Success;299}300301static int302DeviceControl(DeviceIntPtr device, int what)303{304// device pInfo305InputInfoPtr pInfo = device->public.devicePrivate;306// custom private data307struct neko_priv *priv = pInfo->private;308309switch (what) {310case DEVICE_INIT:311device->public.on = FALSE;312313if (InitTouch(pInfo) != Success)314{315xf86IDrvMsg(pInfo, X_ERROR, "unable to init touch\n");316return !Success;317}318break;319320case DEVICE_ON:321xf86IDrvMsg(pInfo, X_INFO, "DEVICE ON\n");322device->public.on = TRUE;323324if (priv->thread == 0)325{326/* start thread */327pthread_create(&priv->thread, NULL, (void *)ReadInput, pInfo);328}329break;330331case DEVICE_OFF:332case DEVICE_CLOSE:333xf86IDrvMsg(pInfo, X_INFO, "DEVICE OFF\n");334device->public.on = FALSE;335break;336}337338return Success;339}340341static int342PreInit(__attribute__ ((unused)) InputDriverPtr drv,343InputInfoPtr pInfo,344__attribute__ ((unused)) int flags)345{346struct neko_priv *priv;347int ret;348349priv = calloc(1, sizeof (struct neko_priv));350if (!priv)351{352xf86IDrvMsg(pInfo, X_ERROR, "%s: out of memory\n", __FUNCTION__);353return BadValue;354}355356pInfo->type_name = (char*)XI_TOUCHSCREEN;357pInfo->device_control = DeviceControl;358pInfo->read_input = NULL;359pInfo->control_proc = NULL;360pInfo->switch_mode = NULL; /* Only support Absolute mode */361pInfo->private = priv;362pInfo->fd = -1;363364/* get socket name from config */365priv->socket_name = xf86SetStrOption(pInfo->options, "SocketName", DEF_SOCKET_NAME);366367/*368* In case the program exited inadvertently on the last run,369* remove the socket.370*/371372unlink(priv->socket_name);373374/* Create local socket. */375priv->listen_socket = socket(AF_UNIX, SOCK_STREAM, 0);376if (priv->listen_socket == -1)377{378xf86IDrvMsg(pInfo, X_ERROR, "unable to create socket\n");379return BadValue;380}381382/*383* For portability clear the whole structure, since some384* implementations have additional (nonstandard) fields in385* the structure.386*/387388memset(&priv->addr, 0, sizeof(struct sockaddr_un));389390/* Bind socket to socket name. */391392priv->addr.sun_family = AF_UNIX;393strncpy(priv->addr.sun_path, priv->socket_name, sizeof(priv->addr.sun_path) - 1);394395ret = bind(priv->listen_socket, (const struct sockaddr *) &priv->addr, sizeof(struct sockaddr_un));396if (ret == -1)397{398xf86IDrvMsg(pInfo, X_ERROR, "unable to bind socket\n");399return BadValue;400}401402/*403* Prepare for accepting connections. The backlog size is set404* to 5. So while one request is being processed other requests405* can be waiting.406*/407408ret = listen(priv->listen_socket, 5);409if (ret == -1)410{411xf86IDrvMsg(pInfo, X_ERROR, "unable to listen on socket\n");412return BadValue;413}414415/* process generic options */416xf86CollectInputOptions(pInfo, NULL);417xf86ProcessCommonOptions(pInfo, pInfo->options);418419/* create valuators */420priv->valuators = valuator_mask_new(MAX_USED_VALUATORS);421if (!priv->valuators)422{423xf86IDrvMsg(pInfo, X_ERROR, "%s: out of memory\n", __FUNCTION__);424return BadValue;425}426427priv->slots = TOUCH_MAX_SLOTS;428priv->width = 0xffff;429priv->height = 0xffff;430priv->pmax = 255;431priv->thread = 0;432433/* Return the configured device */434return Success;435}436437static void438UnInit(__attribute__ ((unused)) InputDriverPtr drv,439InputInfoPtr pInfo,440__attribute__ ((unused)) int flags)441{442struct neko_priv *priv = (struct neko_priv *)(pInfo->private);443444/* close socket */445close(priv->listen_socket);446/* remove socket file */447unlink(priv->socket_name);448449if (priv->thread)450{451/* cancel thread */452pthread_cancel(priv->thread);453/* wait for thread to finish */454pthread_join(priv->thread, NULL);455/* ensure thread is not cancelled again */456priv->thread = 0;457}458459/* free valuators */460valuator_mask_free(&priv->valuators);461462free(pInfo->private);463pInfo->private = NULL;464xf86DeleteInput(pInfo, 0);465}466467/**468* X module information and plug / unplug routines469*/470471_X_EXPORT InputDriverRec NEKO =472{473.driverVersion = 1,474.driverName = "neko",475.Identify = NULL,476.PreInit = PreInit,477.UnInit = UnInit,478.module = NULL479};480481static pointer482Plug(pointer module,483__attribute__ ((unused)) pointer options,484__attribute__ ((unused)) int *errmaj,485__attribute__ ((unused)) int *errmin)486{487xf86AddInputDriver(&NEKO, module, 0);488return module;489}490491static void492Unplug(__attribute__ ((unused)) pointer module)493{494}495496static XF86ModuleVersionInfo versionRec =497{498.modname = "neko",499.vendor = MODULEVENDORSTRING,500._modinfo1_ = MODINFOSTRING1,501._modinfo2_ = MODINFOSTRING2,502.xf86version = XORG_VERSION_CURRENT,503.majorversion = PACKAGE_VERSION_MAJOR,504.minorversion = PACKAGE_VERSION_MINOR,505.patchlevel = PACKAGE_VERSION_PATCHLEVEL,506.abiclass = ABI_CLASS_XINPUT,507.abiversion = ABI_XINPUT_VERSION,508.moduleclass = MOD_CLASS_XINPUT,509.checksum = {0, 0, 0, 0} /* signature, to be patched into the file by a tool */510};511512_X_EXPORT XF86ModuleData nekoModuleData =513{514.vers = &versionRec,515.setup = Plug,516.teardown = Unplug517};518519520