/* * Copyright 2003 by orc@pell.chi.il.us (David L. Parsons) * * 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 David L. * Parsons not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. David L. Parsons makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * DAVID L. PARSONS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL DAVID L. PARSONS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE * OR PERFORMANCE OF THIS SOFTWARE. */ /* * The Cross iPen is a (discontinued) simple drawing tablet. It uses * a simple two-button pen and has a really horrible interface in that * whenever the pen is on the writable part of the pad, it spits out * events like mad and when it's not, it doesn't. Okay, so that's not * particularly bad, but the tablet doesn't send any sort of proximity * event when you either pick the pen up or it goes off the edge of * the active region. */ #include "Xos.h" #include #include #define NEED_EVENTS #include "X.h" #include "Xproto.h" #include "misc.h" #include "inputstr.h" #include "scrnintstr.h" #include "XI.h" #include "XIproto.h" #if defined(sun) && !defined(i386) #define POSIX_TTY #include #include #include #include #include "extio.h" #else #include "compiler.h" #ifdef XFree86LOADER #include "xf86_libc.h" #endif #include "xf86.h" #include "xf86Procs.h" #include "xf86_OSlib.h" #include "xf86_Config.h" #include "xf86Xinput.h" #include "atKeynames.h" #include "xf86Version.h" #endif #if !defined(sun) || defined(i386) #include "osdep.h" #include "exevents.h" #include "extnsionst.h" #include "extinit.h" #endif #if defined(__QNX__) || defined(__QNXNTO__) #define POSIX_TTY #endif /* ** Debugging macros */ #ifdef DBG #undef DBG #endif static int debug_level = 0; #if DEBUG # define DBG(lvl, f) {if ((lvl) <= debug_level) f;} #else # define DBG(lvl, f) #endif #define LPI2CPM(res) (res * 1000 / 25.4) typedef struct { char *iPenDevice; /* device file name */ int iPenX; /* previous X position */ int iPenY; /* previous Y position */ int iPenButtons; /* button settings */ int flags; /* various flags */ int Index; /* read position */ unsigned char Data[5]; /* latest event */ } iPenData, *iPenDevicePtr; #define IPENX 7000 #define IPENY 5230 /* ** Configuration data */ #define IPEN_SECTION_NAME "iPen" #define PORT 1 #define DEVICENAME 2 #define ALWAYS_CORE 3 #define DEBUG_LEVEL 4 #if !defined(sun) || defined(i386) static SymTabRec iPenTab[] = { {ENDSUBSECTION, "endsubsection"}, {PORT, "port"}, {DEVICENAME, "devicename"}, {DEBUG_LEVEL, "debug"}, {ALWAYS_CORE, "core"}, {-1, ""} }; #endif /* ** Contants and macro */ #define BUFFER_SIZE 256 /* size of reception buffer */ #define XI_NAME "IPEN" /* X device name for the stylus */ #define SYSCALL(call) while(((call) == -1) && (errno == EINTR)) /* ** External declarations */ #if defined(sun) && !defined(i386) #define ENQUEUE suneqEnqueue #else #define ENQUEUE xf86eqEnqueue extern void xf86eqEnqueue( #if NeedFunctionPrototypes xEventPtr /*e*/ #endif ); #endif extern void miPointerDeltaCursor( #if NeedFunctionPrototypes int /*dx*/, int /*dy*/, unsigned long /*time*/ #endif ); #if !defined(sun) || defined(i386) /* ** xf86iPenConfig ** Reads the iPen section from the XF86Config file */ static Bool xf86iPenConfig(LocalDevicePtr *array, int inx, int max, LexPtr val) { LocalDevicePtr dev = array[inx]; iPenDevicePtr priv = (iPenDevicePtr)(dev->private); int token; int mtoken; DBG(1, ErrorF("xf86iPenConfig\n")); while ((token = xf86GetToken(iPenTab)) != ENDSUBSECTION) { switch(token) { case DEVICENAME: if (xf86GetToken(NULL) != STRING) xf86ConfigError("Option string expected"); else { dev->name = strdup(val->str); if (xf86Verbose) ErrorF("%s iPen X device name is %s\n", XCONFIG_GIVEN, dev->name); } break; case PORT: if (xf86GetToken(NULL) != STRING) xf86ConfigError("Option string expected"); else { priv->iPenDevice = strdup(val->str); if (xf86Verbose) ErrorF("%s iPen port is %s\n", XCONFIG_GIVEN, priv->iPenDevice); } break; case DEBUG_LEVEL: if (xf86GetToken(NULL) != NUMBER) xf86ConfigError("Option number expected"); debug_level = val->num; if (xf86Verbose) { #if DEBUG ErrorF("%s iPen debug now %d\n", XCONFIG_GIVEN, debug_level); #else ErrorF("%s iPen debug not set to %d because" " debugging is not compiled\n", XCONFIG_GIVEN, debug_level); #endif } break; case ALWAYS_CORE: xf86AlwaysCore(dev, TRUE); if (xf86Verbose) ErrorF("%s iPen device always stays core pointer\n", XCONFIG_GIVEN); break; case EOF: FatalError("Unexpected EOF (missing EndSubSection)"); break; default: xf86ConfigError("iPen subsection keyword expected"); break; } } DBG(1, ErrorF("xf86iPenConfig name=%s\n", priv->iPenDevice)); return Success; } #endif /* ** xf86iPenConvert ** Convert valuators to X and Y. */ static Bool xf86iPenConvert(LocalDevicePtr local, int first, int num, int v0, int v1, int v2, int v3, int v4, int v5, int* x, int* y) { iPenDevicePtr priv = (iPenDevicePtr) local->private; if (first != 0 || num == 1) return FALSE; *x = (v0 * screenInfo.screens[0]->width) / IPENX; *y = (v1 * screenInfo.screens[0]->height) / IPENY; if (*x < 0) *x = 0; if (*y < 0) *y = 0; if (*x > screenInfo.screens[0]->width) *x = screenInfo.screens[0]->width; if (*y > screenInfo.screens[0]->height) *y = screenInfo.screens[0]->height; DBG(6, ErrorF("iPenConvert x=%d y=%d\n", *x, *y)); return TRUE; } /* ** xf86iPenReverseConvert ** Convert X and Y to valuators. */ static Bool xf86iPenReverseConvert(LocalDevicePtr local, int x, int y, int *rev) { iPenDevicePtr priv = (iPenDevicePtr) local->private; rev[0] = ((x * IPENX) / screenInfo.screens[0]->width); rev[1] = ((y * IPENX) / screenInfo.screens[0]->height); DBG(6, ErrorF("iPenReverseConvert x=%d y=%d\n", rev[0], rev[1])); return TRUE; } /* ** xf86iPenReadInput ** Reads from the iPen and posts any new events to the server. */ static void xf86iPenReadInput(LocalDevicePtr local) { iPenDevicePtr priv = (iPenDevicePtr) local->private; int len, loop; int x, y, buttons; DeviceIntPtr device; unsigned char buffer[50/*room for 10 packets*/]; DBG(7, ErrorF("xf86iPenReadInput BEGIN device=%s fd=%d\n", priv->iPenDevice, local->fd)); SYSCALL(len = read(local->fd, buffer, sizeof buffer)); DBG(7, ErrorF("xf86iPenReadInput got %d byte%s\n", len, (len==1)?"":"s")); if (len <= 0) { Error("error reading iPen device"); return; } /* Format of 5 bytes data packet for iPen Tablets Byte 1 data & 0xA0 bit 1-2 Button status bit 4 Button status change Byte 2 bit 7 Always 0 bits 6-0 = X6 - X0 Byte 3 bit 7 Always 0 bits 6-0 = X13 - X7 Byte 4 bit 7 Always 0 bits 6-0 = Y6 - Y0 Byte 5 bit 7 Always 0 bits 6-0 = Y13 - Y7 */ if (priv->Index >= 5 || priv->Index < 0) { ErrorF("xf86iPenReadInput: sequence error (%d)\n", priv->Index); priv->Index = 0; } for(loop=0; loopIndex == 0) { if ((buffer[loop] & 0xA0) != 0xA0) { DBG(6, ErrorF("iPen out of sync\n")); continue; } } priv->Data[priv->Index++] = buffer[loop]; if (priv->Index >= 5) { /* got a packet; read it and go back around for more */ priv->Index = 0; x = priv->Data[1] | (priv->Data[2] << 7); y = IPENY - (priv->Data[3] | (priv->Data[4] << 7)); buttons = (priv->Data[0]) & 0x03; device = local->dev; DBG(6, ErrorF("x=%d\ty=%d\tbuttons=%d\n", x, y, buttons)); /* coordonates are ready we can send events */ xf86PostProximityEvent(device, 1, 0, 2, x, y); if (priv->iPenX != x || priv->iPenY != y) xf86PostMotionEvent(device, 1, 0, 2, x, y); if (priv->iPenButtons != buttons) { int delta; int button; delta = buttons - priv->iPenButtons; button = (delta > 0)? delta: ((delta == 0)? priv->iPenButtons : -delta); if (priv->iPenButtons != buttons) { DBG(6, ErrorF("xf86iPenReadInput button=%d delta=%d\n", button, delta)); xf86PostButtonEvent(device, 1, button, (delta > 0), 0, 2, x, y); } priv->iPenButtons = buttons; priv->iPenX = x; priv->iPenY = y; } } } DBG(7, ErrorF("xf86iPenReadInput END device=0x%x\n", local->dev)); } /* ** xf86iPenControlProc */ static void xf86iPenControlProc(DeviceIntPtr device, PtrCtrl *ctrl) { DBG(2, ErrorF("xf86iPenControlProc\n")); } /* ** iPenModemControl() ** Poke at the modem control lines for the given port, then wait a while. */ static int iPenModemControl(fd, raise, sig, delay) { int err; struct timeval zzz; DBG(1, ErrorF("iPenModemControl(fd,%d,%d,%d)\n", raise, sig, delay)); SYSCALL(err = ioctl(fd, raise ? TIOCMBIS : TIOCMBIC, &sig)); if (err == -1) { Error(raise ? "iPen TIOCMBIS" : "iPen TIOCMBIC"); return 0; } zzz.tv_sec = 0; zzz.tv_usec = delay * 1000; SYSCALL(err = select(0, NULL, NULL, NULL, &zzz)); if (err == -1) { Error("iPen select"); return 0; } return 1; } /* ** xf86iPenOpen ** Open and initialize the tablet, as well as probe for any needed data. */ static Bool xf86iPenOpen(LocalDevicePtr local) { struct termios tty; char buffer[256]; char dbuffer[256]; int err, idx; int sig; iPenDevicePtr priv = (iPenDevicePtr)local->private; DBG(1, ErrorF("opening %s\n", priv->iPenDevice)); SYSCALL(local->fd = open(priv->iPenDevice, O_RDWR|O_NDELAY, 0)); if (local->fd == -1) { Error(priv->iPenDevice); return !Success; } DBG(2, ErrorF("%s opened as fd %d\n", priv->iPenDevice, local->fd)); #ifdef POSIX_TTY err = tcgetattr(local->fd, &tty); if (err == -1) { Error("iPen tcgetattr"); return !Success; } cfsetspeed(&tty, B19200); if (tcsetattr(local->fd, TCSANOW, &tty) == -1) { Error("iPen tcsetattr TCSANOW"); return !Success; } #else Code for someone else to write to handle OSs without POSIX tty functions #endif DBG(1, ErrorF("initializing iPen tablet\n")); if ( iPenModemControl(local->fd, 1, TIOCM_DTR|TIOCM_RTS, 500) && iPenModemControl(local->fd, 0, TIOCM_RTS, 2) && iPenModemControl(local->fd, 1, TIOCM_RTS, 20) ) { if (write(local->fd, "@C0", 3) == 3) return Success; Error("iPen write"); } return !Success; } /* ** xf86iPenOpenDevice ** Opens and initializes the device driver stuff or sumpthin. */ static int xf86iPenOpenDevice(DeviceIntPtr piPen) { LocalDevicePtr local = (LocalDevicePtr)piPen->public.devicePrivate; iPenDevicePtr priv = (iPenDevicePtr)PRIVATE(piPen); if (xf86iPenOpen(local) != Success) { if (local->fd >= 0) { SYSCALL(close(local->fd)); } local->fd = -1; } /* Set the real values */ InitValuatorAxisStruct(piPen, 0, 0, /* min val */ IPENX, /* max val */ LPI2CPM(1000), /* resolution */ 0, /* min_res */ LPI2CPM(1000)); /* max_res */ InitValuatorAxisStruct(piPen, 1, 0, /* min val */ IPENY, /* max val */ LPI2CPM(1000), /* resolution */ 0, /* min_res */ LPI2CPM(1000)); /* max_res */ return local->fd != -1; } /* ** xf86iPenProc ** Handle requests to do stuff to the driver. */ static int xf86iPenProc(DeviceIntPtr piPen, int what) { CARD8 map[25]; int nbaxes; int nbbuttons; int loop; LocalDevicePtr local = (LocalDevicePtr)piPen->public.devicePrivate; iPenDevicePtr priv = (iPenDevicePtr)PRIVATE(piPen); DBG(2, ErrorF("BEGIN xf86iPenProc dev=0x%x priv=0x%x what=%d\n", piPen, priv, what)); switch (what) { case DEVICE_INIT: DBG(1, ErrorF("xf86iPenProc piPen=0x%x what=INIT\n", piPen)); nbaxes = 2; /* X, Y */ nbbuttons = 2; for(loop=1; loop<=nbbuttons; loop++) map[loop] = loop; if (InitButtonClassDeviceStruct(piPen, nbbuttons, map) == FALSE) { ErrorF("unable to allocate Button class device\n"); return !Success; } if (InitFocusClassDeviceStruct(piPen) == FALSE) { ErrorF("unable to init Focus class device\n"); return !Success; } if (InitPtrFeedbackClassDeviceStruct(piPen, xf86iPenControlProc) == FALSE) { ErrorF("unable to init ptr feedback\n"); return !Success; } if (InitProximityClassDeviceStruct(piPen) == FALSE) { ErrorF("unable to init proximity class device\n"); return !Success; } if (InitValuatorClassDeviceStruct(piPen, nbaxes, xf86GetMotionEvents, 5, Absolute) == FALSE) { ErrorF("unable to allocate Valuator class device\n"); return !Success; } /* allocate the motion history buffer if needed */ xf86MotionHistoryAllocate(local); AssignTypeAndName(piPen, local->atom, local->name); /* open the device to gather informations */ xf86iPenOpenDevice(piPen); break; case DEVICE_ON: DBG(1, ErrorF("xf86iPenProc piPen=0x%x what=ON\n", piPen)); if ((local->fd < 0) && (!xf86iPenOpenDevice(piPen))) { return !Success; } AddEnabledDevice(local->fd); piPen->public.on = TRUE; break; case DEVICE_OFF: DBG(1, ErrorF("xf86iPenProc piPen=0x%x what=%s\n", piPen, (what == DEVICE_CLOSE) ? "CLOSE" : "OFF")); if (local->fd >= 0) RemoveEnabledDevice(local->fd); piPen->public.on = FALSE; break; case DEVICE_CLOSE: DBG(1, ErrorF("xf86iPenProc piPen=0x%x what=%s\n", piPen, (what == DEVICE_CLOSE) ? "CLOSE" : "OFF")); SYSCALL(close(local->fd)); local->fd = -1; break; default: ErrorF("unsupported mode=%d\n", what); return !Success; break; } DBG(2, ErrorF("END xf86iPenProc Success what=%d dev=0x%x priv=0x%x\n", what, piPen, priv)); return Success; } /* ** xf86iPenClose ** It... Uh... Closes the physical device? */ static void xf86iPenClose(LocalDevicePtr local) { if (local->fd >= 0) { SYSCALL(close(local->fd)); } local->fd = -1; } /* ** xf86iPenChangeControl ** When I figure out what it does, it will do it. */ static int xf86iPenChangeControl(LocalDevicePtr local, xDeviceCtl *control) { return Success; } /* ** xf86iPenFalse ** returns failure */ static int xf86iPenFalse() { return !Success; } /* ** xf86iPenAllocate ** Allocates the device structures for the iPen. */ static LocalDevicePtr xf86iPenAllocate() { LocalDevicePtr local = (LocalDevicePtr)xalloc(sizeof(LocalDeviceRec)); iPenDevicePtr priv = (iPenDevicePtr)xalloc(sizeof(iPenData)); #if defined (sun) && !defined(i386) char *dev_name = getenv("IPEN_DEV"); #endif local->name = XI_NAME; local->type_name = "iPen"; local->flags = 0; /*XI86_NO_OPEN_ON_INIT;*/ #if !defined(sun) || defined(i386) local->device_config = xf86iPenConfig; #endif local->device_control = xf86iPenProc; local->read_input = xf86iPenReadInput; local->control_proc = xf86iPenChangeControl; local->close_proc = xf86iPenClose; local->switch_mode = xf86iPenFalse; local->conversion_proc = xf86iPenConvert; local->reverse_conversion_proc = xf86iPenReverseConvert; local->fd = -1; local->atom = 0; local->dev = NULL; memset(priv, 0, sizeof *priv); local->private = priv; local->private_flags = 0; local->history_size = 0; priv->iPenDevice = ""; /* device file name */ #if defined(sun) && !defined(i386) if (def_name) { priv->iPenDevice = (char *)xalloc(strlen(dev_name) + 1); strcpy(priv->iPenDevice, device_name); ErrorF("xf86iPenOpen port changed to '%s'\n", priv->iPenDevice); } #endif priv->iPenX = -1; /* previous X position */ priv->iPenY = -1; /* previous Y position */ return local; } /* ** iPen device association ** Device section name and allocation function. */ DeviceAssocRec iPen_assoc = { IPEN_SECTION_NAME, /* config_section_name */ xf86iPenAllocate /* device_allocate */ }; #ifdef DYNAMIC_MODULE /* ** init_module ** Entry point for dynamic module. */ int #ifndef DLSYM_BUG init_module(unsigned long server_version) #else init_xf86iPen(unsigned long server_version) #endif { xf86AddDeviceAssoc(&iPen_assoc); if (server_version != XF86_VERSION_CURRENT) { ErrorF("Warning: iPen module compiled for version %s\n", XF86_VERSION); return 0; } else return 1; } #endif #ifdef XFree86LOADER /* * Entry point for the loader code */ XF86ModuleVersionInfo xf86iPenVersion = { "xf86iPen", MODULEVENDORSTRING, MODINFOSTRING1, MODINFOSTRING2, XF86_VERSION_CURRENT, 0x00010000, {0,0,0,0} }; void xf86iPenModuleInit(data, magic) pointer *data; INT32 *magic; { static int cnt = 0; switch (cnt) { case 0: *magic = MAGIC_VERSION; *data = &xf86iPenVersion; cnt++; break; case 1: *magic = MAGIC_ADD_XINPUT_DEVICE; *data = &iPen_assoc; cnt++; break; default: *magic = MAGIC_DONE; *data = NULL; break; } } #endif /* end of xf86iPen.c */