On 20 Nov, the Loial Raven wrote: > I am using XFree86-4.0.1 and have noticed that my tablet is not > working properly... > Pressure sensitivity is the problem... it seems like gimp is either > getting a pressure of 0%, 100%, or some value higher that causes > feedback when using some paint tools. I was thinking it was a problem > with Xfree, but when i used the xinputev, i found it was giving proper > values... There's a bug in the XInput driver for the USB version (thus I assume you're using this one) which maps the pressure to a range between 0.25 and 1.25 instead of 0 to 1.00. I'll attach you a fixed version. I still have to make some changes and remove some crufty code, but this one should work. If you need the module itself feel free to get back to me and request it for i386 and/or PPC. -- Servus, Daniel
/* * Copyright 1995-1999 by Frederic Lepied, France. <Lepied@xxxxxxxxxxx> * Copyright 2000 by Daniel Egger, Germany. <egger@xxxxxxx> * * Modified for Linux USB by MATSUMURA Namihiko. * FrñÅñÓic Lepied <lepied@xxxxxxxxxxx>. * Additional changes by Brion Vibber <brion@xxxxxxxxx> * and Aaron Optimizer Digulla <digulla@xxxxxxxx> * * 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 Frederic Lepied not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Frederic Lepied makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * FREDERIC LEPIED DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL FREDERIC LEPIED BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * */ #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> #include <keysym.h> #include <mipointer.h> #ifdef XFree86LOADER #include <xf86Module.h> #endif #undef read #define read(a,b,c) xf86ReadSerial((a),(b),(c)) #undef write #define write(a,b,c) xf86WriteSerial((a),(char*)(b),(c)) #undef close #define close(a) xf86CloseSerial((a)) #define XCONFIG_PROBED "(==)" #define XCONFIG_GIVEN "(**)" #define xf86Verbose 1 #undef PRIVATE #define PRIVATE(x) XI_PRIVATE(x) static const char *default_options[] = { NULL }; static InputDriverPtr wcmDrv; #ifdef size_t #undef size_t #endif #include <asm/types.h> #include "linux/input.h" #ifndef O_NDELAY #ifndef O_NONBLOCK #define O_NONBLOCK 04000 #endif #define O_NDELAY O_NONBLOCK #endif #define INI_DEBUG_LEVEL 5 #ifndef INI_DEBUG_LEVEL #define INI_DEBUG_LEVEL 0 #endif static int debug_level = INI_DEBUG_LEVEL; #define DEBUG 1 #if DEBUG #define DBG(lvl, f) {if ((lvl) <= debug_level) f;} #else #define DBG(lvl, f) #endif /****************************************************************************** * WacomDeviceRec flags *****************************************************************************/ #define DEVICE_ID(flags) ((flags) & 0x07) #define STYLUS_ID (1<<0) #define CURSOR_ID (1<<1) #define ERASER_ID (1<<2) #define ABSOLUTE_FLAG (1<<3) #define FIRST_TOUCH_FLAG (1<<4) #define KEEP_SHAPE_FLAG (1<<5) /****************************************************************************** * WacomCommonRec flags *****************************************************************************/ #define TILT_FLAG 1 #define GRAPHIRE_FLAG 2 typedef struct { int state; int coord[3]; int tilt[3]; } WacomFilterState; typedef struct { int device_id; int device_type; unsigned int serial_num; int x; int y; int buttons; int pressure; int tilt_x; int tilt_y; int rotation; int wheel; int discard_first; int proximity; WacomFilterState x_filter; WacomFilterState y_filter; } WacomDeviceState; #define PEN(ds) (((ds->device_id) & 0x07ff) == 0x0022) #define AIRBRUSH(ds) (((ds->device_id) & 0x07ff) == 0x0112) #define MOUSE_4D(ds) (((ds->device_id) & 0x07ff) == 0x0094) #define LENS_CURSOR(ds) (((ds->device_id) & 0x07ff) == 0x0096) #define INKING_PEN(ds) (((ds->device_id) & 0x07ff) == 0x0012) typedef struct { unsigned char flags; /* various flags (type, absolute, first touch...) */ int topX; /* X top */ int topY; /* Y top */ int bottomX; /* X bottom */ int bottomY; /* Y bottom */ double factorX; /* X factor */ double factorY; /* Y factor */ unsigned int serial; /* device serial number */ int initNumber; /* magic number for the init phasis */ struct _WacomCommonRec *common; /* common info pointer */ int oldX; /* previous X position */ int oldY; /* previous Y position */ int oldPressure; /* previous pressure */ int oldTiltX; /* previous tilt in x direction */ int oldTiltY; /* previous tilt in y direction */ int oldWheel; /* previous wheel value */ int oldButtons; /* previous buttons state */ int oldProximity; /* previous proximity */ } WacomDeviceRec, *WacomDevicePtr; typedef struct _WacomCommonRec { char *wcmDevice; /* device file name */ int wcmDeviceType; /* STYLUS/CURSOR/ERASE_ID */ int wcmSuppress; /* transmit position if increment is superior */ unsigned char wcmFlags; /* various flags (handle tilt) */ int wcmMaxX; /* max X value */ int wcmMaxY; /* max Y value */ int wcmMaxPressure; /* max pressure */ int wcmResolX; /* X resolution in points/inch */ int wcmResolY; /* Y resolution in points/inch */ int wcmResolPressure; /* FIXME: Making sense? */ LocalDevicePtr *wcmDevices; /* array of all devices sharing the same port */ int wcmNumDevices; /* number of devices */ int wcmIndex; /* number of bytes read */ int wcmPktLength; /* length of a packet */ unsigned char wcmData[9]; /* data read on the device */ Bool wcmHasEraser; /* True if an eraser has been configured */ Bool wcmStylusSide; /* eraser or stylus ? */ Bool wcmStylusProximity; /* the stylus is in proximity ? */ int wcmProtocolLevel; /* 4 for Wacom IV, 5 for Wacom V */ int wcmThreshold; /* Threshold for counting pressure as a button */ WacomDeviceState wcmDevStat[2]; /* device state for each tool */ int wcmInitNumber; /* magic number for the init phasis */ } WacomCommonRec, *WacomCommonPtr; /****************************************************************************** * configuration stuff *****************************************************************************/ #define CURSOR_SECTION_NAME "wacomcursor" #define STYLUS_SECTION_NAME "wacomstylus" #define ERASER_SECTION_NAME "wacomeraser" /****************************************************************************** * constant and macros declarations *****************************************************************************/ #define BUFFER_SIZE 256 /* size of reception buffer */ #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 */ #define MAX_VALUE 100 /* number of positions */ #define MAXTRY 3 /* max number of try to receive magic number */ #define MAX_COORD_RES 1270.0 /* Resolution of the returned MaxX and MaxY */ #define INVALID_THRESHOLD 30000 /* Invalid threshold value used to test if the user * configured it or not */ #define SYSCALL(call) while(((call) == -1) && (errno == EINTR)) /* defines to discriminate second side button and the eraser */ #define ERASER_PROX 4 #define OTHER_PROX 1 #define HANDLE_TILT(comm) ((comm)->wcmPktLength == 9) /* Strange GCC syntax: Evaluate 'v' only once */ #define ABS(v) ({ int _v = (v); _v < 0 ? -_v : _v; }) #define mils(res) (res * 100 / 2.54) /* resolution */ /****************************************************************************** * Function/Macro keys variables *****************************************************************************/ static KeySym wacom_map[] = { NoSymbol, /* 0x00 */ NoSymbol, /* 0x01 */ NoSymbol, /* 0x02 */ NoSymbol, /* 0x03 */ NoSymbol, /* 0x04 */ NoSymbol, /* 0x05 */ NoSymbol, /* 0x06 */ NoSymbol, /* 0x07 */ XK_F1, /* 0x08 */ XK_F2, /* 0x09 */ XK_F3, /* 0x0a */ XK_F4, /* 0x0b */ XK_F5, /* 0x0c */ XK_F6, /* 0x0d */ XK_F7, /* 0x0e */ XK_F8, /* 0x0f */ XK_F8, /* 0x10 */ XK_F10, /* 0x11 */ XK_F11, /* 0x12 */ XK_F12, /* 0x13 */ XK_F13, /* 0x14 */ XK_F14, /* 0x15 */ XK_F15, /* 0x16 */ XK_F16, /* 0x17 */ XK_F17, /* 0x18 */ XK_F18, /* 0x19 */ XK_F19, /* 0x1a */ XK_F20, /* 0x1b */ XK_F21, /* 0x1c */ XK_F22, /* 0x1d */ XK_F23, /* 0x1e */ XK_F24, /* 0x1f */ XK_F25, /* 0x20 */ XK_F26, /* 0x21 */ XK_F27, /* 0x22 */ XK_F28, /* 0x23 */ XK_F29, /* 0x24 */ XK_F30, /* 0x25 */ XK_F31, /* 0x26 */ XK_F32 /* 0x27 */ }; /* minKeyCode = 8 because this is the min legal key code */ static KeySymsRec wacom_keysyms = { /* map minKeyCode maxKC width */ wacom_map, 8, 0x27, 1 }; #if NeedFunctionPrototypes static LocalDevicePtr xf86WcmAllocateStylus(void); static LocalDevicePtr xf86WcmAllocateCursor(void); static LocalDevicePtr xf86WcmAllocateEraser(void); #endif /* *************************************************************************** * * xf86WcmConvert -- * Convert valuators to X and Y. * *************************************************************************** */ static Bool xf86WcmConvert(LocalDevicePtr local, int first, int num, int v0, int v1, int v2, int v3, int v4, int v5, int* x, int* y) { WacomDevicePtr priv = (WacomDevicePtr) local->private; DBG(6, ErrorF("xf86WcmConvert\n")); if (first != 0 || num == 1) return FALSE; priv->factorX = ((double) miPointerCurrentScreen()->width) / (priv->bottomX - priv->topX); priv->factorY = ((double) miPointerCurrentScreen()->height) / (priv->bottomY - priv->topY); *x = v0 * priv->factorX; *y = v1 * priv->factorY; DBG(6, ErrorF("Wacom converted v0=%d v1=%d to x=%d y=%d\n", v0, v1, *x, *y)); return TRUE; } /* *************************************************************************** * * xf86WcmReverseConvert -- * Convert X and Y to valuators. * *************************************************************************** */ static Bool xf86WcmReverseConvert(LocalDevicePtr local, int x, int y, int *valuators) { WacomDevicePtr priv = (WacomDevicePtr) local->private; priv->factorX = ((double) miPointerCurrentScreen()->width) / (priv->bottomX - priv->topX); priv->factorY = ((double) miPointerCurrentScreen()->height) / (priv->bottomY - priv->topY); valuators[0] = x / priv->factorX; valuators[1] = y / priv->factorY; DBG(6, ErrorF("Wacom converted x=%d y=%d to v0=%d v1=%d\n", x, y, valuators[0], valuators[1])); return TRUE; } /* *************************************************************************** * * xf86WcmSendButtons -- * Send button events by comparing the current button mask with the * previous one. * *************************************************************************** */ static void xf86WcmSendButtons(LocalDevicePtr local, int buttons, int rx, int ry, int rpressure, int rtilt_x, int rtilt_y, int rwheel) { int button; WacomDevicePtr priv = (WacomDevicePtr) local->private; for (button=1; button<=16; button++) { int mask = 1 << (button-1); if ((mask & priv->oldButtons) != (mask & buttons)) { xf86PostButtonEvent(local->dev, (priv->flags & ABSOLUTE_FLAG), button, (buttons & mask) != 0, 0, 6, rx, ry, rpressure, rtilt_x, rtilt_y, rwheel); } } } /* *************************************************************************** * * xf86WcmSendEvents -- * Send events according to the device state. * *************************************************************************** */ static void xf86WcmSendEvents(LocalDevicePtr local, int type, unsigned int serial, int is_stylus, int is_button, int is_proximity, int x, int y, int pressure, int buttons, int tilt_x, int tilt_y, int wheel) { WacomDevicePtr priv = (WacomDevicePtr) local->private; WacomCommonPtr common = priv->common; int rx, ry, rpressure, rtilt_x, rtilt_y, rwheel; int is_core_pointer, is_absolute; if ((DEVICE_ID(priv->flags) != type) || ((common->wcmProtocolLevel == 5) && priv->serial && (serial != priv->serial))) { return; } /* Translate coordinates according to Top and Bottom points * if we are outside the zone do as a ProximityOut event. */ if (x > priv->bottomX) { is_proximity = FALSE; buttons = 0; x = priv->bottomX; } if (y > priv->bottomY) { is_proximity = FALSE; buttons = 0; y = priv->bottomY; } x = x - priv->topX; y = y - priv->topY; if (x < 0) { is_proximity = FALSE; buttons = 0; x = 0; } if (y < 0) { is_proximity = FALSE; buttons = 0; y = 0; } is_absolute = (priv->flags & ABSOLUTE_FLAG); is_core_pointer = xf86IsCorePointer(local->dev); /* Hardware filtering isn't working on Graphire so we do it here. */ if ((common->wcmFlags & GRAPHIRE_FLAG) && ((is_proximity && priv->oldProximity) || ((is_proximity == 0) && (priv->oldProximity == 0))) && (buttons == priv->oldButtons) && (ABS(x - priv->oldX) <= common->wcmSuppress) && (ABS(y - priv->oldY) <= common->wcmSuppress) && (ABS(pressure - priv->oldPressure) < 3) && (ABS(tilt_x - priv->oldTiltX) < 3) && (ABS(tilt_y - priv->oldTiltY) < 3)) { return; } /* sets rx and ry according to the mode */ if (is_absolute) { rx = x; ry = y; rpressure = pressure; rtilt_x = tilt_x; rtilt_y = tilt_y; rwheel = wheel; } else { rx = x - priv->oldX; ry = y - priv->oldY; /* rpressure = pressure - priv->oldPressure; FIXME: Makes sense? rtilt_x = tilt_x - priv->oldTiltX; rtilt_y = tilt_y - priv->oldTiltY; rwheel = wheel - priv->oldWheel; */ rpressure = pressure; rtilt_x = tilt_x; rtilt_y = tilt_y; rwheel = wheel; } /* coordinates are ready we can send events */ if (is_proximity) { if (!priv->oldProximity) { xf86PostProximityEvent(local->dev, 1, 0, 6, rx, ry, pressure, tilt_x, tilt_y, rwheel); priv->flags |= FIRST_TOUCH_FLAG; if (common->wcmProtocolLevel == 4) { /* handle the two sides switches in the stylus */ if (is_stylus && (buttons == 4)) { priv->oldProximity = ERASER_PROX; } else { priv->oldProximity = OTHER_PROX; } } else { priv->oldProximity = OTHER_PROX; } } if (common->wcmProtocolLevel == 4 && !(common->wcmFlags & GRAPHIRE_FLAG)) { /* The stylus reports button 4 for the second side * switch and button 4/5 for the eraser tip. We know * how to choose when we come in proximity for the * first time. If we are in proximity and button 4 then * we have the eraser else we have the second side * switch. */ if (is_stylus) { if (buttons == 4) { buttons = (priv->oldProximity == ERASER_PROX) ? 0 : 3; } else { if (priv->oldProximity == ERASER_PROX && buttons == 5) { buttons = ((DEVICE_ID(priv->flags) == ERASER_ID) ? 1 : 4); } } } else { /* If the button flag is pressed, but the switch state * is zero, this means that cursor button 16 was pressed */ if (is_button && buttons == 0) { buttons = 16; } } } /* Turn button index reported into a bit mask for WACOM IV. * The WACOM V and Graphire models already report buttons * as a bit mask. */ if (common->wcmProtocolLevel == 4 && !(common->wcmFlags & GRAPHIRE_FLAG)) { buttons = 1 << (buttons - 1); } if ((priv->oldX != x) || (priv->oldY != y) || (priv->oldPressure != pressure) || (is_stylus && HANDLE_TILT(common) && (tilt_x != priv->oldTiltX || tilt_y != priv->oldTiltY))) { if (!is_absolute && (priv->flags & FIRST_TOUCH_FLAG)) { priv->flags -= FIRST_TOUCH_FLAG; } else { xf86PostMotionEvent(local->dev, is_absolute, 0, 6, rx, ry, rpressure, rtilt_x, rtilt_y, rwheel); } } if (priv->oldButtons != buttons) { xf86WcmSendButtons (local, buttons, rx, ry, rpressure, rtilt_x, rtilt_y, rwheel); } /* Simulate buttons 4 and 5 for Graphire wheel */ if ((common->wcmProtocolLevel == 4) && !is_stylus && (common->wcmFlags & GRAPHIRE_FLAG) && (wheel != 0)) { int fake_button = (wheel > 0) ? 5 : 4; xf86PostButtonEvent(local->dev, (priv->flags & ABSOLUTE_FLAG), fake_button, 1, 0, 6, rx, ry, rpressure, rtilt_x, rtilt_y, rwheel); xf86PostButtonEvent(local->dev, (priv->flags & ABSOLUTE_FLAG), fake_button, 0, 0, 6, rx, ry, rpressure, rtilt_x, rtilt_y, rwheel); } priv->oldButtons = buttons; priv->oldX = x; priv->oldY = y; priv->oldPressure = pressure; priv->oldTiltX = tilt_x; priv->oldTiltY = tilt_y; priv->oldWheel = wheel; } else { /* !PROXIMITY */ /* reports button up when the device has been down and becomes out of proximity */ if (priv->oldButtons) { xf86WcmSendButtons (local, 0, rx, ry, rpressure, rtilt_x, rtilt_y, rwheel); priv->oldButtons = 0; } if (!is_core_pointer) { /* macro button management */ if (common->wcmProtocolLevel == 4 && buttons) { int macro = pressure / 2; /* First available Keycode begins at 8 => macro+7 */ xf86PostKeyEvent(local->dev, macro+7, 1, is_absolute, 0, 6, 0, 0, buttons, rtilt_x, rtilt_y, rwheel); xf86PostKeyEvent(local->dev, macro+7, 0, is_absolute, 0, 6, 0, 0, buttons, rtilt_x, rtilt_y, rwheel); } if (priv->oldProximity) { xf86PostProximityEvent(local->dev, 0, 0, 6, rx, ry, rpressure, rtilt_x, rtilt_y, rwheel); } } priv->oldProximity = 0; } } /* *************************************************************************** * * xf86WcmSuppress -- * Determine whether device state has changed enough - return 1 * if not. * *************************************************************************** */ static int xf86WcmSuppress(int suppress, WacomDeviceState *ds1, WacomDeviceState *ds2) { if (ds1->buttons != ds2->buttons) return 0; if (ds1->proximity != ds2->proximity) return 0; if (ABS(ds1->x - ds2->x) >= suppress) return 0; if (ABS(ds1->y - ds2->y) >= suppress) return 0; if (ABS(ds1->pressure - ds2->pressure) >= suppress) return 0; if ((1800 + ds1->rotation - ds2->rotation) % 1800 >= suppress && (1800 + ds2->rotation - ds1->rotation) % 1800 >= suppress) return 0; if (ABS(ds1->wheel - ds2->wheel) >= suppress) return 0; return 1; } /* *************************************************************************** * * xf86WcmReadInput -- * Read the new events from the device, and enqueue them. * *************************************************************************** */ /* Was completely rewritten */ static void xf86WcmReadInput(LocalDevicePtr local) { WacomDevicePtr priv = (WacomDevicePtr) local->private; WacomCommonPtr common = priv->common; /* FIXME This is not very good; it would be much better to determine the * device and use the last values stored in there. Otherwise, we might * run into trouble when the device changes. */ static int device = 0; static int serial = 0; static int is_proximity = 0; static int x = 0; static int y = 0; static int pressure = 0; static int buttons = 0; static int tilt_x = 0; static int tilt_y = 0; static int wheel = 0; ssize_t len; int idx; struct input_event * event; char eventbuf[BUFFER_SIZE]; #define MOD_BUTTONS(bit, value) \ { int _b=bit, _v=value; buttons = (((_v) != 0) ? (buttons | _b) : (buttons & ~ _b)); } SYSCALL(len = read(local->fd, eventbuf, BUFFER_SIZE)); if (len <= 0) { ErrorF("Error reading wacom device : %s\n", strerror(errno)); return; } for (event=(struct input_event *)eventbuf; event<(struct input_event *)(eventbuf+len); event++) { switch (event->type) { case EV_ABS: switch (event->code) { case ABS_X: x = event->value; break; case ABS_Y: y = event->value; break; case ABS_TILT_X: tilt_x = event->value; break; case ABS_TILT_Y: tilt_y = event->value; break; case ABS_PRESSURE: pressure = event->value; break; case ABS_DISTANCE: /* This is not sent by the driver */ break; } break; /* EV_ABS */ case EV_REL: switch (event->code) { case REL_WHEEL: /* FIXME */ break; } break; /* EV_REL */ case EV_KEY: switch (event->code) { case BTN_TOOL_PEN: device = STYLUS_ID; is_proximity = (event->value != 0); break; case BTN_TOOL_RUBBER: device = ERASER_ID; is_proximity = (event->value != 0); break; case BTN_TOOL_MOUSE: device = CURSOR_ID; is_proximity = (event->value != 0); break; case BTN_TOUCH: MOD_BUTTONS (1, event->value); break; case BTN_STYLUS: MOD_BUTTONS (2, event->value); break; case BTN_STYLUS2: MOD_BUTTONS (4, event->value); break; case BTN_LEFT: MOD_BUTTONS (1, event->value); break; case BTN_RIGHT: MOD_BUTTONS (2, event->value); break; case BTN_MIDDLE: MOD_BUTTONS (4, event->value); break; } break; /* EV_KEY */ } } for (idx=0; idx<common->wcmNumDevices; idx++) { WacomDevicePtr dev; int id; dev = common->wcmDevices[idx]->private; id = DEVICE_ID (dev->flags); /* Find the device the current events are meant for */ if (id == device) { xf86WcmSendEvents(common->wcmDevices[idx], device, serial, (device == STYLUS_ID || device == ERASER_ID), !!(buttons), is_proximity, x, y, pressure, buttons, tilt_x, tilt_y, wheel ); } } } /* *************************************************************************** * * xf86WcmControlProc -- * *************************************************************************** */ static void xf86WcmControlProc(DeviceIntPtr device, PtrCtrl *ctrl) { DBG(2, ErrorF("xf86WcmControlProc\n")); } /* *************************************************************************** * * xf86WcmOpen -- * *************************************************************************** */ static Bool xf86WcmOpen(LocalDevicePtr local) { struct timeval timeout; char buffer[256]; int err = 0; WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; int loop, idx; int is_a_penpartner = 0; SYSCALL(local->fd = open(common->wcmDevice, O_RDONLY|O_NDELAY, 0)); if (local->fd == -1) { ErrorF("Error opening %s : %s\n", common->wcmDevice, strerror(errno)); return !Success; } DBG(2, ErrorF("setup is max X=%d max Y=%d resol X=%d resol Y=%d\n", common->wcmMaxX, common->wcmMaxY, common->wcmResolX, common->wcmResolY)); /* send the tilt mode command after setup because it must be enabled */ /* after multi-mode to take precedence */ if (HANDLE_TILT(common)) { /* Unfortunately, the USB driver doesn't allow to send this * command to the tablet. Any other solutions ? */ DBG(2, ErrorF("Sending tilt mode order\n")); } { char buf[20]; if (common->wcmSuppress < 0) { common->wcmSuppress = 0; } else { if (common->wcmSuppress > 100) { common->wcmSuppress = 99; } } /* Cannot send WC_SUPPRESS to the table. Will have to do * this manually. */ } priv->topX = 0; priv->bottomX = common->wcmMaxX; priv->topY = 0; priv->bottomY = common->wcmMaxY; if (xf86Verbose) ErrorF("%s Wacom tablet maximum X=%d maximum Y=%d " "X resolution=%d Y resolution=%d suppress=%d%s\n", XCONFIG_PROBED, common->wcmMaxX, common->wcmMaxY, common->wcmResolX, common->wcmResolY, common->wcmSuppress, HANDLE_TILT(common) ? " Tilt" : ""); if (err < 0) { ErrorF("ERROR: %d\n", err); SYSCALL(close(local->fd)); return !Success; } return Success; } /* *************************************************************************** * * xf86WcmOpenDevice -- * Open the physical device and init information structs. * *************************************************************************** */ static int xf86WcmOpenDevice(DeviceIntPtr pWcm) { LocalDevicePtr local = (LocalDevicePtr)pWcm->public.devicePrivate; WacomDevicePtr priv = (WacomDevicePtr)PRIVATE(pWcm); WacomCommonPtr common = priv->common; double screenRatio, tabletRatio; int gap; int loop; if (local->fd < 0) { if (common->wcmInitNumber > 2 || priv->initNumber == common->wcmInitNumber) { if (xf86WcmOpen(local) != Success) { if (local->fd >= 0) { SYSCALL(close(local->fd)); } local->fd = -1; } else { /* report the file descriptor to all devices */ for(loop=0; loop<common->wcmNumDevices; loop++) { common->wcmDevices[loop]->fd = local->fd; } } common->wcmInitNumber++; priv->initNumber = common->wcmInitNumber; } else { priv->initNumber = common->wcmInitNumber; } } if (local->fd != -1 && priv->factorX == 0.0) { if (priv->bottomX == 0) priv->bottomX = common->wcmMaxX; if (priv->bottomY == 0) priv->bottomY = common->wcmMaxY; /* Verify Box validity */ if (priv->topX > common->wcmMaxX || priv->topX < 0) { ErrorF("Wacom invalid TopX (%d) reseting to 0\n", priv->topX); priv->topX = 0; } if (priv->topY > common->wcmMaxY || priv->topY < 0) { ErrorF("Wacom invalid TopY (%d) reseting to 0\n", priv->topY); priv->topY = 0; } if (priv->bottomX > common->wcmMaxX || priv->bottomX < priv->topX) { ErrorF("Wacom invalid BottomX (%d) reseting to %d\n", priv->bottomX, common->wcmMaxX); priv->bottomX = common->wcmMaxX; } if (priv->bottomY > common->wcmMaxY || priv->bottomY < priv->topY) { ErrorF("Wacom invalid BottomY (%d) reseting to %d\n", priv->bottomY, common->wcmMaxY); priv->bottomY = common->wcmMaxY; } /* Calculate the ratio according to KeepShape, TopX and TopY */ if (priv->flags & KEEP_SHAPE_FLAG) { screenRatio = ((double) screenInfo.screens[0]->width) / screenInfo.screens[0]->height; tabletRatio = ((double) (common->wcmMaxX - priv->topX)) / (common->wcmMaxY - priv->topY); if (screenRatio > tabletRatio) { gap = common->wcmMaxY * (1 - tabletRatio/screenRatio); priv->bottomX = common->wcmMaxX; priv->bottomY = common->wcmMaxY - gap; } else { gap = common->wcmMaxX * (1 - screenRatio/tabletRatio); priv->bottomX = common->wcmMaxX - gap; priv->bottomY = common->wcmMaxY; } } priv->factorX = ((double) screenInfo.screens[0]->width) / (priv->bottomX - priv->topX); priv->factorY = ((double) screenInfo.screens[0]->height) / (priv->bottomY - priv->topY); if (xf86Verbose) ErrorF("%s Wacom tablet top X=%d top Y=%d " "bottom X=%d bottom Y=%d\n", XCONFIG_PROBED, priv->topX, priv->topY, priv->bottomX, priv->bottomY); } if (common->wcmThreshold > ((common->wcmMaxPressure / 2) - 1) || common->wcmThreshold < - (common->wcmMaxPressure / 2)) { if (((common->wcmProtocolLevel == 5) || (common->wcmFlags & GRAPHIRE_FLAG)) && xf86Verbose && common->wcmThreshold != INVALID_THRESHOLD) ErrorF("%s Wacom invalid threshold %d. Reset to %d\n", XCONFIG_PROBED, common->wcmThreshold, - (common->wcmMaxPressure / 3)); common->wcmThreshold = - (common->wcmMaxPressure / 3); } /* Set the real values */ InitValuatorAxisStruct(pWcm, 0, 0, /* min val */ priv->bottomX - priv->topX, /* max val */ mils(common->wcmResolX), /* resolution */ 0, /* min_res */ mils(common->wcmResolX)); /* max_res */ InitValuatorAxisStruct(pWcm, 1, 0, /* min val */ priv->bottomY - priv->topY, /* max val */ mils(common->wcmResolY), /* resolution */ 0, /* min_res */ mils(common->wcmResolY)); /* max_res */ InitValuatorAxisStruct(pWcm, 2, 0, /* min val */ common->wcmMaxPressure, /* max val */ mils(common->wcmResolPressure), /* resolution */ 0, /* min_res */ mils(common->wcmResolPressure)); /* max_res */ InitValuatorAxisStruct(pWcm, 3, -64, /* min val */ 63, /* max val */ 128, /* resolution ??? */ 0, 128); InitValuatorAxisStruct(pWcm, 4, -64, /* min val */ 63, /* max val */ 128, /* resolution ??? */ 0, 128); InitValuatorAxisStruct(pWcm, 5, 0, /* min val */ 1023, /* max val */ 128, /* resolution ??? */ 0, 128); return (local->fd != -1); } /* *************************************************************************** * * xf86WcmClose -- * *************************************************************************** */ static void xf86WcmClose(LocalDevicePtr local) { WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; int loop; int num = 0; for(loop=0; loop<common->wcmNumDevices; loop++) { if (common->wcmDevices[loop]->fd >= 0) { num++; } } if (num == 1) { SYSCALL(close(local->fd)); } local->fd = -1; } /* *************************************************************************** * * xf86WcmProc -- * Handle the initialization, etc. of a wacom * *************************************************************************** */ static int xf86WcmProc(DeviceIntPtr pWcm, int what) { CARD8 map[(32 << 4) + 1]; int nbaxes; int nbbuttons; int loop; LocalDevicePtr local = (LocalDevicePtr)pWcm->public.devicePrivate; WacomDevicePtr priv = (WacomDevicePtr)PRIVATE(pWcm); switch (what) { case DEVICE_INIT: nbaxes = 6; /* X, Y, Pressure, Tilt-X, Tilt-Y, Wheel */ switch(DEVICE_ID(priv->flags)) { case ERASER_ID: nbbuttons = 1; break; case STYLUS_ID: nbbuttons = 4; break; default: nbbuttons = 16; break; } for(loop=1; loop<=nbbuttons; loop++) map[loop] = loop; if (InitButtonClassDeviceStruct(pWcm, nbbuttons, map) == FALSE) { ErrorF("unable to allocate Button class device\n"); return !Success; } if (InitFocusClassDeviceStruct(pWcm) == FALSE) { ErrorF("unable to init Focus class device\n"); return !Success; } if (InitPtrFeedbackClassDeviceStruct(pWcm, xf86WcmControlProc) == FALSE) { ErrorF("unable to init ptr feedback\n"); return !Success; } if (InitProximityClassDeviceStruct(pWcm) == FALSE) { ErrorF("unable to init proximity class device\n"); return !Success; } if (InitKeyClassDeviceStruct(pWcm, &wacom_keysyms, NULL) == FALSE) { ErrorF("unable to init key class device\n"); return !Success; } if (InitValuatorClassDeviceStruct(pWcm, nbaxes, xf86GetMotionEvents, local->history_size, ((priv->flags & ABSOLUTE_FLAG) ? Absolute : Relative) | OutOfProximity) == FALSE) { ErrorF("unable to allocate Valuator class device\n"); return !Success; } else { /* allocate the motion history buffer if needed */ xf86MotionHistoryAllocate(local); } /* open the device to gather informations */ xf86WcmOpenDevice(pWcm); break; case DEVICE_ON: if ((local->fd < 0) && (!xf86WcmOpenDevice(pWcm))) { return !Success; } xf86AddEnabledDevice(local); pWcm->public.on = TRUE; break; case DEVICE_OFF: if (local->fd >= 0) { xf86RemoveEnabledDevice(local); xf86WcmClose(local); } pWcm->public.on = FALSE; break; case DEVICE_CLOSE: DBG(1, ErrorF("xf86WcmProc pWcm=0x%x what=%s\n", pWcm, (what == DEVICE_CLOSE) ? "CLOSE" : "OFF")); xf86WcmClose(local); break; default: ErrorF("unsupported mode=%d\n", what); return !Success; break; } return Success; } /* *************************************************************************** * * xf86WcmChangeControl -- * *************************************************************************** */ static int xf86WcmChangeControl(LocalDevicePtr local, xDeviceCtl *control) { xDeviceResolutionCtl *res; int *resolutions; char str[10]; res = (xDeviceResolutionCtl *)control; if ((control->control != DEVICE_RESOLUTION) || (res->num_valuators < 1)) return (BadMatch); resolutions = (int *)(res +1); DBG(3, ErrorF("xf86WcmChangeControl changing to %d (suppressing under)\n", resolutions[0])); /* throw command in here... */ return Success; } /* *************************************************************************** * * xf86WcmSwitchMode -- We're trying to switch mode (Relative vs. Absolute) * here. * *************************************************************************** */ static int xf86WcmSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode) { LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate; WacomDevicePtr priv = (WacomDevicePtr)local->private; switch (mode) { case Absolute: priv->flags = priv->flags | ABSOLUTE_FLAG; break; case Relative: priv->flags = priv->flags & ~ABSOLUTE_FLAG; break; default: return BadMatch; } return Success; } /* *************************************************************************** * * xf86WcmAllocate -- * *************************************************************************** */ static LocalDevicePtr xf86WcmAllocate(char * name, int flag) { LocalDevicePtr local = xf86AllocateInput(wcmDrv, 0); WacomDevicePtr priv = (WacomDevicePtr) xalloc(sizeof(WacomDeviceRec)); WacomCommonPtr common = (WacomCommonPtr) xalloc(sizeof(WacomCommonRec)); #if defined(sun) && !defined(i386) char *dev_name = (char *) getenv("WACOM_DEV"); #endif local->name = name; local->flags = 0; local->device_control = xf86WcmProc; local->read_input = xf86WcmReadInput; local->control_proc = xf86WcmChangeControl; local->close_proc = xf86WcmClose; local->switch_mode = xf86WcmSwitchMode; local->conversion_proc = xf86WcmConvert; local->reverse_conversion_proc = xf86WcmReverseConvert; local->fd = -1; local->atom = 0; local->dev = NULL; local->private = priv; local->private_flags = 0; local->history_size = 0; local->old_x = -1; local->old_y = -1; priv->flags = flag; /* various flags (device type, absolute, first touch...) */ priv->oldX = -1; /* previous X position */ priv->oldY = -1; /* previous Y position */ priv->oldPressure = -1; /* previous pressure */ priv->oldTiltX = -1; /* previous tilt in x direction */ priv->oldTiltY = -1; /* previous tilt in y direction */ priv->oldButtons = 0; /* previous buttons state */ priv->oldProximity = 0; /* previous proximity */ priv->topX = 0; /* X top */ priv->topY = 0; /* Y top */ priv->bottomX = 0; /* X bottom */ priv->bottomY = 0; /* Y bottom */ priv->factorX = 0.0; /* X factor */ priv->factorY = 0.0; /* Y factor */ priv->common = common; /* common info pointer */ priv->oldProximity = 0; /* previous proximity */ priv->serial = 0; /* serial number */ priv->initNumber = 0; /* magic number for the init phasis */ common->wcmDevice = ""; /* device file name */ common->wcmDeviceType = DEVICE_ID(flag); /* device file name */ #if defined(sun) && !defined(i386) if (dev_name) { common->wcmDevice = (char*) xalloc(strlen(dev_name)+1); strcpy(common->wcmDevice, dev_name); ErrorF("xf86WcmOpen port changed to '%s'\n", common->wcmDevice); } #endif common->wcmSuppress = -1; /* transmit position if increment is superior */ common->wcmFlags = 0; /* various flags */ common->wcmDevices = (LocalDevicePtr*) xalloc(sizeof(LocalDevicePtr)); common->wcmDevices[0] = local; common->wcmNumDevices = 1; /* number of devices */ common->wcmIndex = 0; /* number of bytes read */ common->wcmPktLength = 7; /* length of a packet */ /* FIXME: Should get this values from the HID driver */ common->wcmMaxX = 30480; /* max X value */ common->wcmMaxY = 23060; /* max Y value */ common->wcmMaxPressure = 1023; /* max pressure */ common->wcmResolX = 1270; /* X resolution in points/inch */ common->wcmResolY = 1270; /* Y resolution in points/inch */ common->wcmResolPressure = 1270; /* pressure resolution in points/inch */ /* ? */ common->wcmHasEraser = (flag & ERASER_ID) ? TRUE : FALSE; /* True if an eraser has been configured */ common->wcmStylusSide = TRUE; /* eraser or stylus ? */ common->wcmStylusProximity = FALSE; /* a stylus is in proximity ? */ common->wcmProtocolLevel = 4; /* protocol level */ common->wcmThreshold = INVALID_THRESHOLD; /* button 1 threshold for some tablet models */ common->wcmInitNumber = 0; /* magic number for the init phasis */ return local; } /* *************************************************************************** * * xf86WcmAllocateStylus -- initialize new Stylus * *************************************************************************** */ static LocalDevicePtr xf86WcmAllocateStylus() { LocalDevicePtr local = xf86WcmAllocate(XI_STYLUS, STYLUS_ID); local->type_name = "Wacom Stylus"; return local; } /* *************************************************************************** * * xf86WcmAllocateCursor -- * *************************************************************************** */ static LocalDevicePtr xf86WcmAllocateCursor() { LocalDevicePtr local = xf86WcmAllocate(XI_CURSOR, CURSOR_ID); local->type_name = "Wacom Cursor"; return local; } /* *************************************************************************** * * xf86WcmAllocateEraser -- * *************************************************************************** */ static LocalDevicePtr xf86WcmAllocateEraser() { LocalDevicePtr local = xf86WcmAllocate(XI_ERASER, ABSOLUTE_FLAG|ERASER_ID); local->type_name = "Wacom Eraser"; return local; } /* * xf86WcmUninit -- * * called when the driver is unloaded. */ static void xf86WcmUninit(InputDriverPtr drv, LocalDevicePtr local, int flags) { WacomDevicePtr priv = (WacomDevicePtr) local->private; xf86WcmProc(local->dev, DEVICE_OFF); xfree (priv); xf86DeleteInput(local, 0); } /* * xf86WcmInit -- * * called when the module subsection is found in XF86Config */ static InputInfoPtr xf86WcmInit(InputDriverPtr drv, IDevPtr dev, int flags) { LocalDevicePtr local = NULL; LocalDevicePtr fakeLocal = NULL; LocalDevicePtr localDevices = NULL; WacomDevicePtr priv = NULL; WacomCommonPtr common = NULL; char *s = NULL; wcmDrv = drv; fakeLocal = (LocalDevicePtr) xcalloc(1, sizeof(LocalDeviceRec)); fakeLocal->conf_idev = dev; xf86CollectInputOptions(fakeLocal, default_options, NULL); s = xf86FindOptionValue(fakeLocal->options, "Type"); if (s && (xf86NameCmp(s, "stylus") == 0)) { local = xf86WcmAllocateStylus(); } else if (s && (xf86NameCmp(s, "cursor") == 0)) { local = xf86WcmAllocateCursor(); } else if (s && (xf86NameCmp(s, "eraser") == 0)) { local = xf86WcmAllocateEraser(); } else { xf86Msg(X_ERROR, "%s: No type or invalid type specified.\n" "Must be one of stylus, cursor or eraser\n", dev->identifier); goto SetupProc_fail; } if (local) priv = (WacomDevicePtr) local->private; if (priv) common = priv->common; if (!local || !priv || !common) { goto SetupProc_fail; } local->options = fakeLocal->options; local->conf_idev = fakeLocal->conf_idev; local->name = dev->identifier; xfree(fakeLocal); common->wcmDevice = xf86FindOptionValue(local->options, "Device"); if (!common->wcmDevice) { xf86Msg (X_ERROR, "%s: No Device specified.\n", dev->identifier); goto SetupProc_fail; } localDevices = xf86FirstLocalDevice(); while(localDevices) { if ((local != localDevices) && (localDevices->read_input == xf86WcmReadInput) && (strcmp(((WacomDevicePtr)localDevices->private)->common->wcmDevice, common->wcmDevice) == 0)) { ((WacomDevicePtr) localDevices->private)->common->wcmHasEraser |= common->wcmHasEraser; xfree(common->wcmDevices); xfree(common); common = priv->common = ((WacomDevicePtr) localDevices->private)->common; common->wcmNumDevices++; common->wcmDevices = (LocalDevicePtr *) xrealloc(common->wcmDevices, sizeof(LocalDevicePtr) * common->wcmNumDevices); common->wcmDevices[common->wcmNumDevices - 1] = local; break; } localDevices = localDevices->next; } /* Process the common options. */ xf86ProcessCommonOptions(local, local->options); /* Optional configuration */ xf86Msg(X_CONFIG, "%s device is %s\n", dev->identifier, common->wcmDevice); debug_level = xf86SetIntOption(local->options, "DebugLevel", 0); if (debug_level > 0) { xf86Msg(X_CONFIG, "WACOM: debug level set to %d\n", debug_level); } s = xf86FindOptionValue(local->options, "Mode"); if (s && (xf86NameCmp(s, "absolute") == 0)) { priv->flags = priv->flags | ABSOLUTE_FLAG; } else if (s && (xf86NameCmp(s, "relative") == 0)) { priv->flags = priv->flags & ~ABSOLUTE_FLAG; } else if (s) { xf86Msg(X_ERROR, "%s: invalid Mode (should be absolute or relative). Using default.\n", dev->identifier); } xf86Msg(X_CONFIG, "%s is in %s mode\n", local->name, (priv->flags & ABSOLUTE_FLAG) ? "absolute" : "relative"); common->wcmSuppress = xf86SetIntOption(local->options, "Suppress", 0); if (common->wcmSuppress != -1) { xf86Msg(X_CONFIG, "WACOM: suppress value is %d\n", XCONFIG_GIVEN, common->wcmSuppress); } /* FIXME: Why using "or" here??? */ if (xf86SetBoolOption(local->options, "Tilt", 0)) { common->wcmFlags |= TILT_FLAG; } if (xf86SetBoolOption(local->options, "KeepShape", 0)) { priv->flags |= KEEP_SHAPE_FLAG; xf86Msg(X_CONFIG, "%s: keeps shape\n", dev->identifier); } priv->topX = xf86SetIntOption(local->options, "TopX", 0); if (priv->topX != 0) { xf86Msg(X_CONFIG, "%s: top x = %d\n", dev->identifier, priv->topX); } priv->topY = xf86SetIntOption(local->options, "TopY", 0); if (priv->topY) { xf86Msg(X_CONFIG, "%s: top x = %d\n", dev->identifier, priv->topY); } priv->bottomX = xf86SetIntOption(local->options, "BottomX", 0); if (priv->bottomX) { xf86Msg(X_CONFIG, "%s: bottom x = %d\n", dev->identifier, priv->bottomX); } priv->bottomY = xf86SetIntOption(local->options, "BottomY", 0); if (priv->bottomY) { xf86Msg(X_CONFIG, "%s: bottom x = %d\n", dev->identifier, priv->bottomY); } priv->serial = xf86SetIntOption(local->options, "Serial", 0); if (priv->bottomY) { xf86Msg(X_CONFIG, "%s: serial number = %u\n", dev->identifier, priv->serial); } common->wcmThreshold = xf86SetIntOption(local->options, "Threshold", INVALID_THRESHOLD); if (common->wcmThreshold != INVALID_THRESHOLD) { xf86Msg(X_CONFIG, "%s: threshold = %d\n", dev->identifier, common->wcmThreshold); } /* mark the device configured */ local->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED; /* return the LocalDevice */ return (local); SetupProc_fail: if (common) xfree(common); if (priv) xfree(priv); if (local) xfree(local); return NULL; } #ifdef XFree86LOADER static #endif InputDriverRec WACOM = { 1, /* driver version */ "wacomUSB", /* driver name */ NULL, /* identify */ xf86WcmInit, /* pre-init */ xf86WcmUninit, /* un-init */ NULL, /* module */ 0 /* ref count */ }; /* *************************************************************************** * * Dynamic loading functions * *************************************************************************** */ #ifdef XFree86LOADER /* * xf86WcmUnplug -- * * called when the module subsection is found in XF86Config */ static void xf86WcmUnplug(pointer p) { return; } /* * xf86WcmPlug -- * * called when the module subsection is found in XF86Config */ static pointer xf86WcmPlug(pointer module, pointer options, int *errmaj, int *errmin) { xf86AddInputDriver(&WACOM, module, 0); return module; } static XF86ModuleVersionInfo xf86WcmVersionRec = { "wacomUSB", 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 wacomUSBModuleData = {&xf86WcmVersionRec, xf86WcmPlug, xf86WcmUnplug}; #endif /* XFree86LOADER */