summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaleb Keithley <kaleb@freedesktop.org>2003-11-25 19:28:46 +0000
committerKaleb Keithley <kaleb@freedesktop.org>2003-11-25 19:28:46 +0000
commit4b1ef06b77c87bb5bd943a607130e556042bd61c (patch)
tree629133e6aaa2ecac285affa62cb1d27b0235f756
Initial revisionXORG-STABLE
-rw-r--r--man/aiptek.man159
-rw-r--r--src/xf86Aiptek.c2459
-rw-r--r--src/xf86Aiptek.h366
3 files changed, 2984 insertions, 0 deletions
diff --git a/man/aiptek.man b/man/aiptek.man
new file mode 100644
index 0000000..466c871
--- /dev/null
+++ b/man/aiptek.man
@@ -0,0 +1,159 @@
+.\" $XFree86: xc/programs/Xserver/hw/xfree86/input/aiptek/aiptek.man,v 1.1 2003/06/30 16:52:57 eich Exp $
+.\" shorthand for double quote that works everywhere.
+.ds q \N'34'
+.TH AIPTEK __drivermansuffix__ __vendorversion__
+.SH NAME
+aiptek \- Aiptek USB Digital Tablet Input Driver for Linux
+.SH SYNOPSIS
+.nf
+.B "Section \*qInputDevice\*q"
+.BI " Identifier \*q" idevname \*q
+.B " Driver \*qaiptek\*q"
+.BI " Option \*qDevice\*q \*q" devpath \*q
+\ \ ...
+.B EndSection
+.fi
+.SH DESCRIPTION
+.B aiptek
+is an XFree86 input driver for Aiptek HyperPen USB-based tablet devices.
+This driver only supports the USB protocol, and only under Linux; for
+RS-232C-based HyperPens, please see the \fI"hyperpen"\fP driver.
+.PP
+The
+.B aiptek
+driver functions as a pointer input device, and may be used as the
+X server's core pointer.
+.SH SUPPORTED HARDWARE
+This driver supports the Aiptek HyperPen 4000U, 5000U, 6000U, 8000U
+and 12000U USB-based input tablet on some Linux platforms.
+.SH CONFIGURATION DETAILS
+Please refer to XF86Config(__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
+Multiple instances of the Aiptek devices can cohabit. It can be useful
+to define multiple devices with different active zones. Each device
+supports the following entries:
+.RS 8
+.TP 4
+.B Option \fI"Type"\fP \fI"stylus"|"eraser"|"cursor"\fP
+sets the type of tool the device represent. This option is mandatory.
+.TP 4
+.B Option \fI"Device"\fP \fI"path"\fP
+sets the path to the special file which represents serial line where
+the tablet is plugged. You have to specify it for each subsection with
+the same value if you want to have multiple devices with the same tablet.
+This option is mandatory.
+.TP 4
+.B Option \fI"USB"\fP \fI"on"\fP
+specifies that you are using the USB bus to communicate with your tablet.
+This setting is mandatory, as USB is the only protocol supported by this driver.
+.TP 4
+.B Option \fI"DeviceName"\fP \fI"name"\fP
+sets the name of the X device.
+.TP 4
+.B Option \fI"Mode"\fP \fI"Relative"|"Absolute"\fP
+sets the mode of the device.
+.TP 4
+.B Option \fI"HistorySize"\fP \fI"number"\fP
+sets the motion history size. By default the value is zero.
+.TP 4
+.B Option \fI"AlwaysCore"\fP \fI"on"\fP
+enables the sharing of the core pointer. When this feature is enabled, the
+device will take control of the core pointer (and thus will emit core events)
+and at the same time will be able, when asked so, to report extended events.
+You can use the last available integer feedback to control this feature. When
+the value of the feedback is zero, the feature is disabled. The feature is
+enabled for any other value.
+.TP 4
+.B Option \fI"XTop"\fP \fI"number"\fP
+First of three sets of parameters to set the active zone. This sets the X coordinate of the top corner of the active zone. \fI"TopX"\fP is a synonym.
+.TP 4
+.B Option \fI"YTop"\fP \fI"number"\fP
+First of three sets of parameters to set the active zone. This sets the Y coordinate of the top corner of the active zone. \fI"TopY"\fP is a synonym.
+.TP 4
+.B Option \fI"XBottom"\fP \fI"Inumber"\fP
+First of three sets of parameters to set the active zone. This sets the X coordinate of the bottom corner of the active zone. \fI"BottomX"\fP is a synonym.
+.TP 4
+.B Option \fI"YBottom"\fP \fI"number"\fP
+First of three sets of parameters to set the active zone. This sets the Y coordinate of the bottom corner of the active zone. \fI"BottomY"\fP is a synonym.
+.TP 4
+.B Option \fI"XMax"\fP \fI"number"\fP
+Second of three sets of parameters to set the active zone. This sets the the X
+coordinate of the bottom corner of the active zone. The Top X corner's
+coordinate is fixed at 0. \fI"MaxX"\fP is a synomyn.
+.TP 4
+.B Option \fI"YMax"\fP \fI"number"\fP
+Second of three sets of parameters to set the active zone. This sets the the Y
+coordinate of the bottom corner of the active zone. The Top Y corner's
+coordinate is fixed at 0. \fI"MaxY"\fP is a synomyn.
+.TP 4
+.B Option \fI"XOffset"\fP \fI"number"\fP
+Third of three sets of parameters to set the active zone. This sets the X
+coordinate of the top corner of the active zone. \fI"OffsetX"\fP is a synomyn.
+.TP 4
+.B Option \fI"YOffset"\fP \fI"number"\fP
+Third of three sets of parameters to set the active zone. This sets the Y
+coordinate of the top corner of the active zone. \fI"OffsetY"\fP is a synomyn.
+.TP 4
+.B Option \fI"XSize"\fP \fI"number"\fP
+Third of three sets of parameters to set the active zone. This sets the X
+coordinate of the bottom corner of the active zone. Unlike others,
+this parameter is expressed in \fIrelative\fP coordinates from the
+\fI"XOffset"\fP parameter. \fI"XSize"\fP is a synomyn.
+.TP 4
+.B Option \fI"YSize"\fP \fI"number"\fP
+Third of three sets of parameters to set the active zone. This sets the Y
+coordinate of the bottom corner of the active zone. Unlike others,
+this parameter is expressed in \fIrelative\fP coordinates from the
+\fI"YOffset"\fP parameter. \fI"YSize"\fP is a synomyn.
+.TP 4
+.B Option \fI"ZMin"\fP \fI"number"\fP
+Minimum pressure reading that will be accepted from the Stylus tool. \fI"MinZ\fP" is a synomyn.
+.TP 4
+.B Option \fI"ZMax"\fP \fI"number"\fP
+Maximum pressure reading that will be accepted from the Stylus tool. \fI"MaxZ\fP" is a synomyn.
+.TP 4
+.B Option \fI"XThreshold"\fP \fI"number"\fP
+Minimal change in X coordinate position that will be accepted as data input.
+\fI"ThresholdX"\fP is a synomyn.
+.TP 4
+.B Option \fI"YThreshold"\fP \fI"number"\fP
+Minimal change in Y coordinate position that will be accepted as data input.
+\fI"ThresholdY"\fP is a synomyn.
+.TP 4
+.B Option \fI"ZThreshold"\fP \fI"number"\fP
+Minimal change in pressure reading that will be accepted as data input.
+\fI"ThresholdZ"\fP is a synomyn.
+.TP 4
+.B Option \fI"InvX"\fP \fI"on"\fP
+Inverts X coordinate reports. \fI"XInv"\fP is a synomyn.
+.TP 4
+.B Option \fI"InvY"\fP \fI"on"\fP
+Inverts Y coordinate reports. \fI"YInv"\fP is a synomyn.
+.TP 4
+.B Option \fI"Pressure"\fP \fI"soft"|"hard"|"linear"\fP
+Pressure reports either delivered in linearly incremental values (default),
+or perturbed by one of two log-linear algorithms (\fI"soft"\fP or \fI"hard"\fP.)
+.TP 4
+.B Option \fI"KeepShape"\fP \fI"on"\fP
+When this option is enabled, the active zone begins according to TopX
+and TopY. The bottom corner is adjusted to keep the ratio width/height
+of the active zone the same as the screen while maximizing the area
+described by the active area set of parameters, XTop/YTop/XBottom/YBottom,
+XMax/YMax, or XOffset/YOffset/XSize/YSize.
+.TP 4
+.B Option \fI"DebugLevel"\fP \fInumber \fP
+sets the level of debugging info reported.
+.RE
+.PP
+This driver is currently Linux specific.
+.PP
+.SH "SEE ALSO"
+XFree86(1), XF86Config(__filemansuffix__), xf86config(1), Xserver(1), X(__miscmansuffix__), hyperpen(__drivermansuffix__).
+.SH AUTHORS
+Bryan W. Headley <bheadley@earthlink.net>
+.SH PROJECT PAGE
+http://aiptektablet.sourceforge.net tracks ongoing development of this driver,
+the Linux kernel driver, and a GUI front-end application that works in
+concert with the above.
diff --git a/src/xf86Aiptek.c b/src/xf86Aiptek.c
new file mode 100644
index 0000000..39b8f9d
--- /dev/null
+++ b/src/xf86Aiptek.c
@@ -0,0 +1,2459 @@
+/*
+ * xf86Aiptek
+ *
+ * This driver assumes Linux USB/HID support, available for USB devices.
+ *
+ * Version 0.0, 1-Jan-2003, Bryan W. Headley
+ *
+ * Copyright 2003 by Bryan W. Headley. <bwheadley@earthlink.net>
+ *
+ * Lineage: This driver is based on both the xf86HyperPen and xf86Wacom tablet
+ * drivers.
+ *
+ * xf86HyperPen -- modified from xf86Summa (c) 1996 Steven Lang
+ * (c) 2000 Roland Jansen
+ * (c) 2000 Christian Herzog (button & 19200 baud support)
+ *
+ * xf86Wacom -- (c) 1995-2001 Frederic Lepied
+ *
+ *
+ * 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 Bryan W. Headley not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. Bryan W. Headley makes no
+ * representations about the suitability of this software for any purpose.
+ * It is provided "as is" without express or implied warranty.
+ *
+ * BRYAN W. HEADLEY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL BRYAN W. HEADLEY 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 ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/* $XFree86: xc/programs/Xserver/hw/xfree86/input/aiptek/xf86Aiptek.c,v 1.1 2003/06/25 18:06:25 eich Exp $ */
+
+/*
+ *
+ * Section "InputDevice"
+ * Identifier "stylus"
+ * Driver "aiptek"
+ * Option "Device" "pathname" {/dev/input/event0}
+ * Option "Type" "string" {stylus|cursor|eraser}
+ * Option "Mode" "string" {absolute|relative}
+ * Option "Cursor" "string" {stylus|puck}
+ * Option "USB" "bool" {on|off}
+ * Option "ScreenNo" "int"
+ * Option "KeepShape" "bool" {on|off}
+ *
+ * # The tablet reports top-right as 0 for the given coordinate
+ * # to bottom-left as num (value dependent on tablet.)
+ * # If you choose to invert X, Y or both, the bottom-left coordinate
+ * # is reported as 0.
+ * Option "InvX" "bool" {on|off}
+ * Option "InvY" "bool" {on|off}
+ *
+ * # XSize/YSize/XOffset/YOffset allow you to specify an active
+ * # area within yout tablet.
+ *
+ * Option "XSize" "int"
+ * Option "YSize" "int"
+ *
+ * Option "XTop" "int"
+ * Option "YTop" "int"
+ * Option "XBottom" "int"
+ * Option "YBottom" "int"
+ * Option "XOffset" "int"
+ * Option "YOffset" "int"
+ *
+ * Option "XMax" "int"
+ * Option "YMax" "int"
+ * Option "ZMax" "int"
+ * Option "ZMin" "int"
+ *
+ * Option "XThreshold" "int"
+ * Option "YThreshold" "int"
+ * Option "ZThreshold" "int"
+ *
+ * Option "Pressure" "Soft|Hard|Linear" defaults to Linear
+ *
+ * Option "alwayscore" "bool" {on|off}
+ * Option "debuglevel" "int"
+ * Option "HistorySize" "int"
+ * EndSection
+ *
+ * Commentary:
+ * 1. Identifier: what you name your input device is not too
+ * significant.
+ * but what it infers is that you can have a driver with
+ * a name of "stylus" (whose type would be 'stylus') and
+ * another one with identifier "cursor" (whose type would be
+ * 'cursor') that both would be driver by the same aiptek
+ * driver. Note though that the identifier keyword has
+ * no devicetype connotations: you can identify your input
+ * device as "zzz",
+ *
+ */
+#include "xf86Aiptek.h"
+
+static const char identification[] = "$Identification: 0 $";
+static InputDriverPtr aiptekDrv;
+static int debug_level = INI_DEBUG_LEVEL;
+
+#ifdef XFree86LOADER
+static
+#endif
+InputDriverRec AIPTEK =
+{
+ 1, /* driver version */
+ "aiptek", /* driver name */
+ NULL, /* identify */
+ xf86AiptekInit, /* pre-init */
+ xf86AiptekUninit, /* un-init */
+ NULL, /* module */
+ 0 /* ref count */
+};
+
+/*
+ * Function/Macro keys variables.
+ *
+ * This is a list of X keystrokes the macro keys can send.
+ */
+static KeySym aiptek_map[] =
+{
+ /* 0x00 .. 0x07 */
+ NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,NoSymbol,
+ /* 0x08 .. 0x0f */
+ XK_F1, XK_F2, XK_F3, XK_F4, XK_F5, XK_F6, XK_F7, XK_F8,
+ /* 0x10 .. 0x17 */
+ XK_F9, XK_F10, XK_F11, XK_F12, XK_F13, XK_F14, XK_F15, XK_F16,
+ /* 0x18 .. 0x1f */
+ XK_F17, XK_F18, XK_F19, XK_F20, XK_F21, XK_F22, XK_F23, XK_F24,
+ /* 0x20 .. 0x27 */
+ XK_F25, XK_F26, XK_F27, XK_F28, XK_F29, XK_F30, XK_F31, XK_F32
+};
+
+/*
+ * This is the map of Linux Event Input system keystrokes sent for
+ * the macro keys. There are discrepancies in the mappings, so for example,
+ * if we wanted to implement full macro key-to-string conversion in the
+ * Linux driver, we'd have to accept 1-to-many keyboard events, several of
+ * whom would not have the same encoding. For this reason, we're biting
+ * the bullet now & implementing a simple lookup/translation scheme.
+ * A simple 'KEY_F1 = XK_F1' layout wouldn't work, because X wants an
+ * offset into the KeySym array above, and it'll look up that this means
+ * XK_whatever...
+ */
+
+static int linux_inputDevice_keyMap[] =
+{
+ KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8,
+ KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16,
+ KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, KEY_F24,
+ KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO, KEY_FRONT, KEY_COPY,
+ KEY_OPEN, KEY_PASTE
+};
+
+/* minKeyCode = 8 because this is the min legal key code */
+static KeySymsRec keysyms =
+{
+ /* map minKeyCode maxKC width */
+ aiptek_map, 8, 0x27, 1
+};
+
+static const char *default_options[] =
+{
+ "BaudRate", "9600",
+ "StopBits", "1",
+ "DataBits", "8",
+ "Parity", "None",
+ "VMin", "1",
+ "Vtime", "10",
+ "FlowControl", "Xoff",
+ NULL
+};
+
+/*
+ * xf86AiptekConvert
+ * Convert valuators to X and Y. We deal with multiple X screens, adjusting
+ * for xTop/xBottom/yTop/yBottom (or xSize/ySize).
+ */
+static Bool
+xf86AiptekConvert(LocalDevicePtr local,
+ int first,
+ int num,
+ int v0,
+ int v1,
+ int v2,
+ int v3,
+ int v4,
+ int v5,
+ int* x,
+ int* y)
+{
+ AiptekDevicePtr device = (AiptekDevicePtr) local->private;
+ int xSize, ySize;
+ int width, height;
+
+ DBG(6, ErrorF("xf86AiptekConvert\n"));
+ xf86Msg(X_CONFIG, " xf86AiptekConvert(), with: first=%d, num=%d, v0=%d, "
+ "v1=%d, v2=%d, v3=%d,, v4=%d, v5=%d, x=%d, y=%d\n",
+ first, num, v0, v1, v2, v3, v4, v5, *x, *y);
+
+ if (first != 0 || num == 1)
+ {
+ return FALSE;
+ }
+
+ xSize = device->xBottom - device->xTop;
+ ySize = device->yBottom - device->yTop;
+
+ width = screenInfo.screens[device->screenNo]->width;
+ height = screenInfo.screens[device->screenNo]->height;
+
+ *x = (v0 * width) / xSize;
+ *y = (v1 * height) / ySize;
+
+ /* Deal with coordinate inversion */
+ if ( device->flags & INVX_FLAG)
+ {
+ *x = width - *x;
+ }
+ if ( device->flags & INVY_FLAG)
+ {
+ *y = height - *y;
+ }
+
+ /* Normalize the adjusted sizes. */
+ if (*x < 0)
+ {
+ *x = 0;
+ }
+ if (*x > width)
+ {
+ *x = width;
+ }
+
+ if (*y < 0)
+ {
+ *y = 0;
+ }
+ if (*y > height)
+ {
+ *y = height;
+ }
+
+ if (device->screenNo != 0)
+ {
+ xf86XInputSetScreen(local, device->screenNo, *x, *y);
+ }
+ xf86Msg(X_CONFIG, ": xf86AiptekConvert() exits, with: x=%d, y=%d\n",
+ *x, *y);
+
+ return TRUE;
+}
+
+/*
+ * xf86AiptekReverseConvert
+ * Convert X and Y to valuators.
+ */
+static Bool
+xf86AiptekReverseConvert(LocalDevicePtr local,
+ int x,
+ int y,
+ int* valuators)
+{
+ AiptekDevicePtr device = (AiptekDevicePtr) local->private;
+ int xSize, ySize;
+
+ xf86Msg(X_CONFIG, ": xf86AiptekReverseConvert(), with: x=%d, y=%d, "
+ "valuators[0]=%d, valuators[1]=%d\n",
+ x, y, valuators[0], valuators[1] );
+
+ /*
+ * Adjust by tablet ratio
+ */
+ xSize = device->xBottom - device->xTop;
+ ySize = device->yBottom - device->yTop;
+
+ valuators[0] = (x*xSize) / screenInfo.screens[device->screenNo]->width;
+ valuators[1] = (y*ySize) / screenInfo.screens[device->screenNo]->height;
+
+ DBG(6, ErrorF("converted x,y (%d, %d) to (%d, %d)\n",
+ x, y, valuators[0], valuators[1] ));
+
+ if (device->screenNo != 0)
+ {
+ xf86XInputSetScreen(local,device->screenNo,valuators[0], valuators[1]);
+ }
+ xf86Msg(X_CONFIG, ": xf86AiptekReverseConvert() exits, with: "
+ "valuators[0]=%d, valuators[1]=%d\n",
+ valuators[0], valuators[1] );
+
+ return TRUE;
+}
+
+/**********************************************************************
+ * xf86AiptekSendEvents
+ * Send events according to the device state.
+ */
+static void
+xf86AiptekSendEvents(LocalDevicePtr local, int r_z)
+{
+ AiptekDevicePtr device = (AiptekDevicePtr) local->private;
+ AiptekCommonPtr common = device->common;
+
+ int bCorePointer, bAbsolute;
+ int x, y, z, xTilt, yTilt;
+
+ if ((DEVICE_ID(device->flags) != common->currentValues.eventType))
+ {
+ DBG(7,ErrorF("xf86AiptekSendEvents: not the same device type (%u,%u)\n",
+ DEVICE_ID(device->flags), common->currentValues.eventType));
+ return;
+ }
+
+ bAbsolute = (device->flags & ABSOLUTE_FLAG);
+ bCorePointer = xf86IsCorePointer(local->dev);
+
+ /*
+ * Normalize X and Y coordinates. This includes dealing
+ * with absolute/relative coordinate mode.
+ */
+ if (bAbsolute)
+ {
+ x = common->currentValues.x;
+ y = common->currentValues.y;
+ z = r_z;
+ xTilt = common->currentValues.xTilt;
+ yTilt = common->currentValues.yTilt;
+ }
+ else
+ {
+ x = common->currentValues.x - common->previousValues.x;
+ y = common->currentValues.y - common->previousValues.y;
+ z = r_z - common->previousValues.z;
+ xTilt = common->currentValues.xTilt - common->previousValues.xTilt;
+ yTilt = common->currentValues.yTilt - common->previousValues.yTilt;
+ }
+
+ /* Translate coordinates according to Top and Bottom points.
+ */
+ if (x > device->xBottom) {
+ x = device->xBottom;
+ }
+
+ if (y > device->yBottom) {
+ y = device->yBottom;
+ }
+
+ if (device->xTop > 0) {
+ DBG(10, ErrorF("Adjusting x, with xTop=%d\n", device->xTop));
+ x -= device->xTop;
+ }
+
+ if (device->yTop > 0) {
+ DBG(10, ErrorF("Adjusting y, with yTop=%d\n", device->yTop));
+ y -= device->yTop;
+ }
+
+ if (x < 0) {
+ x = 0;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+
+ /* Deal with pressure min..max, which differs from threshold. */
+ if (z < device->zMin) {
+ z = 0;
+ }
+
+ if (z > device->zMax) {
+ z = device->zMax;
+ }
+
+ /*
+ * First, handle the macro keys.
+ */
+ if (common->currentValues.macroKey != VALUE_NA)
+ {
+ int i;
+
+ /* This is a little silly, but: The Linux Event Input
+ * system uses a slightly different keymap than does X
+ * (it also has more keys defined). So, we have to go
+ * through a translation process. It's made sillier than
+ * required because X wants an offset to it's KeySym table,
+ * rather than an event key -- it'll do it's own lookup.
+ * It DOES support arbitrary ordering of key events, and
+ * partial keyboard matrices, so that speaks in favor of this
+ * scheme.
+ */
+ for (i = 0;
+ i < sizeof(linux_inputDevice_keyMap)/
+ sizeof(linux_inputDevice_keyMap[0]);
+ ++i)
+ {
+ if (linux_inputDevice_keyMap[0]==common->currentValues.macroKey)
+ {
+ break;
+ }
+ }
+
+ /* First available Keycode begins at 8 => macro+7.
+ * It's pervasive throughout the Xinput drivers, and
+ * no, I don't know why they purposively waste the first 8
+ * positions of the KeySym map...
+ */
+
+ /* Keyboard 'make' (press) event */
+ xf86PostKeyEvent(local->dev, i+7, TRUE,
+ bAbsolute, 0, 5,
+ x, y, common->currentValues.button, xTilt, yTilt);
+ /* Keyboard 'break' (depress) event */
+ xf86PostKeyEvent(local->dev, i+7, FALSE,
+ bAbsolute, 0, 5,
+ x, y, common->currentValues.button, xTilt, yTilt);
+ }
+
+ /* As the coordinates are ready, we can send events to X */
+ if (common->currentValues.proximity)
+ {
+ if (!common->previousValues.proximity)
+ {
+ if (!bCorePointer)
+ {
+ xf86PostProximityEvent(local->dev, 1, 0, 5,
+ x, y, z, xTilt, yTilt);
+ }
+ }
+
+ if ((bAbsolute &&
+ (common->previousValues.x != common->currentValues.x ||
+ common->previousValues.y != common->currentValues.y ||
+ common->previousValues.z != common->currentValues.z)) ||
+ (!bAbsolute &&
+ (common->currentValues.x || common->currentValues.y)))
+ {
+ if (bAbsolute || common->previousValues.proximity)
+ {
+ xf86PostMotionEvent(local->dev, bAbsolute, 0, 5,
+ x, y, z, xTilt, yTilt);
+ }
+ }
+
+ if (common->previousValues.button != common->currentValues.button)
+ {
+ int delta;
+ delta = common->currentValues.button ^ common->previousValues.button;
+ while(delta)
+ {
+ int id;
+ id = ffs(delta);
+ delta &= ~(1 << (id-1));
+ xf86PostButtonEvent(local->dev, bAbsolute, id,
+ (common->currentValues.button & (1<<(id-1))), 0, 5,
+ x, y, z, xTilt, yTilt);
+ }
+ }
+ }
+ else
+ {
+ if (!bCorePointer)
+ {
+ if (common->previousValues.proximity)
+ {
+ xf86PostProximityEvent(local->dev, 0, 0, 5, x, y, z,
+ xTilt, yTilt);
+ }
+ }
+ common->previousValues.proximity = 0;
+ }
+}
+
+/*
+ ***************************************************************************
+ * xf86AiptekHIDReadInput --
+ * Read the new events from the device, and enqueue them.
+ */
+static void
+xf86AiptekHIDReadInput(LocalDevicePtr local)
+{
+ AiptekDevicePtr device = (AiptekDevicePtr) local->private;
+ AiptekCommonPtr common = device->common;
+
+ ssize_t len;
+ int i;
+ struct input_event* event;
+ char eventbuf[sizeof(struct input_event) * MAX_EVENTS];
+ int eventsInMessage;
+ double d_z;
+ double d_zCapacity;
+
+ SYSCALL(len = read(local->fd, eventbuf, sizeof(eventbuf)));
+
+ if (len <= 0)
+ {
+ ErrorF("Error reading Aiptek tablet: %s\n", strerror(errno));
+ return;
+ }
+
+ eventsInMessage = 0;
+ for (event=(struct input_event *)(eventbuf);
+ event<(struct input_event *)(eventbuf+len);
+ ++event)
+ {
+ /*
+ * Unprocessed events:
+ * ABS_RZ - rotate stylus
+ * ABS_DISTANCE - unknown
+ * ABS_THROTTLE - unknown
+ * ABS_WHEEL - we have no wheel
+ * REL_WHEEL - we have no wheel
+ *
+ * Synthesized events
+ * ABS_X_TILT - The aiptek tablet does not report these,
+ * ABS_Y_TILT - but the Linux kernel driver sends synthetic values.
+ */
+ switch (event->type)
+ {
+ case EV_ABS:
+ {
+ switch (event->code)
+ {
+ case ABS_X:
+ {
+ ++eventsInMessage;
+ common->currentValues.x = event->value;
+ }
+ break;
+
+ case ABS_Y:
+ {
+ ++eventsInMessage;
+ common->currentValues.y = event->value;
+ }
+ break;
+
+ case ABS_PRESSURE:
+ {
+ ++eventsInMessage;
+ common->currentValues.z = event->value;
+ }
+ break;
+
+ case ABS_TILT_X:
+ case ABS_RZ:
+ {
+ ++eventsInMessage;
+ common->currentValues.xTilt = event->value;
+ }
+ break;
+
+ case ABS_TILT_Y:
+ {
+ ++eventsInMessage;
+ common->currentValues.yTilt = event->value;
+ }
+ break;
+
+ case ABS_DISTANCE:
+ {
+ ++eventsInMessage;
+ common->currentValues.distance = event->value;
+ }
+ break;
+
+ case ABS_WHEEL:
+ case ABS_THROTTLE:
+ {
+ ++eventsInMessage;
+ common->currentValues.wheel = event->value;
+ }
+ break;
+
+ case ABS_MISC:
+ {
+ /* We have an agreement with the
+ * Linux Aiptek HID driver to send
+ * the proximity bit through ABS_MISC.
+ * We do this solely if proximity is
+ * being reported through the Stylus tool;
+ * else, if mouse, we'll get proximity through
+ * REL_MISC.
+ */
+ ++eventsInMessage;
+ common->currentValues.proximity =
+ (event->value > 0 ? 1 : 0);
+ }
+ break;
+ }
+ }
+ break; /* EV_ABS */
+
+ case EV_REL:
+ {
+ switch (event->code)
+ {
+ case REL_X:
+ {
+ /* Normalize all relative events into absolute
+ * coordinates.
+ */
+ ++eventsInMessage;
+ common->currentValues.x =
+ common->previousValues.x + event->value;
+ }
+ break;
+
+ case REL_Y:
+ {
+ /* Normalize all relative events into absolute
+ * coordinates.
+ */
+ ++eventsInMessage;
+ common->currentValues.y =
+ common->previousValues.y + event->value;
+ }
+ break;
+
+ case REL_WHEEL:
+ {
+ /* Normalize all relative events into absolute
+ * coordinates.
+ */
+ ++eventsInMessage;
+ common->currentValues.wheel =
+ common->previousValues.wheel + event->value;
+ }
+
+ case REL_MISC:
+ {
+ /* We have an agreement with the
+ * Linux Aiptek HID driver to send
+ * the proximity bit through REL_MISC.
+ * We do this solely if proximity is
+ * being reported through the Mouse tool;
+ * else, if stylus, we'll get proximity through
+ * ABS_MISC.
+ */
+ ++eventsInMessage;
+ common->currentValues.proximity =
+ (event->value > 0 ? 1 : 0);
+ }
+ break;
+ }
+ }
+ break; /* EV_REL */
+
+ case EV_KEY:
+ {
+ switch (event->code)
+ {
+ /*
+ * Events begun with a BTN_TOOL_PEN, PENCIL,
+ * BRUSH or AIRBRUSH indicate that they are
+ * destined for the STYLUS device.
+ *
+ * This should probably change, and we should
+ * have devices for each type. We'll address that
+ * later.
+ */
+ case BTN_TOOL_PEN:
+ case BTN_TOOL_PENCIL:
+ case BTN_TOOL_BRUSH:
+ case BTN_TOOL_AIRBRUSH:
+ {
+ ++eventsInMessage;
+ common->currentValues.eventType = STYLUS_ID;
+ }
+ break;
+
+ /*
+ * Events begun with a BTN_TOOL_RUBBER indicate
+ * that they are destined for the ERASER device.
+ */
+ case BTN_TOOL_RUBBER:
+ {
+ ++eventsInMessage;
+ common->currentValues.eventType = ERASER_ID;
+ }
+ break;
+
+ /*
+ * A TOOL_LENS would be for a true PUCK/CURSOR.
+ * Aiptek instead gives us a mouse, that we can pretend
+ * is a puck.
+ */
+ case BTN_TOOL_MOUSE:
+ case BTN_TOOL_LENS:
+ {
+ ++eventsInMessage;
+ common->currentValues.eventType = CURSOR_ID;
+ }
+ break;
+
+ /*
+ * Normal button handling: TOUCH, STYLUS and
+ * STYLUS2 all buttons that we'll report to X
+ * as normal buttons.
+ */
+ case BTN_TOUCH:
+ {
+ ++eventsInMessage;
+ common->currentValues.button |=
+ (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_TOUCH;
+ }
+ break;
+
+ case BTN_STYLUS:
+ {
+ ++eventsInMessage;
+ common->currentValues.button |=
+ (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_STYLUS;
+ }
+ break;
+
+ case BTN_STYLUS2:
+ {
+ ++eventsInMessage;
+ common->currentValues.button |=
+ (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_STYLUS2;
+ }
+ break;
+
+ /*
+ * Normal Mouse button handling: LEFT, RIGHT and
+ * MIDDLE all buttons that we'll report to X
+ * as normal buttons. Note that the damned things
+ * re-use the same bitmasks as the Stylus buttons,
+ * above.
+ */
+ case BTN_LEFT:
+ {
+ ++eventsInMessage;
+ common->currentValues.button |=
+ (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_MOUSE_LEFT;
+ }
+ break;
+
+ case BTN_MIDDLE:
+ {
+ ++eventsInMessage;
+ common->currentValues.button |=
+ (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_MOUSE_MIDDLE;
+ }
+ break;
+
+ case BTN_RIGHT:
+ {
+ ++eventsInMessage;
+ common->currentValues.button |=
+ (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_MOUSE_RIGHT;
+ }
+ break;
+
+ case BTN_SIDE:
+ {
+ ++eventsInMessage;
+ common->currentValues.button |=
+ (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_SIDE_BTN;
+ }
+ break;
+
+ case BTN_EXTRA:
+ {
+ ++eventsInMessage;
+ common->currentValues.button |=
+ (event->value > 0 ? 1 : 0) * BUTTONS_EVENT_EXTRA_BTN;
+ }
+ break;
+
+ /*
+ * Any other EV_KEY event is assumed to be
+ * the pressing of a macro key from the tablet.
+ */
+ default:
+ {
+ ++eventsInMessage;
+ common->currentValues.macroKey = event->value;
+ }
+ break;
+ }
+ }
+ break; /* EV_KEY */
+
+ } /* switch event->type */
+
+ /* We have two potential event terminators. EV_MSC was used
+ * by (unwritten) convention to indicate the end-of-report.
+ * Problem is, EV_MSC is supposed to actually report data,
+ * so a new event type, EV_SYN, was created in Linux 2.5.x
+ * expressively for this purpose.
+ *
+ * Theoretically, if EV_SYN is defined, I should only terminate
+ * the population of device->currentValues struct IFF I receive
+ * that event. The fact of the matter is, the EV_MSC is assumed
+ * to be an ugliness that will take some time to be deprecated.
+ * For the nonce, we'll support both. But, if you have a tablet
+ * that's actually supplying something interesting with EV_MSC,
+ * this is obviously some code that requires modifications.
+ */
+#ifndef EV_SYN
+ if (event->type != EV_MSC)
+#else
+ if (event->type != EV_MSC && event->type != EV_SYN)
+#endif
+ {
+ continue;
+ }
+
+ /*
+ * We've seen EV_MSCs in the incoming data trail with no
+ * other message types in-between. We use 'eventsInMessage'
+ * to count all 'real' messages in-between. If there were none,
+ * do NOT copy common->currentValues to common->previousValues
+ * (as this will make the jitter filter useless). Just go and
+ * read the subsequent events.
+ */
+ if (eventsInMessage == 0)
+ {
+ continue;
+ }
+ eventsInMessage = 0;
+
+ /*
+ * This filter throws out reports that do not meet minimum threshold
+ * requirements for movement along that axis.
+ *
+ * Presently, we discard the entire report if any dimension of the
+ * currentValues struct does not meet it's minimum threshold.
+ *
+ * Also, we only do the comparison IFF a threshold has been set
+ * for that given dimension.
+ */
+ if ((device->xThreshold > 1 &&
+ ABS(common->currentValues.x - common->previousValues.x)
+ <= device->xThreshold) ||
+ (device->yThreshold > 1 &&
+ ABS(common->currentValues.y - common->previousValues.y)
+ <= device->yThreshold) ||
+ (device->zThreshold > 1 &&
+ ABS(common->currentValues.z - common->previousValues.z)
+ <= device->zThreshold) ||
+ (device->xTiltThreshold > 1 &&
+ ABS(common->currentValues.xTilt - common->previousValues.xTilt)
+ <= device->xTiltThreshold) ||
+ (device->yTiltThreshold > 1 &&
+ ABS(common->currentValues.yTilt - common->previousValues.yTilt)
+ <= device->yTiltThreshold))
+ {
+ DBG(10, ErrorF("Event Filtered Out by Thresholds\n"));
+ continue;
+ }
+
+ /*
+ * If this report somehow has exactly the same readings as the
+ * previous report for all dimensions, throw the report out.
+ */
+ if ((common->currentValues.x == common->previousValues.x) &&
+ (common->currentValues.y == common->previousValues.y) &&
+ (common->currentValues.z == common->previousValues.z) &&
+ (common->currentValues.proximity ==
+ common->previousValues.proximity) &&
+ (common->currentValues.button ==
+ common->previousValues.button) &&
+ (common->currentValues.macroKey ==
+ common->previousValues.macroKey))
+ {
+ DBG(10, ErrorF("Event Filtered Out\n"));
+ continue;
+ }
+
+ /*
+ * We have three different methods by which we report pressure, Z.
+ * One is to use linear values from 0 to common->zCapacity. The
+ * other two, SOFT_SMOOTH and HARD_SMOOTH, use different
+ * algorithms to 'smooth out' the values.
+ */
+ d_z = (double)common->currentValues.z;
+ d_zCapacity = (double)common->zCapacity;
+
+ switch (device->zMode)
+ {
+ case VALUE_NA:
+ case PRESSURE_MODE_LINEAR:
+ {
+ /* Leave Z alone. */
+ }
+ break;
+
+ case PRESSURE_MODE_SOFT_SMOOTH:
+ {
+ d_z = (d_z * d_z / d_zCapacity)+ 0.5;
+ }
+ break;
+
+ case PRESSURE_MODE_HARD_SMOOTH:
+ {
+ d_z = (d_zCapacity * sqrt( d_z / d_zCapacity)) + 0.5;
+ }
+ break;
+ }
+
+ /* Dispatch events to all of our configured devices. */
+ for (i=0; i < common->numDevices; ++i)
+ {
+ AiptekDevicePtr dev = common->deviceArray[i]->private;
+ int id;
+
+ id = DEVICE_ID (dev->flags);
+
+ /* Find the device the current events are meant for */
+ if (id == common->currentValues.eventType)
+ {
+ /* We left 'z' alone during smoothing, so send up
+ * perturbed value outside of the struct
+ */
+ xf86AiptekSendEvents(common->deviceArray[i], (int) d_z);
+ }
+ }
+
+ /*
+ * Copy the values just processed into the previousValues struct,
+ * so we can check for 'jittering' in the subsequent report.
+ */
+ common->previousValues.eventType = common->currentValues.eventType;
+ common->previousValues.x = common->currentValues.x;
+ common->previousValues.y = common->currentValues.y;
+ common->previousValues.z = common->currentValues.z;
+ common->previousValues.proximity = common->currentValues.proximity;
+ common->previousValues.button = common->currentValues.button;
+ common->previousValues.macroKey = common->currentValues.macroKey;
+ common->previousValues.xTilt = common->currentValues.xTilt;
+ common->previousValues.yTilt = common->currentValues.yTilt;
+ common->previousValues.distance = common->currentValues.distance;
+ common->previousValues.wheel = common->currentValues.wheel;
+
+ common->currentValues.macroKey = VALUE_NA;
+ }
+}
+
+/*
+ ***************************************************************************
+ *
+ * xf86AiptekHIDOpen --
+ *
+ ***************************************************************************
+ */
+static Bool
+xf86AiptekHIDOpen(LocalDevicePtr local)
+{
+ AiptekDevicePtr device = (AiptekDevicePtr)local->private;
+ AiptekCommonPtr common = device->common;
+ char name[256] = "Unknown";
+ int abs[5];
+ unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
+ int i, j;
+ int err = 0;
+ int version;
+
+ local->fd = xf86OpenSerial(local->options);
+ if (local->fd == -1)
+ {
+ ErrorF("xf86AiptekHIDOpen Error opening %s : %s\n", common->deviceName, strerror(errno));
+ return !Success;
+ }
+
+ ioctl(local->fd, EVIOCGNAME(sizeof(name)), name);
+ ErrorF("%s HID Device name: \"%s\"\n", XCONFIG_PROBED, name);
+
+ ioctl(local->fd, EVIOCGVERSION, &version);
+ ErrorF("%s HID Driver Version: %d.%d.%d\n", XCONFIG_PROBED,
+ version>>16, (version>>8) & 0xff, version & 0xff);
+
+ ErrorF("%s HID Driver knows it has %d devices configured\n", XCONFIG_PROBED,
+ common->numDevices);
+ ErrorF("%s HID Driver is using %d as the fd\n", XCONFIG_PROBED, local->fd);
+
+ for (i = 0; i < common->numDevices; ++i)
+ {
+ common->deviceArray[i]->read_input = xf86AiptekHIDReadInput;
+ common->deviceArray[i]->fd = local->fd;
+ common->deviceArray[i]->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED;
+ }
+ common->open = xf86AiptekHIDOpen;
+
+ memset(bit, 0, sizeof(bit));
+ ioctl(local->fd, EVIOCGBIT(0, EV_MAX), bit[0]);
+
+ for (i = 0; i < EV_MAX; ++i)
+ {
+ if (TEST_BIT(i, bit[0]))
+ {
+ ioctl(local->fd, EVIOCGBIT(i, KEY_MAX), bit[i]);
+ for (j = 0; j < KEY_MAX; ++j)
+ {
+ if (TEST_BIT(j, bit[i]))
+ {
+ if (i == EV_ABS)
+ {
+ ioctl(local->fd, EVIOCGABS(j), abs);
+ switch (j)
+ {
+ case ABS_X:
+ {
+ ErrorF("From ioctl() xCapacity=%d\n", abs[2]);
+ common->xCapacity = abs[2];
+ }
+ break;
+
+ case ABS_Y:
+ {
+ ErrorF("From ioctl() yCapacity=%d\n", abs[2]);
+ common->yCapacity = abs[2];
+ }
+ break;
+
+ case ABS_Z:
+ {
+ ErrorF("From ioctl() zCapacity=%d\n", abs[2]);
+ common->zCapacity = abs[2];
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (err < 0)
+ {
+ ErrorF("xf86AiptekHIDOpen ERROR: %d\n", err);
+ SYSCALL(close(local->fd));
+ return !Success;
+ }
+
+ return Success;
+}
+
+/*
+ * xf86AiptekControlProc
+ */
+static void
+xf86AiptekControlProc(DeviceIntPtr device, PtrCtrl *ctrl)
+{
+ DBG(2, ErrorF("xf86AiptekControlProc\n"));
+}
+
+/*
+** xf86AiptekOpen
+ * Open and initialize the tablet, as well as probe for any needed data.
+ * (This is TTY style open)
+ */
+
+static Bool
+xf86AiptekOpen(LocalDevicePtr local)
+{
+ AiptekDevicePtr device = (AiptekDevicePtr)local->private;
+ AiptekCommonPtr common = device->common;
+ int err, version;
+
+ DBG(1, ErrorF("Opening %s\n", common->deviceName));
+
+ local->fd = xf86OpenSerial(local->options);
+ if (local->fd < 0)
+ {
+ ErrorF("Error opening %s: %s\n", common->deviceName, strerror(errno));
+ return !Success;
+ }
+
+ DBG(1, ErrorF("Testing USB\n"));
+
+ SYSCALL(err = ioctl(local->fd, EVIOCGVERSION, &version));
+ if (!err)
+ {
+ int j;
+
+ SYSCALL(close(local->fd));
+
+ for(j=0; j<common->numDevices; ++j)
+ {
+ common->deviceArray[j]->read_input=xf86AiptekHIDReadInput;
+ }
+ common->open=xf86AiptekHIDOpen;
+
+ return xf86AiptekHIDOpen(local);
+ }
+
+ /* We do not support TTY mode, so just exit angry. */
+ return !Success;
+}
+
+/*
+ * xf86AiptekOpenDevice
+ * Opens and initializes the device driver.
+ */
+static int
+xf86AiptekOpenDevice(DeviceIntPtr pDriver)
+{
+ LocalDevicePtr local = (LocalDevicePtr)pDriver->public.devicePrivate;
+ AiptekDevicePtr device = (AiptekDevicePtr)PRIVATE(pDriver);
+ AiptekCommonPtr common = device->common;
+ double tabletRatio, screenRatio;
+ double xFactor, yFactor;
+ int gap, loop;
+
+ DBG(2, ErrorF("In xf86AiptekOpenDevice, with fd=%d\n", local->fd));
+
+ if (local->fd < 0)
+ {
+ if (common->initNumber > 2 ||
+ device->initNumber == common->initNumber)
+ {
+ if (common->open(local) != Success)
+ {
+ if (local->fd >= 0)
+ {
+ SYSCALL(close(local->fd));
+ }
+ local->fd = -1;
+ return !Success;
+ }
+ else
+ {
+ /* Report the file descriptor to all devices */
+ for (loop=0; loop < common->numDevices; ++loop)
+ {
+ common->deviceArray[loop]->fd = local->fd;
+ }
+ }
+ common->initNumber++;
+ }
+ device->initNumber = common->initNumber;
+ }
+
+ /*
+ * Check our active area parameters. We support the following
+ * three sets of mutually exclusive parameter sets:
+ * 1) xMax/yMax. The origin (0,0) of the active area is the origin
+ * of the physical tablet. You therefore are describing the
+ * width and height of that active area.
+ * 2) xOffset/xSize,yOffset/ySize. The origin (0,0) of the active
+ * area is defined as (xOffset,yOffset) (which we'll report as
+ * (0,0)). The size of the active area in width and height are
+ * expressed in coordinates as xSize/ySize. That is to say,
+ * if xOffset=5; yOffset=5, and xSize=10; ySize=10, then we will
+ * have an active area beginning at (5,5) and ending at (15,15).
+ * Physical coordinate (5,5) is reported as (0,0); (15,15) is
+ * reported as (10,10). The rest of the tablet is inert, as far as
+ * drawing area goes,
+ * 3) xTop/xBottom,yTop/yBottom. The difference between this and
+ * #2 above is that all four parameters are physical coordinates
+ * on the tablet. Using the example above, xTop=5; yTop=5, and
+ * xBottom=15; yBottom=15. It is inferred mathematically that
+ * the overall active area is 10x10 coordinates.
+ *
+ * NOTE: Mutually exclusive means just that: choose the set of
+ * parameters you like, and use them throughout. If you user xSize,
+ * yOffset and xBottom, we'll have NO idea what you want, and quite
+ * frankly you'll have tempted Fate enough that Bad Things(tm) will
+ * happen to you. Do not complain to us!
+ */
+ if (device->xMax != VALUE_NA ||
+ device->yMax != VALUE_NA)
+ {
+ /* Deal with larger-than-tablet and NA values.
+ */
+ if (device->xMax > common->xCapacity ||
+ device->xMax == VALUE_NA)
+ {
+ device->xMax = common->xCapacity;
+ xf86Msg(X_CONFIG, "xMax value invalid; adjusting to %d\n",
+ device->xMax);
+ }
+ if (device->yMax > common->yCapacity ||
+ device->yMax == VALUE_NA)
+ {
+ device->yMax = common->yCapacity;
+ xf86Msg(X_CONFIG,"yMax value invalid; adjusting to %d\n",
+ device->yMax);
+ }
+
+ /*
+ * Internally we use xTop/yTop/xBottom/yBottom
+ * for everything. It's the easiest for us to work
+ * with, vis-a-vis filtering.
+ */
+
+ device->xTop = 0;
+ device->yTop = 0;
+ device->xBottom = device->xMax;
+ device->yBottom = device->yMax;
+ }
+
+ /*
+ * Deal with xOffset/yOffset;xSize/ySize parameters
+ */
+ if (device->xSize != VALUE_NA ||
+ device->ySize != VALUE_NA ||
+ device->xOffset != VALUE_NA ||
+ device->yOffset != VALUE_NA)
+ {
+ int message = 0;
+ /* Simple sanity tests: nothing larger than the
+ * tablet; nothing negative, except for an NA value.
+ */
+ if (device->xOffset != VALUE_NA &&
+ (device->xOffset > common->xCapacity ||
+ device->xOffset < 0))
+ {
+ message = 1;
+ device->xOffset = 0;
+ }
+ if (device->yOffset != VALUE_NA &&
+ (device->yOffset > common->yCapacity ||
+ device->yOffset < 0))
+ {
+ message = 1;
+ device->yOffset = 0;
+ }
+ if (device->xSize != VALUE_NA &&
+ (device->xSize > common->xCapacity ||
+ device->xSize < 0))
+ {
+ message = 1;
+ device->xSize = common->xCapacity;
+ }
+ if (device->ySize != VALUE_NA &&
+ (device->ySize > common->yCapacity ||
+ device->ySize < 0))
+ {
+ message = 1;
+ device->ySize = common->yCapacity;
+ }
+
+ /*
+ * If one parameter is set but not the other, we'll
+ * guess at something reasonable for the missing one.
+ */
+ if (device->xOffset == VALUE_NA ||
+ device->xSize == VALUE_NA)
+ {
+ if (device->xOffset == VALUE_NA)
+ {
+ message = 1;
+ device->xOffset = 0;
+ }
+ else
+ {
+ message = 1;
+ device->xSize = common->xCapacity - device->xOffset;
+ }
+ }
+ if (device->yOffset == VALUE_NA ||
+ device->ySize == VALUE_NA)
+ {
+ if (device->yOffset == VALUE_NA)
+ {
+ message = 1;
+ device->yOffset = 0;
+ }
+ else
+ {
+ message = 1;
+ device->ySize = common->yCapacity - device->yOffset;
+ }
+ }
+
+ /*
+ * Do not allow the active area to exceed the size of the
+ * tablet. To do this, we have to consider both parameters.
+ * Assumption: xOffset/yOffset is always correct; deliver less
+ * of the tablet than they asked for, if they asked for too much.
+ */
+ if (device->xOffset + device->xSize > common->xCapacity)
+ {
+ message = 1;
+ device->xSize = common->xCapacity - device->xOffset;
+ }
+ if (device->yOffset + device->ySize > common->yCapacity)
+ {
+ message = 1;
+ device->ySize = common->yCapacity - device->yOffset;
+ }
+
+ /*
+ * 'message' is used to indicate that we've changed some parameter
+ * during our filtration process. It's conceivable that we may
+ * have changed parameters several times, so we without commentary
+ * to the very end.
+ */
+ if (message == 1)
+ {
+ xf86Msg(X_CONFIG,"xOffset/yOffset;xSize/ySize values wrong.\n");
+ xf86Msg(X_CONFIG,"xOffset adjusted to %d\n", device->xOffset);
+ xf86Msg(X_CONFIG,"yOffset adjusted to %d\n", device->yOffset);
+ xf86Msg(X_CONFIG,"xSize adjusted to %d\n", device->xSize);
+ xf86Msg(X_CONFIG,"ySize adjusted to %d\n", device->ySize);
+ }
+
+ /*
+ * Internally we use xTop/yTop/xBottom/yBottom
+ * for everything. It's the easiest for us to work
+ * with, vis-a-vis filtering.
+ */
+ device->xTop = device->xOffset;
+ device->yTop = device->yOffset;
+ device->xBottom = device->xOffset + device->xSize;
+ device->yBottom = device->yOffset + device->ySize;
+ }
+
+ /*
+ * Third set of parameters. Because everything internally
+ * is expressed as xTop/yTop, etc., I'll do tests on transformed
+ * values from the other parameters as need. My last chance to do
+ * so.
+ */
+ if (device->xTop == VALUE_NA ||
+ device->xTop < 0 ||
+ device->xTop > common->xCapacity)
+ {
+ device->xTop = 0;
+ xf86Msg(X_CONFIG,"xTop invalid; adjusted to %d\n", device->xTop);
+ }
+ if (device->yTop == VALUE_NA ||
+ device->yTop < 0 ||
+ device->yTop > common->yCapacity)
+ {
+ device->yTop = 0;
+ xf86Msg(X_CONFIG,"yTop invalid; adjusted to %d\n", device->yTop);
+ }
+ if (device->xBottom == VALUE_NA ||
+ device->xBottom < 0 ||
+ device->xBottom > common->xCapacity)
+ {
+ device->xBottom = common->xCapacity;
+ xf86Msg(X_CONFIG,"xBottom invalid; adjusted to %d\n",
+ device->xBottom);
+ }
+ if (device->yBottom == VALUE_NA ||
+ device->yBottom < 0 ||
+ device->yBottom > common->yCapacity)
+ {
+ device->yBottom = common->yCapacity;
+ xf86Msg(X_CONFIG,"yBottom invalid; adjusted to %d\n",
+ device->yBottom);
+ }
+
+ /*
+ * Determine the X screen we're going to be using.
+ * If NA, or larger than the number of screens we
+ * have, or negative, we've going for screen 0, e.g.,
+ * 'default' screen.
+ */
+ if ( device->screenNo >= screenInfo.numScreens ||
+ device->screenNo == VALUE_NA ||
+ device->screenNo < 0)
+ {
+ device->screenNo = 0;
+ xf86Msg(X_CONFIG,"ScreenNo invalid; adjusted to %d\n",
+ device->screenNo);
+ }
+
+ /* Calculate the ratio according to KeepShape, TopX and TopY */
+
+ if (device->flags & KEEP_SHAPE_FLAG)
+ {
+ int xDiff, yDiff;
+
+ xDiff = common->xCapacity - device->xTop;
+ yDiff = common->yCapacity - device->yTop;
+
+ tabletRatio = (double) xDiff / (double) yDiff;
+ screenRatio = (double) screenInfo.screens[device->screenNo]->width /
+ (double) screenInfo.screens[device->screenNo]->height;
+
+ DBG(2, ErrorF("Screen %d: screenRatio = %.3g, tabletRatio = %.3g\n",
+ device->screenNo, screenRatio, tabletRatio));
+
+ if (screenRatio > tabletRatio)
+ {
+ gap = (int)((double)common->yCapacity *
+ (1.0 - tabletRatio/screenRatio));
+ device->xBottom = common->xCapacity;
+ device->yBottom = common->yCapacity - gap;
+ DBG(2, ErrorF("Screen %d: 'Y' Gap of %d computed\n",
+ device->screenNo, gap));
+ }
+ else
+ {
+ gap = (int)((double)common->xCapacity *
+ (1.0 - screenRatio/tabletRatio));
+ device->xBottom = common->xCapacity - gap;
+ device->yBottom = common->yCapacity;
+ DBG(2, ErrorF("Screen %d: 'X' Gap of %d computed\n",
+ device->screenNo, gap));
+ }
+ }
+
+ xFactor = (double)screenInfo.screens[device->screenNo]->width/
+ (double)(device->xBottom - device->xTop);
+ yFactor = (double)screenInfo.screens[device->screenNo]->height/
+ (double)(device->yBottom - device->yTop);
+
+ /*
+ * Check threshold correctness
+ */
+ if (device->xThreshold > common->xCapacity ||
+ device->xThreshold == VALUE_NA ||
+ device->xThreshold < 0)
+ {
+ device->xThreshold = 0;
+ }
+
+ if (device->yThreshold > common->yCapacity ||
+ device->yThreshold == VALUE_NA ||
+ device->yThreshold < 0)
+ {
+ device->yThreshold = 0;
+ }
+
+ if (device->zThreshold > common->zCapacity ||
+ device->zThreshold == VALUE_NA ||
+ device->zThreshold < 0)
+ {
+ device->zThreshold = 0;
+ }
+
+ /* Create axisStructs for every axis we support.
+ * NOTE: min_resolution and max_resolution infers to
+ * me a programmability to increase/decrease resolution.
+ * We don't support that, so min & max = current_resolution.
+ */
+ InitValuatorAxisStruct(pDriver, /* X resolution */
+ 0, /* axis_id */
+ 0, /* min value */
+ device->xBottom - device->xTop, /* max value */
+ LPI2CPM(375), /* resolution */
+ LPI2CPM(375), /* min_resolution */
+ LPI2CPM(375)); /* max_resolution */
+
+ InitValuatorAxisStruct(pDriver, /* Y Resolution */
+ 1, /* axis_id */
+ 0, /* min value */
+ device->yBottom - device->yTop, /* max value */
+ LPI2CPM(375), /* resolution */
+ LPI2CPM(375), /* min_resolution */
+ LPI2CPM(375)); /* max_resolution */
+
+ InitValuatorAxisStruct(pDriver, /* Pressure */
+ 2, /* axis_id */
+ 0, /* min value */
+ 511, /* max value */
+ 512, /* resolution */
+ 512, /* min_resolution */
+ 512); /* max_resolution */
+
+ InitValuatorAxisStruct(pDriver, /* xTilt */
+ 3, /* axis id */
+ -128, /* min value */
+ 127, /* max value */
+ 256, /* resolution */
+ 256, /* min_resolution */
+ 256); /* max_resolution */
+
+ InitValuatorAxisStruct(pDriver, /* yTilt */
+ 4, /* axis_id */
+ -128, /* min value */
+ 127, /* max value */
+ 256, /* resolution */
+ 256, /* min_resolution */
+ 256); /* max_resolution */
+
+ /*
+ * The sixth axis would be for wheel support. We do not have
+ * any wheel devices. But if we did, it would be allocated
+ * here.
+ */
+ return (local->fd != -1);
+}
+
+/*
+ * xf86AiptekProc
+ *
+ * Call dispatcher for this driver.
+ */
+static int
+xf86AiptekProc(DeviceIntPtr pAiptek, int requestCode)
+{
+ CARD8 map[512+1];
+ int numAxes;
+ int numButtons;
+ int loop;
+ LocalDevicePtr local = (LocalDevicePtr)pAiptek->public.devicePrivate;
+ AiptekDevicePtr device = (AiptekDevicePtr)PRIVATE(pAiptek);
+
+ DBG(2, ErrorF("xf86AiptekProc() type=%s flags=%d request=%d\n",
+ (DEVICE_ID(device->flags) == STYLUS_ID) ? "stylus" :
+ (DEVICE_ID(device->flags) == CURSOR_ID) ? "cursor" : "eraser",
+ device->flags, requestCode));
+
+ switch (requestCode)
+ {
+ case DEVICE_INIT:
+ {
+ DBG(1, ErrorF("xf86AiptekProc request=INIT\n"));
+ numAxes = 5; /* X, Y, Z, xTilt, yTilt */
+ numButtons = 5;
+
+ for(loop=1; loop<=numButtons; ++loop)
+ {
+ map[loop] = loop;
+ }
+
+ if (InitButtonClassDeviceStruct(pAiptek,numButtons,map) == FALSE)
+ {
+ ErrorF("Unable to init Button Class Device\n");
+ return !Success;
+ }
+
+ if (InitFocusClassDeviceStruct(pAiptek) == FALSE)
+ {
+ ErrorF("Unable to init Focus Class Device\n");
+ return !Success;
+ }
+
+ if (InitPtrFeedbackClassDeviceStruct(pAiptek,
+ xf86AiptekControlProc) == FALSE)
+ {
+ ErrorF("Unable to init Pointer Feedback Class Device\n");
+ return !Success;
+ }
+
+ if (InitProximityClassDeviceStruct(pAiptek) == FALSE)
+ {
+ ErrorF("Unable to init Proximity Class Device\n");
+ return !Success;
+ }
+
+ if (InitKeyClassDeviceStruct(pAiptek, &keysyms, NULL) ==FALSE)
+ {
+ ErrorF("Unable to init Key Class Device\n");
+ return !Success;
+ }
+
+ if (InitValuatorClassDeviceStruct(pAiptek,
+ numAxes,
+ xf86GetMotionEvents,
+ local->history_size,
+ ((device->flags & ABSOLUTE_FLAG)
+ ? Absolute : Relative) | OutOfProximity ) == FALSE)
+ {
+ ErrorF("Unable to allocate Valuator Class Device\n");
+ return !Success;
+ }
+
+ /* Allocate the motion history buffer if needed */
+ xf86MotionHistoryAllocate(local);
+
+ /* Open the device to gather information */
+ xf86AiptekOpenDevice(pAiptek);
+ }
+ break;
+
+ case DEVICE_ON:
+ {
+ DBG(1, ErrorF("xf86AiptekProc request=ON\n"));
+
+ if ((local->fd < 0) &&
+ (!xf86AiptekOpenDevice(pAiptek)))
+ {
+ ErrorF("Unable to open aiptek device\n");
+ return !Success;
+ }
+ ErrorF("Able to open aiptek device\n");
+ xf86AddEnabledDevice(local);
+ pAiptek->public.on = TRUE;
+ }
+ break;
+
+ case DEVICE_OFF:
+ {
+ DBG(1, ErrorF("xf86AiptekProc request=%s\n",
+ (requestCode == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
+ if (local->fd >= 0)
+ {
+ xf86AiptekClose(local);
+ xf86RemoveEnabledDevice(local);
+ }
+ pAiptek->public.on = FALSE;
+ }
+ break;
+
+ case DEVICE_CLOSE:
+ {
+ DBG(1, ErrorF("xf86AiptekProc request=%s\n",
+ (requestCode == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
+ xf86AiptekClose(local);
+ }
+ break;
+
+ default:
+ {
+ ErrorF("xf86AiptekProc - Unsupported mode=%d\n", requestCode);
+ return !Success;
+ }
+ break;
+ }
+ DBG(2, ErrorF("xf86AiptekProc Success request=%d\n", requestCode ));
+ return Success;
+}
+
+/*
+ * xf86AiptekClose
+ * Perhaps this will close the device
+ */
+static void
+xf86AiptekClose(LocalDevicePtr local)
+{
+ if (local->fd >= 0)
+ {
+ SYSCALL(close(local->fd));
+ }
+ local->fd = -1;
+}
+
+/*
+ * xf86AiptekChangeControl
+ * Allow the user to change the tablet resolution -- we have an issue
+ * insofar as we don't know how to write to the tablet. And furthermore,
+ * even if we DID know how to write to the tablet, it doesn't support
+ * a "change resolution" call. We tried to avoid this by claiming when
+ * creating axisStructs that minRes = curRes = maxRes. So, we should never
+ * get dispatched.
+ */
+static int
+xf86AiptekChangeControl(LocalDevicePtr local, xDeviceCtl *control)
+{
+ xDeviceResolutionCtl *res;
+ int *resolutions;
+
+ DBG(3, ErrorF("xf86AiptekChangeControl() entered\n"));
+
+ res = (xDeviceResolutionCtl *)control;
+
+ if ((control->control != DEVICE_RESOLUTION) ||
+ (res->num_valuators < 1))
+ {
+ DBG(3, ErrorF("xf86AiptekChangeControl abends\n"));
+ return (BadMatch);
+ }
+
+ resolutions = (int *)(res +1);
+
+ DBG(3, ErrorF("xf86AiptekChangeControl changing to res %d\n",
+ resolutions[0]));
+
+ /* We don't know how to write, yet
+ *
+ * sprintf(str, "SU%d\r", resolutions[0]);
+ * SYSCALL(write(local->fd, str, strlen(str)));
+ */
+
+ return(Success);
+}
+
+/*
+ * xf86AiptekSwitchMode
+ * Switches the mode. For now just absolute or relative, hopefully
+ * more on the way.
+ */
+static int
+xf86AiptekSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode)
+{
+ LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate;
+ AiptekDevicePtr device = (AiptekDevicePtr)(local->private);
+
+ DBG(3, ErrorF("xf86AiptekSwitchMode() dev=0x%02x mode=%d\n", dev, mode));
+
+ switch(mode)
+ {
+ case Absolute:
+ {
+ device->flags |= ABSOLUTE_FLAG;
+ }
+ break;
+
+ case Relative:
+ {
+ device->flags &= ~ABSOLUTE_FLAG;
+ }
+ break;
+
+ default:
+ {
+ DBG(1, ErrorF("xf86AiptekSwitchMode dev=0x%02x invalid mode=%d\n",
+ dev, mode));
+ return BadMatch;
+ }
+ break;
+ }
+
+ return Success;
+}
+
+/*
+ * xf86AiptekAllocate
+ * Allocates the device structures for the Aiptek.
+ */
+static LocalDevicePtr
+xf86AiptekAllocate(char* name,
+ int flag)
+{
+ LocalDevicePtr local;
+ LocalDevicePtr* deviceArray;
+ AiptekDevicePtr device;
+ AiptekCommonPtr common;
+
+ DBG(3, ErrorF("xf86AiptekAllocate, with %s and %d\n", name, flag));
+
+ device = (AiptekDevicePtr) xalloc(sizeof(AiptekDeviceRec));
+ if (!device)
+ {
+ DBG(3, ErrorF("xf86AiptekAllocate failed to allocate 'device'\n"));
+ return NULL;
+ }
+
+ common = (AiptekCommonPtr) xalloc(sizeof(AiptekCommonRec));
+ if (!common)
+ {
+ DBG(3, ErrorF("xf86AiptekAllocate failed to allocate 'common'\n"));
+ xfree(device);
+ return NULL;
+ }
+
+ deviceArray = (LocalDevicePtr*) xalloc(sizeof(LocalDevicePtr));
+ if (!deviceArray)
+ {
+ DBG(3, ErrorF("xf86AiptekAllocate failed to allocate 'deviceArray'\n"));
+ xfree(device);
+ xfree(common);
+ return NULL;
+ }
+
+
+ local = xf86AllocateInput(aiptekDrv, 0);
+ if (!local)
+ {
+ DBG(3, ErrorF("xf86AiptekAllocate failed at xf86AllocateInput()\n"));
+ xfree(device);
+ xfree(common);
+ xfree(deviceArray);
+ return NULL;
+ }
+
+ local->name = name;
+ local->type_name = "Aiptek";
+ local->flags = 0;
+ local->device_control = xf86AiptekProc;
+ local->read_input = xf86AiptekHIDReadInput;
+ local->control_proc = xf86AiptekChangeControl;
+ local->close_proc = xf86AiptekClose;
+ local->switch_mode = xf86AiptekSwitchMode;
+ local->conversion_proc = xf86AiptekConvert;
+ local->reverse_conversion_proc = xf86AiptekReverseConvert;
+
+ local->fd = VALUE_NA;
+ local->atom = 0;
+ local->dev = NULL;
+ local->private = device;
+ local->private_flags = 0;
+ local->history_size = 0;
+
+ device->flags = flag; /* various flags (device type,
+ * coordinate type */
+ device->xSize = VALUE_NA; /* Active Area X */
+ device->ySize = VALUE_NA; /* Active Area Y */
+ device->xOffset = VALUE_NA; /* Active area offset X */
+ device->yOffset = VALUE_NA; /* Active area offset Y */
+ device->xMax = VALUE_NA; /* Max allowed X value */
+ device->yMax = VALUE_NA; /* Max allowed Y value */
+ device->zMin = VALUE_NA; /* Min allowed Z value */
+ device->zMax = VALUE_NA; /* Max allowed Z value */
+ device->xTop = VALUE_NA; /* Upper Left X coordinate */
+ device->yTop = VALUE_NA; /* Upper Left Y coordinate */
+ device->xBottom = VALUE_NA; /* Lower Right X coordinate */
+ device->yBottom = VALUE_NA; /* Lower Right Y coordinate */
+ device->xThreshold = VALUE_NA; /* X delta must be greater than */
+ device->yThreshold = VALUE_NA; /* Y delta must be greater than */
+ device->zThreshold = VALUE_NA; /* Z delta must be greater than */
+ device->xTiltThreshold =VALUE_NA;
+ device->yTiltThreshold =VALUE_NA;
+ device->zMode = VALUE_NA; /* Z: linear, soft, hard log */
+
+ device->initNumber = VALUE_NA; /* avoid re-init devices */
+ device->screenNo = VALUE_NA; /* Attached to X screen */
+
+ device->common = common; /* Common info pointer */
+
+ /* Record of the event currently being read of the queue */
+ common->currentValues.eventType = 0; /* Device event is for, e.g., */
+ /* STYLUS, RUBBER, CURSOR */
+ common->currentValues.x = 0; /* X coordinate */
+ common->currentValues.y = 0; /* Y coordinate */
+ common->currentValues.z = 0; /* Z (pressure) */
+ common->currentValues.xTilt = 0; /* XTilt */
+ common->currentValues.yTilt = 0; /* YTilt */
+ common->currentValues.proximity = 0; /* proximity bit */
+ common->currentValues.macroKey = VALUE_NA; /* tablet macro key code */
+ common->currentValues.button = 0; /* bitmask of buttons pressed */
+ common->currentValues.distance = 0; /* currently unsupported */
+ common->currentValues.wheel = 0; /* likewise */
+
+ /* Record of the event previously read off of the queue */
+ common->previousValues.eventType = 0; /* Same comments as above */
+ common->previousValues.x = 0;
+ common->previousValues.y = 0;
+ common->previousValues.z = 0;
+ common->previousValues.xTilt = 0;
+ common->previousValues.yTilt = 0;
+ common->previousValues.proximity = 0;
+ common->previousValues.macroKey = VALUE_NA;
+ common->previousValues.button = 0;
+ common->previousValues.distance = 0;
+ common->previousValues.wheel = 0;
+
+ common->deviceName = ""; /* device file name */
+ common->flags = 0; /* various flags */
+ common->deviceArray = deviceArray; /* Array of tablets */
+ common->deviceArray[0]= local; /* with local as element */
+ common->numDevices = 1; /* number of devices */
+
+ common->xCapacity = 0; /* tablet's max X value */
+ common->yCapacity = 0; /* tablet's max Y value */
+ common->zCapacity = 0; /* tablet's max Z value */
+ common->open = xf86AiptekOpen; /* Open function */
+
+ return local;
+}
+
+/*
+ * xf86AiptekAllocateStylus
+ */
+static LocalDevicePtr
+xf86AiptekAllocateStylus(void)
+{
+ LocalDevicePtr local = xf86AiptekAllocate(XI_STYLUS, STYLUS_ID);
+
+ if (local)
+ {
+ local->type_name = "Stylus";
+ }
+ return local;
+}
+
+/*
+ * xf86AiptekAllocateCursor
+ */
+static LocalDevicePtr
+xf86AiptekAllocateCursor(void)
+{
+ LocalDevicePtr local = xf86AiptekAllocate(XI_CURSOR, CURSOR_ID);
+
+ if (local)
+ {
+ local->type_name = "Cursor";
+ }
+ return local;
+}
+
+/*
+ * xf86AiptekAllocateEraser
+ */
+static LocalDevicePtr
+xf86AiptekAllocateEraser(void)
+{
+ LocalDevicePtr local = xf86AiptekAllocate(XI_ERASER,
+ ABSOLUTE_FLAG|ERASER_ID);
+
+ if (local)
+ {
+ local->type_name = "Eraser";
+ }
+ return local;
+}
+
+/*
+ * Stylus device association
+ */
+DeviceAssocRec aiptek_stylus_assoc =
+{
+ STYLUS_SECTION_NAME, /* config_section_name */
+ xf86AiptekAllocateStylus /* device_allocate */
+};
+
+/*
+ * Cursor device association
+ */
+DeviceAssocRec aiptek_cursor_assoc =
+{
+ CURSOR_SECTION_NAME, /* config_section_name */
+ xf86AiptekAllocateCursor /* device_allocate */
+};
+
+/*
+ * Eraser device association
+ */
+DeviceAssocRec aiptek_eraser_assoc =
+{
+ ERASER_SECTION_NAME, /* config_section_name */
+ xf86AiptekAllocateEraser /* device_allocate */
+};
+
+/*
+ * xf86AiptekUninit --
+ *
+ * called when the driver is unloaded.
+ */
+static void
+xf86AiptekUninit(InputDriverPtr drv,
+ LocalDevicePtr local,
+ int flags)
+{
+ AiptekDevicePtr device = (AiptekDevicePtr) local->private;
+
+ DBG(1, ErrorF("xf86AiptekUninit\n"));
+
+ xf86AiptekProc(local->dev, DEVICE_OFF);
+
+ if (device->common && device->common->xCapacity != -10101)
+ {
+ device->common->xCapacity = -10101;
+ xfree(device->common);
+ }
+ xfree (device);
+ xf86DeleteInput(local, 0);
+}
+
+/*
+ * xf86AiptekInit --
+ *
+ * Called when the module subsection is found in XF86Config
+ */
+static InputInfoPtr
+xf86AiptekInit(InputDriverPtr drv,
+ IDevPtr dev,
+ int flags)
+{
+ LocalDevicePtr local = NULL;
+ LocalDevicePtr fakeLocal = NULL;
+ AiptekDevicePtr device = NULL;
+ AiptekCommonPtr common = NULL;
+ LocalDevicePtr locals;
+ char* s;
+ int shared;
+
+ aiptekDrv = drv;
+
+ xf86Msg(X_INFO, "xf86AiptekInit(): begins\n");
+
+ fakeLocal = (LocalDevicePtr) xcalloc(1, sizeof(LocalDeviceRec));
+ if (!fakeLocal)
+ {
+ return NULL;
+ }
+
+ fakeLocal->conf_idev = dev;
+
+ /*
+ * fakeLocal is here so it can have default serial init values.
+ * Is this something to remove? TODO
+ */
+ xf86CollectInputOptions(fakeLocal, default_options, NULL);
+
+/* Type */
+ s = xf86FindOptionValue(fakeLocal->options, "Type");
+ if (s && (xf86NameCmp(s, "stylus") == 0))
+ {
+ local = xf86AiptekAllocateStylus();
+ }
+ else if (s && (xf86NameCmp(s, "cursor") == 0))
+ {
+ local = xf86AiptekAllocateCursor();
+ }
+ else if (s && (xf86NameCmp(s, "eraser") == 0))
+ {
+ local = xf86AiptekAllocateEraser();
+ }
+ else
+ {
+ xf86Msg(X_ERROR, "%s: No type or invalid type specified.\n"
+ "Must be one of 'stylus', 'cursor', or 'eraser'\n",
+ dev->identifier);
+ }
+
+ if(!local)
+ {
+ xfree(fakeLocal);
+ return NULL;
+ }
+
+ device = (AiptekDevicePtr) local->private;
+
+ common = device->common;
+
+ local->options = fakeLocal->options;
+ local->conf_idev = fakeLocal->conf_idev;
+ local->name = dev->identifier;
+ xfree(fakeLocal);
+
+/* Device */
+/* (mandatory) */
+ common->deviceName = xf86FindOptionValue(local->options, "Device");
+ if(!common->deviceName)
+ {
+ xf86Msg(X_ERROR, "%s: No Device specified.\n", dev->identifier);
+ goto SetupProc_fail;
+ }
+
+ /*
+ * Lookup to see if there is another aiptek device sharing the
+ * same device.
+ */
+
+ shared = 0;
+ for (locals = xf86FirstLocalDevice();
+ locals != NULL;
+ locals = locals->next)
+ {
+ if((local != locals) &&
+ (locals->device_control == xf86AiptekProc) &&
+ (strcmp(((AiptekDevicePtr)locals->private)->common->deviceName,
+ common->deviceName) == 0))
+ {
+ xf86Msg(X_CONFIG,
+ "xf86AiptekConfig: device shared between %s and %s\n",
+ local->name,
+ locals->name);
+
+ shared = 1;
+
+ xfree(common->deviceArray);
+ xfree(common);
+
+ common = device->common =
+ ((AiptekDevicePtr) locals->private)->common;
+ common->numDevices++;
+ common->deviceArray = (LocalDevicePtr*)xrealloc(common->deviceArray,
+ sizeof(LocalDevicePtr)*common->numDevices);
+ common->deviceArray[common->numDevices-1] = local;
+ break;
+ }
+ else
+ {
+ xf86Msg(X_CONFIG,
+ "xf86AiptekConfig: device not shared btw %s and %s\n",
+ local->name, locals->name);
+ }
+ }
+
+ /* Process the common options */
+ xf86ProcessCommonOptions(local, local->options);
+
+ /* If this is the first device using the aiptek driver, let's open
+ * the interface so as to obtain legit values for xCapacity and yCapacity
+ * (and then quickly close it). Yes, the word for this is "kludge".
+ * I am sufficiently ashamed :-) TODO kludge alert!
+ */
+ if ( shared == 0)
+ {
+ xf86AiptekHIDOpen(local);
+ close(local->fd);
+ local->fd=-1;
+ }
+
+ /* Optional configuration */
+ xf86Msg(X_CONFIG, "%s device is %s\n", dev->identifier,
+ common->deviceName);
+
+/* DebugLevel */
+ debug_level = xf86SetIntOption(local->options, "DebugLevel", debug_level);
+ if ( debug_level > 0)
+ {
+ xf86Msg(X_CONFIG, "Debug level set to %d\n", debug_level);
+ }
+
+/* zMode */
+ s = xf86FindOptionValue(local->options, "Pressure");
+ if ( s && (xf86NameCmp(s, "hard") == 0))
+ {
+ device->zMode = PRESSURE_MODE_HARD_SMOOTH;
+ }
+ else if ( s && (xf86NameCmp(s, "soft") == 0))
+ {
+ device->zMode = PRESSURE_MODE_SOFT_SMOOTH;
+ }
+ else if (s && (xf86NameCmp(s, "normal") == 0))
+ {
+ device->zMode = PRESSURE_MODE_LINEAR;
+ }
+ else if (s)
+ {
+ xf86Msg(X_ERROR, "%s: invalid Mode ('normal', 'soft' or 'hard').\n",
+ dev->identifier);
+ }
+
+/* Mode */
+ s = xf86FindOptionValue(local->options, "Mode");
+ if (s && (xf86NameCmp(s, "absolute") == 0))
+ {
+ device->flags |= ABSOLUTE_FLAG;
+ }
+ else if (s && (xf86NameCmp(s, "relative") == 0))
+ {
+ device->flags &= ~ABSOLUTE_FLAG;
+ }
+ else if (s)
+ {
+ xf86Msg(X_ERROR, "%s: invalid Mode ('absolute' or 'relative').\n",
+ dev->identifier);
+ device->flags |= ABSOLUTE_FLAG;
+ }
+ xf86Msg(X_CONFIG, "%s is in %s mode\n", local->name,
+ (device->flags & ABSOLUTE_FLAG) ? "absolute" : "relative");
+
+#ifdef LINUX_INPUT
+ /* The define-name is accurate; the XFree86 keyword is not. We are
+ * reading from a Linux kernel "Input" device. The Input device
+ * layer generally supports mice, joysticks, and keyboards. As
+ * an extension, the Input device layer also supports HID devices.
+ * HID is a standard specified by the USB Implementors Forum. Ergo,
+ * 99.9% of HID devices are USB devices.
+ *
+ * This option is misnamed, misunderstood, misanthrope. Maybe.
+ */
+ if (xf86SetBoolOption(local->options, "USB",
+ (common->open == xf86AiptekHIDOpen)))
+ {
+ local->read_input=xf86AiptekHIDReadInput;
+ common->open=xf86AiptekHIDOpen;
+ xf86Msg(X_CONFIG, "%s: reading USB link\n", dev->identifier);
+ }
+#else
+ if (xf86SetBoolOption(local->options, "USB", 0))
+ {
+ ErrorF("The Aiptek USB driver isn't available for your platform.\n");
+ goto SetupProc_fail;
+ }
+#endif
+
+/* ScreenNo */
+ device->screenNo = xf86SetIntOption(local->options, "ScreenNo", VALUE_NA);
+ if (device->screenNo != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: attached to screen number %d\n",
+ dev->identifier, device->screenNo);
+ }
+
+/* KeepShape */
+ if (xf86SetBoolOption(local->options, "KeepShape", 0))
+ {
+ device->flags |= KEEP_SHAPE_FLAG;
+ xf86Msg(X_CONFIG, "%s: keeps shape\n", dev->identifier);
+ }
+
+/* XSize */
+ device->xSize = xf86SetIntOption(local->options, "XSize", device->xSize);
+ device->xSize = xf86SetIntOption(local->options, "SizeX", device->xSize);
+ if (device->xSize != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: XSize/SizeX = %d\n", dev->identifier,
+ device->xSize);
+ }
+
+/* YSize */
+ device->ySize = xf86SetIntOption(local->options, "YSize", device->ySize);
+ device->ySize = xf86SetIntOption(local->options, "SizeY", device->ySize);
+ if (device->ySize != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: YSize/SizeY = %d\n", dev->identifier,
+ device->ySize);
+ }
+
+/* XOffset */
+ device->xOffset = xf86SetIntOption(local->options, "XOffset",
+ device->xOffset);
+ device->xOffset = xf86SetIntOption(local->options, "OffsetX",
+ device->xOffset);
+ if (device->xOffset != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: XOffset/OffsetX = %d\n", dev->identifier,
+ device->xOffset);
+ }
+
+/* YOffset */
+ device->yOffset = xf86SetIntOption(local->options, "YOffset",
+ device->yOffset);
+ device->yOffset = xf86SetIntOption(local->options, "OffsetY",
+ device->yOffset);
+ if (device->yOffset != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: YOffset/OffsetY = %d\n", dev->identifier,
+ device->yOffset);
+ }
+
+/* XThreshold */
+ device->xThreshold = xf86SetIntOption(local->options, "XThreshold",
+ device->xThreshold);
+ device->xThreshold = xf86SetIntOption(local->options, "ThresholdX",
+ device->xThreshold);
+ if (device->xThreshold != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: XThreshold/ThresholdX = %d\n",
+ dev->identifier, device->xThreshold);
+ }
+
+/* YThreshold */
+ device->yThreshold = xf86SetIntOption(local->options, "YThreshold",
+ device->yThreshold);
+ device->yThreshold = xf86SetIntOption(local->options, "ThresholdY",
+ device->yThreshold);
+ if (device->yThreshold != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: YThreshold/ThresholdY = %d\n",
+ dev->identifier, device->yThreshold);
+ }
+
+/* ZThreshold */
+ device->zThreshold = xf86SetIntOption(local->options, "ZThreshold",
+ device->zThreshold);
+ device->zThreshold = xf86SetIntOption(local->options, "ThresholdZ",
+ device->zThreshold);
+ if (device->zThreshold != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: ZThreshold/ThresholdZ = %d\n",
+ dev->identifier, device->zThreshold);
+ }
+
+/* XTiltThreshold */
+ device->xTiltThreshold = xf86SetIntOption(local->options, "XTiltThreshold",
+ device->xTiltThreshold);
+ device->xTiltThreshold = xf86SetIntOption(local->options, "ThresholdXTilt",
+ device->xTiltThreshold);
+ if (device->xTiltThreshold != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: XTiltThreshold = %d\n",
+ dev->identifier, device->xTiltThreshold);
+ }
+
+/* YTiltThreshold */
+ device->yTiltThreshold = xf86SetIntOption(local->options, "YTiltThreshold",
+ device->yTiltThreshold);
+ device->yTiltThreshold = xf86SetIntOption(local->options, "ThresholdYTilt",
+ device->yTiltThreshold);
+ if (device->yTiltThreshold != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: YTiltThreshold = %d\n",
+ dev->identifier, device->yTiltThreshold);
+ }
+
+/* XMax */
+ device->xMax = xf86SetIntOption(local->options, "XMax", device->xMax);
+ device->xMax = xf86SetIntOption(local->options, "MaxX", device->xMax);
+ if (device->xMax != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: XMax/MaxX = %d\n", dev->identifier,
+ device->xMax);
+ }
+
+/* YMax */
+ device->yMax = xf86SetIntOption(local->options, "YMax", device->yMax);
+ device->yMax = xf86SetIntOption(local->options, "MaxY", device->yMax);
+ if (device->yMax != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: YMax/MaxY = %d\n", dev->identifier,
+ device->yMax);
+ }
+
+/* ZMax */
+ device->zMax = xf86SetIntOption(local->options, "ZMax", device->zMax);
+ device->zMax = xf86SetIntOption(local->options, "MaxZ", device->zMax);
+ if (device->zMax != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: ZMax/MaxZ = %d\n", dev->identifier,
+ device->zMax);
+ }
+
+/* ZMin */
+ device->zMin = xf86SetIntOption(local->options, "ZMin", device->zMin);
+ device->zMin = xf86SetIntOption(local->options, "MinZ", device->zMin);
+ if (device->zMin != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: ZMin/MinZ = %d\n", dev->identifier,
+ device->zMin);
+ }
+
+/* TopX */
+ device->xTop = xf86SetIntOption(local->options, "TopX", device->xTop);
+ device->xTop = xf86SetIntOption(local->options, "XTop", device->xTop);
+ if (device->xTop != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: TopX/XTop = %d\n", dev->identifier,
+ device->xTop);
+ }
+
+/* TopY */
+ device->yTop = xf86SetIntOption(local->options, "TopY", device->yTop);
+ device->yTop = xf86SetIntOption(local->options, "YTop", device->yTop);
+ if (device->yTop != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: TopY/YTop = %d\n", dev->identifier,
+ device->yTop);
+ }
+
+/* BottomX */
+ device->xBottom = xf86SetIntOption(local->options, "BottomX",
+ device->xBottom);
+ device->xBottom = xf86SetIntOption(local->options, "XBottom",
+ device->xBottom);
+ if (device->xBottom != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: BottomX/XBottom = %d\n", dev->identifier,
+ device->xBottom);
+ }
+
+/* BottomY */
+ device->yBottom = xf86SetIntOption(local->options, "BottomY",
+ device->yBottom);
+ device->yBottom = xf86SetIntOption(local->options, "YBottom",
+ device->yBottom);
+ if (device->yBottom != VALUE_NA)
+ {
+ xf86Msg(X_CONFIG, "%s: BottomY/YBottom = %d\n", dev->identifier,
+ device->yBottom);
+ }
+
+/* InvX */
+ if (xf86SetBoolOption(local->options, "InvX", FALSE))
+ {
+ device->flags |= INVX_FLAG;
+ xf86Msg(X_CONFIG, "%s: InvX\n", dev->identifier);
+ }
+
+/* InvY */
+ if (xf86SetBoolOption(local->options, "InvY", FALSE))
+ {
+ device->flags |= INVY_FLAG;
+ xf86Msg(X_CONFIG, "%s: InvY\n", dev->identifier);
+ }
+
+/* BaudRate */
+ {
+ int val;
+ val = xf86SetIntOption(local->options, "BaudRate", 0);
+
+ switch(val)
+ {
+ case 19200:
+ break;
+ case 9600:
+ break;
+ default:
+ xf86Msg(X_ERROR, "%s: Illegal BaudRate (9600 or 19200).",
+ dev->identifier);
+ break;
+ }
+ if (xf86Verbose)
+ {
+ xf86Msg(X_CONFIG, "%s: BaudRate %u\n", dev->identifier,
+ val);
+ }
+ }
+ xf86Msg(X_CONFIG, "%s: xf86AiptekInit() finished\n", dev->identifier);
+
+ /* Mark the device as configured */
+ local->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED;
+
+ /* return the LocalDevice */
+ return (local);
+
+SetupProc_fail:
+ if (common)
+ xfree(common);
+ if (device)
+ xfree(device);
+ if (local)
+ xfree(local);
+ return NULL;
+}
+
+/*
+ ***************************************************************************
+ *
+ * Dynamic loading functions
+ *
+ ***************************************************************************
+ */
+#ifdef XFree86LOADER
+/*
+ * xf86AiptekUnplug --
+ *
+ * called when the module subsection is found in XF86Config
+ */
+static void
+xf86AiptekUnplug(pointer p)
+{
+ DBG(1, ErrorF("xf86AiptekUnplug\n"));
+}
+
+/*
+ * xf86AiptekPlug --
+ *
+ * called when the module subsection is found in XF86Config
+ */
+static pointer
+xf86AiptekPlug(pointer module,
+ pointer options,
+ int* errmaj,
+ int* errmin)
+{
+ DBG(1, ErrorF("xf86AiptekPlug\n"));
+
+ xf86AddInputDriver(&AIPTEK, module, 0);
+
+ return module;
+}
+
+static XF86ModuleVersionInfo xf86AiptekVersionRec =
+{
+ "aiptek",
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XF86_VERSION_CURRENT,
+ 1, 0, 0,
+ ABI_CLASS_XINPUT,
+ ABI_XINPUT_VERSION,
+ MOD_CLASS_XINPUT,
+ {0, 0, 0, 0} /* signature, to be patched into the file by
+ * a tool
+ * */
+};
+
+XF86ModuleData aiptekModuleData =
+{
+ &xf86AiptekVersionRec,
+ xf86AiptekPlug,
+ xf86AiptekUnplug
+};
+
+#endif /* XFree86LOADER */
+
+/* end of xf86Aiptek.c */
diff --git a/src/xf86Aiptek.h b/src/xf86Aiptek.h
new file mode 100644
index 0000000..2ed4751
--- /dev/null
+++ b/src/xf86Aiptek.h
@@ -0,0 +1,366 @@
+/*
+ * xf86Aiptek
+ *
+ * Lineage: This driver is based on both the xf86HyperPen and xf86Wacom tablet
+ * drivers.
+ *
+ * xf86HyperPen -- modified from xf86Summa (c) 1996 Steven Lang
+ * (c) 2000 Roland Jansen
+ * (c) 2000 Christian Herzog (button & 19200 baud support)
+ *
+ * xf86Wacom -- (c) 1995-2001 Frederic Lepied
+ *
+ * This driver assumes Linux HID support, available for USB devices.
+ *
+ * Version 0.0, 1-Jan-2003, Bryan W. Headley
+ *
+ * Copyright 2003 by Bryan W. Headley. <bwheadley@earthlink.net>
+ *
+ * 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 Bryan W. Headley not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. Bryan W. Headley makes no
+ * representations about the suitability of this software for any purpose.
+ * It is provided "as is" without express or implied warranty.
+ *
+ * BRYAN W. HEADLEY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL BRYAN W. HEADLEY 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 ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/* $XFree86: xc/programs/Xserver/hw/xfree86/input/aiptek/xf86Aiptek.h,v 1.2 2003/11/03 05:36:32 tsi Exp $ */
+
+#ifndef _AIPTEK_H_
+#define _AIPTEK_H_
+
+
+#ifdef LINUX_INPUT
+# include <asm/types.h>
+# include <linux/input.h>
+/* keithp - a hack to avoid redefinitions of these in xf86str.h */
+# ifdef BUS_PCI
+# undef BUS_PCI
+# endif
+# ifdef BUS_ISA
+# undef BUS_ISA
+# endif
+#endif
+
+#include <xf86Version.h>
+
+#ifndef XFree86LOADER
+# include <unistd.h>
+# include <errno.h>
+#endif
+
+#include <misc.h>
+#include <xf86.h>
+
+#define NEED_XF86_TYPES
+
+#if !defined(DGUX)
+# include <xf86_ansic.h>
+# include <xisb.h>
+#endif
+
+#include <xf86_OSproc.h>
+#include <xf86Xinput.h>
+#include <exevents.h> /* Needed for InitValuator/Proximity stuff */
+#include <keysym.h>
+#include <mipointer.h>
+
+#ifdef XFree86LOADER
+# include <xf86Module.h>
+#endif
+
+#define XCONFIG_PROBED "(==)"
+#define XCONFIG_GIVEN "(**)"
+
+#define xf86Verbose 1
+
+#undef PRIVATE
+#define PRIVATE(x) XI_PRIVATE(x)
+
+#define CURSOR_SECTION_NAME "AiptekCursor"
+#define STYLUS_SECTION_NAME "AiptekStylus"
+#define ERASER_SECTION_NAME "AiptekEraser"
+
+#define XI_STYLUS "STYLUS" /* X device name for the stylus */
+#define XI_CURSOR "CURSOR" /* X device name for the cursor */
+#define XI_ERASER "ERASER" /* X device name for the eraser */
+
+/* macro from counts/inch to counts/meter */
+#define LPI2CPM(res) (res * 1000 / 25.4)
+
+/* max number of input events to read in one read call */
+#define MAX_EVENTS 50
+
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define TEST_BIT(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define LONG(x) ((x)/BITS_PER_LONG)
+
+/******************************************************************************
+ * Debugging macros
+ */
+#ifdef DBG
+# undef DBG
+#endif
+
+#ifdef DEBUG
+# undef DEBUG
+#endif
+
+#ifndef INI_DEBUG_LEVEL
+# define INI_DEBUG_LEVEL 0
+#endif
+
+#define DEBUG 1
+#if DEBUG
+# define DBG(lvl, f) {if ((lvl) <= debug_level) f;}
+#else
+# define DBG(lvl, f)
+#endif
+
+/******************************************************************************
+ * AiptekDeviceRec flag bitmasks
+ */
+#define DEVICE_ID(flags) ((flags) & 0x07)
+
+#define STYLUS_ID 0x01
+#define CURSOR_ID 0x02
+#define ERASER_ID 0x04
+
+#define INVX_FLAG 0x08
+#define INVY_FLAG 0x10
+
+#define FIRST_TOUCH_FLAG 0x20
+#define ABSOLUTE_FLAG 0x40
+#define KEEP_SHAPE_FLAG 0x80
+
+#define DEFAULT_BTN_TOUCH_THRESHOLD 100
+
+/******************************************************************************
+ * AiptekDeviceState 'buttons' bitmasks
+ */
+
+ /* Stylus events */
+#define BUTTONS_EVENT_TOUCH 0x01
+#define BUTTONS_EVENT_STYLUS 0x02
+#define BUTTONS_EVENT_STYLUS2 0x04
+#define BUTTONS_EVENT_SIDE_BTN 0x08
+#define BUTTONS_EVENT_EXTRA_BTN 0x10
+
+ /* Mouse events */
+#define BUTTONS_EVENT_MOUSE_LEFT 0x01
+#define BUTTONS_EVENT_MOUSE_RIGHT 0x02
+#define BUTTONS_EVENT_MOUSE_MIDDLE 0x04
+
+/******************************************************************************
+ * AiptekDeviceRec 'zMode' settings
+ */
+#define PRESSURE_MODE_LINEAR 0
+#define PRESSURE_MODE_SOFT_SMOOTH 1
+#define PRESSURE_MODE_HARD_SMOOTH 2
+
+/******************************************************************************
+ * Used throughout to indicate numeric options
+ * whose value has not been set (e.g., do not enable it's functionality,
+ * or resort to default behavior).
+ */
+
+#define VALUE_NA -1
+
+/*****************************************************************************
+ * AiptekDeviceState is used to contain the 'record' of events read
+ * from the tablet. In theory, all of these events are collected
+ * prior to a report to XInput. In practice, some of these won't be
+ * known.
+ */
+typedef struct
+{
+ int eventType; /* STYLUS_ID, CURSOR_ID, ERASER_ID */
+ int x; /* X value read */
+ int y; /* Y value read */
+ int z; /* Z value read */
+ int xTilt; /* Angle at which stylus is held, X coord */
+ int yTilt; /* Angle at which stylus is held, Y coord */
+ int proximity; /* Stylus proximity bit. */
+ int macroKey; /* Macrokey read from tablet */
+ int button; /* Button bitmask */
+ int distance; /* Future capacity */
+ int wheel; /* Future capacity */
+} AiptekStateRec, *AiptekStatePtr;
+
+/*****************************************************************************
+ * AiptekDeviceRec contains information on how a physical device
+ * or pseudo-device is configured. Latter needs explanation: a tablet can
+ * act as an input device with, say, a stylus, a cursor/puck, and
+ * an eraser. The physical device is the Aiptek; but for purposes of inane
+ * fun, the stylus, cursor/puck, and the eraser are considered to
+ * be separate 'pseudo' devices. What this means is that you might set
+ * the stylus to only work in a subset of the active area; the cursor/puck
+ * may have access to the entire region, and the eraser has it's X & Y
+ * coordinates inverted.
+ *
+ * Second point: the cursor device is also known as a 'puck'. Aiptek though
+ * gives us a mouse to serve as our puck/cursor. But to keep our sanity,
+ * we refer throughout this driver as either stylus or puck, as those are
+ * Input Tablet terms. Hmmph. :-)
+ */
+
+typedef struct
+{
+ /* configuration fields */
+ unsigned char flags; /* we keep device type,
+ * absolute | relative coordinates,
+ * firstTouch bits inside. */
+
+ /* If unspecified, we will set xSize and ySize to match the
+ * active area of the tablet, and also set xOffset and yOffset to 0.
+ *
+ * Otherwise: this documents an active area of the tablet, outside
+ * of whose coordinates input is ignored. Size is computed relative
+ * to upper-left corner, known as 0,0.
+ *
+ * Now, to that upper-left corner, xOffset and yOffset may be
+ * applied, which moves the origin coordinate right and down,
+ * respectively.
+ */
+ int xSize; /* Active area X */
+ int ySize; /* Active area Y */
+ int xOffset; /* Active area offset X */
+ int yOffset; /* Active area offset Y */
+
+ /* Maximum size of the tablet. Presumed to be the size of
+ * the tablet drawing area if unspecified; can be used
+ * to define a cut-off point from where on the tablet
+ * input will be ignored. Yes, very synonymous with 'active area',
+ * above. Just a different way of expressing the same concept,
+ * except there is no xOffset/yOffset parameters.
+ */
+
+ int xMax; /* Maximum X value */
+ int yMax; /* Maximum Y value */
+
+ /* Limit the range of pressure values we will accept
+ * as input. Note that throughout, coordinate 'Z' refers
+ * to stylus pen pressure.
+ *
+ * Also, note that zMin/zMax is different than zThreshold.
+ */
+ int zMin; /* Minimum Z value */
+ int zMax; /* Maximum Z value */
+
+ /* Changing xTop/xBottom/yTop/yBottom affects the resolution
+ * in points/inch for that given axis.
+ */
+ int xTop;
+ int yTop;
+ int xBottom;
+ int yBottom;
+
+ /* Threshold allows the user to specify a minimal amount the device
+ * has to move in a given direction before the input is accepted.
+ * The benefits here are to help eliminate garbage input from unsteady
+ * hands/tool.
+ */
+ int xThreshold; /* accept X report if delta > threshold */
+ int yThreshold; /* accept Y report if delta > threshold */
+ int zThreshold; /* accept pressure if delta > threshold */
+ int xTiltThreshold; /* accept xTilt if delta > threshold */
+ int yTiltThreshold; /* accept yTilt if delta > threshold */
+ /* We provide the Z coordinate either in linear or log mode.
+ * The nomenclature "log" is fairly generic, except that
+ * our smoothing algorithms are based on sqrt(). We support,
+ * "Soft"
+ * z = z * z / maxPressure (512)
+ * "Hard"
+ * z = maxPressure * sqrt( z / maxPressure )
+ */
+ int zMode; /* Z reported linearly or 'smoothed' */
+
+ /* This is magic pixie dust, and no, I don't know why it's here, and
+ * yes, I want to remove it, and no, not yet, but yes, soon. TODO!
+ */
+ int initNumber; /* magic number for the init phasis */
+
+ /* By default, the tablet runs in window :0.0. However, you can change
+ * things such that it only supports screen 1, or whatever. I know what
+ * you are thinking: xinerama with two tablets. I am resisting you.
+ */
+ int screenNo; /* associated screen */
+
+ /* Previous state values. Allows us to filter out getting the same
+ * tablet report over and over again.
+ */
+
+
+ /* This struct is used to keep parameters, etc., that
+ * are common between ALL pseudo-devices of the aiptek
+ * driver. E.g., the physical size of the drawing
+ * area is kept here.
+ */
+ struct _AiptekCommonRec* common;/* common info pointer */
+
+} AiptekDeviceRec, *AiptekDevicePtr;
+
+
+typedef struct _AiptekCommonRec
+{
+ char* deviceName; /* device file name */
+ unsigned char flags; /* various flags (handle tilt) */
+
+ /* Same goddamn magic pixie dust thingie that's also in
+ * AiptekDeviceRec. I MUST get rid of this nonsense. TODO
+ */
+ int initNumber;
+
+ /*
+ * Data read from the 'evdev' device has to be collected into a single
+ * record before we can present it to the XInput layer. Because we need
+ * to do threshold comparisons, we need the current record and the
+ * previous record submitted.
+ *
+ * This information is common insofar as we have up to three Aiptek
+ * devices (cursor, stylus, eraser), and we decide on receipt of the
+ * data which device to submit it to. E.g., the commonality is the
+ * single tablet.
+ */
+ AiptekStateRec currentValues;
+ AiptekStateRec previousValues;
+
+ /* The physical capacity of the tablet in X, Y, and Z
+ * coordinates. Comes directly from the tablet; cannot
+ * be overwritten via XF86Config-4 parameters.
+ */
+ int xCapacity; /* reported from tablet: max X coord */
+ int yCapacity; /* reported from tablet: max Y coord */
+ int zCapacity; /* reported from tablet: max Z (pres.) */
+
+ unsigned char data[9]; /* data read on the device */
+
+ int numDevices; /* number of tablet */
+ LocalDevicePtr* deviceArray; /* array of tablets sharing the device */
+
+ Bool (*open)(LocalDevicePtr); /* function to open (serial or USB) */
+} AiptekCommonRec, *AiptekCommonPtr;
+
+static InputInfoPtr xf86AiptekInit(InputDriverPtr, IDevPtr, int);
+static void xf86AiptekUninit(InputDriverPtr, LocalDevicePtr, int);
+static void xf86AiptekClose(LocalDevicePtr);
+static LocalDevicePtr xf86AiptekAllocateStylus(void);
+static LocalDevicePtr xf86AiptekAllocateCursor(void);
+static LocalDevicePtr xf86AiptekAllocateEraser(void);
+
+#define SYSCALL(call) while(((call) == -1) && (errno == EINTR))
+#define ABS(x) ((x) > 0 ? (x) : -(x))
+
+#endif