/* $XdotOrg: xc/programs/Xserver/hw/xfree86/input/wacom/xf86Wacom.c,v 1.2 2004-04-23 19:54:06 eich Exp $ */ /* $XConsortium: xf86Wacom.c /main/20 1996/10/27 11:05:20 kaleb $ */ /* * Copyright 1995-2001 by Frederic Lepied, France. * * 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. * */ /* $XFree86: xc/programs/Xserver/hw/xfree86/input/wacom/xf86Wacom.c,v 1.44tsi Exp $ */ /* * This driver is only able to handle the Wacom IV and Wacom V protocols. * * Wacom V protocol work done by Raph Levien and * Frédéric Lepied . * * Many thanks to Dave Fleck from Wacom for the help provided to * build this driver. * * Modified for Linux USB by MATSUMURA Namihiko, * Daniel Egger, Germany. , * Frederic Lepied , * Brion Vibber , * Aaron Optimizer Digulla , * Jonathan Layes , * Ping Cheng , * John Joganic . * */ static const char identification[] = "$Identification: 42 $"; #include #ifdef LINUX_INPUT #include #include /* 2.4.6 module support */ #ifndef EV_MSC #define EV_MSC 0x04 #endif #ifndef MSC_SERIAL #define MSC_SERIAL 0x00 #endif /* max number of input events to read in one read call */ #define MAX_EVENTS 50 /* 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 #ifndef XFree86LOADER #include #include #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 xf86WcmWaitForTablet(fd) xf86WaitForInput((fd), 1000000) #define xf86WcmFlushTablet(fd) xf86FlushInput((fd)) #define xf86WcmOpenTablet(local) xf86OpenSerial((local)->options) #define xf86WcmSetSerialSpeed(fd,rate) xf86SetSerialSpeed((fd),(rate)) #define xf86WcmRead(a,b,c) xf86ReadSerial((a),(b),(c)) #define xf86WcmWrite(a,b,c) xf86WriteSerial((a),(char*)(b),(c)) #define xf86WcmClose(a) xf86CloseSerial((a)) #define XCONFIG_PROBED "(==)" #define XCONFIG_GIVEN "(**)" #define xf86Verbose 1 #undef PRIVATE #define PRIVATE(x) XI_PRIVATE(x) /****************************************************************************** * Forward Declarations *****************************************************************************/ typedef struct _WacomModule WacomModule; typedef struct _WacomModule4 WacomModule4; typedef struct _WacomModule3 WacomModule3; typedef struct _WacomModel WacomModel, *WacomModelPtr; typedef struct _WacomDeviceRec WacomDeviceRec, *WacomDevicePtr; typedef struct _WacomDeviceState WacomDeviceState, *WacomDeviceStatePtr; typedef struct _WacomChannel WacomChannel, *WacomChannelPtr; typedef struct _WacomCommonRec WacomCommonRec, *WacomCommonPtr; typedef struct _WacomFilterState WacomFilterState, *WacomFilterStatePtr; typedef struct _WacomDeviceClass WacomDeviceClass, *WacomDeviceClassPtr; /***************************************************************************** * General Inlined functions and Prototypes ****************************************************************************/ #define SYSCALL(call) while(((call) == -1) && (errno == EINTR)) #define RESET_RELATIVE(ds) do { (ds).relwheel = 0; } while (0) static int xf86WcmWait(int t); static int xf86WcmReady(int fd); static LocalDevicePtr xf86WcmAllocate(char* name, int flag); static LocalDevicePtr xf86WcmAllocateStylus(void); static LocalDevicePtr xf86WcmAllocateCursor(void); static LocalDevicePtr xf86WcmAllocateEraser(void); static Bool xf86WcmOpen(LocalDevicePtr local); static int xf86WcmDevOpen(DeviceIntPtr pWcm); static int xf86WcmInitTablet(WacomCommonPtr common, WacomModelPtr model, int fd, const char* id, float version); static void xf86WcmDevReadInput(LocalDevicePtr local); static void xf86WcmReadPacket(LocalDevicePtr local); static void xf86WcmEvent(WacomCommonPtr common, unsigned int channel, const WacomDeviceState* ds); /* Serial Support */ static int xf86WcmSerialValidate(WacomCommonPtr common, const unsigned char* data); static Bool serialDetect(LocalDevicePtr pDev); static Bool serialInit(LocalDevicePtr pDev); static int serialInitTablet(WacomCommonPtr common, int fd); static void serialInitIntuos(WacomCommonPtr common, int fd, const char* id, float version); static void serialInitIntuos2(WacomCommonPtr common, int fd, const char* id, float version); static void serialInitCintiq(WacomCommonPtr common, int fd, const char* id, float version); static void serialInitPenPartner(WacomCommonPtr common, int fd, const char* id, float version); static void serialInitGraphire(WacomCommonPtr common, int fd, const char* id, float version); static void serialInitProtocol4(WacomCommonPtr common, int fd, const char* id, float version); static void serialGetResolution(WacomCommonPtr common, int fd); static int serialGetRanges(WacomCommonPtr common, int fd); static int serialResetIntuos(WacomCommonPtr common, int fd); static int serialResetCintiq(WacomCommonPtr common, int fd); static int serialResetPenPartner(WacomCommonPtr common, int fd); static int serialResetProtocol4(WacomCommonPtr common, int fd); static int serialEnableTiltProtocol4(WacomCommonPtr common, int fd); static int serialEnableSuppressProtocol4(WacomCommonPtr common, int fd); static int serialSetLinkSpeedIntuos(WacomCommonPtr common, int fd); static int serialSetLinkSpeedProtocol5(WacomCommonPtr common, int fd); static int serialStartTablet(WacomCommonPtr common, int fd); static int serialParseCintiq(WacomCommonPtr common, const unsigned char* data); static int serialParseGraphire(WacomCommonPtr common, const unsigned char* data); static int serialParseProtocol4(WacomCommonPtr common, const unsigned char* data); static int serialParseProtocol5(WacomCommonPtr common, const unsigned char* data); static void serialParseP4Common(WacomCommonPtr common, const unsigned char* data, WacomDeviceState* last, WacomDeviceState* ds); #ifdef LINUX_INPUT static Bool usbDetect(LocalDevicePtr local); static Bool usbInit(LocalDevicePtr local); static void usbInitProtocol5(WacomCommonPtr common, int fd, const char* id, float version); static void usbInitProtocol4(WacomCommonPtr common, int fd, const char* id, float version); static int usbGetRanges(WacomCommonPtr common, int fd); static int usbParse(WacomCommonPtr common, const unsigned char* data); static void usbParseEvent(WacomCommonPtr common, const struct input_event* event); static void usbParseChannel(WacomCommonPtr common, int channel, int serial); #endif static Bool isdv4Detect(LocalDevicePtr); static Bool isdv4Init(LocalDevicePtr); static void isdv4InitISDV4(WacomCommonPtr common, int fd, const char* id, float version); static int isdv4Parse(WacomCommonPtr common, const unsigned char* data); static int xf86WcmFilterCoord(WacomCommonPtr common, WacomChannelPtr pChannel, WacomDeviceStatePtr ds); static int xf86WcmFilterIntuos(WacomCommonPtr common, WacomChannelPtr pChannel, WacomDeviceStatePtr ds); static void xf86WcmSendEvents(LocalDevicePtr local, const WacomDeviceState* ds); static Bool xf86WcmDevConvert(LocalDevicePtr local, int first, int num, int v0, int v1, int v2, int v3, int v4, int v5, int *x, int *y); static Bool xf86WcmDevReverseConvert(LocalDevicePtr local, int x, int y, int *valuators); static int xf86WcmDevProc(DeviceIntPtr pWcm, int what); static void xf86WcmDevControlProc(DeviceIntPtr device, PtrCtrl *ctrl); static void xf86WcmDevClose(LocalDevicePtr local); static int xf86WcmDevChangeControl(LocalDevicePtr local, xDeviceCtl* control); static int xf86WcmDevSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode); /* * Be sure to set vmin appropriately for your device's protocol. You want to * read a full packet before returning */ static const char *default_options[] = { "BaudRate", "9600", "StopBits", "1", "DataBits", "8", "Parity", "None", "Vmin", "1", "Vtime", "10", "FlowControl", "Xoff", NULL }; #if defined(__QNX__) || defined(__QNXNTO__) #define POSIX_TTY #endif /****************************************************************************** * debugging macro *****************************************************************************/ #ifdef DBG #undef DBG #endif #ifdef DEBUG #undef DEBUG #endif static int debug_level = 0; #define DEBUG 1 #if DEBUG #define DBG(lvl, f) {if ((lvl) <= debug_level) f;} #else #define DBG(lvl, f) #endif #define ABS(x) ((x) > 0 ? (x) : -(x)) #define mils(res) (res * 100 / 2.54) /* resolution */ /****************************************************************************** * WacomDeviceRec flags *****************************************************************************/ #define DEVICE_ID(flags) ((flags) & 0x07) #define STYLUS_ID 1 #define CURSOR_ID 2 #define ERASER_ID 4 #define ABSOLUTE_FLAG 8 #define KEEP_SHAPE_FLAG 16 #define BAUD_19200_FLAG 32 #define BETA_FLAG 64 #define BUTTONS_ONLY_FLAG 128 #define IsCursor(priv) (DEVICE_ID((priv)->flags) == CURSOR_ID) #define IsStylus(priv) (DEVICE_ID((priv)->flags) == STYLUS_ID) #define IsEraser(priv) (DEVICE_ID((priv)->flags) == ERASER_ID) #define MAX_SAMPLES 4 #define PEN(ds) (((ds->device_id) & 0x07ff) == 0x0022 \ || ((ds->device_id) & 0x07ff) == 0x0042 \ || ((ds->device_id) & 0x07ff) == 0x0052) #define STROKING_PEN(ds) (((ds->device_id) & 0x07ff) == 0x0032) #define AIRBRUSH(ds) (((ds->device_id) & 0x07ff) == 0x0112) #define MOUSE_4D(ds) (((ds->device_id) & 0x07ff) == 0x0094) #define MOUSE_2D(ds) (((ds->device_id) & 0x07ff) == 0x0007) #define LENS_CURSOR(ds) (((ds->device_id) & 0x07ff) == 0x0096) #define INKING_PEN(ds) (((ds->device_id) & 0x07ff) == 0x0012) #define STYLUS_TOOL(ds) (PEN(ds) || STROKING_PEN(ds) || INKING_PEN(ds) || \ AIRBRUSH(ds)) #define CURSOR_TOOL(ds) (MOUSE_4D(ds) || LENS_CURSOR(ds) || MOUSE_2D(ds)) typedef int (*FILTERFUNC)(WacomDevicePtr pDev, WacomDeviceStatePtr pState); /* FILTERFUNC return values: * -1 - data should be discarded * 0 - data is valid */ #define FILTER_PRESSURE_RES 2048 /* maximum points in pressure curve */ typedef enum { TV_NONE = 0, TV_ABOVE_BELOW = 1, TV_LEFT_RIGHT = 2 } tvMode; /****************************************************************************** * configuration stuff *****************************************************************************/ #define CURSOR_SECTION_NAME "wacomcursor" #define STYLUS_SECTION_NAME "wacomstylus" #define ERASER_SECTION_NAME "wacomeraser" /****************************************************************************** * constant and macros declarations *****************************************************************************/ #define DEFAULT_SPEED 1.0 /* default relative cursor speed */ #define MAX_ACCEL 7 /* number of acceleration levels */ #define DEFAULT_SUPPRESS 2 /* default suppress */ #define MAX_SUPPRESS 6 /* max value of suppress */ #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 SYSCALL(call) while(((call) == -1) && (errno == EINTR)) #define WC_RESET "\r#" /* reset to wacom IV command set or wacom V reset */ #define WC_RESET_BAUD "\r$" /* reset baud rate to default (wacom V) or switch to wacom IIs (wacom IV) */ #define WC_CONFIG "~R\r" /* request a configuration string */ #define WC_COORD "~C\r" /* request max coordinates */ #define WC_MODEL "~#\r" /* request model and ROM version */ #define WC_MULTI "MU1\r" /* multi mode input */ #define WC_UPPER_ORIGIN "OC1\r" /* origin in upper left */ #define WC_SUPPRESS "SU" /* suppress mode */ #define WC_ALL_MACRO "~M0\r" /* enable all macro buttons */ #define WC_NO_MACRO1 "~M1\r" /* disable macro buttons of group 1 */ #define WC_RATE "IT0\r" /* max transmit rate (unit of 5 ms) */ #define WC_TILT_MODE "FM1\r" /* enable extra protocol for tilt management */ #define WC_NO_INCREMENT "IN0\r" /* do not enable increment mode */ #define WC_STREAM_MODE "SR\r" /* enable continuous mode */ #define WC_PRESSURE_MODE "PH1\r" /* enable pressure mode */ #define WC_ZFILTER "ZF1\r" /* stop sending coordinates */ #define WC_STOP "\nSP\r" /* stop sending coordinates */ #define WC_START "ST\r" /* start sending coordinates */ #define WC_NEW_RESOLUTION "NR" /* change the resolution */ /****************************************************************************** * WacomCommonRec flags *****************************************************************************/ #define TILT_REQUEST_FLAG 1 #define TILT_ENABLED_FLAG 2 #define RAW_FILTERING_FLAG 4 #define DEVICE_ISDV4 0x000C #define ROTATE_NONE 0 #define ROTATE_CW 1 #define ROTATE_CCW 2 #define MAX_CHANNELS 2 #define MAX_USB_EVENTS 32 #define HANDLE_TILT(comm) ((comm)->wcmFlags & TILT_ENABLED_FLAG) #define RAW_FILTERING(comm) ((comm)->wcmFlags & RAW_FILTERING_FLAG) struct _WacomFilterState { int npoints; int x[3]; int y[3]; int tiltx[3]; int tilty[3]; int statex; int statey; }; struct _WacomDeviceState { int device_id; int device_type; unsigned int serial_num; int x; int y; int buttons; int pressure; int tiltx; int tilty; int rotation; int abswheel; int relwheel; int distance; int throttle; int discard_first; int proximity; int sample; /* wraps every 24 days */ }; struct _WacomChannel { /* data stored in this structure is raw data from the tablet, prior * to transformation and user-defined filtering. Suppressed values * will not be included here, and hardware filtering may occur between * the work stage and the valid state. */ WacomDeviceState work; /* next state */ /* the following struct contains the current known state of the * device channel, as well as the previous MAX_SAMPLES states * for use in detecting hardware defects, jitter, trends, etc. */ /* the following union contains the current known state of the * device channel, as well as the previous MAX_SAMPLES states * for use in detecting hardware defects, jitter, trends, etc. */ union { WacomDeviceState state; /* current state */ WacomDeviceState states[MAX_SAMPLES]; /* states 0..MAX */ } valid; int nSamples; LocalDevicePtr pDev; /* device associated with this channel */ WacomFilterState rawFilter; }; struct _WacomDeviceRec { /* configuration fields */ unsigned int flags; /* various flags (device type, absolute, 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 screen_no; /* associated screen */ int button[16]; /* buttons */ WacomCommonPtr common; /* common info pointer */ /* state fields */ int oldX; /* previous X position */ int oldY; /* previous Y position */ int oldZ; /* previous pressure */ int oldTiltX; /* previous tilt in x direction */ int oldTiltY; /* previous tilt in y direction */ int oldWheel; /* previous wheel value */ int oldRot; /* previous rotation value */ int oldThrottle; /* previous throttle value */ int oldButtons; /* previous buttons state */ int oldProximity; /* previous proximity */ double speed; /* relative mode speed */ int accel; /* relative mode acceleration */ int numScreen; /* number of configured screens */ int currentScreen; /* current screen in display */ double dscaleX; /* scale in X for dual screens */ double dscaleY; /* scale in Y for dual screens */ int doffsetX; /* offset in X for dual screens */ int doffsetY; /* offset in Y for dual screens */ tvMode twinview; /* using twinview mode of gfx card */ int tvResolution[4];/* twinview screens' resultion */ int throttleStart; /* time in ticks for last wheel movement */ int throttleLimit; /* time in ticks for next wheel movement */ int throttleValue; /* current throttle value */ int *pPressCurve; /* pressure curve */ int nPressCtrl[4]; /* control points for curve */ }; struct _WacomCommonRec { char *wcmDevice; /* device file name */ 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 wcmMaxZ; /* max Z value */ int wcmResolX; /* X resolution in points/inch */ int wcmResolY; /* Y resolution in points/inch */ int wcmUserResolX; /* user-defined X resolution */ int wcmUserResolY; /* user-defined Y resolution */ int wcmUserResolZ; /* user-defined Z resolution, * value equal to 100% pressure */ LocalDevicePtr *wcmDevices; /* array of all devices sharing the same port */ int wcmNumDevices; /* number of devices */ int wcmPktLength; /* length of a packet */ int wcmProtocolLevel;/* 4 for Wacom IV, 5 for Wacom V */ float wcmVersion; /* ROM version */ int wcmForceDevice; /* force device type (used by ISD V4) */ int wcmRotate; /* rotate screen (for TabletPC) */ int wcmThreshold; /* Threshold for button pressure */ int wcmChannelCnt; /* number of channels available */ WacomChannel wcmChannel[MAX_CHANNELS]; /* channel device state */ int wcmInitialized; /* device is initialized */ unsigned int wcmLinkSpeed; /* serial link speed */ WacomDeviceClassPtr wcmDevCls; /* device class functions */ WacomModelPtr wcmModel; /* model-specific functions */ int wcmGimp; /* support Gimp on Xinerama Enabled multi-monitor desktop */ int bufpos; /* position with buffer */ unsigned char buffer[BUFFER_SIZE]; /* data read from device */ #ifdef LINUX_INPUT int wcmEventCnt; struct input_event wcmEvents[MAX_USB_EVENTS]; /* events for current change */ #endif }; struct _WacomModule4 { InputDriverPtr wcmDrv; }; struct _WacomModule { int debugLevel; KeySym* keymap; const char* identification; WacomModule4 v4; int (*DevOpen)(DeviceIntPtr pWcm); void (*DevReadInput)(LocalDevicePtr local); void (*DevControlProc)(DeviceIntPtr device, PtrCtrl* ctrl); void (*DevClose)(LocalDevicePtr local); int (*DevProc)(DeviceIntPtr pWcm, int what); int (*DevChangeControl)(LocalDevicePtr local, xDeviceCtl* control); int (*DevSwitchMode)(ClientPtr client, DeviceIntPtr dev, int mode); Bool (*DevConvert)(LocalDevicePtr local, int first, int num, int v0, int v1, int v2, int v3, int v4, int v5, int* x, int* y); Bool (*DevReverseConvert)(LocalDevicePtr local, int x, int y, int* valuators); }; /****************************************************************************** * WacomModel - model-specific device capabilities *****************************************************************************/ struct _WacomModel { const char* name; void (*Initialize)(WacomCommonPtr common, int fd, const char* id, float version); void (*GetResolution)(WacomCommonPtr common, int fd); int (*GetRanges)(WacomCommonPtr common, int fd); int (*Reset)(WacomCommonPtr common, int fd); int (*EnableTilt)(WacomCommonPtr common, int fd); int (*EnableSuppress)(WacomCommonPtr common, int fd); int (*SetLinkSpeed)(WacomCommonPtr common, int fd); int (*Start)(WacomCommonPtr common, int fd); int (*Parse)(WacomCommonPtr common, const unsigned char* data); int (*FilterRaw)(WacomCommonPtr common, WacomChannelPtr pChannel, WacomDeviceStatePtr ds); }; static const char * setup_string = WC_MULTI WC_UPPER_ORIGIN WC_ALL_MACRO WC_NO_MACRO1 WC_RATE WC_NO_INCREMENT WC_STREAM_MODE WC_ZFILTER; static const char * pl_setup_string = WC_UPPER_ORIGIN WC_RATE WC_STREAM_MODE; static const char * penpartner_setup_string = WC_PRESSURE_MODE WC_START; #define WC_V_SINGLE "MT0\r" #define WC_V_MULTI "MT1\r" #define WC_V_HEIGHT "HT1\r" #define WC_V_ID "ID1\r" #define WC_V_19200 "BA19\r" #define WC_V_38400 "BA38\r" /* #define WC_V_9600 "BA96\r" */ #define WC_V_9600 "$\r" #define WC_RESET_19200 "\r$" /* reset to 9600 baud */ #define WC_RESET_19200_IV "\r#" static const char * intuos_setup_string = WC_V_MULTI WC_V_ID WC_RATE WC_START; #define COMMAND_SET_MASK 0xc0 #define BAUD_RATE_MASK 0x0a #define PARITY_MASK 0x30 #define DATA_LENGTH_MASK 0x40 #define STOP_BIT_MASK 0x80 #define HEADER_BIT 0x80 #define ZAXIS_SIGN_BIT 0x40 #define ZAXIS_BIT 0x04 #define ZAXIS_BITS 0x3f #define POINTER_BIT 0x20 #define PROXIMITY_BIT 0x40 #define BUTTON_FLAG 0x08 #define BUTTONS_BITS 0x78 #define TILT_SIGN_BIT 0x40 #define TILT_BITS 0x3f /* defines to discriminate second side button and the eraser */ #define ERASER_PROX 4 #define OTHER_PROX 1 /****************************************************************************** * 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_F9, /* 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 }; #define XWACOM_PARAM_TOPX 1 #define XWACOM_PARAM_TOPY 2 #define XWACOM_PARAM_BOTTOMX 3 #define XWACOM_PARAM_BOTTOMY 4 #define XWACOM_PARAM_BUTTON1 5 #define XWACOM_PARAM_BUTTON2 6 #define XWACOM_PARAM_BUTTON3 7 #define XWACOM_PARAM_BUTTON4 8 #define XWACOM_PARAM_BUTTON5 9 #define XWACOM_PARAM_DEBUGLEVEL 10 #define XWACOM_PARAM_PRESSCURVE 11 #define XWACOM_PARAM_RAWFILTER 12 #define XWACOM_PARAM_MODE 13 #define XWACOM_PARAM_SPEEDLEVEL 14 #define XWACOM_PARAM_CLICKFORCE 15 #define XWACOM_PARAM_ACCEL 16 #define XWACOM_PARAM_XYDEFAULT 65 #define XWACOM_PARAM_FILEMODEL 100 #define XWACOM_PARAM_FILEOPTION 101 #define XWACOM_PARAM_GIMP 102 WacomModule gWacomModule = { 0, /* debug level */ wacom_map, /* key map */ identification, /* version */ { NULL }, /* input driver pointer */ /* device procedures */ xf86WcmDevOpen, xf86WcmDevReadInput, xf86WcmDevControlProc, xf86WcmDevClose, xf86WcmDevProc, xf86WcmDevChangeControl, xf86WcmDevSwitchMode, xf86WcmDevConvert, xf86WcmDevReverseConvert, }; /****************************************************************************** * WacomDeviceClass *****************************************************************************/ struct _WacomDeviceClass { Bool (*Detect)(LocalDevicePtr local); /* detect device */ Bool (*Init)(LocalDevicePtr local); /* initialize device */ void (*Read)(LocalDevicePtr local); /* reads device */ }; static WacomDeviceClass gWacomSerialDevice = { serialDetect, serialInit, xf86WcmReadPacket, }; static WacomDeviceClass gWacomISDV4Device = { isdv4Detect, isdv4Init, xf86WcmReadPacket, }; #ifdef LINUX_INPUT static WacomDeviceClass gWacomUSBDevice = { usbDetect, usbInit, xf86WcmReadPacket, }; static WacomModel usbUnknown = { "Unknown USB", usbInitProtocol5, /* assume the best */ NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, NULL, /* input filtering not needed */ }; static WacomModel usbPenPartner = { "USB PenPartner", usbInitProtocol4, NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, xf86WcmFilterCoord, /* input filtering */ }; static WacomModel usbGraphire = { "USB Graphire", usbInitProtocol4, NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, xf86WcmFilterCoord, /* input filtering */ }; static WacomModel usbGraphire2 = { "USB Graphire2", usbInitProtocol4, NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, xf86WcmFilterCoord, /* input filtering */ }; static WacomModel usbGraphire3 = { "USB Graphire3", usbInitProtocol4, NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, xf86WcmFilterCoord, /* input filtering */ }; static WacomModel usbCintiq = { "USB Cintiq", usbInitProtocol4, NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, xf86WcmFilterCoord, /* input filtering */ }; static WacomModel usbCintiqPartner = { "USB CintiqPartner", usbInitProtocol4, NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, xf86WcmFilterCoord, /* input filtering */ }; static WacomModel usbIntuos = { "USB Intuos", usbInitProtocol5, NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, xf86WcmFilterIntuos, /* input filtering recommended */ }; static WacomModel usbIntuos2 = { "USB Intuos2", usbInitProtocol5, NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, xf86WcmFilterIntuos, /* input filtering recommended */ }; static WacomModel usbVolito = { "USB Volito", usbInitProtocol4, NULL, /* resolution not queried */ usbGetRanges, NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ usbParse, xf86WcmFilterCoord, /* input filtering */ }; #endif /* LINUX_INPUT */ static WacomDeviceClass* wcmDeviceClasses[] = { /* Current USB implementation requires LINUX_INPUT */ #ifdef LINUX_INPUT &gWacomUSBDevice, #endif &gWacomISDV4Device, &gWacomSerialDevice, NULL }; #ifdef LINUX_INPUT /***************************************************************************** * usbDetect -- * Test if the attached device is USB. ****************************************************************************/ static Bool usbDetect(LocalDevicePtr local) { int version; int err; DBG(1, ErrorF("usbDetect\n")); SYSCALL(err = ioctl(local->fd, EVIOCGVERSION, &version)); if (!err) { ErrorF("%s Wacom Kernel Input driver version is %d.%d.%d\n", XCONFIG_PROBED, version >> 16, (version >> 8) & 0xff, version & 0xff); return 1; } return 0; } /***************************************************************************** * usbInit -- ****************************************************************************/ static Bool usbInit(LocalDevicePtr local) { short sID[4]; char id[BUFFER_SIZE]; WacomModelPtr model = NULL; WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; DBG(1, ErrorF("initializing USB tablet\n")); /* fetch vendor, product, and model name */ ioctl(local->fd, EVIOCGID, sID); ioctl(local->fd, EVIOCGNAME(sizeof(id)), id); /* vendor is wacom */ if (sID[1] == 0x056A) { /* switch on product */ switch (sID[2]) { case 0x00: /* PenPartner */ model = &usbPenPartner; break; case 0x10: /* Graphire */ model = &usbGraphire; break; case 0x11: /* Graphire2 4x5 */ case 0x12: /* Graphire2 5x7 */ model = &usbGraphire2; break; case 0x13: /* Graphire2 4x5 */ case 0x14: /* Graphire2 6x8 */ model = &usbGraphire3; break; case 0x20: /* Intuos 4x5 */ case 0x21: /* Intuos 6x8 */ case 0x22: /* Intuos 9x12 */ case 0x23: /* Intuos 12x12 */ case 0x24: /* Intuos 12x18 */ model = &usbIntuos; break; case 0x03: /* PTU600 */ model = &usbCintiqPartner; break; case 0x30: /* PL400 */ case 0x31: /* PL500 */ case 0x32: /* PL600 */ case 0x33: /* PL600SX */ case 0x34: /* PL550 */ case 0x35: /* PL800 */ model = &usbCintiq; break; case 0x41: /* Intuos2 4x5 */ case 0x42: /* Intuos2 6x8 */ case 0x43: /* Intuos2 9x12 */ case 0x44: /* Intuos2 12x12 */ case 0x45: /* Intuos2 12x18 */ case 0x47: /* Intuos2 6x8 (verified in the field) */ model = &usbIntuos2; break; case 0x60: /* Volito */ model = &usbVolito; break; } } if (!model) model = &usbUnknown; return xf86WcmInitTablet(common,model,local->fd,id,0.0); } static void usbInitProtocol5(WacomCommonPtr common, int fd, const char* id, float version) { DBG(2, ErrorF("detected a protocol 5 model (%s)\n",id)); common->wcmResolX = common->wcmResolY = 2540; common->wcmProtocolLevel = 5; common->wcmChannelCnt = 2; common->wcmPktLength = sizeof(struct input_event); } static void usbInitProtocol4(WacomCommonPtr common, int fd, const char* id, float version) { DBG(2, ErrorF("detected a protocol 4 model (%s)\n",id)); common->wcmResolX = common->wcmResolY = 1016; common->wcmProtocolLevel = 4; common->wcmPktLength = sizeof(struct input_event); } #define BIT(x) (1<<(x)) #define BITS_PER_LONG (sizeof(long) * 8) #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) #define ISBITSET(x,y) ((x)[LONG(y)] & BIT(y)) #define OFF(x) ((x)%BITS_PER_LONG) #define LONG(x) ((x)/BITS_PER_LONG) static int usbGetRanges(WacomCommonPtr common, int fd) { int nValues[5]; unsigned long ev[NBITS(EV_MAX)]; unsigned long abs[NBITS(ABS_MAX)]; if (ioctl(fd, EVIOCGBIT(0 /*EV*/, sizeof(ev)), ev) < 0) { ErrorF("WACOM: unable to ioctl event bits.\n"); return !Success; } /* absolute values */ if (ISBITSET(ev,EV_ABS)) { if (ioctl(fd, EVIOCGBIT(EV_ABS,sizeof(abs)),abs) < 0) { ErrorF("WACOM: unable to ioctl abs bits.\n"); return !Success; } /* max x */ if (common->wcmMaxX == 0) { if (ioctl(fd, EVIOCGABS(ABS_X), nValues) < 0) { ErrorF("WACOM: unable to ioctl xmax value.\n"); return !Success; } common->wcmMaxX = nValues[2]; } /* max y */ if (common->wcmMaxY == 0) { if (ioctl(fd, EVIOCGABS(ABS_Y), nValues) < 0) { ErrorF("WACOM: unable to ioctl ymax value.\n"); return !Success; } common->wcmMaxY = nValues[2]; } /* max z cannot be configured */ if (ioctl(fd, EVIOCGABS(ABS_PRESSURE), nValues) < 0) { ErrorF("WACOM: unable to ioctl press max value.\n"); return !Success; } common->wcmMaxZ = nValues[2]; } return Success; } static int usbParse(WacomCommonPtr common, const unsigned char* data) { usbParseEvent(common,(const struct input_event*)data); return common->wcmPktLength; } static void usbParseEvent(WacomCommonPtr common, const struct input_event* event) { int i, serial, channel; /* store events until we receive the MSC_SERIAL containing * the serial number; without it we cannot determine the * correct channel. */ /* space left? bail if not. */ if (common->wcmEventCnt >= (sizeof(common->wcmEvents)/sizeof(*common->wcmEvents))) { DBG(1, ErrorF("usbParse: Exceeded event queue (%d)\n", common->wcmEventCnt)); common->wcmEventCnt = 0; return; } /* save it for later */ common->wcmEvents[common->wcmEventCnt++] = *event; /* packet terminated by MSC_SERIAL on kernel 2.4 and SYN_REPORT on kernel 2.5 */ if ((event->type != EV_MSC) || (event->code != MSC_SERIAL)) { #ifdef EV_SYN /* none serial number tools fall here */ if ((event->type == EV_SYN) && (event->code == SYN_REPORT)) { usbParseChannel(common,0,0); common->wcmEventCnt = 0; } #endif return; } /* serial number is key for channel */ serial = event->value; channel = -1; /* one channel only? must be it. */ if (common->wcmChannelCnt == 1) channel = 0; else { /* otherwise, find the channel */ /* find existing channel */ for (i=0; iwcmChannelCnt; ++i) { if (common->wcmChannel[i].work.serial_num == serial) { channel = i; break; } } /* find an empty channel */ if (channel < 0) { for (i=0; iwcmChannelCnt; ++i) { if (common->wcmChannel[i].work.proximity == 0) { /* clear out channel */ memset(&common->wcmChannel[i],0, sizeof(WacomChannel)); channel = i; break; } } } /* fresh out of channels */ if (channel < 0) { /* this should never happen in normal use */ DBG(1, ErrorF("usbParse: Exceeded channel count; ignoring.\n")); return; } } usbParseChannel(common,channel,serial); common->wcmEventCnt = 0; } static void usbParseChannel(WacomCommonPtr common, int channel, int serial) { int i; WacomDeviceState* ds; struct input_event* event; # define MOD_BUTTONS(bit, value) do { \ ds->buttons = (((value) != 0) ? \ (ds->buttons | (bit)) : (ds->buttons & ~(bit))); \ } while (0) /* all USB data operates from previous context except relative values*/ ds = &common->wcmChannel[channel].work; ds->relwheel = 0; ds->serial_num = serial; /* loop through all events in group */ for (i=0; iwcmEventCnt; ++i) { event = common->wcmEvents + i; DBG(11, ErrorF("usbParseChannel event[%d]->type=%d code=%d value=%d\n", i, event->type, event->code, event->value)); /* absolute events */ if (event->type == EV_ABS) { if (event->code == ABS_X) ds->x = event->value; else if (event->code == ABS_Y) ds->y = event->value; else if (event->code == ABS_RZ) ds->rotation = event->value; else if (event->code == ABS_TILT_X) ds->tiltx = event->value - 64; else if (event->code == ABS_TILT_Y) ds->tilty = event->value - 64; else if (event->code == ABS_PRESSURE) { ds->pressure = event->value; MOD_BUTTONS (1, event->value > common->wcmThreshold ? 1 : 0); /* pressure button should be downstream */ } else if (event->code == ABS_DISTANCE) ds->distance = event->value; else if (event->code == ABS_WHEEL) ds->abswheel = event->value; else if (event->code == ABS_THROTTLE) ds->throttle = event->value; } else if (event->type == EV_REL) { if (event->code == REL_WHEEL) ds->relwheel = event->value; else { ErrorF("wacom: rel event recv'd (%d)!\n", event->code); } } else if (event->type == EV_KEY) { if ((event->code == BTN_TOOL_PEN) || (event->code == BTN_TOOL_PENCIL) || (event->code == BTN_TOOL_BRUSH) || (event->code == BTN_TOOL_AIRBRUSH)) { ds->device_type = STYLUS_ID; ds->proximity = (event->value != 0); DBG(6, ErrorF("USB stylus detected %x\n", event->code)); } else if (event->code == BTN_TOOL_RUBBER) { ds->device_type = ERASER_ID; ds->proximity = (event->value != 0); if (ds->proximity) ds->proximity = ERASER_PROX; DBG(6, ErrorF("USB eraser detected %x\n", event->code)); } else if ((event->code == BTN_TOOL_MOUSE) || (event->code == BTN_TOOL_LENS)) { DBG(6, ErrorF("USB mouse detected %x\n", event->code)); ds->device_type = CURSOR_ID; ds->proximity = (event->value != 0); } else if (event->code == BTN_TOUCH) { /* we use the pressure to determine the button 1 for now */ } else if ((event->code == BTN_STYLUS) || (event->code == BTN_MIDDLE)) { MOD_BUTTONS (2, event->value); } else if ((event->code == BTN_STYLUS2) || (event->code == BTN_RIGHT)) { MOD_BUTTONS (4, event->value); } else if (event->code == BTN_LEFT) MOD_BUTTONS (1, event->value); else if (event->code == BTN_SIDE) MOD_BUTTONS (8, event->value); else if (event->code == BTN_EXTRA) MOD_BUTTONS (16, event->value); } } /* next event */ /* dispatch event */ xf86WcmEvent(common, channel, ds); } #endif /* LINUX_INPUT */ static WacomModel serialIntuos = { "Serial Intuos", serialInitIntuos, NULL, /* resolution not queried */ serialGetRanges, serialResetIntuos, NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ serialSetLinkSpeedIntuos, serialStartTablet, serialParseProtocol5, xf86WcmFilterIntuos, }; static WacomModel serialIntuos2 = { "Serial Intuos2", serialInitIntuos2, NULL, /* resolution not queried */ serialGetRanges, serialResetIntuos, /* same as Intuos */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ serialSetLinkSpeedProtocol5, serialStartTablet, serialParseProtocol5, xf86WcmFilterIntuos, }; static WacomModel serialCintiq = { "Serial Cintiq", serialInitCintiq, serialGetResolution, serialGetRanges, serialResetCintiq, serialEnableTiltProtocol4, serialEnableSuppressProtocol4, NULL, /* link speed cannot be changed */ serialStartTablet, serialParseCintiq, xf86WcmFilterCoord, }; static WacomModel serialPenPartner = { "Serial PenPartner", serialInitPenPartner, NULL, /* resolution not queried */ serialGetRanges, serialResetPenPartner, serialEnableTiltProtocol4, serialEnableSuppressProtocol4, NULL, /* link speed cannot be changed */ serialStartTablet, serialParseProtocol4, xf86WcmFilterCoord, }; static WacomModel serialGraphire = { "Serial Graphire", serialInitGraphire, NULL, /* resolution not queried */ NULL, /* ranges not supported */ serialResetPenPartner, /* functionally very similar */ serialEnableTiltProtocol4, serialEnableSuppressProtocol4, NULL, /* link speed cannot be changed */ serialStartTablet, serialParseGraphire, xf86WcmFilterCoord, }; static WacomModel serialProtocol4 = { "Serial UD", serialInitProtocol4, serialGetResolution, serialGetRanges, serialResetProtocol4, serialEnableTiltProtocol4, serialEnableSuppressProtocol4, NULL, /* link speed cannot be changed */ serialStartTablet, serialParseProtocol4, xf86WcmFilterCoord, }; /* *************************************************************************** * * xf86WcmSendRequest -- * send a request and wait for the answer. * the answer must begin with the first two chars of the request. * The last character in the answer string is replaced by a \0. * *************************************************************************** */ static char * xf86WcmSendRequest(int fd, char *request, char *answer, int maxlen) { int len, nr; int maxtry = MAXTRY; if (maxlen < 3) return NULL; /* send request string */ do { SYSCALL(len = xf86WcmWrite(fd, request, strlen(request))); if ((len == -1) && (errno != EAGAIN)) { ErrorF("Wacom xf86WcmWrite error : %s", strerror(errno)); return NULL; } maxtry--; } while ((len == -1) && maxtry); if (maxtry == 0) { ErrorF("Wacom unable to xf86WcmWrite request string '%s' after %d tries\n", request, MAXTRY); return NULL; } do { maxtry = MAXTRY; /* Read the first byte of the answer which must be equal to the first * byte of the request. */ do { if ((nr = xf86WcmWaitForTablet(fd)) > 0) { SYSCALL(nr = xf86WcmRead(fd, answer, 1)); if ((nr == -1) && (errno != EAGAIN)) { ErrorF("Wacom xf86WcmRead error : %s\n", strerror(errno)); return NULL; } DBG(10, ErrorF("%c err=%d [0]\n", answer[0], nr)); } maxtry--; } while ((answer[0] != request[0]) && maxtry); if (maxtry == 0) { ErrorF("Wacom unable to read first byte of request '%c%c' answer after %d tries\n", request[0], request[1], MAXTRY); return NULL; } /* Read the second byte of the answer which must be equal to the second * byte of the request. */ do { maxtry = MAXTRY; do { if ((nr = xf86WcmWaitForTablet(fd)) > 0) { SYSCALL(nr = xf86WcmRead(fd, answer+1, 1)); if ((nr == -1) && (errno != EAGAIN)) { ErrorF("Wacom xf86WcmRead error : %s\n", strerror(errno)); return NULL; } DBG(10, ErrorF("%c err=%d [1]\n", answer[1], nr)); } maxtry--; } while ((nr <= 0) && maxtry); if (maxtry == 0) { ErrorF("Wacom unable to read second byte of request '%c%c' answer after %d tries\n", request[0], request[1], MAXTRY); return NULL; } if (answer[1] != request[1]) answer[0] = answer[1]; } while ((answer[0] == request[0]) && (answer[1] != request[1])); } while ((answer[0] != request[0]) && (answer[1] != request[1])); /* Read until we don't get anything or timeout. */ len = 2; maxtry = MAXTRY; do { do { if ((nr = xf86WcmWaitForTablet(fd)) > 0) { SYSCALL(nr = xf86WcmRead(fd, answer+len, 1)); if ((nr == -1) && (errno != EAGAIN)) { ErrorF("Wacom xf86WcmRead error : %s\n", strerror(errno)); return NULL; } DBG(10, ErrorF("%c err=%d [%d]\n", answer[len], nr, len)); } else { if (len == 2) { DBG(10, ErrorF("timeout remains %d tries\n", maxtry)); maxtry--; } } } while ((nr <= 0) && len == 2 && maxtry); if (nr > 0) { len += nr; if (len >= (maxlen - 1)) return NULL; } if (maxtry == 0) { ErrorF("Wacom unable to read last byte of request '%c%c' answer after %d tries\n", request[0], request[1], MAXTRY); break; } } while (nr > 0); if (len <= 3) return NULL; answer[len-1] = '\0'; return answer; } static Bool serialDetect(LocalDevicePtr pDev) { return 1; } static Bool serialInit(LocalDevicePtr local) { int err; WacomCommonPtr common = ((WacomDevicePtr)(local->private))->common; DBG(1, ErrorF("initializing serial tablet\n")); /* Set the speed of the serial link to 38400 */ if (xf86WcmSetSerialSpeed(local->fd, 38400) < 0) return !Success; /* Send reset to the tablet */ SYSCALL(err = xf86WcmWrite(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 250 mSecs */ if (xf86WcmWait(250)) return !Success; /* Send reset to the tablet */ SYSCALL(err = xf86WcmWrite(local->fd, WC_RESET, strlen(WC_RESET))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 75 mSecs */ if (xf86WcmWait(75)) return !Success; /* Set the speed of the serial link to 19200 */ if (xf86WcmSetSerialSpeed(local->fd, 19200) < 0) return !Success; /* Send reset to the tablet */ SYSCALL(err = xf86WcmWrite(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 250 mSecs */ if (xf86WcmWait(250)) return !Success; /* Send reset to the tablet */ SYSCALL(err = xf86WcmWrite(local->fd, WC_RESET, strlen(WC_RESET))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 75 mSecs */ if (xf86WcmWait(75)) return !Success; /* Set the speed of the serial link to 9600 */ if (xf86WcmSetSerialSpeed(local->fd, 9600) < 0) return !Success; /* Send reset to the tablet */ SYSCALL(err = xf86WcmWrite(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 250 mSecs */ if (xf86WcmWait(250)) return !Success; SYSCALL(err = xf86WcmWrite(local->fd, WC_STOP, strlen(WC_STOP))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 30 mSecs */ if (xf86WcmWait(30)) return !Success; xf86WcmFlushTablet(local->fd); if (serialInitTablet(common,local->fd) == !Success) { SYSCALL(xf86WcmClose(local->fd)); local->fd = -1; return !Success; } return Success; } /***************************************************************************** * serialInitTablet -- * Initialize the tablet ****************************************************************************/ static int serialInitTablet(WacomCommonPtr common, int fd) { int loop, idx; char id[BUFFER_SIZE]; float version; WacomModelPtr model = NULL; /* if model is forced, initialize */ if (model != NULL) { id[0] = '\0'; version = 0.0F; } /* otherwise, query and initialize */ else { DBG(2, ErrorF("reading model\n")); if (!xf86WcmSendRequest(fd, WC_MODEL, id, sizeof(id))) return !Success; DBG(2, ErrorF("%s\n", id)); if (xf86Verbose) ErrorF("%s Wacom tablet model : %s\n", XCONFIG_PROBED, id+2); /* Answer is in the form ~#Tablet-Model VRom_Version * look for the first V from the end of the string * this seems to be the better way to find the version * of the ROM */ for(loop=strlen(id); loop>=0 && *(id+loop) != 'V'; loop--); for(idx=loop; idxwcmChannel[0].valid.state; WacomDeviceState* ds; /* positive value is skip */ if ((n = xf86WcmSerialValidate(common,data)) > 0) return n; /* pick up where we left off, minus relative values */ ds = &common->wcmChannel[0].work; RESET_RELATIVE(*ds); /* get pressure */ ds->pressure = ((data[6]&ZAXIS_BITS) << 2 ) + ((data[3]&ZAXIS_BIT) >> 1) + ((data[3]&PROXIMITY_BIT) >> 6) + ((data[6]&ZAXIS_SIGN_BIT) ? 0 : 0x100); /* get buttons */ ds->buttons = (data[3] & 0x38) >> 3; /* requires button info, so it goes down here. */ serialParseP4Common(common,data,last,ds); /* handle relative wheel for non-stylus device */ if (ds->device_type == CURSOR_ID) { ds->relwheel = (data[6] & 0x30) >> 4; if (data[6] & 0x40) ds->relwheel = -ds->relwheel; } xf86WcmEvent(common,0,ds); return common->wcmPktLength; } static int serialParseCintiq(WacomCommonPtr common, const unsigned char* data) { int n; WacomDeviceState* last = &common->wcmChannel[0].valid.state; WacomDeviceState* ds; /* positive value is skip */ if ((n = xf86WcmSerialValidate(common,data)) > 0) return n; /* pick up where we left off, minus relative values */ ds = &common->wcmChannel[0].work; RESET_RELATIVE(*ds); /* get pressure */ if (common->wcmMaxZ == 255) { ds->pressure = ((data[6] & ZAXIS_BITS) << 1 ) | ((data[3] & ZAXIS_BIT) >> 2) | ((data[6] & ZAXIS_SIGN_BIT) ? 0 : 0x80); } else { /* PL550, PL800, and Graphire apparently */ ds->pressure = ((data[6]&ZAXIS_BITS) << 2 ) + ((data[3]&ZAXIS_BIT) >> 1) + ((data[3]&PROXIMITY_BIT) >> 6) + ((data[6]&ZAXIS_SIGN_BIT) ? 0 : 0x100); } /* get buttons */ ds->buttons = (data[3] & 0x38) >> 3; /* requires button info, so it goes down here. */ serialParseP4Common(common,data,last,ds); xf86WcmEvent(common,0,ds); return common->wcmPktLength; } static int serialParseProtocol4(WacomCommonPtr common, const unsigned char* data) { int n; WacomDeviceState* last = &common->wcmChannel[0].valid.state; WacomDeviceState* ds; /* positive value is skip */ if ((n = xf86WcmSerialValidate(common,data)) > 0) return n; /* pick up where we left off, minus relative values */ ds = &common->wcmChannel[0].work; RESET_RELATIVE(*ds); /* get pressure */ if (common->wcmMaxZ == 255) ds->pressure = ((data[6] & ZAXIS_BITS) << 1 ) | ((data[3] & ZAXIS_BIT) >> 2) | ((data[6] & ZAXIS_SIGN_BIT) ? 0 : 0x80); else ds->pressure = (data[6] & ZAXIS_BITS) | (data[6] & ZAXIS_SIGN_BIT) ? 0 : 0x40; /* get button state */ ds->buttons = (data[3] & BUTTONS_BITS) >> 3; /* requires button info, so it goes down here. */ serialParseP4Common(common,data,last,ds); xf86WcmEvent(common,0,ds); return common->wcmPktLength; } static int serialParseProtocol5(WacomCommonPtr common, const unsigned char* data) { int n; int have_data=0; int channel; WacomDeviceState* ds; /* positive value is skip */ if ((n = xf86WcmSerialValidate(common,data)) > 0) return n; /* Protocol 5 devices support 2 data channels */ channel = data[0] & 0x01; /* pick up where we left off, minus relative values */ ds = &common->wcmChannel[channel].work; RESET_RELATIVE(*ds); DBG(7, ErrorF("packet header = 0x%x\n", (unsigned int)data[0])); /* Device ID packet */ if ((data[0] & 0xfc) == 0xc0) { /* start from scratch */ memset(ds, 0, sizeof(*ds)); ds->proximity = 1; ds->device_id = (((data[1] & 0x7f) << 5) | ((data[2] & 0x7c) >> 2)); ds->serial_num = (((data[2] & 0x03) << 30) | ((data[3] & 0x7f) << 23) | ((data[4] & 0x7f) << 16) | ((data[5] & 0x7f) << 9) | ((data[6] & 0x7f) << 2) | ((data[7] & 0x60) >> 5)); if ((ds->device_id & 0xf06) != 0x802) ds->discard_first = 1; if (STYLUS_TOOL(ds)) ds->device_type = STYLUS_ID; else if (CURSOR_TOOL(ds)) ds->device_type = CURSOR_ID; else ds->device_type = ERASER_ID; DBG(6, ErrorF("device_id=0x%x serial_num=%u type=%s\n", ds->device_id, ds->serial_num, (ds->device_type == STYLUS_ID) ? "stylus" : (ds->device_type == CURSOR_ID) ? "cursor" : "eraser")); } else if ((data[0] & 0xfe) == 0x80) { /* Out of proximity packet */ ds->proximity = 0; have_data = 1; } /* General pen packet or eraser packet or airbrush first packet * airbrush second packet */ else if (((data[0] & 0xb8) == 0xa0) || ((data[0] & 0xbe) == 0xb4)) { ds->x = (((data[1] & 0x7f) << 9) | ((data[2] & 0x7f) << 2) | ((data[3] & 0x60) >> 5)); ds->y = (((data[3] & 0x1f) << 11) | ((data[4] & 0x7f) << 4) | ((data[5] & 0x78) >> 3)); if ((data[0] & 0xb8) == 0xa0) { ds->pressure = (((data[5] & 0x07) << 7) | (data[6] & 0x7f)); ds->buttons = (((data[0]) & 0x06) | (ds->pressure >= common->wcmThreshold)); /* pressure button should go down stream */ } else { /* 10 bits for absolute wheel position */ ds->abswheel = (((data[5] & 0x07) << 7) | (data[6] & 0x7f)); } ds->tiltx = (data[7] & TILT_BITS); ds->tilty = (data[8] & TILT_BITS); if (data[7] & TILT_SIGN_BIT) ds->tiltx -= (TILT_BITS + 1); if (data[8] & TILT_SIGN_BIT) ds->tilty -= (TILT_BITS + 1); ds->proximity = (data[0] & PROXIMITY_BIT); have_data = 1; } /* end pen packet */ /* 4D mouse 1st packet or Lens cursor packet or 2D mouse packet*/ else if (((data[0] & 0xbe) == 0xa8) || ((data[0] & 0xbe) == 0xb0)) { ds->x = (((data[1] & 0x7f) << 9) | ((data[2] & 0x7f) << 2) | ((data[3] & 0x60) >> 5)); ds->y = (((data[3] & 0x1f) << 11) | ((data[4] & 0x7f) << 4) | ((data[5] & 0x78) >> 3)); ds->tilty = 0; if (MOUSE_4D(ds)) { /* 4D mouse */ ds->throttle = (((data[5] & 0x07) << 7) | (data[6] & 0x7f)); if (data[8] & 0x08) ds->throttle = -ds->throttle; ds->buttons = (((data[8] & 0x70) >> 1) | (data[8] & 0x07)); have_data = !ds->discard_first; } else if (LENS_CURSOR(ds)) { /* Lens cursor */ ds->buttons = data[8]; have_data = 1; } else if (MOUSE_2D(ds)) { /* 2D mouse */ ds->buttons = (data[8] & 0x1C) >> 2; ds->relwheel = -(data[8] & 1) + ((data[8] & 2) >> 1); have_data = 1; /* must send since relwheel is reset */ } ds->proximity = (data[0] & PROXIMITY_BIT); } /* end 4D mouse 1st packet */ else if ((data[0] & 0xbe) == 0xaa) { /* 4D mouse 2nd packet */ ds->x = (((data[1] & 0x7f) << 9) | ((data[2] & 0x7f) << 2) | ((data[3] & 0x60) >> 5)); ds->y = (((data[3] & 0x1f) << 11) | ((data[4] & 0x7f) << 4) | ((data[5] & 0x78) >> 3)); ds->rotation = (((data[6] & 0x0f) << 7) | (data[7] & 0x7f)); if (ds->rotation < 900) ds->rotation = -ds->rotation; else ds->rotation = 1799 - ds->rotation; ds->proximity = (data[0] & PROXIMITY_BIT); have_data = 1; ds->discard_first = 0; } else { DBG(10, ErrorF("unknown wacom V packet 0x%x\n", data[0])); } /* if new data is available, send it */ if (have_data) { xf86WcmEvent(common,channel,ds); } else { /* otherwise, initialize channel and wait for next packet */ common->wcmChannel[channel].pDev = NULL; } return common->wcmPktLength; } /***************************************************************************** * Model-specific functions ****************************************************************************/ static void serialInitIntuos(WacomCommonPtr common, int fd, const char* id, float version) { DBG(2, ErrorF("detected an Intuos model\n")); common->wcmProtocolLevel = 5; common->wcmVersion = version; common->wcmMaxZ = 1023; /* max Z value */ common->wcmResolX = 2540; /* tablet X resolution in points/inch */ common->wcmResolY = 2540; /* tablet Y resolution in points/inch */ common->wcmPktLength = 9; /* length of a packet */ common->wcmFlags |= TILT_ENABLED_FLAG; } static void serialInitIntuos2(WacomCommonPtr common, int fd, const char* id, float version) { DBG(2, ErrorF("detected an Intuos2 model\n")); common->wcmProtocolLevel = 5; common->wcmVersion = version; common->wcmMaxZ = 1023; /* max Z value */ common->wcmResolX = 2540; /* tablet X resolution in points/inch */ common->wcmResolY = 2540; /* tablet Y resolution in points/inch */ common->wcmPktLength = 9; /* length of a packet */ common->wcmFlags |= TILT_ENABLED_FLAG; } static void serialInitCintiq(WacomCommonPtr common, int fd, const char* id, float version) { DBG(2, ErrorF("detected a Cintiq model\n")); common->wcmProtocolLevel = 4; common->wcmPktLength = 7; common->wcmVersion = version; if (id[5] == '2') { /* PL-250 */ if ( id[6] == '5' ) { /* PL-250 */ common->wcmMaxZ = 255; } else { /* PL-270 */ common->wcmMaxZ = 255; } } else if (id[5] == '3') { /* PL-300 */ common->wcmMaxZ = 255; } else if (id[5] == '4') { /* PL-400 */ common->wcmMaxZ = 255; } else if (id[5] == '5') { /* PL-550 */ if ( id[6] == '5' ) { common->wcmMaxZ = 511; } else { /* PL-500 */ common->wcmMaxZ = 255; } } else if (id[5] == '6') { /* PL-600SX */ if ( id[8] == 'S' ) { common->wcmMaxZ = 255; } else { /* PL-600 */ common->wcmMaxZ = 255; } } else if (id[5] == '8') { /* PL-800 */ common->wcmMaxZ = 511; } common->wcmResolX = 508; /* tablet X resolution in points/inch */ common->wcmResolY = 508; /* tablet Y resolution in points/inch */ } static void serialInitPenPartner(WacomCommonPtr common, int fd, const char* id, float version) { DBG(2, ErrorF("detected a PenPartner model\n")); common->wcmProtocolLevel = 4; common->wcmPktLength = 7; common->wcmVersion = version; common->wcmMaxZ = 256; common->wcmResolX = 1000; /* tablet X resolution in points/inch */ common->wcmResolY = 1000; /* tablet Y resolution in points/inch */ } static void serialInitGraphire(WacomCommonPtr common, int fd, const char* id, float version) { DBG(2, ErrorF("detected a Graphire model\n")); common->wcmProtocolLevel = 4; common->wcmPktLength = 7; common->wcmVersion = version; /* Graphire models don't answer WC_COORD requests */ common->wcmMaxX = 5103; common->wcmMaxY = 3711; common->wcmMaxZ = 512; common->wcmResolX = 1000; /* tablet X resolution in points/inch */ common->wcmResolY = 1000; /* tablet Y resolution in points/inch */ } static void serialInitProtocol4(WacomCommonPtr common, int fd, const char* id, float version) { DBG(2, ErrorF("detected a Protocol4 model\n")); common->wcmProtocolLevel = 4; common->wcmPktLength = 7; common->wcmVersion = version; /* If no maxZ is set, determine from version */ if (!common->wcmMaxZ) { /* the rom version determines the max z */ if (version >= (float)1.2) common->wcmMaxZ = 255; else common->wcmMaxZ = 120; } } static void serialGetResolution(WacomCommonPtr common, int fd) { int a, b; char buffer[BUFFER_SIZE], header[BUFFER_SIZE]; if (!(common->wcmResolX && common->wcmResolY)) { DBG(2, ErrorF("Requesting resolution from device\n")); if (xf86WcmSendRequest(fd, WC_CONFIG, buffer, sizeof(buffer))) { DBG(2, ErrorF("%s\n", buffer)); /* The header string is simply a place to put the * unwanted config header don't use buffer+xx because * the header size varies on different tablets */ if (sscanf(buffer, "%[^,],%d,%d,%d,%d", header, &a, &b, &common->wcmResolX, &common->wcmResolY) == 5) { DBG(6, ErrorF("WC_CONFIG Header = %s\n", header)); } else { ErrorF("WACOM: unable to parse resolution. Using default.\n"); common->wcmResolX = common->wcmResolY = 1270; } } else { ErrorF("WACOM: unable to read resolution. Using default.\n"); common->wcmResolX = common->wcmResolY = 1270; } } DBG(2, ErrorF("serialGetResolution: ResolX=%d ResolY=%d\n", common->wcmResolX, common->wcmResolY)); } static int serialGetRanges(WacomCommonPtr common, int fd) { char buffer[BUFFER_SIZE]; if (!(common->wcmMaxX && common->wcmMaxY)) { DBG(2, ErrorF("Requesting max coordinates\n")); if (!xf86WcmSendRequest(fd, WC_COORD, buffer, sizeof(buffer))) { ErrorF("WACOM: unable to read max coordinates. " "Use the MaxX and MaxY options.\n"); return !Success; } DBG(2, ErrorF("%s\n", buffer)); if (sscanf(buffer+2, "%d,%d", &common->wcmMaxX, &common->wcmMaxY) != 2) { ErrorF("WACOM: unable to parse max coordinates. " "Use the MaxX and MaxY options.\n"); return !Success; } } DBG(2, ErrorF("serialGetRanges: maxX=%d maxY=%d (%g,%g in)\n", common->wcmMaxX, common->wcmMaxY, (double)common->wcmMaxX / common->wcmResolX, (double)common->wcmMaxY / common->wcmResolY)); return Success; } static int serialResetIntuos(WacomCommonPtr common, int fd) { int err; SYSCALL(err = xf86WcmWrite(fd, intuos_setup_string, strlen(intuos_setup_string))); return (err == -1) ? !Success : Success; } static int serialResetCintiq(WacomCommonPtr common, int fd) { int err; SYSCALL(err = xf86WcmWrite(fd, WC_RESET, strlen(WC_RESET))); if (xf86WcmWait(75)) return !Success; SYSCALL(err = xf86WcmWrite(fd, pl_setup_string, strlen(pl_setup_string))); if (err == -1) return !Success; SYSCALL(err = xf86WcmWrite(fd, penpartner_setup_string, strlen(penpartner_setup_string))); return (err == -1) ? !Success : Success; } static int serialResetPenPartner(WacomCommonPtr common, int fd) { int err; SYSCALL(err = xf86WcmWrite(fd, penpartner_setup_string, strlen(penpartner_setup_string))); return (err == -1) ? !Success : Success; } static int serialResetProtocol4(WacomCommonPtr common, int fd) { int err; SYSCALL(err = xf86WcmWrite(fd, WC_RESET, strlen(WC_RESET))); if (xf86WcmWait(75)) return !Success; SYSCALL(err = xf86WcmWrite(fd, setup_string, strlen(setup_string))); if (err == -1) return !Success; SYSCALL(err = xf86WcmWrite(fd, penpartner_setup_string, strlen(penpartner_setup_string))); return (err == -1) ? !Success : Success; } static int serialEnableTiltProtocol4(WacomCommonPtr common, int fd) { return Success; } static int serialEnableSuppressProtocol4(WacomCommonPtr common, int fd) { char buf[20]; int err; sprintf(buf, "%s%d\r", WC_SUPPRESS, common->wcmSuppress); SYSCALL(err = xf86WcmWrite(fd, buf, strlen(buf))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } return Success; } static int serialSetLinkSpeedIntuos(WacomCommonPtr common, int fd) { if ((common->wcmLinkSpeed == 38400) && (common->wcmVersion < 2.0F)) { ErrorF("Wacom: 38400 speed not supported with this Intuos " "firmware (%f)\n", common->wcmVersion); ErrorF("Switching to 19200\n"); common->wcmLinkSpeed = 19200; } return serialSetLinkSpeedProtocol5(common,fd); } static int serialSetLinkSpeedProtocol5(WacomCommonPtr common, int fd) { int err; char* speed_init_string; DBG(1, ErrorF("Switching serial link to %d\n", common->wcmLinkSpeed)); /* set init string according to speed */ speed_init_string = (common->wcmLinkSpeed == 38400) ? WC_V_38400 : WC_V_19200; /* Switch the tablet to the requested speed */ SYSCALL(err = xf86WcmWrite(fd, speed_init_string, strlen(speed_init_string))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 75 mSecs */ if (xf86WcmWait(75)) return !Success; /* Set speed of serial link to requested speed */ if (xf86WcmSetSerialSpeed(fd, common->wcmLinkSpeed) < 0) return !Success; return Success; } static int serialStartTablet(WacomCommonPtr common, int fd) { int err; /* Tell the tablet to start sending coordinates */ SYSCALL(err = xf86WcmWrite(fd, WC_START, strlen(WC_START))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } return Success; } static void serialParseP4Common(WacomCommonPtr common, const unsigned char* data, WacomDeviceState* last, WacomDeviceState* ds) { int is_stylus = (data[0] & POINTER_BIT); int cur_type = is_stylus ? ((ds->buttons & 4) ? ERASER_ID : STYLUS_ID) : CURSOR_ID; /* proximity bit */ ds->proximity = (data[0] & PROXIMITY_BIT); /* x and y coordinates */ ds->x = (((data[0] & 0x3) << 14) + (data[1] << 7) + data[2]); ds->y = (((data[3] & 0x3) << 14) + (data[4] << 7) + data[5]); /* first time into prox */ if (!last->proximity && ds->proximity) ds->device_type = cur_type; /* out of prox */ else if (!ds->proximity) memset(ds,0,sizeof(*ds)); /* check on previous proximity */ else if (is_stylus) { /* we were fooled by tip and second * sideswitch when it came into prox */ if ((ds->device_type != cur_type) && (ds->device_type == ERASER_ID)) { /* send a prox-out for old device */ WacomDeviceState out = { 0 }; xf86WcmEvent(common,0,&out); ds->device_type = cur_type; } } DBG(8, ErrorF("serialParseP4Common %s\n", ds->device_type == CURSOR_ID ? "CURSOR" : ds->device_type == ERASER_ID ? "ERASER " : ds->device_type == STYLUS_ID ? "STYLUS" : "NONE")); /* handle tilt values only for stylus */ if (HANDLE_TILT(common) && is_stylus) { ds->tiltx = (data[7] & TILT_BITS); ds->tilty = (data[8] & TILT_BITS); if (data[7] & TILT_SIGN_BIT) ds->tiltx -= 64; if (data[8] & TILT_SIGN_BIT) ds->tilty -= 64; } } /***************************************************************************** * xf86WcmSerialValidate -- validates serial packet; returns 0 on success, * positive number of bytes to skip on error. ****************************************************************************/ int xf86WcmSerialValidate(WacomCommonPtr common, const unsigned char* data) { int i, bad = 0; /* check magic */ for (i=0; iwcmPktLength; ++i) { if ( ((i==0) && !(data[i] & HEADER_BIT)) || ((i!=0) && (data[i] & HEADER_BIT))) { bad = 1; DBG(6, ErrorF("xf86WcmSerialValidate: bad magic at %d " "v=%p l=%d\n", i, data, common->wcmPktLength)); if (i!=0 && (data[i] & HEADER_BIT)) return i; } } if (bad) return common->wcmPktLength; else return 0; } static WacomModel isdv4General = { "General ISDV4", isdv4InitISDV4, NULL, /* resolution not queried */ NULL, /* ranges not queried */ NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ NULL, /* start not supported */ isdv4Parse, xf86WcmFilterCoord, /* input filtering */ }; /***************************************************************************** * isdv4Detect -- Test if the attached device is ISDV4. ****************************************************************************/ static Bool isdv4Detect(LocalDevicePtr local) { WacomDevicePtr priv = (WacomDevicePtr) local->private; WacomCommonPtr common = priv->common; return (common->wcmForceDevice == DEVICE_ISDV4) ? 1 : 0; } /***************************************************************************** * isdv4Init -- ****************************************************************************/ static Bool isdv4Init(LocalDevicePtr local) { int err; WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; DBG(1, ErrorF("initializing ISDV4 tablet\n")); DBG(1, ErrorF("resetting tablet\n")); /* Set the speed of the serial link to 38400 */ if (xf86WcmSetSerialSpeed(local->fd, 38400) < 0) return !Success; /* Send reset to the tablet */ SYSCALL(err = xf86WcmWrite(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 250 mSecs */ if (xf86WcmWait(250)) return !Success; /* Send reset to the tablet */ SYSCALL(err = xf86WcmWrite(local->fd, WC_RESET, strlen(WC_RESET))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 75 mSecs */ if (xf86WcmWait(75)) return !Success; /* Set the speed of the serial link to 19200 */ if (xf86WcmSetSerialSpeed(local->fd, 19200) < 0) return !Success; /* Send reset to the tablet */ SYSCALL(err = xf86WcmWrite(local->fd, WC_RESET_BAUD, strlen(WC_RESET_BAUD))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 250 mSecs */ if (xf86WcmWait(250)) return !Success; /* Send reset to the tablet */ SYSCALL(err = xf86WcmWrite(local->fd, WC_RESET, strlen(WC_RESET))); if (err == -1) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Wait 75 mSecs */ if (xf86WcmWait(75)) return !Success; xf86WcmFlushTablet(local->fd); DBG(2, ErrorF("not reading model -- Wacom TabletPC ISD V4\n")); return xf86WcmInitTablet(common,&isdv4General,local->fd,"unknown",0.0); } /***************************************************************************** * isdv4InitISDV4 -- Setup the device ****************************************************************************/ static void isdv4InitISDV4(WacomCommonPtr common, int fd, const char* id, float version) { DBG(2, ErrorF("initializing as ISDV4 model\n")); /* set parameters */ common->wcmProtocolLevel = 0; common->wcmMaxZ = 255; /* max Z value (pressure)*/ common->wcmResolX = 2570; /* X resolution in points/inch */ common->wcmResolY = 2570; /* Y resolution in points/inch */ common->wcmPktLength = 9; /* length of a packet */ if (common->wcmRotate==ROTATE_NONE) { common->wcmMaxX = 21136; common->wcmMaxY = 15900; } else if (common->wcmRotate==ROTATE_CW || common->wcmRotate==ROTATE_CCW) { common->wcmMaxX = 15900; common->wcmMaxY = 21136; } } static int isdv4Parse(WacomCommonPtr common, const unsigned char* data) { WacomDeviceState* last = &common->wcmChannel[0].valid.state; WacomDeviceState* ds; int n, cur_type, tmp_coord; if ((n = xf86WcmSerialValidate(common,data)) > 0) return n; /* pick up where we left off, minus relative values */ ds = &common->wcmChannel[0].work; RESET_RELATIVE(*ds); ds->proximity = (data[0] & 0x20); /* x and y in "normal" orientetion (wide length is X) */ ds->x = (((int)data[6] & 0x60) >> 5) | ((int)data[2] << 2) | ((int)data[1] << 9); ds->y = (((int)data[6] & 0x18) >> 3) | ((int)data[4] << 2) | ((int)data[3] << 9); /* rotation mixes x and y up a bit */ if (common->wcmRotate == ROTATE_CW) { tmp_coord = ds->x; ds->x = ds->y; ds->y = common->wcmMaxY - tmp_coord; } else if (common->wcmRotate == ROTATE_CCW) { tmp_coord = ds->y; ds->y = ds->x; ds->x = common->wcmMaxX - tmp_coord; } /* pressure */ ds->pressure = ((data[6] & 0x01) << 7) | (data[5] & 0x7F); /* button order is slightly amiss so that styluses with only * one side button have buttons 1 and 3 */ /* report touch as button 1 */ ds->buttons = (data[0] & 0x01) ? 1 : 0 ; /* report side switch as button 3 */ ds->buttons |= (data[0] & 0x02) ? 0x04 : 0 ; /* report as button 2 (subject eraser check below) */ ds->buttons |= (data[0] & 0x04) ? 0x02 : 0 ; /* the bit used for button 2 is also used to ID the * eraser. If the tool comes into proximity with this bit * set, it is assumed to be the eraser. If the tool was * previously in proximity, it is assumed to be the button. * The mistaken identity case is handled with proximity below. */ /* check which device we have */ cur_type = (data[0] & 0x04) ? ERASER_ID : STYLUS_ID; /* If there's no eraser configured, let's not even try to * report an eraser (handy for making button 2 action more * definite) */ if ( common->wcmNumDevices == 1 && cur_type == ERASER_ID ) cur_type = STYLUS_ID; /* first time into prox */ if (!last->proximity && ds->proximity) ds->device_type = cur_type; /* out of prox */ else if (!ds->proximity) memset(ds,0,sizeof(*ds)); /* check on previous proximity */ else { /* we were fooled by tip and second * sideswitch when it came into prox */ if ((ds->device_type != cur_type) && (ds->device_type == ERASER_ID)) { /* send a prox-out for old device */ WacomDeviceState out = { 0 }; xf86WcmEvent(common,0,&out); ds->device_type = cur_type; } } DBG(8, ErrorF("isdv4Parse %s\n", ds->device_type == ERASER_ID ? "ERASER " : ds->device_type == STYLUS_ID ? "STYLUS" : "NONE")); xf86WcmEvent(common,0,ds); return common->wcmPktLength; } static void filterNearestPoint(double x0, double y0, double x1, double y1, double a, double b, double* x, double* y) { double vx, vy, wx, wy, d1, d2, c; wx = a - x0; wy = b - y0; vx = x1 - x0; vy = y1 - y0; d1 = vx * wx + vy * wy; if (d1 <= 0) { *x = x0; *y = y0; } else { d2 = vx * vx + vy * vy; if (d1 >= d2) { *x = x1; *y = y1; } else { c = d1 / d2; *x = x0 + c * vx; *y = y0 + c * vy; } } } static int filterOnLine(double x0, double y0, double x1, double y1, double a, double b) { double x, y, d; filterNearestPoint(x0,y0,x1,y1,a,b,&x,&y); d = (x-a)*(x-a) + (y-b)*(y-b); return d < 0.00001; /* within 100th of a point (1E-2 squared) */ } static void filterLine(int* pCurve, int nMax, int x0, int y0, int x1, int y1) { int dx, dy, ax, ay, sx, sy, x, y, d; /* sanity check */ if ((x0 < 0) || (y0 < 0) || (x1 < 0) || (y1 < 0) || (x0 > nMax) || (y0 > nMax) || (x1 > nMax) || (y1 > nMax)) return; dx = x1 - x0; ax = abs(dx) * 2; sx = (dx>0) ? 1 : -1; dy = y1 - y0; ay = abs(dy) * 2; sy = (dy>0) ? 1 : -1; x = x0; y = y0; /* x dominant */ if (ax > ay) { d = ay - ax / 2; while (1) { pCurve[x] = y; if (x == x1) break; if (d >= 0) { y += sy; d -= ax; } x += sx; d += ay; } } else { /* y dominant */ d = ax - ay / 2; while (1) { pCurve[x] = y; if (y == y1) break; if (d >= 0) { x += sx; d -= ay; } y += sy; d += ax; } } } static void filterCurveToLine(int* pCurve, int nMax, double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3) { double x01,y01,x32,y32,xm,ym; double c1,d1,c2,d2,e,f; /* check if control points are on line */ if (filterOnLine(x0,y0,x3,y3,x1,y1) && filterOnLine(x0,y0,x3,y3,x2,y2)) { filterLine(pCurve,nMax,(int)(x0*nMax),(int)(y0*nMax), (int)(x3*nMax),(int)(y3*nMax)); return; } /* calculate midpoints */ x01 = (x0 + x1) / 2; y01 = (y0 + y1) / 2; x32 = (x3 + x2) / 2; y32 = (y3 + y2) / 2; /* calc split point */ xm = (x1 + x2) / 2; ym = (y1 + y2) / 2; /* calc control points and midpoint */ c1 = (x01 + xm) / 2; d1 = (y01 + ym) / 2; c2 = (x32 + xm) / 2; d2 = (y32 + ym) / 2; e = (c1 + c2) / 2; f = (d1 + d2) / 2; /* do each side */ filterCurveToLine(pCurve,nMax,x0,y0,x01,y01,c1,d1,e,f); filterCurveToLine(pCurve,nMax,e,f,c2,d2,x32,y32,x3,y3); } static void filterIntuosCoord(int* coord, int* current, int tilt, int* state) { int ts; int x0_pred; int x0_pred1; int x0, x1, x2, x3; x0 = *current; x1 = coord[0]; x2 = coord[1]; x3 = coord[2]; coord[0] = x0; coord[1] = x1; coord[2] = x2; ts = tilt >= 0 ? 1 : -1; if (*state == 0 || *state == 3) { if (ts * (x0 - 2 * x1 - x2) > 12 && ts * (x0 - 3 * x2 - 2 * x3) > 12) { /* detected a jump at x0 */ *state = 1; *current = x1; } else if (*state == 0) { x0_pred = 7 * x0 + 14 * x1 + 15 * x2 + 16; x0_pred1 = 4 * x3; if (x0_pred > x0_pred1) *current = ((CARD32)(x0_pred - x0_pred1)) >> 5; else *current = 0; } else { /* state == 3 * a jump at x3 was detected */ *current = (x0 + 2 * x1 + x2 + 2) >> 2; *state = 0; } } else if (*state == 1) { /* a jump at x1 was detected */ x0_pred = 3 * x0 + 7 * x2 + 4; x0_pred1 = 2 * x3; if (x0_pred > x0_pred1) *current = ((CARD32)(x0_pred - x0_pred1)) >> 3; else *current = 0; *state = 2; } else { /* state == 2 * a jump at x2 was detected */ *current = x1; *state = 3; } } /***************************************************************************** * filterIntuosTilt -- * Correct some hardware defects we've been seeing in Intuos pads, * but also cuts down quite a bit on jitter. ****************************************************************************/ static void filterIntuosTilt(int* state, int* tilt) { int tx; tx = *tilt + state[0] + state[1] + state[2]; state[2] = state[1]; state[1] = state[0]; state[0] = *tilt; tx /= MAX_SAMPLES; if (tx > 63) tx = 63; else if (tx < -64) tx = -64; *tilt = tx; } /***************************************************************************** * filterIntuosStylus -- * Correct some hardware defects we've been seeing in Intuos pads, * but also cuts down quite a bit on jitter. ****************************************************************************/ static void filterIntuosStylus(WacomFilterStatePtr state, WacomDeviceStatePtr ds) { if (!state->npoints) { ++state->npoints; DBG(2,ErrorF("filterIntuosStylus: first sample NO_FILTER\n")); state->x[0] = state->x[1] = state->x[2] = ds->x; state->y[0] = state->y[1] = state->y[2] = ds->y; state->tiltx[0] = state->tiltx[1] = state->tiltx[2] = ds->tiltx; state->tilty[0] = state->tilty[1] = state->tilty[2] = ds->tilty; return; } /* filter x */ filterIntuosCoord(state->x, &ds->x, ds->tiltx, &state->statex); /* filter y */ filterIntuosCoord(state->y, &ds->y, ds->tilty, &state->statey); /* filter tiltx */ filterIntuosTilt(state->tiltx, &ds->tiltx); /* filter tilty */ filterIntuosTilt(state->tilty, &ds->tilty); } /***************************************************************************** * xf86WcmSetPressureCurve -- apply user-defined curve to pressure values ****************************************************************************/ static void xf86WcmSetPressureCurve(WacomDevicePtr pDev, int x0, int y0, int x1, int y1) { int i; /* sanity check values */ if ((x0 < 0) || (x0 > 100) || (y0 < 0) || (y0 > 100) || (x1 < 0) || (x1 > 100) || (y1 < 0) || (y1 > 100)) return; xf86Msg(X_INFO, "xf86WcmSetPressureCurve: setting to %d,%d %d,%d\n", x0, y0, x1, y1); /* if curve is not allocated, do it now. */ if (!pDev->pPressCurve) { pDev->pPressCurve = (int*) xalloc(sizeof(int) * (FILTER_PRESSURE_RES + 1)); if (!pDev->pPressCurve) { xf86Msg(X_ERROR, "xf86WcmSetPressureCurve: failed to " "allocate memory for curve\n"); return; } } /* linear by default */ for (i=0; i<=FILTER_PRESSURE_RES; ++i) pDev->pPressCurve[i] = i; /* draw bezier line from bottom-left to top-right using ctrl points */ filterCurveToLine(pDev->pPressCurve, FILTER_PRESSURE_RES, 0.0, 0.0, /* bottom left */ x0/100.0, y0/100.0, /* control point 1 */ x1/100.0, y1/100.0, /* control point 2 */ 1.0, 1.0); /* top right */ for (i=0; i<=FILTER_PRESSURE_RES; i+=128) DBG(6, ErrorF("PRESSCURVE: %d=%d (%d)\n",i,pDev->pPressCurve[i], pDev->pPressCurve[i] - i)); pDev->nPressCtrl[0] = x0; pDev->nPressCtrl[1] = y0; pDev->nPressCtrl[2] = x1; pDev->nPressCtrl[3] = y1; } /***************************************************************************** * xf86WcmFilterIntuos -- provide error correction to all transducers except Intuos ****************************************************************************/ static int xf86WcmFilterCoord(WacomCommonPtr common, WacomChannelPtr pChannel, WacomDeviceStatePtr ds) { /* Only noise correction should happen here. If there's a problem that * cannot be fixed, return 1 such that the data is discarded. */ WacomDeviceState* pLast; int *x, *y; int filter_x, filter_y; x = pChannel->rawFilter.x; y = pChannel->rawFilter.y; if (!pChannel->rawFilter.npoints) { ++pChannel->rawFilter.npoints; DBG(2,ErrorF("xf86WcmFilterCoord: first sample NO_FILTER\n")); x[0] = x[1] = x[2] = ds->x; y[0] = y[1] = y[2] = ds->y; return 0; } pLast = &pChannel->valid.state; filter_x = (ds->x + x[0] + x[1] + x[2])/4; filter_y = (ds->y + y[0] + y[1] + y[2])/4; x[2] = x[1]; y[2] = y[1]; x[1] = x[0]; y[1] = y[0]; x[0] = ds->x; y[0] = ds->y; if (abs(filter_x - pLast->x) > 4) ds->x = filter_x; else ds->x = pLast->x; if (abs(filter_y - pLast->y) > 4) ds->y = filter_y; else ds->y = pLast->y; return 0; /* lookin' good */ } /***************************************************************************** * xf86WcmFilterCoord -- provide error correction to Intuos and Intuos2 ****************************************************************************/ static int xf86WcmFilterIntuos(WacomCommonPtr common, WacomChannelPtr pChannel, WacomDeviceStatePtr ds) { /* Only error correction should happen here. If there's a problem that * cannot be fixed, return 1 such that the data is discarded. */ if (ds->device_type != CURSOR_ID) filterIntuosStylus(&pChannel->rawFilter, ds); else xf86WcmFilterCoord(common, pChannel, ds); return 0; /* lookin' good */ } /* *************************************************************************** * * xf86WcmDevConvert -- * Convert valuators to X and Y. Only used by core events. * *************************************************************************** */ static Bool xf86WcmDevConvert(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("xf86WcmDevConvert\n")); if (first != 0 || num == 1) return FALSE; #ifdef PANORAMIX if (!noPanoramiXExtension && (priv->flags & ABSOLUTE_FLAG) && priv->common->wcmGimp) { int i, totalWidth, leftPadding = 0; for (i = 0; i < priv->currentScreen; i++) leftPadding += screenInfo.screens[i]->width; for (totalWidth = leftPadding; i < priv->numScreen; i++) totalWidth += screenInfo.screens[i]->width; v0 -= (priv->bottomX - priv->topX) * leftPadding / (double)totalWidth + 0.5; } #endif if (priv->twinview != TV_NONE && (priv->flags & ABSOLUTE_FLAG)) { v0 -= priv->topX; v1 -= priv->topY; if (priv->twinview == TV_LEFT_RIGHT) { if (v0 > priv->bottomX) { v0 -= priv->bottomX; priv->currentScreen = 1; if (priv->screen_no == 0) { priv->currentScreen = 0; } } else { priv->currentScreen = 0; if (priv->screen_no == 1) { priv->currentScreen = 1; } } if (priv->currentScreen == 1) { *x = priv->tvResolution[0] + priv->tvResolution[2] * v0 / (priv->bottomX - priv->topX); *y = v1 * priv->tvResolution[3] / (priv->bottomY - priv->topY) + 0.5; } else { *x = priv->tvResolution[0] * v0 / (priv->bottomX - priv->topX); *y = v1 * priv->tvResolution[1] / (priv->bottomY - priv->topY) + 0.5; } } if (priv->twinview == TV_ABOVE_BELOW) { if (v1 > priv->bottomY) { v1 -= priv->bottomY; priv->currentScreen = 1; if (priv->screen_no == 0) { priv->currentScreen = 0; } } else { priv->currentScreen = 0; if (priv->screen_no == 1) { priv->currentScreen = 1; } } if (priv->currentScreen == 1) { *x = v0 * priv->tvResolution[2] / (priv->bottomX - priv->topX) + 0.5; *y = priv->tvResolution[1] + priv->tvResolution[3] * v1 / (priv->bottomY - priv->topY); } else { *x = v0 * priv->tvResolution[0] / (priv->bottomX - priv->topX) + 0.5; *y = priv->tvResolution[1] * v1 / (priv->bottomY - priv->topY); } } } else { *x = v0 * priv->factorX + 0.5; *y = v1 * priv->factorY + 0.5; } DBG(6, ErrorF("Wacom converted v0=%d v1=%d to x=%d y=%d\n", v0, v1, *x, *y)); return TRUE; } /* *************************************************************************** * * xf86WcmDevReverseConvert -- * Convert X and Y to valuators. Only used by core events. * *************************************************************************** */ static Bool xf86WcmDevReverseConvert(LocalDevicePtr local, int x, int y, int *valuators) { WacomDevicePtr priv = (WacomDevicePtr) local->private; valuators[0] = x / priv->factorX + 0.5; valuators[1] = y / priv->factorY + 0.5; #ifdef PANORAMIX if (!noPanoramiXExtension && (priv->flags & ABSOLUTE_FLAG) && priv->common->wcmGimp) { int i, totalWidth, leftPadding = 0; for (i = 0; i < priv->currentScreen; i++) leftPadding += screenInfo.screens[i]->width; for (totalWidth = leftPadding; i < priv->numScreen; i++) totalWidth += screenInfo.screens[i]->width; valuators[0] += (priv->bottomX - priv->topX) * leftPadding / (double)totalWidth + 0.5; } #endif if (priv->twinview != TV_NONE && (priv->flags & ABSOLUTE_FLAG)) { if (priv->twinview == TV_LEFT_RIGHT) { if (x > priv->tvResolution[0]) { x -= priv->tvResolution[0]; priv->currentScreen = 1; if (priv->screen_no == 0) { priv->currentScreen = 0; } } else { priv->currentScreen = 0; if (priv->screen_no == 1) { priv->currentScreen = 1; } } if (priv->currentScreen == 1) { valuators[0] = x * (priv->bottomX - priv->topX) / priv->tvResolution[2] + priv->bottomX - priv->topX + 0.5; valuators[1] = y * (priv->bottomY - priv->topY) / priv->tvResolution[3] + 0.5; } else { valuators[0] = x * (priv->bottomX - priv->topX) / priv->tvResolution[0] + 0.5; valuators[1] = y * (priv->bottomY - priv->topY) / priv->tvResolution[1] + 0.5; } } if (priv->twinview == TV_ABOVE_BELOW) { if (y > priv->tvResolution[1]) { y -= priv->tvResolution[1]; priv->currentScreen = 1; if (priv->screen_no == 0) { priv->currentScreen = 0; } } else { priv->currentScreen = 0; if (priv->screen_no == 1) { priv->currentScreen = 1; } } if (priv->currentScreen == 1) { valuators[0] = x * (priv->bottomX - priv->topX) / priv->tvResolution[2] + 0.5; valuators[1] = y *(priv->bottomY - priv->topY) / priv->tvResolution[3] + priv->bottomY - priv->topY + 0.5; } else { valuators[0] = x * (priv->bottomX - priv->topX) / priv->tvResolution[0] + 0.5; valuators[1] = y *(priv->bottomY - priv->topY) / priv->tvResolution[1] + 0.5; } } valuators[0] += priv->topX; valuators[1] += priv->topY; } DBG(6, ErrorF("Wacom converted x=%d y=%d to v0=%d v1=%d\n", x, y, valuators[0], valuators[1])); return TRUE; } /* *************************************************************************** * * xf86WcmSetScreen -- * set to the proper screen according to the converted (x,y). * this only supports for horizontal setup now. * need to know screen's origin (x,y) to support * combined horizontal and vertical setups * *************************************************************************** */ static void xf86WcmSetScreen(LocalDevicePtr local, int *v0, int *v1) { WacomDevicePtr priv = (WacomDevicePtr) local->private; int screenToSet = miPointerCurrentScreen()->myNum; int i; int x, y; int totalWidth = 0, maxHeight = 0, leftPadding = 0; double sizeX = priv->bottomX - priv->topX; double sizeY = priv->bottomY - priv->topY; /* set factorX and factorY for single screen setup since * Top X Y and Bottom X Y can be changed while driver is running */ if (screenInfo.numScreens == 1) { priv->factorX = screenInfo.screens[0]->width / sizeX; priv->factorY = screenInfo.screens[0]->height / sizeY; return; } if (!(local->flags & (XI86_ALWAYS_CORE | XI86_CORE_POINTER))) return; if (!(priv->flags & ABSOLUTE_FLAG)) { /* screenToSet lags by one event, but not that important */ screenToSet = miPointerCurrentScreen()->myNum; priv->factorX = screenInfo.screens[screenToSet]->width / sizeX; priv->factorY = screenInfo.screens[screenToSet]->height / sizeY; priv->currentScreen = screenToSet; return; } for (i = 0; i < priv->numScreen; i++) { totalWidth += screenInfo.screens[i]->width; if (maxHeight < screenInfo.screens[i]->height) maxHeight = screenInfo.screens[i]->height; } if (priv->screen_no == -1) { for (i = 0; i < priv->numScreen; i++) { if (*v0 * totalWidth <= (leftPadding + screenInfo.screens[i]->width) * sizeX) { screenToSet = i; break; } leftPadding += screenInfo.screens[i]->width; } } #ifdef PANORAMIX else if (!noPanoramiXExtension && priv->common->wcmGimp) { screenToSet = priv->screen_no; for (i = 0; i < screenToSet; i++) leftPadding += screenInfo.screens[i]->width; *v0 = (sizeX * leftPadding + *v0 * screenInfo.screens[screenToSet]->width) / (double)totalWidth + 0.5; *v1 = *v1 * screenInfo.screens[screenToSet]->height / (double)maxHeight + 0.5; } if (!noPanoramiXExtension && priv->common->wcmGimp) { priv->factorX = totalWidth/sizeX; priv->factorY = maxHeight/sizeY; x = (*v0 - sizeX * leftPadding / totalWidth) * priv->factorX + 0.5; y = *v1 * priv->factorY + 0.5; if (x >= screenInfo.screens[screenToSet]->width) x = screenInfo.screens[screenToSet]->width - 1; if (y >= screenInfo.screens[screenToSet]->height) y = screenInfo.screens[screenToSet]->height - 1; } else #endif { if (priv->screen_no == -1) *v0 = (*v0 * totalWidth - sizeX * leftPadding) / screenInfo.screens[screenToSet]->width; else screenToSet = priv->screen_no; priv->factorX = screenInfo.screens[screenToSet]->width / sizeX; priv->factorY = screenInfo.screens[screenToSet]->height / sizeY; x = *v0 * priv->factorX + 0.5; y = *v1 * priv->factorY + 0.5; } xf86XInputSetScreen(local, screenToSet, x, y); DBG(10, ErrorF("xf86WcmSetScreen current=%d ToSet=%d\n", priv->currentScreen, screenToSet)); priv->currentScreen = screenToSet; } /* *************************************************************************** * * 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 rz, int rtx, int rty, int rrot, int rth, int rwheel) { int button, newb; WacomDevicePtr priv = (WacomDevicePtr) local->private; for (button=1; button<=16; button++) { int mask = 1 << (button-1); if ((mask & priv->oldButtons) != (mask & buttons)) { DBG(4, ErrorF("xf86WcmSendButtons button=%d state=%d, for %s\n", button, (buttons & mask) != 0, local->name)); /* set to the configured buttons */ newb = button; if (priv->button[button-1] != button) newb = priv->button[button-1]; /* translate into Left Double Click */ if (newb == 17) { newb = 1; if (buttons & mask) { /* Left button down */ if (IsCursor(priv)) xf86PostButtonEvent(local->dev, (priv->flags & ABSOLUTE_FLAG), newb, 1, 0, 6, rx, ry, rz, rrot, rth, rwheel); else xf86PostButtonEvent(local->dev, (priv->flags & ABSOLUTE_FLAG), newb, 1, 0, 6, rx, ry, rz, rtx, rty, rwheel); /* Left button up */ if (IsCursor(priv)) xf86PostButtonEvent(local->dev, (priv->flags & ABSOLUTE_FLAG), newb, 0, 0, 6, rx, ry, rz, rrot, rth, rwheel); else xf86PostButtonEvent(local->dev, (priv->flags & ABSOLUTE_FLAG), newb, 0, 0, 6, rx, ry, rz, rtx, rty, rwheel); } } if (newb <= 17) { if (IsCursor(priv)) xf86PostButtonEvent(local->dev, (priv->flags & ABSOLUTE_FLAG), newb, (buttons & mask) != 0, 0, 6, rx, ry, rz, rrot, rth, rwheel); else xf86PostButtonEvent(local->dev, (priv->flags & ABSOLUTE_FLAG), newb, (buttons & mask) != 0, 0, 6, rx, ry, rz, rtx, rty, rwheel); } } } } /* *************************************************************************** * * xf86WcmSendEvents -- * Send events according to the device state. * *************************************************************************** */ static void xf86WcmSendEvents(LocalDevicePtr local, const WacomDeviceState *ds) { int type = ds->device_type; int is_button = !!(ds->buttons); int is_proximity = ds->proximity; int x = ds->x; int y = ds->y; int z = ds->pressure; int buttons = ds->buttons; int tx = ds->tiltx; int ty = ds->tilty; int rot = ds->rotation; int throttle = ds->throttle; int wheel = ds->abswheel; WacomDevicePtr priv = (WacomDevicePtr) local->private; WacomCommonPtr common = priv->common; int rx, ry, rz, rtx, rty, rrot, rth, rw; int is_core_pointer, is_absolute; int aboveBelowSwitch = (priv->twinview == TV_ABOVE_BELOW) ? ((y < priv->topY) ? -1 : ((priv->bottomY < y) ? 1 : 0)) : 0; int leftRightSwitch = (priv->twinview == TV_LEFT_RIGHT) ? ((x < priv->topX) ? -1 : ((priv->bottomX < x) ? 1 : 0)) : 0; DBG(7, ErrorF("[%s] prox=%s\tx=%d\ty=%d\tz=%d\tbutton=%s\tbuttons=%d\ttx=%d ty=%d\twl=%d rot=%d th=%d\n", (type == STYLUS_ID) ? "stylus" : (type == CURSOR_ID) ? "cursor" : "eraser", is_proximity ? "true" : "false", x, y, z, is_button ? "true" : "false", buttons, tx, ty, wheel, rot, throttle)); is_absolute = (priv->flags & ABSOLUTE_FLAG); is_core_pointer = xf86IsCorePointer(local->dev); if ( is_proximity || x || y || z || buttons || tx || ty || wheel ) { switch ( leftRightSwitch ) { case -1: priv->doffsetX = 0; break; case 1: priv->doffsetX = priv->bottomX - priv->topX; break; } switch ( aboveBelowSwitch ) { case -1: priv->doffsetY = 0; break; case 1: priv->doffsetY = priv->bottomY - priv->topY; break; } } x += priv->doffsetX; y += priv->doffsetY; /* sets rx and ry according to the mode */ if (is_absolute) { if (priv->twinview == TV_NONE) { rx = x > priv->bottomX ? priv->bottomX - priv->topX : x < priv->topX ? 0 : x - priv->topX; ry = y > priv->bottomY ? priv->bottomY - priv->topY : y < priv->topY ? 0 : y - priv->topY; } else { rx = x; ry = y; } rz = z; rtx = tx; rty = ty; rrot = rot; rth = throttle; rw = wheel; } else { if (priv->oldProximity) { /* unify acceleration in both directions */ rx = (x - priv->oldX) * priv->factorY / priv->factorX; ry = y - priv->oldY; } else { rx = 0; ry = 0; } if (priv->speed != DEFAULT_SPEED ) { /* don't apply acceleration for fairly small * increments (but larger than speed setting). */ int no_jitter = priv->speed * 3; double param = priv->speed; double relacc = (MAX_ACCEL-priv->accel)*(MAX_ACCEL-priv->accel); if (ABS(rx) > no_jitter) { /* don't apply acceleration when too fast. */ param += priv->accel > 0 ? rx/relacc : 0; if (param > 20.00) rx *= param; } if (ABS(ry) > no_jitter) { param += priv->accel > 0 ? ry/relacc : 0; if (param > 20.00) ry *= param; } } rz = z - priv->oldZ; rtx = tx - priv->oldTiltX; rty = ty - priv->oldTiltY; rrot = rot - priv->oldRot; rth = throttle - priv->oldThrottle; rw = wheel - priv->oldWheel; } /* for multiple monitor support, we need to set the proper * screen and modify the axes before posting events */ xf86WcmSetScreen(local, &rx, &ry); /* coordinates are ready we can send events */ if (is_proximity) { if (!priv->oldProximity) { if (IsCursor(priv)) xf86PostProximityEvent(local->dev, 1, 0, 6, rx, ry, rz, rrot, rth, rw); else xf86PostProximityEvent(local->dev, 1, 0, 6, rx, ry, rz, rtx, rty, rw); } if(!(priv->flags & BUTTONS_ONLY_FLAG)) { if (IsCursor(priv)) xf86PostMotionEvent(local->dev, is_absolute, 0, 6, rx, ry, rz, rrot, rth, rw); else xf86PostMotionEvent(local->dev, is_absolute, 0, 6, rx, ry, rz, rtx, rty, rw); } /* simulate button 4 and 5 for relative wheel */ if ( ds->relwheel ) { int fakeButton = ds->relwheel > 0 ? 5 : 4; int i; for (i=0; irelwheel); i++) { xf86PostButtonEvent(local->dev, is_absolute, fakeButton, 1, 0, 6, rx, ry, rz, rrot, rth, rw); xf86PostButtonEvent(local->dev, is_absolute, fakeButton, 0, 0, 6, rx, ry, rz, rrot, rth, rw); } } if (priv->oldButtons != buttons) { xf86WcmSendButtons (local, buttons, rx, ry, rz, rtx, rty, rrot, rth, rw); } } else { /* !PROXIMITY */ /* reports button up when the device has been down and becomes out of proximity */ if (priv->oldButtons) { buttons = 0; xf86WcmSendButtons (local, buttons, rx, ry, rz, rtx, rty, rrot, rth, rw); } if (!is_core_pointer) { /* macro button management */ if (common->wcmProtocolLevel == 4 && buttons) { int macro = z / 2; DBG(6, ErrorF("macro=%d buttons=%d wacom_map[%d]=%lx\n", macro, buttons, macro, (unsigned long)wacom_map[macro])); /* First available Keycode begins at 8 => macro+7 */ /* key down */ if (IsCursor(priv)) xf86PostKeyEvent(local->dev,macro+7,1,is_absolute, 0,6,0,0,buttons,rrot,rth,rw); else xf86PostKeyEvent(local->dev,macro+7,1,is_absolute, 0,6,0,0,buttons,rtx,rty,rw); /* key up */ if (IsCursor(priv)) xf86PostKeyEvent(local->dev,macro+7,0,is_absolute, 0,6,0,0,buttons,rrot,rth,rw); else xf86PostKeyEvent(local->dev,macro+7,0,is_absolute, 0,6,0,0,buttons,rtx,rty,rw); } } if (priv->oldProximity) { if (IsCursor(priv)) xf86PostProximityEvent(local->dev, 0, 0, 6, rx, ry, rz, rrot, rth, rw); else xf86PostProximityEvent(local->dev,0, 0, 6, rx, ry, rz, rtx, rty, rw); } } priv->oldProximity = is_proximity; priv->oldButtons = buttons; priv->oldWheel = wheel; priv->oldX = x; priv->oldY = y; priv->oldZ = z; priv->oldTiltX = tx; priv->oldTiltY = ty; priv->oldRot = rot; priv->oldThrottle = throttle; } /* *************************************************************************** * * 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 (ABS(ds1->throttle - ds2->throttle) > suppress) return 0; if ((1800 + ds1->rotation - ds2->rotation) % 1800 > suppress && (1800 + ds2->rotation - ds1->rotation) % 1800 > suppress) return 0; if (ABS(ds1->abswheel - ds2->abswheel) > suppress || (ds2->relwheel) != 0) return 0; return 1; } /* *************************************************************************** * * xf86WcmDevReadInput -- * Read the device on IO signal. * *************************************************************************** */ static void xf86WcmDevReadInput(LocalDevicePtr local) { int loop=0; # define MAX_READ_LOOPS 10 WacomCommonPtr common = ((WacomDevicePtr)local->private)->common; /* move data until we exhaust the device */ for (loop=0; loop < MAX_READ_LOOPS; ++loop) { /* dispatch */ common->wcmDevCls->Read(local); /* verify that there is still data in pipe */ if (!xf86WcmReady(local->fd)) break; } /* report how well we're doing */ if (loop >= MAX_READ_LOOPS) { DBG(1,ErrorF("xf86WcmDevReadInput: Can't keep up!!!\n")); } else { if (loop > 1) DBG(10,ErrorF("xf86WcmDevReadInput: Read (%d)\n",loop)); } } void xf86WcmReadPacket(LocalDevicePtr local) { WacomCommonPtr common = ((WacomDevicePtr)(local->private))->common; int len, pos, cnt, remaining; if (!common->wcmModel) return; remaining = sizeof(common->buffer) - common->bufpos; DBG(7, ErrorF("xf86WcmDevReadPacket: device=%s fd=%d " "pos=%d remaining=%d\n", common->wcmDevice, local->fd, common->bufpos, remaining)); /* fill buffer with as much data as we can handle */ SYSCALL(len = xf86WcmRead(local->fd, common->buffer + common->bufpos, remaining)); if (len <= 0) { ErrorF("Error reading wacom device : %s\n", strerror(errno)); return; } /* account for new data */ common->bufpos += len; DBG(10, ErrorF("xf86WcmReadPacket buffer has %d bytes\n", common->bufpos)); pos = 0; /* while there are whole packets present, parse data */ while ((common->bufpos - pos) >= common->wcmPktLength) { /* parse packet */ cnt = common->wcmModel->Parse(common, common->buffer + pos); if (cnt <= 0) { DBG(1,ErrorF("Misbehaving parser returned %d\n",cnt)); break; } pos += cnt; } if (pos) { /* if half a packet remains, move it down */ if (pos < common->bufpos) { DBG(7, ErrorF("MOVE %d bytes\n", common->bufpos - pos)); memmove(common->buffer,common->buffer+pos, common->bufpos-pos); common->bufpos -= pos; } /* otherwise, reset the buffer for next time */ else common->bufpos = 0; } } /* *************************************************************************** * * xf86WcmDevControlProc -- * *************************************************************************** */ static void xf86WcmDevControlProc(DeviceIntPtr device,PtrCtrl *ctrl) { DBG(2, ErrorF("xf86WcmDevControlProc\n")); } /***************************************************************************** * xf86WcmInitDevice -- * Open and initialize the tablet ****************************************************************************/ static Bool xf86WcmInitDevice(LocalDevicePtr local) { WacomCommonPtr common = ((WacomDevicePtr)local->private)->common; int loop; DBG(1,ErrorF("xf86WcmInitDevice: ")); if (common->wcmInitialized) { DBG(1,ErrorF("already initialized\n")); return TRUE; } DBG(1,ErrorF("initializing\n")); /* attempt to open the device */ if ((xf86WcmOpen(local) != Success) || (local->fd < 0)) { DBG(1,ErrorF("Failed to open device (fd=%d)\n",local->fd)); if (local->fd >= 0) { DBG(1,ErrorF("Closing device\n")); SYSCALL(xf86WcmClose(local->fd)); } local->fd = -1; return FALSE; } /* on success, mark all other local devices as open and initialized */ common->wcmInitialized = TRUE; DBG(1,ErrorF("Marking all devices open\n")); /* report the file descriptor to all devices */ for (loop=0; loopwcmNumDevices; loop++) common->wcmDevices[loop]->fd = local->fd; return TRUE; } /***************************************************************************** * xf86WcmDevClose -- ****************************************************************************/ static void xf86WcmDevClose(LocalDevicePtr local) { WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; int loop, num = 0; for (loop=0; loopwcmNumDevices; loop++) { if (common->wcmDevices[loop]->fd >= 0) num++; } DBG(4, ErrorF("Wacom number of open devices = %d\n", num)); if (num == 1) { DBG(1,ErrorF("Closing device; uninitializing.\n")); SYSCALL(xf86WcmClose(local->fd)); common->wcmInitialized = FALSE; } local->fd = -1; } int xf86WcmWait(int t) { int err = xf86WaitForInput(-1, ((t) * 1000)); if (err != -1) return Success; ErrorF("Wacom select error : %s\n", strerror(errno)); return err; } int xf86WcmReady(int fd) { int n = xf86WaitForInput(fd, 0); if (n >= 0) return n ? 1 : 0; ErrorF("Wacom select error : %s\n", strerror(errno)); return 0; } /* *************************************************************************** * * xf86WcmOpen -- * *************************************************************************** */ static Bool xf86WcmOpen(LocalDevicePtr local) { WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; WacomDeviceClass** ppDevCls; DBG(1, ErrorF("opening %s\n", common->wcmDevice)); local->fd = xf86WcmOpenTablet(local); if (local->fd < 0) { ErrorF("Error opening %s : %s\n", common->wcmDevice, strerror(errno)); return !Success; } /* Detect device class; default is serial device */ for (ppDevCls=wcmDeviceClasses; *ppDevCls!=NULL; ++ppDevCls) { if ((*ppDevCls)->Detect(local)) { common->wcmDevCls = *ppDevCls; break; } } /* Initialize the tablet */ return common->wcmDevCls->Init(local); } /* reset raw data counters for filters */ static void resetSampleCounter(const WacomChannelPtr pChannel) { /* if out of proximity, reset hardware filter */ if (!pChannel->valid.state.proximity) { pChannel->nSamples = 0; pChannel->rawFilter.npoints = 0; pChannel->rawFilter.statex = 0; pChannel->rawFilter.statey = 0; } } /***************************************************************************** ** Transformations *****************************************************************************/ static void transPressureCurve(WacomDevicePtr pDev, WacomDeviceStatePtr pState) { if (pDev->pPressCurve) { int p = pState->pressure; /* clip */ p = (p < 0) ? 0 : (p > pDev->common->wcmMaxZ) ? pDev->common->wcmMaxZ : p; /* rescale pressure to FILTER_PRESSURE_RES */ p = (p * FILTER_PRESSURE_RES) / pDev->common->wcmMaxZ; /* apply pressure curve function */ p = pDev->pPressCurve[p]; /* scale back to wcmMaxZ */ pState->pressure = (p * pDev->common->wcmMaxZ) / FILTER_PRESSURE_RES; } } static void commonDispatchDevice(WacomCommonPtr common, const WacomChannelPtr pChannel) { int id, idx; WacomDevicePtr priv; LocalDevicePtr pDev = NULL; LocalDevicePtr pLastDev = pChannel->pDev; WacomDeviceState* ds = &pChannel->valid.states[0]; WacomDeviceState* pLast = &pChannel->valid.states[1]; DBG(10, ErrorF("commonDispatchEvents\n")); /* Find the device the current events are meant for */ for (idx=0; idxwcmNumDevices; idx++) { priv = common->wcmDevices[idx]->private; id = DEVICE_ID(priv->flags); if (id == ds->device_type && ((!priv->serial) || (ds->serial_num == priv->serial))) { if ((priv->topX <= ds->x && priv->bottomX > ds->x && priv->topY <= ds->y && priv->bottomY > ds->y)) { DBG(11, ErrorF("tool id=%d for %s\n", id, common->wcmDevices[idx]->name)); pDev = common->wcmDevices[idx]; break; } /* Fallback to allow the cursor to move smoothly along screen edges */ else if (priv->oldProximity) { pDev = common->wcmDevices[idx]; } } } DBG(11, ErrorF("commonDispatchEvents: %p %p\n", (void *)pDev, (void *)pLastDev)); /* if the logical device of the same physical tool has changed, * send proximity out to the previous one */ if (pLastDev && (pLastDev != pDev) && (pLast->serial_num == ds->serial_num)) { pLast->proximity = 0; xf86WcmSendEvents(pLastDev, pLast); } /* if a device matched criteria, handle filtering per device * settings, and send event to XInput */ if (pDev) { WacomDeviceState filtered = pChannel->valid.state; WacomDevicePtr priv = pDev->private; /* Device transformations come first */ /* transform pressure */ transPressureCurve(priv,&filtered); /* User-requested filtering comes next */ /* User-requested transformations come last */ #if 0 /* not quite ready for prime-time; * it needs to be possible to disable, * and returning throttle to zero does * not reset the wheel, yet. */ int sampleTime, ticks; /* get the sample time */ sampleTime = GetTimeInMillis(); ticks = ThrottleToRate(ds->throttle); /* throttle filter */ if (!ticks) { priv->throttleLimit = -1; } else if ((priv->throttleStart > sampleTime) || (priv->throttleLimit == -1)) { priv->throttleStart = sampleTime; priv->throttleLimit = sampleTime + ticks; } else if (priv->throttleLimit < sampleTime) { DBG(6, ErrorF("LIMIT REACHED: s=%d l=%d n=%d v=%d " "N=%d\n", priv->throttleStart, priv->throttleLimit, sampleTime, ds->throttle, sampleTime + ticks)); ds->relwheel = (ds->throttle > 0) ? 1 : (ds->throttle < 0) ? -1 : 0; priv->throttleStart = sampleTime; priv->throttleLimit = sampleTime + ticks; } else priv->throttleLimit = priv->throttleStart + ticks; #endif /* throttle */ /* force out-prox when height is greater than 112. * This only applies to USB protocol V tablets * which aimed at improving relative movement support. */ if (ds->distance > 112 && !(priv->flags & ABSOLUTE_FLAG)) { ds->proximity = 0; filtered.proximity = 0; } xf86WcmSendEvents(pDev, &filtered); } /* otherwise, if no device matched... */ else { DBG(11, ErrorF("no device matches with id=%d, serial=%d\n", ds->device_type, ds->serial_num)); } /* save the last device */ pChannel->pDev = pDev; } /***************************************************************************** * xf86WcmEvent - * Handles suppression, transformation, filtering, and event dispatch. ****************************************************************************/ void xf86WcmEvent(WacomCommonPtr common, unsigned int channel, const WacomDeviceState* pState) { WacomDeviceState* pLast; WacomDeviceState ds; WacomChannelPtr pChannel; /* sanity check the channel */ if (channel >= MAX_CHANNELS) return; pChannel = common->wcmChannel + channel; pLast = &pChannel->valid.state; /* we must copy the state because certain types of filtering * will need to change the values (ie. for error correction) */ ds = *pState; /* timestamp the state for velocity and acceleration analysis */ ds.sample = GetTimeInMillis(); DBG(10, ErrorF("xf86WcmEvent: c=%d i=%d t=%d s=0x%X x=%d y=%d b=0x%X " "p=%d rz=%d tx=%d ty=%d aw=%d rw=%d t=%d df=%d px=%d st=%d\n", channel, ds.device_id, ds.device_type, ds.serial_num, ds.x, ds.y, ds.buttons, ds.pressure, ds.rotation, ds.tiltx, ds.tilty, ds.abswheel, ds.relwheel, ds.throttle, ds.discard_first, ds.proximity, ds.sample)); /* Filter raw data, fix hardware defects, perform error correction */ if (RAW_FILTERING(common) && common->wcmModel->FilterRaw) { if (common->wcmModel->FilterRaw(common,pChannel,&ds)) { DBG(10, ErrorF("Raw filtering discarded data.\n")); resetSampleCounter(pChannel); return; /* discard */ } } /* Discard unwanted data */ if (xf86WcmSuppress(common->wcmSuppress, pLast, &ds)) { DBG(10, ErrorF("Suppressing data according to filter\n")); /* If throttle is not in use, discard data. */ if (ABS(ds.throttle) < common->wcmSuppress) { resetSampleCounter(pChannel); return; } /* Otherwise, we need this event for time-rate-of-change * values like the throttle-to-relative-wheel filter. * To eliminate position change events, we reset all values * to last unsuppressed position. */ ds = *pLast; RESET_RELATIVE(ds); } /* JEJ - Do not move this code without discussing it with me. * The device state is invariant of any filtering performed below. * Changing the device state after this point can and will cause * a feedback loop resulting in oscillations, error amplification, * unnecessary quantization, and other annoying effects. */ /* save channel device state and device to which last event went */ memmove(pChannel->valid.states + 1, pChannel->valid.states, sizeof(WacomDeviceState) * (MAX_SAMPLES - 1)); pChannel->valid.state = ds; /*save last raw sample */ if (pChannel->nSamples < 4) ++pChannel->nSamples; commonDispatchDevice(common,pChannel); resetSampleCounter(pChannel); } /***************************************************************************** * xf86WcmInitTablet -- common initialization for all tablets ****************************************************************************/ int xf86WcmInitTablet(WacomCommonPtr common, WacomModelPtr model, int fd, const char* id, float version) { /* Initialize the tablet */ model->Initialize(common,fd,id,version); /* Get tablet resolution */ if (model->GetResolution) model->GetResolution(common,fd); /* Get tablet range */ if (model->GetRanges && (model->GetRanges(common,fd) != Success)) return !Success; /* Default threshold value if not set */ if (common->wcmThreshold <= 0) { /* Threshold for counting pressure as a button */ common->wcmThreshold = common->wcmMaxZ * 3 / 50; ErrorF("%s Wacom using pressure threshold of %d for button 1\n", XCONFIG_PROBED, common->wcmThreshold); } /* Reset tablet to known state */ if (model->Reset && (model->Reset(common,fd) != Success)) { ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno)); return !Success; } /* Enable tilt mode, if requested and available */ if ((common->wcmFlags & TILT_REQUEST_FLAG) && model->EnableTilt) if (model->EnableTilt(common,fd) != Success) return !Success; /* Enable hardware suppress, if requested and available */ if ((common->wcmSuppress != 0) && model->EnableSuppress) if (model->EnableSuppress(common,fd) != Success) return !Success; /* change the serial speed, if requested */ if (common->wcmLinkSpeed != 9600) { if (model->SetLinkSpeed) { if (model->SetLinkSpeed(common,fd) != Success) return !Success; else ErrorF("Tablet does not support setting link " "speed, or not yet implemented\n"); } } /* output tablet state as probed */ if (xf86Verbose) ErrorF("%s Wacom %s tablet speed=%d maxX=%d maxY=%d maxZ=%d " "resX=%d resY=%d suppress=%d tilt=%s\n", XCONFIG_PROBED, model->name, common->wcmLinkSpeed, common->wcmMaxX, common->wcmMaxY, common->wcmMaxZ, common->wcmResolX, common->wcmResolY, common->wcmSuppress, HANDLE_TILT(common) ? "enabled" : "disabled"); /* start the tablet data */ if (model->Start && (model->Start(common,fd) != Success)) return !Success; /*set the model */ common->wcmModel = model; return Success; } /* *************************************************************************** * * xf86WcmDevOpen -- * Open the physical device and init information structs. * *************************************************************************** */ static int xf86WcmDevOpen(DeviceIntPtr pWcm) { LocalDevicePtr local = (LocalDevicePtr)pWcm->public.devicePrivate; WacomDevicePtr priv = (WacomDevicePtr)PRIVATE(pWcm); WacomCommonPtr common = priv->common; double screenRatio, tabletRatio; int totalWidth = 0, maxHeight = 0; if (local->fd < 0) { if (!xf86WcmInitDevice(local) || (local->fd < 0)) { DBG(1,ErrorF("Failed to initialize device\n")); return FALSE; } } if (priv->factorX == 0.0) { if (priv->twinview != TV_NONE && priv->bottomX == 0 && priv->bottomY == 0 && priv->topX == 0 && priv->topY == 0) { if (IsCursor(priv)) { /* for absolute cursor */ priv->topX = 80; priv->topY = 80; } else { /* for absolute stylus and eraser */ priv->topX = 50; priv->topY = 50; } priv->bottomX = common->wcmMaxX - priv->topX; priv->bottomY = common->wcmMaxY - priv->topY; } if (priv->bottomX == 0) priv->bottomX = common->wcmMaxX; if (priv->bottomY == 0) priv->bottomY = common->wcmMaxY; /* Verify Box validity */ if (priv->topX > common->wcmMaxX) { ErrorF("Wacom invalid TopX (%d) reseting to 0\n", priv->topX); priv->topX = 0; } if (priv->topY > common->wcmMaxY) { ErrorF("Wacom invalid TopY (%d) reseting to 0\n", priv->topY); priv->topY = 0; } if (priv->bottomX < priv->topX) { ErrorF("Wacom invalid BottomX (%d) reseting to %d\n", priv->bottomX, common->wcmMaxX); priv->bottomX = common->wcmMaxX; } if (priv->bottomY < priv->topY) { ErrorF("Wacom invalid BottomY (%d) reseting to %d\n", priv->bottomY, common->wcmMaxY); priv->bottomY = common->wcmMaxY; } if (priv->screen_no != -1 && (priv->screen_no >= priv->numScreen || priv->screen_no < 0)) { if (priv->twinview == TV_NONE || priv->screen_no != 1) { ErrorF("%s: invalid screen number %d, resetting to 0\n", local->name, priv->screen_no); priv->screen_no = 0; } } /* Calculate the ratio according to KeepShape, TopX and TopY */ if (priv->screen_no != -1) { priv->currentScreen = priv->screen_no; if (priv->twinview == TV_NONE) { totalWidth = screenInfo.screens[priv->currentScreen]->width; maxHeight = screenInfo.screens[priv->currentScreen]->height; } else { totalWidth = priv->tvResolution[2*priv->currentScreen]; maxHeight = priv->tvResolution[2*priv->currentScreen+1]; } } else { int i; for (i = 0; i < priv->numScreen; i++) { totalWidth += screenInfo.screens[i]->width; if (maxHeight < screenInfo.screens[i]->height) maxHeight=screenInfo.screens[i]->height; } } if (priv->flags & KEEP_SHAPE_FLAG) { screenRatio = (double)totalWidth / (double)maxHeight; tabletRatio = ((double) (common->wcmMaxX - priv->topX)) / (common->wcmMaxY - priv->topY); DBG(2, ErrorF("screenRatio = %.3g, tabletRatio = %.3g\n", screenRatio, tabletRatio)); if (screenRatio > tabletRatio) { priv->bottomX = common->wcmMaxX; priv->bottomY = (common->wcmMaxY - priv->topY) * tabletRatio / screenRatio + priv->topY; } else { priv->bottomX = (common->wcmMaxX - priv->topX) * screenRatio / tabletRatio + priv->topX; priv->bottomY = common->wcmMaxY; } } if (priv->numScreen == 1) { priv->factorX = totalWidth / (double)(priv->bottomX - priv->topX); priv->factorY = maxHeight / (double)(priv->bottomY - priv->topY); DBG(2, ErrorF("X factor = %.3g, Y factor = %.3g\n", priv->factorX, priv->factorY)); } 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); } /* x and y axes */ InitValuatorAxisStruct(pWcm, 0, 0, ((priv->bottomX - priv->topX) * priv->dscaleX), /* max val */ mils(common->wcmResolX), /* tablet resolution */ 0, mils(common->wcmResolX)); /* max_res */ InitValuatorAxisStruct(pWcm, 1, 0, ((priv->bottomY - priv->topY) * priv->dscaleY), /* max val */ mils(common->wcmResolY), /* tablet resolution */ 0, mils(common->wcmResolY)); /* max_res */ /* pressure */ InitValuatorAxisStruct(pWcm, 2, 0, common->wcmMaxZ, 1, 1, 1); if (IsCursor(priv)) { /* z-rot and throttle */ InitValuatorAxisStruct(pWcm, 3, -900, 899, 1, 1, 1); InitValuatorAxisStruct(pWcm, 4, -1023, 1023, 1, 1, 1); } else { /* tilt-x and tilt-y */ InitValuatorAxisStruct(pWcm, 3, -64, 63, 1, 1, 1); InitValuatorAxisStruct(pWcm, 4, -64, 63, 1, 1, 1); } /* wheel */ InitValuatorAxisStruct(pWcm, 5, 0, 1023, 1, 1, 1); return (local->fd != -1); } /* *************************************************************************** * * xf86WcmDevProc -- * Handle the initialization, etc. of a wacom * *************************************************************************** */ static int xf86WcmDevProc(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); DBG(2, ErrorF("BEGIN xf86WcmProc dev=%p priv=%p type=%s flags=%d what=%d\n", (void *)pWcm, (void *)priv, (DEVICE_ID(priv->flags) == STYLUS_ID) ? "stylus" : (DEVICE_ID(priv->flags) == CURSOR_ID) ? "cursor" : "eraser", priv->flags, what)); switch (what) { case DEVICE_INIT: DBG(1, ErrorF("xf86WcmProc pWcm=%p what=INIT\n", (void *)pWcm)); 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, xf86WcmDevControlProc) == 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 */ if (!xf86WcmDevOpen(pWcm)) { /* Sometime PL does not open the first time */ DBG(1, ErrorF("xf86WcmProc try to open pWcm=%p again\n", (void *)pWcm)); if (!xf86WcmDevOpen(pWcm)) { DBG(1, ErrorF("xf86WcmProc pWcm=%p what=INIT FALSE\n", (void *)pWcm)); return !Success; } } break; case DEVICE_ON: DBG(1, ErrorF("xf86WcmProc pWcm=%p what=ON\n", (void *)pWcm)); if ((local->fd < 0) && (!xf86WcmDevOpen(pWcm))) { pWcm->inited = FALSE; return !Success; } xf86AddEnabledDevice(local); pWcm->public.on = TRUE; break; case DEVICE_OFF: case DEVICE_CLOSE: DBG(1, ErrorF("xf86WcmProc pWcm=%p what=%s\n", (void *)pWcm, (what == DEVICE_CLOSE) ? "CLOSE" : "OFF")); if (local->fd >= 0) { xf86RemoveEnabledDevice(local); xf86WcmDevClose(local); } pWcm->public.on = FALSE; break; default: ErrorF("wacom unsupported mode=%d\n", what); return !Success; break; } DBG(2, ErrorF("END xf86WcmProc Success what=%d dev=%p priv=%p\n", what, (void *)pWcm, (void *)priv)); return Success; } /***************************************************************************** * xf86WcmSetParam ****************************************************************************/ static int xf86WcmSetParam(LocalDevicePtr local, int param, int value) { WacomDevicePtr priv = (WacomDevicePtr)local->private; char st[32]; switch (param) { case XWACOM_PARAM_TOPX: xf86ReplaceIntOption(local->options, "TopX", value); priv->topX = xf86SetIntOption(local->options, "TopX", 0); break; case XWACOM_PARAM_TOPY: xf86ReplaceIntOption(local->options, "TopY", value); priv->topY = xf86SetIntOption(local->options, "TopY", 0); break; case XWACOM_PARAM_BOTTOMX: xf86ReplaceIntOption(local->options, "BottomX", value); priv->bottomX = xf86SetIntOption(local->options, "BottomX", 0); break; case XWACOM_PARAM_BOTTOMY: xf86ReplaceIntOption(local->options, "BottomY", value); priv->bottomY = xf86SetIntOption(local->options, "BottomY", 0); break; case XWACOM_PARAM_BUTTON1: if ((value < 0) || (value > 18)) return BadValue; xf86ReplaceIntOption(local->options,"Button1",value); priv->button[0] = xf86SetIntOption(local->options,"Button1",1); break; case XWACOM_PARAM_BUTTON2: if ((value < 0) || (value > 18)) return BadValue; xf86ReplaceIntOption(local->options, "Button2", value); priv->button[1] = xf86SetIntOption(local->options,"Button2",2); break; case XWACOM_PARAM_BUTTON3: if ((value < 0) || (value > 18)) return BadValue; xf86ReplaceIntOption(local->options, "Button3", value); priv->button[2] = xf86SetIntOption(local->options,"Button3",3); break; case XWACOM_PARAM_BUTTON4: if ((value < 0) || (value > 18)) return BadValue; xf86ReplaceIntOption(local->options, "Button4", value); priv->button[3] = xf86SetIntOption(local->options,"Button4",4); break; case XWACOM_PARAM_BUTTON5: if ((value < 0) || (value > 18)) return BadValue; xf86ReplaceIntOption(local->options, "Button5", value); priv->button[4] = xf86SetIntOption(local->options,"Button5",5); break; case XWACOM_PARAM_DEBUGLEVEL: if ((value < 0) || (value > 100)) return BadValue; xf86ReplaceIntOption(local->options, "DebugLevel", value); gWacomModule.debugLevel = value; break; case XWACOM_PARAM_RAWFILTER: if ((value < 0) || (value > 1)) return BadValue; xf86ReplaceIntOption(local->options, "RawFilter", value); if (value) priv->common->wcmFlags |= RAW_FILTERING_FLAG; else priv->common->wcmFlags &= ~(RAW_FILTERING_FLAG); break; case XWACOM_PARAM_PRESSCURVE: { char chBuf[64]; int x0 = (value >> 24) & 0xFF; int y0 = (value >> 16) & 0xFF; int x1 = (value >> 8) & 0xFF; int y1 = value & 0xFF; if ((x0 > 100) || (y0 > 100) || (x1 > 100) || (y1 > 100)) return BadValue; snprintf(chBuf,sizeof(chBuf),"%d %d %d %d",x0,y0,x1,y1); xf86ReplaceStrOption(local->options, "PressCurve",chBuf); xf86WcmSetPressureCurve(priv,x0,y0,x1,y1); break; } case XWACOM_PARAM_MODE: if ((value < 0) || (value > 1)) return BadValue; if (value) { priv->flags |= ABSOLUTE_FLAG; xf86ReplaceStrOption(local->options, "Mode", "Absolute"); } else { priv->flags &= ~ABSOLUTE_FLAG; xf86ReplaceStrOption(local->options, "Mode", "Relative"); } break; case XWACOM_PARAM_SPEEDLEVEL: if ((value < 0) || (value > 10)) return BadValue; if (value > 5) priv->speed = 2.00*((double)value - 5.00); else priv->speed = ((double)value + 1.00) / 6.00; sprintf(st, "%.3f", priv->speed); xf86AddNewOption(local->options, "Speed", st); break; case XWACOM_PARAM_ACCEL: if ((value < 0) || (value > MAX_ACCEL-1)) return BadValue; priv->accel = value; xf86ReplaceIntOption(local->options, "Accel", priv->accel); break; case XWACOM_PARAM_CLICKFORCE: if ((value < 0) || (value > 20)) return BadValue; priv->common->wcmThreshold = (int)((double)(value*priv->common->wcmMaxZ)/100.00+0.5); xf86ReplaceIntOption(local->options, "Threshold", priv->common->wcmThreshold); break; case XWACOM_PARAM_XYDEFAULT: xf86ReplaceIntOption(local->options, "TopX", 0); priv->topX = xf86SetIntOption(local->options, "TopX", 0); xf86ReplaceIntOption(local->options, "TopY", 0); priv->topY = xf86SetIntOption(local->options, "TopY", 0); xf86ReplaceIntOption(local->options, "BottomX", priv->common->wcmMaxX); priv->bottomX = xf86SetIntOption(local->options, "BottomX", priv->common->wcmMaxX); xf86ReplaceIntOption(local->options, "BottomY", priv->common->wcmMaxY); priv->bottomY = xf86SetIntOption(local->options, "BottomY", priv->common->wcmMaxY); break; case XWACOM_PARAM_GIMP: if ((value != 0) && (value != 1)) return BadValue; priv->common->wcmGimp = value; break; default: DBG(10, ErrorF("xf86WcmSetParam invalid param %d\n",param)); return BadMatch; } return Success; } /***************************************************************************** * xf86WcmOptionCommandToFile ****************************************************************************/ static int xf86WcmOptionCommandToFile(LocalDevicePtr local) { WacomDevicePtr priv = (WacomDevicePtr)local->private; char fileName[80] = "/etc/X11/wcm."; char command[256]; FILE *fp = 0; int value; double speed; char *s; strcat(fileName, local->name); fp = fopen(fileName, "w+"); if ( fp ) { /* write user defined options as xsetwacom commands into fp */ s = xf86FindOptionValue(local->options, "TopX"); if ( s && priv->topX ) fprintf(fp, "xsetwacom set %s TopX %s\n", local->name, s); s = xf86FindOptionValue(local->options, "TopY"); if ( s && priv->topY ) fprintf(fp, "xsetwacom set %s TopY %s\n", local->name, s); s = xf86FindOptionValue(local->options, "BottomX"); if ( s && priv->bottomX != priv->common->wcmMaxX ) fprintf(fp, "xsetwacom set %s BottomX %s\n", local->name, s); s = xf86FindOptionValue(local->options, "BottomY"); if ( s && priv->bottomY != priv->common->wcmMaxY ) fprintf(fp, "xsetwacom set %s BottomY %s\n", local->name, s); s = xf86FindOptionValue(local->options, "Button1"); if ( s && priv->button[0] != 1 ) fprintf(fp, "xsetwacom set %s Button1 %s\n", local->name, s); s = xf86FindOptionValue(local->options, "Button2"); if ( s && priv->button[1] != 2 ) fprintf(fp, "xsetwacom set %s Button2 %s\n", local->name, s); s = xf86FindOptionValue(local->options, "Button3"); if ( s && priv->button[2] != 3 ) fprintf(fp, "xsetwacom set %s Button3 %s\n", local->name, s); s = xf86FindOptionValue(local->options, "Button4"); if ( s && priv->button[3] != 4 ) fprintf(fp, "xsetwacom set %s Button4 %s\n", local->name, s); s = xf86FindOptionValue(local->options, "Button5"); if ( s && priv->button[4] != 5 ) fprintf(fp, "xsetwacom set %s Button5 %s\n", local->name, s); s = xf86FindOptionValue(local->options, "PressCurve"); if ( s && !IsCursor(priv) ) fprintf(fp, "xsetwacom set %s PressCurve %s\n", local->name, s); s = xf86FindOptionValue(local->options, "Mode"); if ( s && (((priv->flags & ABSOLUTE_FLAG) && IsCursor(priv)) || (!(priv->flags & ABSOLUTE_FLAG) && !IsCursor(priv)))) fprintf(fp, "xsetwacom set %s Mode %s\n", local->name, s); s = xf86FindOptionValue(local->options, "RawFilter"); if ( s ) fprintf(fp, "xsetwacom set %s RawFilter %s\n", local->name, s); s = xf86FindOptionValue(local->options, "Accel"); if ( s && priv->accel ) fprintf(fp, "xsetwacom set %s Accel %s\n", local->name, s); s = xf86FindOptionValue(local->options, "Suppress"); if ( s ) fprintf(fp, "xsetwacom set %s Suppress %s\n", local->name, s); s = xf86FindOptionValue(local->options, "Speed"); if ( s && priv->speed != DEFAULT_SPEED ) { speed = strtod(s, NULL); if(speed > 10.00) value = 10; else if(speed >= 1.00) value = (int)(speed/2.00 + 5.00); else if(speed < (double)(1.00/6.00)) value = 0; else value = (int)(speed*6.00 - 0.50); fprintf(fp, "xsetwacom set %s SpeedLevel %d\n", local->name, value); } s = xf86FindOptionValue(local->options, "Threshold"); if ( s ) { value = atoi(s); value = (int)((double)value*100.00/(double)priv->common->wcmMaxZ+0.5); fprintf(fp, "xsetwacom set %s ClickForce %d\n", local->name, value); } fprintf(fp, "%s", "default TopX 0\n"); fprintf(fp, "%s", "default TopY 0\n"); fprintf(fp, "default BottomX %d\n", priv->common->wcmMaxX); fprintf(fp, "default BottomY %d\n", priv->common->wcmMaxY); if (IsCursor(priv)) sprintf(command, "default Mode Relative\n"); else sprintf(command, "default Mode Absolute\n"); fprintf(fp, "%s", command); fprintf(fp, "%s", "default SpeedLevel 5\n"); fprintf(fp, "%s", "default ClickForce 6\n"); fprintf(fp, "%s", "default Accel 0\n"); fclose(fp); } return(Success); } /***************************************************************************** * xf86WcmModelToFile ****************************************************************************/ static int xf86WcmModelToFile(LocalDevicePtr local) { FILE *fp = 0; LocalDevicePtr localDevices = xf86FirstLocalDevice(); WacomDevicePtr priv = NULL, lprv; char m1[32], m2[32], *m3; int i = 0, x = 0, y = 0; fp = fopen("/etc/wacom.dat", "w+"); if ( fp ) { while(localDevices) { m3 = xf86FindOptionValue(localDevices->options, "Type"); if (m3 && (strstr(m3, "eraser") || strstr(m3, "stylus") || strstr(m3, "cursor"))) lprv = (WacomDevicePtr)localDevices->private; else lprv = NULL; if (lprv && lprv->common) { sscanf((char*)(lprv->common->wcmModel)->name, "%s %s", m1, m2); fprintf(fp, "%s %s %s\n", localDevices->name, m2, m3); if (lprv->twinview != TV_NONE) { priv = lprv; } if( !priv ) priv = lprv; } localDevices = localDevices->next; } /* write TwinView ScreenInfo */ if (priv->twinview == TV_ABOVE_BELOW) { fprintf(fp, "Screen0 %d %d %d %d\n", priv->tvResolution[0], priv->tvResolution[1], 0, 0); fprintf(fp, "Screen1 %d %d %d %d\n", priv->tvResolution[2], priv->tvResolution[3], 0, priv->tvResolution[1]); } else if (priv->twinview == TV_LEFT_RIGHT) { fprintf(fp, "Screen0 %d %d %d %d\n", priv->tvResolution[0], priv->tvResolution[1], 0, 0); fprintf(fp, "Screen1 %d %d %d %d\n", priv->tvResolution[2], priv->tvResolution[3], priv->tvResolution[0], 0); } else { /* write other screen setup info */ for (i = 0; iwidth, screenInfo.screens[i]->height, x, y); x += screenInfo.screens[i]->width; } } fclose(fp); } return(Success); } /* *************************************************************************** * * xf86WcmDevChangeControl -- * *************************************************************************** */ static int xf86WcmDevChangeControl(LocalDevicePtr local, xDeviceCtl *control) { xDeviceResolutionCtl* res = (xDeviceResolutionCtl *)control; int* r = (int*)(res+1); int param = r[0], value = r[1]; DBG(10, ErrorF("xf86WcmDevChangeControl firstValuator=%d\n", res->first_valuator)); if (control->control != DEVICE_RESOLUTION || !res->num_valuators) return BadMatch; r[0] = 1, r[1] = 1; switch (res->first_valuator) { case 0: /* a new write to wcm.$name */ { return xf86WcmOptionCommandToFile(local); } case 1: /* a new write to wacom.dat */ { return xf86WcmModelToFile(local); } case 4: { DBG(10,ErrorF("xf86WcmDevChangeControl: 0x%x, 0x%x\n", param, value)); return xf86WcmSetParam(local,param,value); } default: DBG(10,ErrorF("xf86WcmDevChangeControl invalid " "firstValuator=%d\n",res->first_valuator)); return BadMatch; } } /* *************************************************************************** * * xf86WcmDevSwitchMode -- * *************************************************************************** */ static int xf86WcmDevSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode) { LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate; WacomDevicePtr priv = (WacomDevicePtr)local->private; DBG(3, ErrorF("xf86WcmSwitchMode dev=%p mode=%d\n", (void *)dev, mode)); if (mode == Absolute) { priv->flags |= ABSOLUTE_FLAG; } else { if (mode == Relative) { priv->flags &= ~ABSOLUTE_FLAG; } else { DBG(1, ErrorF("xf86WcmDevSwitchMode dev=%p invalid mode=%d\n", (void *)dev, mode)); return BadMatch; } } return Success; } /* *************************************************************************** * * xf86WcmAllocate -- * *************************************************************************** */ static LocalDevicePtr xf86WcmAllocate(char * name, int flag) { LocalDevicePtr local; WacomDevicePtr priv; WacomCommonPtr common; int i; priv = (WacomDevicePtr) xalloc(sizeof(WacomDeviceRec)); if (!priv) return NULL; common = (WacomCommonPtr) xalloc(sizeof(WacomCommonRec)); if (!common) { xfree(priv); return NULL; } local = xf86AllocateInput(gWacomModule.v4.wcmDrv, 0); if (!local) { xfree(priv); xfree(common); return NULL; } local->name = name; local->flags = 0; local->device_control = gWacomModule.DevProc; local->read_input = gWacomModule.DevReadInput; local->control_proc = gWacomModule.DevChangeControl; local->close_proc = gWacomModule.DevClose; local->switch_mode = gWacomModule.DevSwitchMode; local->conversion_proc = gWacomModule.DevConvert; local->reverse_conversion_proc = gWacomModule.DevReverseConvert; 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; memset(priv,0,sizeof(*priv)); priv->flags = flag; /* various flags (device type, absolute...) */ priv->oldX = -1; /* previous X position */ priv->oldY = -1; /* previous Y position */ priv->oldZ = -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->oldWheel = 0; /* previous wheel */ 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->screen_no = -1; /* associated screen */ priv->speed = DEFAULT_SPEED; /* rel. mode speed */ priv->accel = 0; /* rel. mode acceleration */ for (i=0; i<16; i++) priv->button[i] = i+1; /* button i value */ priv->numScreen = screenInfo.numScreens; /* number of configureed screens */ priv->currentScreen = 0; /* current screen in display */ priv->dscaleX = 1.0; /* dual screen scale X factor */ priv->dscaleY = 1.0; /* dual screen scale Y factor */ priv->doffsetX = 0; /* dual screen offset X */ priv->doffsetY = 0; /* dual screen offset Y */ priv->twinview = TV_NONE; /* not using twinview gfx */ for (i=0; i<4; i++) priv->tvResolution[i] = 0; /* unconfigured twinview resolution */ priv->throttleValue = 0; priv->throttleStart = 0; priv->throttleLimit = -1; memset(common,0,sizeof(*common)); memset(common->wcmChannel, 0, sizeof(common->wcmChannel)); common->wcmDevice = ""; /* device file name */ common->wcmSuppress = DEFAULT_SUPPRESS; /* transmit position if increment is superior */ common->wcmFlags = RAW_FILTERING_FLAG;/* various flags */ common->wcmDevices = (LocalDevicePtr*) xalloc(sizeof(LocalDevicePtr)); common->wcmDevices[0] = local; common->wcmNumDevices = 1; /* number of devices */ common->wcmMaxX = 0; /* max X value */ common->wcmMaxY = 0; /* max Y value */ common->wcmMaxZ = 0; /* max Z value */ common->wcmResolX = 0; /* X resolution in points/inch */ common->wcmResolY = 0; /* Y resolution in points/inch */ common->wcmChannelCnt = 1; /* number of channels */ common->wcmProtocolLevel = 4; /* protocol level */ common->wcmThreshold = 0; /* button 1 threshold for some tablet models */ common->wcmInitialized = FALSE; /* device is not initialized */ common->wcmLinkSpeed = 9600; /* serial link speed */ common->wcmDevCls = &gWacomSerialDevice; /* device-specific functions */ common->wcmModel = NULL; /* model-specific functions */ common->wcmGimp = 1; /* enabled (=1) to support Gimp when Xinerama Enabled in multi-monitor desktop. Needs to be disabled (=0) for Cintiq calibration */ return local; } /* *************************************************************************** * * xf86WcmAllocateStylus -- * *************************************************************************** */ static LocalDevicePtr xf86WcmAllocateStylus() { LocalDevicePtr local = xf86WcmAllocate(XI_STYLUS, STYLUS_ID); if (local) local->type_name = "Wacom Stylus"; return local; } /* *************************************************************************** * * xf86WcmAllocateCursor -- * *************************************************************************** */ static LocalDevicePtr xf86WcmAllocateCursor() { LocalDevicePtr local = xf86WcmAllocate(XI_CURSOR, CURSOR_ID); if (local) local->type_name = "Wacom Cursor"; return local; } /* *************************************************************************** * * xf86WcmAllocateEraser -- * *************************************************************************** */ static LocalDevicePtr xf86WcmAllocateEraser() { LocalDevicePtr local = xf86WcmAllocate(XI_ERASER, ABSOLUTE_FLAG|ERASER_ID); if (local) local->type_name = "Wacom Eraser"; return local; } /* * xf86WcmUninit -- * * called when the device is no longer needed. */ static void xf86WcmUninit(InputDriverPtr drv, LocalDevicePtr local, int flags) { WacomDevicePtr priv; priv = (WacomDevicePtr) local->private; DBG(1, ErrorF("xf86WcmUninit\n")); gWacomModule.DevProc(local->dev, DEVICE_OFF); /* free pressure curve */ if (priv->pPressCurve) xfree(priv->pPressCurve); xfree (priv); xf86DeleteInput(local, 0); } /* xf86WcmMatchDevice - locate matching device and merge common structure */ static Bool xf86WcmMatchDevice(LocalDevicePtr pMatch, LocalDevicePtr pLocal) { WacomDevicePtr privMatch = (WacomDevicePtr)pMatch->private; WacomDevicePtr priv = (WacomDevicePtr)pLocal->private; WacomCommonPtr common = priv->common; if ((pLocal != pMatch) && (pMatch->device_control == gWacomModule.DevProc) && !strcmp(privMatch->common->wcmDevice, common->wcmDevice)) { DBG(2, ErrorF("xf86WcmInit wacom port share between" " %s and %s\n", pLocal->name, pMatch->name)); xfree(common->wcmDevices); xfree(common); common = priv->common = privMatch->common; common->wcmNumDevices++; common->wcmDevices = (LocalDevicePtr *)xrealloc( common->wcmDevices, sizeof(LocalDevicePtr) * common->wcmNumDevices); common->wcmDevices[common->wcmNumDevices - 1] = pLocal; return 1; } return 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; WacomDevicePtr priv = NULL; WacomCommonPtr common = NULL; char *s, b[10]; int i, oldButton; LocalDevicePtr localDevices; gWacomModule.v4.wcmDrv = drv; fakeLocal = (LocalDevicePtr) xcalloc(1, sizeof(LocalDeviceRec)); if (!fakeLocal) return NULL; fakeLocal->conf_idev = dev; /* Force default serial port options to exist because the serial init * phasis is based on those values. */ xf86CollectInputOptions(fakeLocal, default_options, NULL); /* Type is mandatory */ 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) { xfree(fakeLocal); return NULL; } priv = (WacomDevicePtr) local->private; common = priv->common; local->options = fakeLocal->options; local->conf_idev = fakeLocal->conf_idev; local->name = dev->identifier; xfree(fakeLocal); /* Serial Device is mandatory */ common->wcmDevice = xf86FindOptionValue(local->options, "Device"); if (!common->wcmDevice) { xf86Msg (X_ERROR, "%s: No Device specified.\n", dev->identifier); goto SetupProc_fail; } /* Lookup to see if there is another wacom device sharing * the same serial line. */ localDevices = xf86FirstLocalDevice(); for (; localDevices != NULL; localDevices = localDevices->next) { if (xf86WcmMatchDevice(localDevices,local)) { common = priv->common; break; } } /* Process the common options. */ xf86ProcessCommonOptions(local, local->options); /* Optional configuration */ xf86Msg(X_CONFIG, "%s serial device is %s\n", dev->identifier, common->wcmDevice); gWacomModule.debugLevel = xf86SetIntOption(local->options, "DebugLevel", gWacomModule.debugLevel); if (gWacomModule.debugLevel > 0) { xf86Msg(X_CONFIG, "WACOM: debug level set to %d\n", gWacomModule.debugLevel); } 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); /* stylus/eraser defaults to absolute mode * cursor defaults to relative mode */ if (priv->flags & CURSOR_ID) priv->flags &= ~ABSOLUTE_FLAG; else priv->flags |= ABSOLUTE_FLAG; } xf86Msg(X_CONFIG, "%s is in %s mode\n", local->name, (priv->flags & ABSOLUTE_FLAG) ? "absolute" : "relative"); /* ISDV4 support */ s = xf86FindOptionValue(local->options, "ForceDevice"); if (s && (xf86NameCmp(s, "ISDV4") == 0)) { common->wcmForceDevice=DEVICE_ISDV4; common->wcmDevCls = &gWacomISDV4Device; xf86Msg(X_CONFIG, "%s: forcing TabletPC ISD V4 protocol\n", dev->identifier); } common->wcmRotate=ROTATE_NONE; s = xf86FindOptionValue(local->options, "Rotate"); if (s) { if (xf86NameCmp(s, "CW") == 0) { common->wcmRotate=ROTATE_CW; } else if (xf86NameCmp(s, "CCW") ==0) { common->wcmRotate=ROTATE_CCW; } } common->wcmSuppress = xf86SetIntOption(local->options, "Suppress", common->wcmSuppress); if ((common->wcmSuppress != 0) && /* 0 disables suppression */ (common->wcmSuppress > MAX_SUPPRESS || common->wcmSuppress < DEFAULT_SUPPRESS)) common->wcmSuppress = DEFAULT_SUPPRESS; xf86Msg(X_CONFIG, "WACOM: suppress value is %d\n", common->wcmSuppress); if (xf86SetBoolOption(local->options, "Tilt", (common->wcmFlags & TILT_REQUEST_FLAG))) { common->wcmFlags |= TILT_REQUEST_FLAG; } if (xf86SetBoolOption(local->options, "RawFilter", (common->wcmFlags & RAW_FILTERING_FLAG))) { common->wcmFlags |= RAW_FILTERING_FLAG; } #ifdef LINUX_INPUT if (xf86SetBoolOption(local->options, "USB", (common->wcmDevCls == &gWacomUSBDevice))) { /* best effort attempt at loading the wacom and evdev kernel modules */ (void)xf86LoadKernelModule("wacom"); (void)xf86LoadKernelModule("evdev"); common->wcmDevCls = &gWacomUSBDevice; xf86Msg(X_CONFIG, "%s: reading USB link\n", dev->identifier); #else if (xf86SetBoolOption(local->options, "USB", 0)) { ErrorF("The USB version of the driver isn't available for your platform\n"); #endif } /* pressure curve takes control points x1,y1,x2,y2 * values in range from 0..100. * Linear curve is 0,0,100,100 * Slightly depressed curve might be 5,0,100,95 * Slightly raised curve might be 0,5,95,100 */ s = xf86FindOptionValue(local->options, "PressCurve"); if (s) { int a,b,c,d; if ((sscanf(s,"%d,%d,%d,%d",&a,&b,&c,&d) != 4) || (a < 0) || (a > 100) || (b < 0) || (b > 100) || (c < 0) || (c > 100) || (d < 0) || (d > 100)) xf86Msg(X_CONFIG, "WACOM: PressCurve not valid\n"); else { xf86WcmSetPressureCurve(priv,a,b,c,d); xf86Msg(X_CONFIG, "WACOM: PressCurve %d,%d %d,%d\n", a,b,c,d); } } /* Config Monitors' resoluiton in TwinView setup. * The value is in the form of "1024x768,1280x1024" * for a desktop of monitor 1 at 1024x768 and * monitor 2 at 1280x1024 */ s = xf86FindOptionValue(local->options, "TVResolution"); if (s) { int a,b,c,d; if ((sscanf(s,"%dx%d,%dx%d",&a,&b,&c,&d) != 4) || (a <= 0) || (b <= 0) || (c <= 0) || (d <= 0)) xf86Msg(X_CONFIG, "WACOM: TVResolution not valid\n"); else { priv->tvResolution[0] = a; priv->tvResolution[1] = b; priv->tvResolution[2] = c; priv->tvResolution[3] = d; xf86Msg(X_CONFIG, "WACOM: TVResolution %d,%d %d,%d\n", a,b,c,d); } } priv->screen_no = xf86SetIntOption(local->options, "ScreenNo", -1); if (priv->screen_no != -1) { xf86Msg(X_CONFIG, "%s: attached screen number %d\n", dev->identifier, priv->screen_no); } 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 != 0) { xf86Msg(X_CONFIG, "%s: top y = %d\n", dev->identifier, priv->topY); } priv->bottomX = xf86SetIntOption(local->options, "BottomX", 0); if (priv->bottomX != 0) { xf86Msg(X_CONFIG, "%s: bottom x = %d\n", dev->identifier, priv->bottomX); } priv->bottomY = xf86SetIntOption(local->options, "BottomY", 0); if (priv->bottomY != 0) { xf86Msg(X_CONFIG, "%s: bottom y = %d\n", dev->identifier, priv->bottomY); } priv->serial = xf86SetIntOption(local->options, "Serial", 0); if (priv->serial != 0) { xf86Msg(X_CONFIG, "%s: serial number = %u\n", dev->identifier, priv->serial); } common->wcmThreshold = xf86SetIntOption(local->options, "Threshold", common->wcmThreshold); if (common->wcmThreshold > 0) { xf86Msg(X_CONFIG, "%s: threshold = %d\n", dev->identifier, common->wcmThreshold); } common->wcmMaxX = xf86SetIntOption(local->options, "MaxX", common->wcmMaxX); if (common->wcmMaxX != 0) { xf86Msg(X_CONFIG, "%s: max x = %d\n", dev->identifier, common->wcmMaxX); } common->wcmMaxY = xf86SetIntOption(local->options, "MaxY", common->wcmMaxY); if (common->wcmMaxY != 0) { xf86Msg(X_CONFIG, "%s: max x = %d\n", dev->identifier, common->wcmMaxY); } common->wcmMaxZ = xf86SetIntOption(local->options, "MaxZ", common->wcmMaxZ); if (common->wcmMaxZ != 0) { xf86Msg(X_CONFIG, "%s: max x = %d\n", dev->identifier, common->wcmMaxZ); } common->wcmUserResolX = xf86SetIntOption(local->options, "ResolutionX", common->wcmUserResolX); if (common->wcmUserResolX != 0) { xf86Msg(X_CONFIG, "%s: resol x = %d\n", dev->identifier, common->wcmUserResolX); } common->wcmUserResolY = xf86SetIntOption(local->options, "ResolutionY", common->wcmUserResolY); if (common->wcmUserResolY != 0) { xf86Msg(X_CONFIG, "%s: resol y = %d\n", dev->identifier, common->wcmUserResolY); } common->wcmUserResolZ = xf86SetIntOption(local->options, "ResolutionZ", common->wcmUserResolZ); if (common->wcmUserResolZ != 0) { xf86Msg(X_CONFIG, "%s: resol z = %d\n", dev->identifier, common->wcmUserResolZ); } if (xf86SetBoolOption(local->options, "ButtonsOnly", 0)) { priv->flags |= BUTTONS_ONLY_FLAG; xf86Msg(X_CONFIG, "%s: buttons only\n", dev->identifier); } for (i=0; i<16; i++) { sprintf(b, "Button%d", i+1); oldButton = priv->button[i]; priv->button[i] = xf86SetIntOption(local->options, b, priv->button[i]); if (oldButton != priv->button[i]) { xf86Msg(X_CONFIG, "%s: button%d assigned to %d\n", dev->identifier, i+1, priv->button[i]); } } { int val; val = xf86SetIntOption(local->options, "BaudRate", 0); switch(val) { case 38400: common->wcmLinkSpeed = 38400; break; case 19200: common->wcmLinkSpeed = 19200; break; case 9600: common->wcmLinkSpeed = 9600; break; default: xf86Msg(X_ERROR, "%s: Illegal speed value (must be 9600 or 19200 or 38400).", dev->identifier); break; } if (xf86Verbose) xf86Msg(X_CONFIG, "%s: serial speed %u\n", dev->identifier, val); } priv->speed = xf86SetRealOption(local->options, "Speed", DEFAULT_SPEED); if (priv->speed != DEFAULT_SPEED) { xf86Msg(X_CONFIG, "%s: speed = %.3f\n", dev->identifier, priv->speed); } priv->accel = xf86SetIntOption(local->options, "Accel", 0); if (priv->accel) xf86Msg(X_CONFIG, "%s: Accel = %d\n", dev->identifier, priv->accel); s = xf86FindOptionValue(local->options, "Twinview"); if (s) xf86Msg(X_CONFIG, "%s: Twinview = %s\n", dev->identifier, s); if (s && xf86NameCmp(s, "none") == 0) { priv->twinview = TV_NONE; priv->dscaleX = 1.0; priv->dscaleY = 1.0; priv->doffsetX = 0; priv->doffsetY = 0; } else if (s && xf86NameCmp(s, "horizontal") == 0) { priv->twinview = TV_LEFT_RIGHT; priv->dscaleX = 2.0; priv->dscaleY = 1.0; priv->doffsetX = 0; priv->doffsetY = 0; /* default resolution */ if(!priv->tvResolution[0]) { priv->tvResolution[0] = screenInfo.screens[0]->width/2; priv->tvResolution[1] = screenInfo.screens[0]->height; priv->tvResolution[2] = priv->tvResolution[0]; priv->tvResolution[3] = priv->tvResolution[1]; } } else if (s && xf86NameCmp(s, "vertical") == 0) { priv->twinview = TV_ABOVE_BELOW; priv->dscaleX = 1.0; priv->dscaleY = 2.0; priv->doffsetX = 0; priv->doffsetY = 0; /* default resolution */ if(!priv->tvResolution[0]) { priv->tvResolution[0] = screenInfo.screens[0]->width; priv->tvResolution[1] = screenInfo.screens[0]->height/2; priv->tvResolution[2] = priv->tvResolution[0]; priv->tvResolution[3] = priv->tvResolution[1]; } } else if (s) { xf86Msg(X_ERROR, "%s: invalid Twinview (should be none, vertical or horizontal). Using none.\n", dev->identifier); priv->twinview = TV_NONE; priv->dscaleX = 1.0; priv->dscaleY = 1.0; priv->doffsetX = 0; priv->doffsetY = 0; } /* 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 */ "wacom", /* 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) { DBG(1, ErrorF("xf86WcmUnplug\n")); } /* * xf86WcmPlug -- * * called when the module subsection is found in XF86Config */ static pointer xf86WcmPlug(pointer module, pointer options, int *errmaj, int *errmin) { /* The following message causes xf86cfg to puke. Commented out for now */ #if 0 xf86Msg(X_INFO, "Wacom driver level: %s\n", gWacomModule.identification+strlen("$Identification: ")); #endif xf86AddInputDriver(&WACOM, module, 0); return module; } static XF86ModuleVersionInfo xf86WcmVersionRec = { "wacom", MODULEVENDORSTRING, MODINFOSTRING1, MODINFOSTRING2, XORG_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 wacomModuleData = {&xf86WcmVersionRec, xf86WcmPlug, xf86WcmUnplug}; #endif /* XFree86LOADER */ /* * Local variables: * change-log-default-name: "~/xinput.log" * c-file-style: "bsd" * End: */ /* end of xf86Wacom.c */