/* * mice.c - mouse definitions for gpm-Linux * * Copyright (C) 1993 Andrew Haylett * Copyright (C) 1994-2000 Alessandro Rubini * Copyright (C) 1998,1999 Ian Zimmerman * Copyright (C) 2001-2008 Nico Schottelius * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ********/ /* * This file is part of the mouse server. The information herein * is kept aside from the rest of the server to ease fixing mouse-type * issues. Each mouse type is expected to fill the `buttons', `dx' and `dy' * fields of the Gpm_Event structure and nothing more. * * Absolute-pointing devices (support by Marc Meis), are expecting to * fit `x' and `y' as well. Unfortunately, to do it the window size must * be accessed. The global variable "win" is available for that use. * * The `data' parameter points to a byte-array with event data, as read * by the mouse device. The mouse device should return a fixed number of * bytes to signal an event, and that exact number is read by the server * before calling one of these functions. * * The conversion function defined here should return 0 on success and -1 * on failure. * * Refer to the definition of Gpm_Type to probe further. */ #include #include #include #include #include #include #include #include #include #include #include /* stat() */ #include /* select() */ #include /* MAJOR */ #include /* JOYSTICK */ #ifdef HAVE_LINUX_JOYSTICK_H #include #endif /* EV DEVICE */ #ifdef HAVE_LINUX_INPUT_H #include #endif /* HAVE_LINUX_INPUT_H */ #include "headers/gpmInt.h" #include "headers/daemon.h" #include "headers/twiddler.h" #include "headers/synaptics.h" #include "headers/message.h" /*========================================================================*/ /* Parsing argv: helper dats struct function (should they get elsewhere?) */ /*========================================================================*/ enum argv_type { ARGV_BOOL = 1, ARGV_INT, /* "%i" */ ARGV_DEC, /* "%d" */ ARGV_STRING, /* other types must be added */ ARGV_END = 0 }; typedef struct argv_helper { char *name; enum argv_type type; union u { int *iptr; /* used for int and bool arguments */ char **sptr; /* used for string arguments, by strdup()ing the value */ } u; int value; /* used for boolean arguments */ } argv_helper; static int parse_argv(argv_helper *info, int argc, char **argv) { int i, j = 0, errors = 0; long l; argv_helper *p; char *s, *t; int base = 0; /* for strtol */ for (i=1; itype != ARGV_END; p++) { j = strlen(p->name); if (strncmp(p->name, argv[i], j)) continue; if (isalnum(argv[i][j])) continue; break; } if (p->type == ARGV_END) { /* not found */ fprintf(stderr, "%s: Uknown option \"%s\" for pointer \"%s\"\n", option.progname, argv[i], argv[0]); errors++; continue; } /* Found. Look for trailing stuff, if any */ s = argv[i]+j; while (*s && isspace(*s)) s++; /* skip spaces */ if (*s == '=') s++; /* skip equal */ while (*s && isspace(*s)) s++; /* skip other spaces */ /* Now parse what s is */ switch(p->type) { case ARGV_BOOL: if (*s) { gpm_report(GPM_PR_ERR,GPM_MESS_OPTION_NO_ARG, option.progname,p->name,s); errors++; } *(p->u.iptr) = p->value; break; case ARGV_DEC: base = 10; /* and fall through */ case ARGV_INT: l = strtol(s, &t, base); if (*t) { gpm_report(GPM_PR_ERR,GPM_MESS_INVALID_ARG, option.progname, s, p->name); errors++; break; } *(p->u.iptr) = (int)l; break; case ARGV_STRING: *(p->u.sptr) = strdup(s); break; case ARGV_END: /* let's please "-Wall" */ break; } } /* for i in argc */ if (errors) gpm_report(GPM_PR_ERR,GPM_MESS_CONT_WITH_ERR, option.progname); return errors; } /*========================================================================*/ /* Provide a common error engine by parsing with an empty option-set */ /*========================================================================*/ static volatile int check_no_argv(int argc, char **argv) { static argv_helper optioninfo[] = { {"", ARGV_END} }; return parse_argv(optioninfo, argc, argv); } /*========================================================================*/ /* Parse the "old" -o options */ /*========================================================================*/ static int option_modem_lines(int fd, int argc, char **argv) { static unsigned int err, lines, reallines; static argv_helper optioninfo[] = { {"dtr", ARGV_BOOL, u: {iptr: &lines}, value: TIOCM_DTR}, {"rts", ARGV_BOOL, u: {iptr: &lines}, value: TIOCM_RTS}, {"both", ARGV_BOOL, u: {iptr: &lines}, value: TIOCM_DTR | TIOCM_RTS}, {"", ARGV_END} }; if (argc<2) return 0; if (argc > 2) { gpm_report(GPM_PR_ERR,GPM_MESS_TOO_MANY_OPTS,option.progname, argv[0]); errno = EINVAL; /* used by gpm_oops(), if the caller reports failure */ return -1; } err = parse_argv(optioninfo, argc, argv); if(err) return 0; /* a message has been printed, but go on as good */ /* ok, move the lines */ ioctl(fd, TIOCMGET, &reallines); reallines &= ~lines; ioctl(fd, TIOCMSET, &reallines); return 0; } /*========================================================================*/ /* real absolute coordinates for absolute devices, not very clean */ /*========================================================================*/ #define REALPOS_MAX 16383 /* min 0 max=16383, but due to change. */ int realposx=-1, realposy=-1; /*========================================================================*/ /* * When repeating, it is important not to try to repeat more bits of dx and * dy than the protocol can handle. Otherwise, you may end up repeating the * low bits of a large value, which causes erratic motion. */ /*========================================================================*/ static int limit_delta(int delta, int min, int max) { return delta > max ? max : delta < min ? min : delta; } /*========================================================================*/ /* * Ok, here we are: first, provide the functions; initialization is later. * The return value is the number of unprocessed bytes */ /*========================================================================*/ #ifdef HAVE_LINUX_INPUT_H static int M_evdev (Gpm_Event * state, unsigned char *data) { struct input_event thisevent; (void) memcpy (&thisevent, data, sizeof (struct input_event)); if (thisevent.type == EV_REL) { if (thisevent.code == REL_X) state->dx = (signed char) thisevent.value; else if (thisevent.code == REL_Y) state->dy = (signed char) thisevent.value; } else if (thisevent.type == EV_KEY) { switch(thisevent.code) { case BTN_LEFT: state->buttons ^= GPM_B_LEFT; break; case BTN_MIDDLE: state->buttons ^= GPM_B_MIDDLE; break; case BTN_RIGHT: state->buttons ^= GPM_B_RIGHT; break; case BTN_SIDE: state->buttons ^= GPM_B_MIDDLE; break; } } return 0; } #endif /* HAVE_LINUX_INPUT_H */ static int M_ms(Gpm_Event *state, unsigned char *data) { /* * some devices report a change of middle-button state by * repeating the current button state (patch by Mark Lord) */ static unsigned char prev=0; if (data[0] == 0x40 && !(prev|data[1]|data[2])) state->buttons = GPM_B_MIDDLE; /* third button on MS compatible mouse */ else state->buttons= ((data[0] & 0x20) >> 3) | ((data[0] & 0x10) >> 4); prev = state->buttons; state->dx= (signed char)(((data[0] & 0x03) << 6) | (data[1] & 0x3F)); state->dy= (signed char)(((data[0] & 0x0C) << 4) | (data[2] & 0x3F)); return 0; } static int M_ms_plus(Gpm_Event *state, unsigned char *data) { static unsigned char prev=0; state->buttons= ((data[0] & 0x20) >> 3) | ((data[0] & 0x10) >> 4); state->dx= (signed char)(((data[0] & 0x03) << 6) | (data[1] & 0x3F)); state->dy= (signed char)(((data[0] & 0x0C) << 4) | (data[2] & 0x3F)); /* Allow motion *and* button change (Michael Plass) */ if((state->dx==0) &&(state->dy==0) && (state->buttons==(prev&~GPM_B_MIDDLE))) state->buttons = prev^GPM_B_MIDDLE; /* no move or change: toggle middle */ else state->buttons |= prev&GPM_B_MIDDLE; /* change: preserve middle */ prev=state->buttons; return 0; } static int M_ms_plus_lr(Gpm_Event *state, unsigned char *data) { /* * Same as M_ms_plus but with an addition by Edmund GRIMLEY EVANS */ static unsigned char prev=0; state->buttons= ((data[0] & 0x20) >> 3) | ((data[0] & 0x10) >> 4); state->dx= (signed char)(((data[0] & 0x03) << 6) | (data[1] & 0x3F)); state->dy= (signed char)(((data[0] & 0x0C) << 4) | (data[2] & 0x3F)); /* Allow motion *and* button change (Michael Plass) */ if((state->dx==0)&& (state->dy==0) && (state->buttons==(prev&~GPM_B_MIDDLE))) state->buttons = prev^GPM_B_MIDDLE; /* no move or change: toggle middle */ else state->buttons |= prev&GPM_B_MIDDLE;/* change: preserve middle */ /* Allow the user to reset state of middle button by pressing the other two buttons at once (Edmund GRIMLEY EVANS) */ if (!((~state->buttons)&(GPM_B_LEFT|GPM_B_RIGHT)) && ((~prev)&(GPM_B_LEFT|GPM_B_RIGHT))) state->buttons &= ~GPM_B_MIDDLE; prev=state->buttons; return 0; } /* Summagraphics/Genius/Acecad tablet support absolute mode*/ /* Summagraphics MM-Series format*/ /* (Frank Holtz) hof@bigfoot.de Tue Feb 23 21:04:09 MET 1999 */ int SUMMA_BORDER=100; int summamaxx,summamaxy; char summaid=-1; static int M_summa(Gpm_Event *state, unsigned char *data) { int x, y; x = ((data[2]<<7) | data[1])-SUMMA_BORDER; if (x<0) x=0; if (x>summamaxx) x=summamaxx; state->x = (x * win.ws_col / summamaxx); realposx = (x * 16383 / summamaxx); y = ((data[4]<<7) | data[3])-SUMMA_BORDER; if (y<0) y=0; if (y>summamaxy) y=summamaxy; state->y = 1 + y * (win.ws_row-1)/summamaxy; realposy = y * 16383 / summamaxy; state->buttons= !!(data[0]&1) * GPM_B_LEFT + !!(data[0]&2) * GPM_B_RIGHT + !!(data[0]&4) * GPM_B_MIDDLE; return 0; } /* ps2 */ static int R_ps2(Gpm_Event *state, int fd) { signed char buffer[3]; buffer[0]=((state->buttons & GPM_B_LEFT ) > 0)*1 + ((state->buttons & GPM_B_RIGHT ) > 0)*2 + ((state->buttons & GPM_B_MIDDLE) > 0)*4; buffer[0] |= 8 + ((state->dx < 0) ? 0x10 : 0) + ((state->dy > 0) ? 0x20 : 0); buffer[1] = limit_delta(state->dx, -128, 127); buffer[2] = limit_delta(-state->dy, -128, 127); return write(fd,buffer,3); } /* Thu Jan 28 20:54:47 MET 1999 hof@hof-berlin.de SummaSketch reportformat */ static int R_summa(Gpm_Event *state, int fd) { signed char buffer[5]; static int x,y; if (realposx==-1) { /* real absolute device? */ x=x+(state->dx*25); if (x<0) x=0; if (x>16383) x=16383; y=y+(state->dy*25); if (y<0) y=0; if (y>16383) y=16383; } else { /* yes */ x=realposx; y=realposy; } buffer[0]=0x98+((state->buttons&GPM_B_LEFT)>0?1:0)+ ((state->buttons&GPM_B_MIDDLE)>0?2:0)+ ((state->buttons&GPM_B_RIGHT)>0?3:0); buffer[1]=x & 0x7f; /* X0-6*/ buffer[2]=(x >> 7) & 0x7f; /* X7-12*/ buffer[3]=y & 0x7f; /* Y0-6*/ buffer[4]=(y >> 7) & 0x7f; /* Y7-12*/ return write(fd,buffer,5); } /* 'Genitizer' (kw@dtek.chalmers.se 11/12/97) */ static int M_geni(Gpm_Event *state, unsigned char *data) { /* this is a little confusing. If we use the stylus, we * have three buttons (tip, lower, upper), and if * we use the puck we have four buttons. (And the * protocol is a little mangled if several of the buttons * on the puck are pressed simultaneously. * I don't use the puck, hence I try to decode three buttons * only. tip = left, lower = middle, upper = right */ state->buttons = (data[0] & 0x01)<<2 | (data[0] & 0x02) | (data[0] & 0x04)>>2; state->dx = ((data[1] & 0x3f) ) * ((data[0] & 0x10)?1:-1); state->dy = ((data[2] & 0x3f) ) * ((data[0] & 0x8)?-1:1); return 0; } /* m$ 'Intellimouse' (steveb 20/7/97) */ static int M_ms3(Gpm_Event *state, unsigned char *data) { state->wdx = state->wdy = 0; state->buttons= ((data[0] & 0x20) >> 3) /* left */ | ((data[3] & 0x10) >> 3) /* middle */ | ((data[0] & 0x10) >> 4) /* right */ | (((data[3] & 0x0f) == 0x0f) * GPM_B_UP) /* wheel up */ | (((data[3] & 0x0f) == 0x01) * GPM_B_DOWN); /* wheel down */ state->dx = (signed char) (((data[0] & 0x03) << 6) | (data[1] & 0x3F)); state->dy = (signed char) (((data[0] & 0x0C) << 4) | (data[2] & 0x3F)); switch (data[3] & 0x0f) { case 0x0e: state->wdx = +1; break; case 0x02: state->wdx = -1; break; case 0x0f: state->wdy = +1; break; case 0x01: state->wdy = -1; break; } return 0; } static int R_ms3(Gpm_Event *state, int fd) { int dx, dy; char buf[4] = {0, 0, 0, 0}; buf[0] |= 0x40; if (state->buttons & GPM_B_LEFT) buf[0] |= 0x20; if (state->buttons & GPM_B_MIDDLE) buf[3] |= 0x10; if (state->buttons & GPM_B_RIGHT) buf[0] |= 0x10; if (state->buttons & GPM_B_UP) buf[3] |= 0x0f; if (state->buttons & GPM_B_DOWN) buf[3] |= 0x01; dx = limit_delta(state->dx, -128, 127); buf[1] = dx & ~0xC0; buf[0] |= (dx & 0xC0) >> 6; dy = limit_delta(state->dy, -128, 127); buf[2] = dy & ~0xC0; buf[0] |= (dy & 0xC0) >> 4; /* wheel */ if (state->wdy > 0) buf[3] |= 0x0f; else if (state->wdy < 0) buf[3] |= 0x01; return write(fd,buf,4); } /* M_brw is a variant of m$ 'Intellimouse' the middle button is different */ static int M_brw(Gpm_Event *state, unsigned char *data) { state->buttons= ((data[0] & 0x20) >> 3) /* left */ | ((data[3] & 0x20) >> 4) /* middle */ | ((data[0] & 0x10) >> 4); /* right */ state->dx= (signed char)(((data[0] & 0x03) << 6) | (data[1] & 0x3F)); state->dy= (signed char)(((data[0] & 0x0C) << 4) | (data[2] & 0x3F)); if (((data[0]&0xC0) != 0x40)|| ((data[1]&0xC0) != 0x00)|| ((data[2]&0xC0) != 0x00)|| ((data[3]&0xC0) != 0x00)) { gpm_report(GPM_PR_DEBUG,GPM_MESS_SKIP_DATAP,data[0],data[1],data[2],data[3]); return -1; } /* wheel (dz) is (data[3] & 0x0f) */ /* where is the side button? I can sort of detect it at 9600 baud */ /* Note this mouse is very noisy */ return 0; } static int M_bare(Gpm_Event *state, unsigned char *data) { /* a bare ms protocol */ state->buttons= ((data[0] & 0x20) >> 3) | ((data[0] & 0x10) >> 4); state->dx= (signed char)(((data[0] & 0x03) << 6) | (data[1] & 0x3F)); state->dy= (signed char)(((data[0] & 0x0C) << 4) | (data[2] & 0x3F)); return 0; } static int M_sun(Gpm_Event *state, unsigned char *data) { state->buttons= (~data[0]) & 0x07; state->dx= (signed char)(data[1]); state->dy= -(signed char)(data[2]); return 0; } static int R_sun(Gpm_Event *state, int fd) { signed char buffer[3]; buffer[0]= (state->buttons ^ 0x07) | 0x80; buffer[1]= state->dx; buffer[2]= -(state->dy); return write(fd,buffer,3); } static int M_msc(Gpm_Event *state, unsigned char *data) { state->buttons= (~data[0]) & 0x07; state->dx= (signed char)(data[1]) + (signed char)(data[3]); state->dy= -((signed char)(data[2]) + (signed char)(data[4])); return 0; } /* itz Mon Jan 11 23:51:38 PST 1999 this code moved here from gpm.c */ /* (processMouse) */ static int R_msc(Gpm_Event *state, int fd) { signed char buffer[5]; int dx, dy; /* sluggish... */ buffer[0]=(state->buttons ^ 0x07) | 0x80; dx = limit_delta(state->dx, -256, 254); buffer[3] = state->dx - (buffer[1] = state->dx/2); /* Markus */ dy = limit_delta(state->dy, -256, 254); buffer[4] = -state->dy - (buffer[2] = -state->dy/2); return write(fd,buffer,5); } static int R_imps2(Gpm_Event *state, int fd) { signed char buffer[4]; int dx, dy; dx = limit_delta(state->dx, -256, 255); dy = limit_delta(state->dy, -256, 255); buffer[0] = 8 | (state->buttons & GPM_B_LEFT ? 1 : 0) | (state->buttons & GPM_B_MIDDLE ? 4 : 0) | (state->buttons & GPM_B_RIGHT ? 2 : 0) | (dx < 0 ? 0x10 : 0) | (dy > 0 ? 0x20 : 0); buffer[1] = dx & 0xFF; buffer[2] = (-dy) & 0xFF; if (state->wdy > 0) buffer[3] = 0xff; if (state->wdy < 0) buffer[3] = 0x01; if (state->wdx > 0) buffer[3] = 0xfe; if (state->wdx < 0) buffer[3] = 0x02; return write(fd,buffer,4); } static int M_logimsc(Gpm_Event *state, unsigned char *data) /* same as msc */ { state->buttons= (~data[0]) & 0x07; state->dx= (signed char)(data[1]) + (signed char)(data[3]); state->dy= -((signed char)(data[2]) + (signed char)(data[4])); return 0; } static int M_mm(Gpm_Event *state, unsigned char *data) { state->buttons= data[0] & 0x07; state->dx= (data[0] & 0x10) ? data[1] : - data[1]; state->dy= (data[0] & 0x08) ? - data[2] : data[2]; return 0; } static int M_logi(Gpm_Event *state, unsigned char *data) /* equal to mm */ { state->buttons= data[0] & 0x07; state->dx= (data[0] & 0x10) ? data[1] : - data[1]; state->dy= (data[0] & 0x08) ? - data[2] : data[2]; return 0; } static int M_bm(Gpm_Event *state, unsigned char *data) /* equal to sun */ { state->buttons= (~data[0]) & 0x07; state->dx= (signed char)data[1]; state->dy= -(signed char)data[2]; return 0; } static int M_ps2(Gpm_Event *state, unsigned char *data) { static int tap_active=0; /* there exist glidepoint ps2 mice */ state->buttons= !!(data[0]&1) * GPM_B_LEFT + !!(data[0]&2) * GPM_B_RIGHT + !!(data[0]&4) * GPM_B_MIDDLE; if (data[0]==0 && (which_mouse->opt_glidepoint_tap)) /* by default this is false */ state->buttons = tap_active = (which_mouse->opt_glidepoint_tap); else if (tap_active) { if (data[0]==8) state->buttons = tap_active = 0; else state->buttons = tap_active; } /* Some PS/2 mice send reports with negative bit set in data[0] * and zero for movement. I think this is a bug in the mouse, but * working around it only causes artifacts when the actual report is -256; * they'll be treated as zero. This should be rare if the mouse sampling * rate is set to a reasonable value; the default of 100 Hz is plenty. * (Stephen Tell) */ if(data[1] != 0) state->dx= (data[0] & 0x10) ? data[1]-256 : data[1]; else state->dx = 0; if(data[2] != 0) state->dy= -((data[0] & 0x20) ? data[2]-256 : data[2]); else state->dy = 0; return 0; } static int M_imps2(Gpm_Event *state, unsigned char *data) { static int tap_active=0; /* there exist glidepoint ps2 mice */ state->wdx = state->wdy = 0; /* Clear them.. */ state->dx = state->dy = state->wdx = state->wdy = 0; state->buttons= ((data[0] & 1) << 2) /* left */ | ((data[0] & 6) >> 1); /* middle and right */ if (data[0]==0 && (which_mouse->opt_glidepoint_tap)) // by default this is false state->buttons = tap_active = (which_mouse->opt_glidepoint_tap); else if (tap_active) { if (data[0]==8) state->buttons = tap_active = 0; else state->buttons = tap_active; } /* Standard movement.. */ state->dx = (data[0] & 0x10) ? data[1] - 256 : data[1]; state->dy = (data[0] & 0x20) ? -(data[2] - 256) : -data[2]; /* The wheels.. */ unsigned char wheel = data[3] & 0x0f; if (wheel > 0) { // use the event type GPM_MOVE rather than GPM_DOWN for wheel movement // to avoid single/double/triple click processing: switch (wheel) { /* rodney 13/mar/2008 * The use of GPM_B_UP / GPM_B_DOWN is very unclear; * only mouse type ms3 uses these * For this mouse, we only support the relative movement * i.e. no button is set (same as mouse movement), wdy changes +/- * according to wheel movement (+ for rolling away from user) * wdx (horizontal scroll) is for a second wheel. They do exist! */ case 0x0f: state->wdy = +1; break; case 0x01: state->wdy = -1; break; case 0x0e: state->wdx = +1; break; case 0x02: state->wdx = -1; break; } } return 0; } static int M_netmouse(Gpm_Event *state, unsigned char *data) { /* Avoid these beasts if you can. They connect to normal PS/2 port, * but their protocol is one byte longer... So if you have notebook * (like me) with internal PS/2 mouse, it will not work * together. They have four buttons, but two middle buttons can not * be pressed simultaneously, and two middle buttons do not send * 'up' events (however, they autorepeat...) * Still, you might want to run this mouse in plain PS/2 mode - * where it behaves correctly except that middle 2 buttons do * nothing. * Protocol is * 3 bytes like normal PS/2 * 4th byte: 0xff button 'down', 0x01 button 'up' * [this is so braindamaged that it *must* be some kind of * compatibility glue...] * Pavel Machek */ state->buttons= !!(data[0]&1) * GPM_B_LEFT + !!(data[0]&2) * GPM_B_RIGHT + !!(data[3]) * GPM_B_MIDDLE; if(data[1] != 0) state->dx= (data[0] & 0x10) ? data[1]-256 : data[1]; else state->dx = 0; if(data[2] != 0) state->dy= -((data[0] & 0x20) ? data[2]-256 : data[2]); else state->dy = 0; return 0; } /* standard ps2 */ static Gpm_Type *I_ps2(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { static unsigned char s[] = { 246, 230, 244, 243, 100, 232, 3, }; write (fd, s, sizeof (s)); usleep (30000); tcflush (fd, TCIFLUSH); return type; } static Gpm_Type *I_netmouse(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { unsigned char magic[6] = { 0xe8, 0x03, 0xe6, 0xe6, 0xe6, 0xe9 }; int i; if (check_no_argv(argc, argv)) return NULL; for (i=0; i<6; i++) { unsigned char c = 0; write( fd, magic+i, 1 ); read( fd, &c, 1 ); if (c != 0xfa) { gpm_report(GPM_PR_ERR,GPM_MESS_NETM_NO_ACK,c); return NULL; } } { unsigned char rep[3] = { 0, 0, 0 }; read( fd, rep, 1 ); read( fd, rep+1, 1 ); read( fd, rep+2, 1 ); if (rep[0] || (rep[1] != 0x33) || (rep[2] != 0x55)) { gpm_report(GPM_PR_ERR,GPM_MESS_NETM_INV_MAGIC, rep[0], rep[1], rep[2]); return NULL; } } return type; } #define GPM_B_BOTH (GPM_B_LEFT|GPM_B_RIGHT) static int M_mman(Gpm_Event *state, unsigned char *data) { /* * the damned MouseMan has 3/4 bytes packets. The extra byte * is only there if the middle button is active. * I get the extra byte as a packet with magic numbers in it. * and then switch to 4-byte mode. */ static unsigned char prev=0; static Gpm_Type *mytype=mice; /* it is the first */ unsigned char b = (*data>>4); if(data[1]==GPM_EXTRA_MAGIC_1 && data[2]==GPM_EXTRA_MAGIC_2) { /* got unexpected fourth byte */ if (b > 0x3) return -1; /* just a sanity check */ //if ((b=(*data>>4)) > 0x3) return -1; /* just a sanity check */ state->dx=state->dy=0; mytype->packetlen=4; mytype->getextra=0; } else { /* got 3/4, as expected */ /* motion is independent of packetlen... */ state->dx= (signed char)(((data[0] & 0x03) << 6) | (data[1] & 0x3F)); state->dy= (signed char)(((data[0] & 0x0C) << 4) | (data[2] & 0x3F)); prev= ((data[0] & 0x20) >> 3) | ((data[0] & 0x10) >> 4); if (mytype->packetlen==4) b=data[3]>>4; } if(mytype->packetlen==4) { if(b == 0) { mytype->packetlen=3; mytype->getextra=1; } else { if (b & 0x2) prev |= GPM_B_MIDDLE; if (b & 0x1) prev |= (which_mouse->opt_glidepoint_tap); } } state->buttons=prev; /* This "chord-middle" behaviour was reported by David A. van Leeuwen */ if (((prev^state->buttons) & GPM_B_BOTH)==GPM_B_BOTH ) state->buttons = state->buttons ? GPM_B_MIDDLE : 0; prev=state->buttons; return 0; } /* * Wacom Tablets with pen and mouse: * Relative-Mode And Absolute-Mode; * Stefan Runkel 01/2000 , * Mike Pioskowik 01/2000 */ /* for relative and absolute : */ int WacomModell =-1; /* -1 means "dont know" */ int WacomAbsoluteWanted=0; /* Tell Driver if Relative or Absolute */ int wmaxx, wmaxy; char upmbuf[25]; /* needed only for macro buttons of ultrapad */ /* Data for Wacom Modell Identification */ /* (MaxX, MaxY are for Modells which do not answer resolution requests */ struct WC_MODELL{ char name[15]; char magic[3]; int maxX; int maxY; int border; int treshold; } wcmodell[] = { /* ModellName Magic MaxX MaxY Border Tresh */ { "UltraPad" , "UD", 0, 0, 250, 20 }, /* { "Intuos" , "GD", 0, 0, 0, 20 }, not supported */ { "PenPartner", "CT", 0, 0, 0, 20 }, { "Graphire" , "ET", 5103, 3711, 0, 20 } }; #define IsA(m) ((WacomModell==(-1))? 0:!strcmp(#m,wcmodell[WacomModell].name)) static int M_wacom(Gpm_Event *state, unsigned char *data) { static int ox=-1, oy; int x, y; int macro=0; /* macro buttons from tablet */ /* Bit [0]&64 seems to have different meanings - * graphire: inside of active area; * Ultrapad: Pen in proximity (means: pen detected) * Penpartner: no idea... * this may be of worth sometimes, but for now, we can say, that * if the graphire tells us that we are in the active area, we can tell * also that the graphire has the pen in proximity. */ if (!(data[0]&64)) { /* Tool not in proximity or out of active area */ /* handle Ultrapad macro buttons, if we get a packet without * proximity bit but with the buttonflag set, we know that we have * a macro event */ if (IsA(UltraPad)) { if (data[0]&8) { /* here: macro button has been pressed */ if (data[3]&8) macro=(data[6]); if (data[3]&16) macro=(data[6])+12; if (data[3]&32) macro=(data[6])+24; /* rom-version >= 1.3 */ state->modifiers=macro; /* Here we simulate the middle mousebutton */ /* with ultrapad Eprom Version 1.2 */ /* WHY IS THE FOLLOWING CODE DISABLE ? FIXME gpm_report(GPM_PR_INFO,GPM_MESS_WACOM_MACRO, macro); if (macro==12) state->buttons = GPM_B_MIDDLE; */ } /* end if macrobutton pressed */ } /* end if ultrapad */ if (!IsA(UltraPad)){ /* Tool out of active area */ ox=-1; state->buttons=0; state->dx=state->dy=0; } return 0; /* nothing more to do so leave */ } /* end if Tool out of active area*/ x = (((data[0] & 0x3) << 14) + (data[1] << 7) + data[2]); y = (((data[3] & 0x3) << 14) + (data[4] << 7) + data[5]); if (WacomAbsoluteWanted) { /* Absolute Mode */ if (x>wmaxx) x=wmaxx; if (x<0) x=0; if (y>wmaxy) y=wmaxy; if (y<0) y=0; state->x = (x * win.ws_col / wmaxx); state->y = (y * win.ws_row / wmaxy); realposx = (x / wmaxx); /* this two lines come from the summa driver. */ realposy = (y / wmaxy); /* they seem to be buggy (always give zero). */ } else { /* Relative Mode */ /* Treshold; if greather then treat tool as first time in proximity */ if( abs(x-ox)>(wmaxx/wcmodell[WacomModell].treshold) || abs(y-oy)>(wmaxy/wcmodell[WacomModell].treshold) ) ox=x; oy=y; state->dx= (x-ox) / (wmaxx / win.ws_col / wcmodell[WacomModell].treshold); state->dy= (y-oy) / (wmaxy / win.ws_row / wcmodell[WacomModell].treshold); } ox=x; oy=y; state->buttons= /* for Ultra-Pad and graphire */ !!(data[3]&8) * GPM_B_LEFT + !!(data[3]&16) * GPM_B_RIGHT + !!(data[3]&32) * GPM_B_MIDDLE; /* UD: rom-version >=1.3 */ return 0; } /* Calcomp UltraSlate tablet (John Anderson) * modeled after Wacom and NCR drivers (thanks, guys) * Note: I don't know how to get the tablet size from the tablet yet, so * these defines will have to do for now. */ #define CAL_LIMIT_BRK 255 #define CAL_X_MIN 0x40 #define CAL_X_MAX 0x1340 #define CAL_X_SIZE (CAL_X_MAX - CAL_X_MIN) #define CAL_Y_MIN 0x40 #define CAL_Y_MAX 0xF40 #define CAL_Y_SIZE (CAL_Y_MAX - CAL_Y_MIN) static int M_calus(Gpm_Event *state, unsigned char *data) { int x, y; x = ((data[1] & 0x3F)<<7) | (data[2] & 0x7F); y = ((data[4] & 0x1F)<<7) | (data[5] & 0x7F); state->buttons = GPM_B_LEFT * ((data[0]>>2) & 1) + GPM_B_MIDDLE * ((data[0]>>3) & 1) + GPM_B_RIGHT * ((data[0]>>4) & 1); state->dx = 0; state->dy = 0; state->x = x < CAL_X_MIN ? 0 : x > CAL_X_MAX ? win.ws_col+1 : (long)(x-CAL_X_MIN) * (long)(win.ws_col-1) / CAL_X_SIZE+2; state->y = y < CAL_Y_MIN ? win.ws_row + 1 : y > CAL_Y_MAX ? 0 : (long)(CAL_Y_MAX-y) * (long)win.ws_row / CAL_Y_SIZE + 1; realposx = x < CAL_X_MIN ? 0 : x > CAL_X_MAX ? 16384 : (long)(x-CAL_X_MIN) * (long)(16382) / CAL_X_SIZE+2; realposy = y < CAL_Y_MIN ? 16384 : y > CAL_Y_MAX ? 0 : (long)(CAL_Y_MAX-y) * (long)16383 / CAL_Y_SIZE + 1; return 0; } static int M_calus_rel(Gpm_Event *state, unsigned char *data) { static int ox=-1, oy; int x, y; x = ((data[1] & 0x3F)<<7) | (data[2] & 0x7F); y = ((data[4] & 0x1F)<<7) | (data[5] & 0x7F); if (ox==-1 || abs(x-ox)>CAL_LIMIT_BRK || abs(y-oy)>CAL_LIMIT_BRK) { ox=x; oy=y; } state->buttons = GPM_B_LEFT * ((data[0]>>2) & 1) + GPM_B_MIDDLE * ((data[0]>>3) & 1) + GPM_B_RIGHT * ((data[0]>>4) & 1); state->dx = (x-ox)/5; state->dy = (oy-y)/5; ox=x; oy=y; return 0; } /* ncr pen support (Marc Meis) */ #define NCR_LEFT_X 40 #define NCR_RIGHT_X 2000 #define NCR_BOTTOM_Y 25 #define NCR_TOP_Y 1490 #define NCR_DELTA_X (NCR_RIGHT_X - NCR_LEFT_X) #define NCR_DELTA_Y (NCR_TOP_Y - NCR_BOTTOM_Y) static int M_ncr(Gpm_Event *state, unsigned char *data) { int x,y; state->buttons= (data[0]&1)*GPM_B_LEFT + !!(data[0]&2)*GPM_B_RIGHT; state->dx = (signed char)data[1]; /* currently unused */ state->dy = (signed char)data[2]; x = ((int)data[3] << 8) + (int)data[4]; y = ((int)data[5] << 8) + (int)data[6]; /* these formulaes may look curious, but this is the way it works!!! */ state->x = x < NCR_LEFT_X ? 0 : x > NCR_RIGHT_X ? win.ws_col+1 : (long)(x-NCR_LEFT_X) * (long)(win.ws_col-1) / NCR_DELTA_X+2; state->y = y < NCR_BOTTOM_Y ? win.ws_row + 1 : y > NCR_TOP_Y ? 0 : (long)(NCR_TOP_Y-y) * (long)win.ws_row / NCR_DELTA_Y + 1; realposx = x < NCR_LEFT_X ? 0 : x > NCR_RIGHT_X ? 16384 : (long)(x-NCR_LEFT_X) * (long)(16382) / NCR_DELTA_X+2; realposy = y < NCR_BOTTOM_Y ? 16384 : y > NCR_TOP_Y ? 0 : (long)(NCR_TOP_Y-y) * (long)16383 / NCR_DELTA_Y + 1; return 0; } static int M_twid(Gpm_Event *state, unsigned char *data) { unsigned long message=0UL; int i,h,v; static int lasth, lastv, lastkey, key, lock=0, autorepeat=0; /* build the message as a single number */ for (i=0; i<5; i++) message |= (data[i]&0x7f)<<(i*7); key = message & TW_ANY_KEY; if ((message & TW_MOD_M) == 0) { /* manage keyboard */ if (((message & TW_ANY_KEY) != lastkey) || autorepeat) autorepeat = twiddler_key(message); lastkey = key; lock = 0; return -1; /* no useful mouse data */ } switch (message & TW_ANY1) { case TW_L1: state->buttons = GPM_B_RIGHT; break; case TW_M1: state->buttons = GPM_B_MIDDLE; break; case TW_R1: state->buttons = GPM_B_LEFT; break; case 0: state->buttons = 0; break; } /* also, allow R1 R2 R3 (or L1 L2 L3) to be used as mouse buttons */ if (message & TW_ANY2) state->buttons |= GPM_B_MIDDLE; if (message & TW_L3) state->buttons |= GPM_B_LEFT; if (message & TW_R3) state->buttons |= GPM_B_RIGHT; /* put in modifiers information */ { struct {unsigned long in, out;} *ptr, list[] = { { TW_MOD_S, 1<in; ptr++) if(message & ptr->in) state->modifiers |= ptr->out; } /* now extraxt H/V */ h = (message >> TW_H_SHIFT) & TW_M_MASK; v = (message >> TW_V_SHIFT) & TW_M_MASK; if (h & TW_M_BIT) h = -(TW_M_MASK + 1 - h); if (v & TW_M_BIT) v = -(TW_M_MASK + 1 - v); #ifdef TWIDDLER_STATIC /* static implementation: return movement */ if (!lock) { lasth = h; lastv = v; lock = 1; } state->dx = -(h-lasth); lasth = h; state->dy = -(v-lastv); lastv = v; #elif defined(TWIDDLER_BALLISTIC) { /* in case I'll change the resolution */ static int tw_threshold = 5; /* above this it moves */ static int tw_scale = 5; /* every 5 report one */ if (h > -tw_threshold && h < tw_threshold) state->dx=0; else { h = h - (h<0)*tw_threshold +lasth; lasth = h%tw_scale; state->dx = -(h/tw_scale); } if (v > -tw_threshold && v < tw_threshold) state->dy=0; else { v = v - (v<0)*tw_threshold +lastv; lastv = v%tw_scale; state->dy = -(v/tw_scale); } } #else /* none defined: use mixed approach */ { /* in case I'll change the resolution */ static int tw_threshold = 60; /* above this, movement is ballistic */ static int tw_scale = 10; /* ball: every 6 units move one unit */ static int tw_static_scale = 3; /* stat: every 3 units move one unit */ static int lasthrest, lastvrest; /* integral of small motions uses rest */ if (!lock) { lasth = h; lasthrest = 0; lastv = v; lastvrest = 0; lock = 1; } if (h > -tw_threshold && h < tw_threshold) { state->dx = -(h-lasth+lasthrest)/tw_static_scale; lasthrest = (h-lasth+lasthrest)%tw_static_scale; } else /* ballistic */ { h = h - (h<0)*tw_threshold + lasthrest; lasthrest = h%tw_scale; state->dx = -(h/tw_scale); } lasth = h; if (v > -tw_threshold && v < tw_threshold) { state->dy = -(v-lastv+lastvrest)/tw_static_scale; lastvrest = (v-lastv+lastvrest)%tw_static_scale; } else /* ballistic */ { v = v - (v<0)*tw_threshold + lastvrest; lastvrest = v%tw_scale; state->dy = -(v/tw_scale); } lastv = v; } #endif /* fprintf(stderr,"%4i %4i -> %3i %3i\n",h,v,state->dx,state->dy); */ return 0; } #ifdef HAVE_LINUX_JOYSTICK_H /* Joystick mouse emulation (David Given) */ static int M_js(Gpm_Event *state, unsigned char *data) { struct JS_DATA_TYPE *jdata = (void*)data; static int centerx = 0; static int centery = 0; static int oldbuttons = 0; static int count = 0; int dx; int dy; count++; if (count < 200) { state->buttons = oldbuttons; state->dx = 0; state->dy = 0; return 0; } count = 0; if (centerx == 0) { centerx = jdata->x; centery = jdata->y; } state->buttons = ((jdata->buttons & 1) * GPM_B_LEFT) | ((jdata->buttons & 2) * GPM_B_RIGHT); oldbuttons = state->buttons; dx = (jdata->x - centerx) >> 6; dy = (jdata->y - centery) >> 6; if (dx > 0) state->dx = dx * dx; else state->dx = -(dx * dx); state->dx >>= 2; if (dy > 0) state->dy = dy * dy; else state->dy = -(dy * dy); state->dy >>= 2; /* Prevent pointer drift. (PC joysticks are notoriously inaccurate.) */ if ((state->dx >= -1) && (state->dx <= 1)) state->dx = 0; if ((state->dy >= -1) && (state->dy <= 1)) state->dy = 0; return 0; } #endif /* have joystick.h */ /* Synaptics TouchPad mouse emulation (Henry Davies) */ static int M_synaptics_serial(Gpm_Event *state, unsigned char *data) { syn_process_serial_data (state, data); return 0; } /* Synaptics TouchPad mouse emulation (Henry Davies) */ static int M_synaptics_ps2(Gpm_Event *state, unsigned char *data) { syn_process_ps2_data(state, data); return 0; } static int M_mtouch(Gpm_Event *state, unsigned char *data) { /* * This is a simple decoder for the MicroTouch touch screen * devices. It uses the "tablet" format and only generates button-1 * events. Check README.microtouch for additional information. */ int x, y; static int avgx=-1, avgy; /* average over time, for smooth feeling */ static int upx, upy; /* keep track of last finger-up place */ static struct timeval uptv, tv; /* time of last up, and down events */ #define REAL_TO_XCELL(x) (x * win.ws_col / 0x3FFF) #define REAL_TO_YCELL(y) (y * win.ws_row / 0x3FFF) #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL)) #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \ (t2.tv_usec-t1.tv_usec)/1000) if (!(data[0]&0x40)) { /* * finger-up event: this is usually offset a few pixels, * so ignore this x and y values. And invalidate avg. */ upx = avgx; upy = avgy; GET_TIME(uptv); /* ready for the next finger-down */ tv.tv_sec = 0; state->buttons = 0; avgx=-1; /* invalidate avg */ return 0; } x = data[1] | (data[2]<<7); y = 0x3FFF - (data[3] | (data[4]<<7)); if (avgx < 0) { /* press event */ GET_TIME(tv); if (DIF_TIME(uptv, tv) < (which_mouse->opt_time)) { /* count as button press placed at finger-up pixel */ state->buttons = GPM_B_LEFT; realposx = avgx = upx; state->x = REAL_TO_XCELL(realposx); realposy = avgy = upy; state->y = REAL_TO_YCELL(realposy); upx = (upx - x); /* upx and upy become offsets to use for this drag */ upy = (upy - y); return 0; } /* else, count as a new motion event */ tv.tv_sec = 0; /* invalidate */ realposx = avgx = x; state->x = REAL_TO_XCELL(realposx); realposy = avgy = y; state->y = REAL_TO_YCELL(realposy); } state->buttons = 0; if (tv.tv_sec) { /* a drag event: use position relative to press */ x += upx; y += upy; state->buttons = GPM_B_LEFT; } realposx = avgx = (9*avgx + x)/10; state->x = REAL_TO_XCELL(realposx); realposy = avgy = (9*avgy + y)/10; state->y = REAL_TO_YCELL(realposy); return 0; #undef REAL_TO_XCELL #undef REAL_TO_YCELL #undef GET_TIME #undef DIF_TIME } /* * This decoder is copied and adapted from the above mtouch. * However, this one uses argv, which should be ported above as well. * These variables are used for option passing */ static int gunze_avg = 9; /* the bigger the smoother */ static int gunze_calib[4]; /* x0,y0 x1,y1 (measured at 1/8 and 7/8) */ static int gunze_debounce = 100; /* milliseconds: ignore shorter taps */ static int M_gunze(Gpm_Event *state, unsigned char *data) { /* * This generates button-1 events, by now. * Check README.gunze for additional information. */ int x, y; static int avgx, avgy; /* average over time, for smooth feeling */ static int upx, upy; /* keep track of last finger-up place */ static int released = 0, dragging = 0; static struct timeval uptv, tv; /* time of last up, and down events */ int timediff; #define REAL_TO_XCELL(x) (x * win.ws_col / 0x3FFF) #define REAL_TO_YCELL(y) (y * win.ws_row / 0x3FFF) #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL)) #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \ (t2.tv_usec-t1.tv_usec)/1000) if (data[0] == 'R') { /* * finger-up event: this is usually offset a few pixels, * so ignore this x and y values. And invalidate avg. */ upx = avgx; upy = avgy; GET_TIME(uptv); /* ready for the next finger-down */ state->buttons = 0; released=1; return 0; } if(sscanf(data+1, "%d,%d", &x, &y)!= 2) { gpm_report(GPM_PR_INFO,GPM_MESS_GUNZE_INV_PACK,data); return 10; /* eat one byte only, leave ten of them */ } /* * First of all, calibrate decoded data; * the four numbers are 1/8X 1/8Y, 7/8X, 7/8Y */ x = 128 + 768 * (x - gunze_calib[0])/(gunze_calib[2]-gunze_calib[0]); y = 128 + 768 * (y - gunze_calib[1])/(gunze_calib[3]-gunze_calib[1]); /* range is 0-1023, rescale it upwards (0-REALPOS_MAX) */ x = x * REALPOS_MAX / 1023; y = REALPOS_MAX - y * REALPOS_MAX / 1023; if (x<0) x = 0; if (x > REALPOS_MAX) x = REALPOS_MAX; if (y<0) y = 0; if (y > REALPOS_MAX) y = REALPOS_MAX; /* * there are some issues wrt press events: since the device needs a * non-negligible pressure to detect stuff, it sometimes reports * quick up-down sequences, that actually are just the result of a * little bouncing of the finger. Therefore, we should discard any * release-press pair. This can be accomplished at press time. */ if (released) { /* press event -- or just bounce*/ GET_TIME(tv); timediff = DIF_TIME(uptv, tv); released = 0; if (timediff > gunze_debounce && timediff < (which_mouse->opt_time)) { /* count as button press placed at finger-up pixel */ dragging = 1; state->buttons = GPM_B_LEFT; realposx = avgx = upx; state->x = REAL_TO_XCELL(realposx); realposy = avgy = upy; state->y = REAL_TO_YCELL(realposy); upx = (upx - x); /* upx-upy become offsets to use for this drag */ upy = (upy - y); return 0; } if (timediff <= gunze_debounce) { /* just a bounce, invalidate offset, leave dragging alone */ upx = upy = 0; } else { /* else, count as a new motion event, reset avg */ dragging = 0; realposx = avgx = x; realposy = avgy = y; } } state->buttons = 0; if (dragging) { /* a drag event: use position relative to press */ x += upx; y += upy; state->buttons = GPM_B_LEFT; } realposx = avgx = (gunze_avg * avgx + x)/(gunze_avg+1); state->x = REAL_TO_XCELL(realposx); realposy = avgy = (gunze_avg * avgy + y)/(gunze_avg+1); state->y = REAL_TO_YCELL(realposy); return 0; #undef REAL_TO_XCELL #undef REAL_TO_YCELL #undef GET_TIME #undef DIF_TIME } /* * This decoder is copied and adapted from the above mtouch. */ static int elo_click_ontouch = 0; /* the bigger the smoother */ static int M_etouch(Gpm_Event *state, unsigned char *data) { /* * This is a simple decoder for the EloTouch touch screen devices. * ELO format SmartSet UTsXXYYZZc 9600,N,8,1 * c=checksum = 0xAA+'T'+'U'+s+X+X+Y+Y+Z+Z (XXmax=YYmax=0x0FFF=4095) * s=status bit 0=init touch 1=stream touch 2=release */ #define ELO_CLICK_ONTOUCH /* ifdef then ButtonPress on first Touch else first Move then Touch*/ int x, y; static int avgx=-1, avgy; /* average over time, for smooth feeling */ static int upx, upy; /* keep track of last finger-up place */ static struct timeval uptv, tv; /* time of last up, and down events */ #define REAL_TO_XCELL(x) (x * win.ws_col / 0x3FFF) #define REAL_TO_YCELL(y) (y * win.ws_row / 0x3FFF) #define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL)) #define DIF_TIME(t1,t2) ((t2.tv_sec -t1.tv_sec) *1000+ \ (t2.tv_usec-t1.tv_usec)/1000) if (data[2]&0x04) /* FINGER UP - Release */ { upx = avgx; /* ignore this x, y */ upy = avgy; /* store Finger UP possition */ GET_TIME(uptv); /* set time for the next finger-down */ tv.tv_sec = 0; /* NO DRAG */ avgx=-1; /* FINGER IS UP */ state->buttons = 0; return 0; } /* NOW WE HAVe FINGER DOWN */ x = data[3] | (data[4]<<8); x&=0xfff; y = data[5] | (data[6]<<8); x&=0xfff; x = REALPOS_MAX * (x - gunze_calib[0])/(gunze_calib[2]-gunze_calib[0]); y = REALPOS_MAX * (y - gunze_calib[1])/(gunze_calib[3]-gunze_calib[1]); if (x<0) x = 0; if (x > REALPOS_MAX) x = REALPOS_MAX; if (y<0) y = 0; if (y > REALPOS_MAX) y = REALPOS_MAX; if (avgx < 0) /* INITIAL TOUCH, FINGER WAS UP */ { GET_TIME(tv); state->buttons = 0; if (DIF_TIME(uptv, tv) < (which_mouse->opt_time)) { /* if Initial Touch immediate after finger UP then start DRAG */ x=upx; y=upy; /* A:start DRAG at finger-UP position */ if (elo_click_ontouch==0) state->buttons = GPM_B_LEFT; } else /* 1:MOVE to Initial Touch position */ { upx=x; upy=y; /* store position of Initial Touch into upx, upy */ if (elo_click_ontouch==0) tv.tv_sec=0; /* no DRAG */ } realposx = avgx = x; state->x = REAL_TO_XCELL(realposx); realposy = avgy = y; state->y = REAL_TO_YCELL(realposy); return 0; } /* endof INITIAL TOUCH */ state->buttons = 0; /* Motion event */ if (tv.tv_sec) /* draging or elo_click_ontouch */ { state->buttons = GPM_B_LEFT; if (elo_click_ontouch) { x=avgx=upx; /* 2:BUTTON PRESS at Initial Touch position */ y=avgy=upy; tv.tv_sec=0; /* so next time 3:MOVE again until Finger UP*/ } /* else B:continue DRAG to current possition */ } realposx = avgx = (9*avgx + x)/10; state->x = REAL_TO_XCELL(realposx); realposy = avgy = (9*avgy + y)/10; state->y = REAL_TO_YCELL(realposy); return 0; #undef REAL_TO_XCELL #undef REAL_TO_YCELL #undef GET_TIME #undef DIF_TIME } /* Support for DEC VSXXX-AA and VSXXX-GA serial mice used on */ /* DECstation 5000/xxx, DEC 3000 AXP and VAXstation 4000 */ /* workstations */ /* written 2001/07/11 by Karsten Merker (merker@linuxtag.org) */ /* modified (completion of the protocol specification and */ /* corresponding correction of the protocol identification */ /* mask) 2001/07/12 by Maciej W. Rozycki (macro@ds2.pg.gda.pl) */ static int M_vsxxx_aa(Gpm_Event *state, unsigned char *data) { /* The mouse protocol is as follows: * 4800 bits per second, 8 data bits, 1 stop bit, odd parity * 3 data bytes per data packet: * 7 6 5 4 3 2 1 0 * First Byte: 1 0 0 SignX SignY LMB MMB RMB * Second Byte 0 DX DX DX DX DX DX DX * Third Byte 0 DY DY DY DY DY DY DY * * SignX: sign bit for X-movement * SignY: sign bit for Y-movement * DX and DY: 7-bit-absolute values for delta-X and delta-Y, sign extensions * are in SignX resp. SignY. * * There are a few commands the mouse accepts: * "D" selects the prompt mode, * "P" requests a mouse's position (also selects the prompt mode), * "R" selects the incremental stream mode, * "T" performs a self test and identification (power-up-like), * "Z" performs undocumented test functions (a byte follows). * Parity as well as bit #7 of commands are ignored by the mouse. * * 4 data bytes per self test packet (useful for hot-plug): * 7 6 5 4 3 2 1 0 * First Byte: 1 0 1 0 R3 R2 R1 R0 * Second Byte 0 M2 M1 M0 0 0 1 0 * Third Byte 0 E6 E5 E4 E3 E2 E1 E0 * Fourth Byte 0 0 0 0 0 LMB MMB RMB * * R3-R0: revision, * M2-M0: manufacturer location code, * E6-E0: error code: * 0x00-0x1f: no error (fourth byte is button state), * 0x3d: button error (fourth byte specifies which), * else: other error. * * The mouse powers up in the prompt mode but we use the stream mode. */ state->buttons = data[0]&0x07; state->dx = (data[0]&0x10) ? data[1] : -data[1]; state->dy = (data[0]&0x08) ? -data[2] : data[2]; return 0; } /* Genius Wizardpad tablet -- Matt Kimball (mkimball@xmission.com) */ static int wizardpad_width = -1; static int wizardpad_height = -1; static int M_wp(Gpm_Event *state, unsigned char *data) { int x, y, pressure; x = ((data[4] & 0x1f) << 12) | ((data[3] & 0x3f) << 6) | (data[2] & 0x3f); state->x = x * win.ws_col / (wizardpad_width * 40); realposx = x * 16383 / (wizardpad_width * 40); y = ((data[7] & 0x1f) << 12) | ((data[6] & 0x3f) << 6) | (data[5] & 0x3f); state->y = win.ws_row - y * win.ws_row / (wizardpad_height * 40) - 1; realposy = 16383 - y * 16383 / (wizardpad_height * 40) - 1; pressure = ((data[9] & 0x0f) << 4) | (data[8] & 0x0f); state->buttons= (pressure >= 0x20) * GPM_B_LEFT + !!(data[1] & 0x02) * GPM_B_RIGHT + /* the 0x08 bit seems to catch either of the extra buttons... */ !!(data[1] & 0x08) * GPM_B_MIDDLE; return 0; } /*========================================================================*/ /* Then, mice should be initialized */ static Gpm_Type* I_empty(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { if (check_no_argv(argc, argv)) return NULL; return type; } static int setspeed(int fd,int old,int new,int needtowrite,unsigned short flags) { struct termios tty; char *c; tcgetattr(fd, &tty); tty.c_iflag = IGNBRK | IGNPAR; tty.c_oflag = 0; tty.c_lflag = 0; tty.c_line = 0; tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; switch (old) { case 19200: tty.c_cflag = flags | B19200; break; case 9600: tty.c_cflag = flags | B9600; break; case 4800: tty.c_cflag = flags | B4800; break; case 2400: tty.c_cflag = flags | B2400; break; case 1200: default: tty.c_cflag = flags | B1200; break; } tcsetattr(fd, TCSAFLUSH, &tty); switch (new) { case 19200: c = "*r"; tty.c_cflag = flags | B19200; break; case 9600: c = "*q"; tty.c_cflag = flags | B9600; break; case 4800: c = "*p"; tty.c_cflag = flags | B4800; break; case 2400: c = "*o"; tty.c_cflag = flags | B2400; break; case 1200: default: c = "*n"; tty.c_cflag = flags | B1200; break; } if (needtowrite) write(fd, c, 2); usleep(100000); tcsetattr(fd, TCSAFLUSH, &tty); return 0; } static struct { int sample; char code[2]; } sampletab[]={ { 0,"O"}, { 15,"J"}, { 27,"K"}, { 42,"L"}, { 60,"R"}, { 85,"M"}, {125,"Q"}, {1E9,"N"}, }; static Gpm_Type* I_serial(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { int i; unsigned char c; fd_set set; struct timeval timeout={0,0}; /* used when not debugging */ /* accept "-o dtr", "-o rts" and "-o both" */ if (option_modem_lines(fd, argc, argv)) return NULL; #ifndef DEBUG /* flush any pending input (thanks, Miguel) */ FD_ZERO(&set); for(i=0; /* always */ ; i++) { FD_SET(fd,&set); switch(select(fd+1,&set,(fd_set *)NULL,(fd_set *)NULL,&timeout/*zero*/)){ case 1: if (read(fd,&c,1)==0) break; case -1: continue; } break; } if (type->fun==M_logimsc) write(fd, "QU", 2 ); #if 0 /* Did this ever work? -- I don't know, but should we not remove it, * if it doesn't work ??? -- Nico */ if (type->fun==M_ms && i==2 && c==0x33) { /* Aha.. a mouseman... */ gpm_report(GPM_PR_INFO,GPM_MESS_MMAN_DETECTED); return mice; /* it is the first */ } #endif #endif /* Non mman: change from any available speed to the chosen one */ for (i=9600; i>=1200; i/=2) setspeed(fd, i, (which_mouse->opt_baud), (type->fun != M_mman) /* write */, flags); /* * reset the MouseMan/TrackMan to use the 3/4 byte protocol * (Stephen Lee, sl14@crux1.cit.cornell.edu) * Changed after 1.14; why not having "I_mman" now? */ if (type->fun==M_mman) { setspeed(fd, 1200, 1200, 0, flags); /* no write */ write(fd, "*X", 2); setspeed(fd, 1200, (which_mouse->opt_baud), 0, flags); /* no write */ return type; } if(type->fun==M_geni) { gpm_report(GPM_PR_INFO,GPM_MESS_INIT_GENI); setspeed(fd, 1200, 9600, 1, flags); /* write */ write(fd, ":" ,1); write(fd, "E" ,1); /* setup tablet. relative mode, resolution... */ write(fd, "@" ,1); /* setup tablet. relative mode, resolution... */ } if (type->fun==M_synaptics_serial) { int packet_length; setspeed (fd, 1200, 1200, 1, flags); packet_length = syn_serial_init (fd); setspeed (fd, 1200, 9600, 1, flags); type->packetlen = packet_length; type->howmany = packet_length; } if (type->fun==M_vsxxx_aa) { setspeed (fd, 4800, 4800, 0, flags); /* no write */ write(fd, "R", 1); /* initialize a mouse; without getting an "R" */ /* a mouse does not send a bytestream */ } return type; } static Gpm_Type* I_logi(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { int i; struct stat buf; int busmouse; if (check_no_argv(argc, argv)) return NULL; /* is this a serial- or a bus- mouse? */ if(fstat(fd,&buf)==-1) gpm_report(GPM_PR_OOPS,GPM_MESS_FSTAT); i=MAJOR(buf.st_rdev); /* I don't know why this is herein, but I remove it. I don't think a * hardcoded ttyname in a C file is senseful. I think if the device * exists must be clear before. Not here. */ /********* if (stat("/dev/ttyS0",&buf)==-1) gpm_oops("stat()"); *****/ busmouse=(i != MAJOR(buf.st_rdev)); /* fix the howmany field, so that serial mice have 1, while busmice have 3 */ type->howmany = busmouse ? 3 : 1; /* change from any available speed to the chosen one */ for (i=9600; i>=1200; i/=2) setspeed(fd, i, (which_mouse->opt_baud), 1 /* write */, flags); /* this stuff is peculiar of logitech mice, also for the serial ones */ write(fd, "S", 1); setspeed(fd, (which_mouse->opt_baud), (which_mouse->opt_baud), 1 /* write */, CS8 |PARENB |PARODD |CREAD |CLOCAL |HUPCL); /* configure the sample rate */ for (i=0;(which_mouse->opt_sample)<=sampletab[i].sample;i++) ; write(fd,sampletab[i].code,1); return type; } /* wacom graphire tablet */ #define UD_RESETBAUD "\r$" /* reset baud rate to default (wacom V) */ /* or switch to wacom IIs (wacomIV) */ #define UD_RESET "#\r" /* Reset tablet and enable WACOM IV */ #define UD_SENDCOORDS "ST\r" /* Start sending coordinates */ #define UD_FIRMID "~#\r" /* Request firmware ID string */ #define UD_COORD "~C\r" /* Request max coordinates */ #define UD_STOP "\nSP\r" /* stop sending coordinates */ static void I_wacom_reset_wacom(int fd) { /* Init Wacom communication; this is modified from xf86Wacom.so module */ /* Set speed to 19200 */ setspeed (fd, 1200, 19200, 0, B19200|CS8|CREAD|CLOCAL|HUPCL); /* Send Reset Baudrate Command */ write(fd, UD_RESETBAUD, strlen(UD_RESETBAUD)); usleep(250000); /* Send Reset Command */ write(fd, UD_RESET, strlen(UD_RESET)); usleep(75000); /* Set speed to 9600bps */ setspeed (fd, 1200, 9600, 0, B9600|CS8|CREAD|CLOCAL|HUPCL); /* Send Reset Command */ write(fd, UD_RESET, strlen(UD_RESET)); usleep(250000); write(fd, UD_STOP, strlen(UD_STOP)); usleep(100000); } static int I_wacom_wait_wacom(int fd) { /* * Wait up to 200 ms for Data from Tablet. * Do not read that data. * Give back 0 on timeout condition, -1 on error and 1 for DataPresent */ struct timeval timeout; fd_set readfds; int err; FD_ZERO(&readfds); FD_SET(fd, &readfds); timeout.tv_sec = 0; timeout.tv_usec = 200000; err = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout); return((err>0)?1:err); } static int I_wacom_RequestData(char *cmd, int fd, char *buffer, unsigned int buffer_len, char *p) { int err; /* * Send cmd if not null, and get back answer from tablet. * Get Data to buffer until full or timeout. * Give back 0 for timeout and !0 for buffer full */ if (cmd) write(fd,cmd,strlen(cmd)); memset(buffer,0,sizeof(char)*buffer_len); p=buffer; err=I_wacom_wait_wacom(fd); while (err != -1 && err && (p-buffer)<(sizeof(char)*buffer_len-1)) { p+= read(fd,p,(sizeof(char)*buffer_len-1)-(p-buffer)); err=I_wacom_wait_wacom(fd); } /* return 1 for buffer full */ return ((strlen(buffer) >= (sizeof(char)*buffer_len-1))? !0 :0); } static Gpm_Type *I_wacom(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { #define BUFFER_SIZE 50 char buffer[BUFFER_SIZE], *p; /* * We do both modes, relative and absolute, with the same function. * If WacomAbsoluteWanted is !0 then that function calculates * absolute values, else relative ones. * We set this by the -o switch: * gpm -t wacom -o absolute # does absolute mode * gpm -t wacom # does relative mode * gpm -t wacom -o relative # does relative mode */ /* accept boolean options absolute and relative */ static argv_helper optioninfo[] = { {"absolute", ARGV_BOOL, u: {iptr: &WacomAbsoluteWanted}, value: !0}, {"relative", ARGV_BOOL, u: {iptr: &WacomAbsoluteWanted}, value: 0}, {"", ARGV_END} }; parse_argv(optioninfo, argc, argv); type->absolute = WacomAbsoluteWanted; I_wacom_reset_wacom(fd); /* "Flush" input queque */ while(I_wacom_RequestData(NULL, fd, buffer, BUFFER_SIZE, p)) ; /* read WACOM-ID */ I_wacom_RequestData(UD_FIRMID, fd, buffer, BUFFER_SIZE, p); /* Search for matching modell */ for(WacomModell=0; WacomModell< (sizeof(wcmodell) / sizeof(struct WC_MODELL)); WacomModell++ ) { if (!strncmp(buffer+2,wcmodell[WacomModell].magic, 2)) { /* Magic matches, modell found */ wmaxx=wcmodell[WacomModell].maxX; wmaxy=wcmodell[WacomModell].maxY; break; } } if(WacomModell >= (sizeof(wcmodell) / sizeof(struct WC_MODELL))) WacomModell=-1; gpm_report(GPM_PR_INFO,GPM_MESS_WACOM_MOD, type->absolute? 'A':'R', (WacomModell==(-1))? "Unknown" : wcmodell[WacomModell].name, buffer+2); /* read Wacom max size */ if(WacomModell!=(-1) && (!wcmodell[WacomModell].maxX)) { I_wacom_RequestData(UD_COORD, fd, buffer, BUFFER_SIZE, p); sscanf(buffer+2, "%d,%d", &wmaxx, &wmaxy); wmaxx = (wmaxx-wcmodell[WacomModell].border); wmaxy = (wmaxy-wcmodell[WacomModell].border); } write(fd,UD_SENDCOORDS,4); return type; } static Gpm_Type *I_pnp(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { struct termios tty; /* accept "-o dtr", "-o rts" and "-o both" */ if (option_modem_lines(fd, argc, argv)) return NULL; /* * Just put the device to 1200 baud. Thanks to Francois Chastrette * for his great help and debugging with his own pnp device. */ tcgetattr(fd, &tty); tty.c_iflag = IGNBRK | IGNPAR; tty.c_oflag = 0; tty.c_lflag = 0; tty.c_line = 0; tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; tty.c_cflag = flags | B1200; tcsetattr(fd, TCSAFLUSH, &tty); /* set parameters */ /* * Don't read the silly initialization string. I don't want to see * the vendor name: it is only propaganda, with no information. */ return type; } /* * Sends the SEND_ID command to the ps2-type mouse. * Return one of GPM_AUX_ID_... */ static int read_mouse_id(int fd) { unsigned char c = GPM_AUX_SEND_ID; unsigned char id; write(fd, &c, 1); read(fd, &c, 1); if (c != GPM_AUX_ACK) { return(GPM_AUX_ID_ERROR); } read(fd, &id, 1); return(id); } /** * Writes the given data to the ps2-type mouse. * Checks for an ACK from each byte. * * Returns 0 if OK, or >0 if 1 or more errors occurred. */ static int write_to_mouse(int fd, unsigned char *data, size_t len) { int i; int error = 0; for (i = 0; i < len; i++) { unsigned char c; write(fd, &data[i], 1); read(fd, &c, 1); if (c != GPM_AUX_ACK) error++; } /* flush any left-over input */ usleep (30000); tcflush (fd, TCIFLUSH); return(error); } /* intellimouse, ps2 version: Ben Pfaff and Colin Plumb */ /* Autodetect: Steve Bennett */ static Gpm_Type *I_imps2(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { int id; static unsigned char basic_init[] = { GPM_AUX_ENABLE_DEV, GPM_AUX_SET_SAMPLE, 100 }; static unsigned char imps2_init[] = { GPM_AUX_SET_SAMPLE, 200, GPM_AUX_SET_SAMPLE, 100, GPM_AUX_SET_SAMPLE, 80, }; static unsigned char ps2_init[] = { GPM_AUX_SET_SCALE11, GPM_AUX_ENABLE_DEV, GPM_AUX_SET_SAMPLE, 100, GPM_AUX_SET_RES, 3, }; /* Do a basic init in case the mouse is confused */ write_to_mouse(fd, basic_init, sizeof (basic_init)); /* Now try again and make sure we have a PS/2 mouse */ if (write_to_mouse(fd, basic_init, sizeof (basic_init)) != 0) { gpm_report(GPM_PR_ERR,GPM_MESS_IMPS2_INIT); return(NULL); } /* Try to switch to 3 button mode */ if (write_to_mouse(fd, imps2_init, sizeof (imps2_init)) != 0) { gpm_report(GPM_PR_ERR,GPM_MESS_IMPS2_FAILED); return(NULL); } /* Read the mouse id */ id = read_mouse_id(fd); if (id == GPM_AUX_ID_ERROR) { gpm_report(GPM_PR_ERR,GPM_MESS_IMPS2_MID_FAIL); id = GPM_AUX_ID_PS2; } /* And do the real initialisation */ if (write_to_mouse(fd, ps2_init, sizeof (ps2_init)) != 0) { gpm_report(GPM_PR_ERR,GPM_MESS_IMPS2_SETUP_FAIL); } if (id == GPM_AUX_ID_IMPS2) { /* Really an intellipoint, so initialise 3 button mode (4 byte packets) */ gpm_report(GPM_PR_INFO,GPM_MESS_IMPS2_AUTO); return type; } if (id != GPM_AUX_ID_PS2) { gpm_report(GPM_PR_ERR,GPM_MESS_IMPS2_BAD_ID, id); } else gpm_report(GPM_PR_INFO,GPM_MESS_IMPS2_PS2); for (type=mice; type->fun; type++) if (strcmp(type->name, "ps2") == 0) return(type); /* ps2 was not found!!! */ return(NULL); } /* * This works with Dexxa Optical Mouse, but because in X same initstring * is named ExplorerPS/2 so I named it in the same way. */ static Gpm_Type *I_exps2(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { static unsigned char s1[] = { 243, 200, 243, 200, 243, 80, }; if (check_no_argv(argc, argv)) return NULL; write (fd, s1, sizeof (s1)); usleep (30000); tcflush (fd, TCIFLUSH); return type; } static Gpm_Type *I_twid(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { if (check_no_argv(argc, argv)) return NULL; if (twiddler_key_init() != 0) return NULL; /* * the twiddler is a serial mouse: just drop dtr * and run at 2400 (unless specified differently) */ if((which_mouse->opt_baud)==DEF_BAUD) (which_mouse->opt_baud) = 2400; argv[1] = "dtr"; /* argv[1] is guaranteed to be NULL (this is dirty) */ return I_serial(fd, flags, type, argc, argv); } static Gpm_Type *I_calus(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { if (check_no_argv(argc, argv)) return NULL; if ((which_mouse->opt_baud) == 1200) (which_mouse->opt_baud)=9600; /* default to 9600 */ return I_serial(fd, flags, type, argc, argv); } /* synaptics touchpad, ps2 version: Henry Davies */ static Gpm_Type *I_synps2(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { syn_ps2_init (fd); return type; } static void I_summa_reset_summa(int fd) { write(fd,0,1); /* Reset */ usleep(400000); /* wait */ } static int I_summa_wait_summa(int fd) { struct timeval timeout; fd_set readfds; int err; FD_ZERO(&readfds); FD_SET(fd, &readfds); timeout.tv_sec = 0; timeout.tv_usec = 200000; err = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout); return(err); } static Gpm_Type *I_summa(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { int err; char buffer[255]; char config[5]; /* Summasketchtablet (from xf86summa.o: thanks to Steven Lang )*/ #define SS_PROMPT_MODE "B" /* Prompt mode */ #define SS_FIRMID "z?" /* Request firmware ID string */ #define SS_500LPI "h" /* 500 lines per inch */ #define SS_READCONFIG "a" #define SS_ABSOLUTE "F" /* Absolute Mode */ #define SS_TABID0 "0" /* Tablet ID 0 */ #define SS_UPPER_ORIGIN "b" /* Origin upper left */ #define SS_BINARY_FMT "zb" /* Binary reporting */ #define SS_STREAM_MODE "@" /* Stream mode */ /* Geniustablet (hisketch.txt)*/ #define GEN_MMSERIES ":" char GEN_MODELL=0x7f; /* Set speed to 9600bps */ setspeed (fd, 1200, 9600, 1, B9600|CS8|CREAD|CLOCAL|HUPCL|PARENB|PARODD); I_summa_reset_summa(fd); write(fd, SS_PROMPT_MODE, strlen(SS_PROMPT_MODE)); if (strstr(type->name,"acecad")!=NULL) summaid=11; if (summaid<0) { /* Summagraphics test */ /* read the Summa Firm-ID */ write(fd, SS_FIRMID, strlen(SS_FIRMID)); err=I_summa_wait_summa(fd); if (!((err == -1) || (!err))) { summaid=10; /* Original Summagraphics */ read(fd, buffer, 255); /* Read Firm-ID */ } } if (summaid<0) { /* Genius-test */ I_summa_reset_summa(fd); write(fd,GEN_MMSERIES,1); write(fd,&GEN_MODELL,1); /* Read modell */ err=I_summa_wait_summa(fd); if (!((err == -1) || (!err))) { /* read Genius-ID */ err=I_summa_wait_summa(fd); if (!((err == -1) || (!err))) { err=I_summa_wait_summa(fd); if (!((err == -1) || (!err))) { read(fd,&config,1); summaid=(config[0] & 224) >> 5; /* genius tablet-id (0-7)*/ } } } } /* end of Geniustablet-test */ /* unknown tablet ?*/ if ((summaid<0) || (summaid==11)) { I_summa_reset_summa(fd); write(fd, SS_BINARY_FMT SS_PROMPT_MODE, 3); } /* read tablet size */ err=I_summa_wait_summa(fd); if (!((err == -1) || (!err))) read(fd,buffer,sizeof(buffer)); write(fd,SS_READCONFIG,1); read(fd,&config,5); summamaxx=(config[2]<<7 | config[1])-(SUMMA_BORDER*2); summamaxy=(config[4]<<7 | config[3])-(SUMMA_BORDER*2); write(fd,SS_ABSOLUTE SS_STREAM_MODE SS_UPPER_ORIGIN,3); if (summaid<0) write(fd,SS_500LPI SS_TABID0 SS_BINARY_FMT,4); return type; } static Gpm_Type *I_mtouch(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { struct termios tty; /* Set speed to 9600bps (copied from I_summa, above :) */ tcgetattr(fd, &tty); tty.c_iflag = IGNBRK | IGNPAR; tty.c_oflag = 0; tty.c_lflag = 0; tty.c_line = 0; tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; tty.c_cflag = B9600|CS8|CREAD|CLOCAL|HUPCL; tcsetattr(fd, TCSAFLUSH, &tty); /* Turn it to "format tablet" and "mode stream" */ write(fd,"\001MS\r\n\001FT\r\n",10); return type; } /* simple initialization for the gunze touchscreen */ static Gpm_Type *I_gunze(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { struct termios tty; FILE *f; char s[80]; int i, calibok = 0; #define GUNZE_CALIBRATION_FILE SYSCONFDIR "/gpm-calibration" /* accept a few options */ static argv_helper optioninfo[] = { {"smooth", ARGV_INT, u: {iptr: &gunze_avg}}, {"debounce", ARGV_INT, u: {iptr: &gunze_debounce}}, /* FIXME: add corner tapping */ {"", ARGV_END} }; parse_argv(optioninfo, argc, argv); /* check that the baud rate is valid */ if ((which_mouse->opt_baud) == DEF_BAUD) (which_mouse->opt_baud) = 19200; /* force 19200 as default */ if ((which_mouse->opt_baud) != 9600 && (which_mouse->opt_baud) != 19200) { gpm_report(GPM_PR_ERR,GPM_MESS_GUNZE_WRONG_BAUD,option.progname, argv[0]); (which_mouse->opt_baud) = 19200; } tcgetattr(fd, &tty); tty.c_iflag = IGNBRK | IGNPAR; tty.c_oflag = 0; tty.c_lflag = 0; tty.c_line = 0; tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; tty.c_cflag = ((which_mouse->opt_baud) == 9600 ? B9600 : B19200) |CS8|CREAD|CLOCAL|HUPCL; tcsetattr(fd, TCSAFLUSH, &tty); /* FIXME: try to find some information about the device */ /* retrieve calibration, if not existent, use defaults (uncalib) */ f = fopen(GUNZE_CALIBRATION_FILE, "r"); if (f) { fgets(s, 80, f); /* discard the comment */ if (fscanf(f, "%d %d %d %d", gunze_calib, gunze_calib+1, gunze_calib+2, gunze_calib+3) == 4) calibok = 1; /* Hmm... check */ for (i=0; i<4; i++) if (gunze_calib[i] & ~1023) calibok = 0; if (gunze_calib[0] == gunze_calib[2]) calibok = 0; if (gunze_calib[1] == gunze_calib[3]) calibok = 0; fclose(f); } if (!calibok) { gpm_report(GPM_PR_ERR,GPM_MESS_GUNZE_CALIBRATE, option.progname); gunze_calib[0] = gunze_calib[1] = 128; /* 1/8 */ gunze_calib[2] = gunze_calib[3] = 896; /* 7/8 */ } return type; } /* simple initialization for the elo touchscreen */ static Gpm_Type *I_etouch(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { struct termios tty; FILE *f; char s[80]; int i, calibok = 0; /* Calibration config file (copied from I_gunze, below :) */ #define ELO_CALIBRATION_FILE SYSCONFDIR "/gpm-calibration" /* accept a few options */ static argv_helper optioninfo[] = { {"clickontouch", ARGV_BOOL, u: {iptr: &elo_click_ontouch}, value: !0}, {"", ARGV_END} }; parse_argv(optioninfo, argc, argv); /* Set speed to 9600bps (copied from I_summa, above :) */ tcgetattr(fd, &tty); tty.c_iflag = IGNBRK | IGNPAR; tty.c_oflag = 0; tty.c_lflag = 0; tty.c_line = 0; tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; tty.c_cflag = B9600|CS8|CREAD|CLOCAL|HUPCL; tcsetattr(fd, TCSAFLUSH, &tty); /* retrieve calibration, if not existent, use defaults (uncalib) */ f = fopen(ELO_CALIBRATION_FILE, "r"); if (f) { fgets(s, 80, f); /* discard the comment */ if (fscanf(f, "%d %d %d %d", gunze_calib, gunze_calib+1, gunze_calib+2, gunze_calib+3) == 4) calibok = 1; /* Hmm... check */ for (i=0; i<4; i++) if ((gunze_calib[i] & 0xfff) != gunze_calib[i]) calibok = 0; if (gunze_calib[0] == gunze_calib[2]) calibok = 0; if (gunze_calib[1] == gunze_calib[3]) calibok = 0; fclose(f); } if (!calibok) { gpm_report(GPM_PR_ERR,GPM_MESS_ELO_CALIBRATE, option.progname, ELO_CALIBRATION_FILE); /* "%s: etouch: calibration file %s absent or invalid, using defaults" */ gunze_calib[0] = gunze_calib[1] = 0x010; /* 1/16 */ gunze_calib[2] = gunze_calib[3] = 0xff0; /* 15/16 */ } return type; } /* Genius Wizardpad tablet -- Matt Kimball (mkimball@xmission.com) */ static Gpm_Type *I_wp(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) { struct termios tty; char tablet_info[256]; int count, pos, size; /* Set speed to 9600bps (copied from I_summa, above :) */ tcgetattr(fd, &tty); tty.c_iflag = IGNBRK | IGNPAR; tty.c_oflag = 0; tty.c_lflag = 0; tty.c_line = 0; tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; tty.c_cflag = B9600|CS8|CREAD|CLOCAL|HUPCL; tcsetattr(fd, TCSAFLUSH, &tty); /* Reset the tablet (':') and put it in remote mode ('S') so that it isn't sending anything to us. */ write(fd, ":S", 2); tcsetattr(fd, TCSAFLUSH, &tty); /* Query the model of the tablet */ write(fd, "T", 1); sleep(1); count = read(fd, tablet_info, 255); /* The tablet information should start with "KW" followed by the rest of the model number. If it isn't there, it probably isn't a WizardPad. */ if(count < 2) return NULL; if(tablet_info[0] != 'K' || tablet_info[1] != 'W') return NULL; /* Now, we want the width and height of the tablet. They should be of the form "X###" and "Y###" where ### is the number of units of the tablet. The model I've got is "X130" and "Y095", but I guess there might be other ones sometime. */ for(pos = 0; pos < count; pos++) { if(tablet_info[pos] == 'X' || tablet_info[pos] == 'Y') { if(pos + 3 < count) { size = (tablet_info[pos + 1] - '0') * 100 + (tablet_info[pos + 2] - '0') * 10 + (tablet_info[pos + 3] - '0'); if(tablet_info[pos] == 'X') { wizardpad_width = size; } else { wizardpad_height = size; } } } } /* Set the tablet to stream mode with 180 updates per sec. ('O') */ write(fd, "O", 1); return type; } /*========================================================================*/ /* Finally, the table */ #define STD_FLG (CREAD|CLOCAL|HUPCL) /* * Note that mman must be the first, and ms the second (I use this info * in mouse-test.c, as a quick and dirty hack * * We should clean up mouse-test and sort the table alphabeticly! --nico */ /* * For those who are trying to add a new type, here a brief * description of the structure. Please refer to gpmInt.h and gpm.c * for more information: * * The first three strings are the name, an help line, a long name (if any) * Then come the functions: the decoder and the initializazion function * (called I_* and M_*) * Follows an array of four bytes: it is the protocol-identification, based * on the first two bytes of a packet: if * "((byte0 & proto[0]) == proto[1]) && ((byte1 & proto[2]) == proto[3])" * then we are at the beginning of a packet. * The following numbers are: * bytes-per-packet, * bytes-to-read (use 1, bus mice are pathological) * has-extra-byte (boolean, for mman pathological protocol) * is-absolute-coordinates (boolean) * Finally, a pointer to a repeater function, if any. */ Gpm_Type mice[]={ {"mman", "The \"MouseMan\" and similar devices (3/4 bytes per packet).", "Mouseman", M_mman, I_serial, CS7 | STD_FLG, /* first */ {0x40, 0x40, 0x40, 0x00}, 3, 1, 1, 0, 0}, {"ms", "The original ms protocol, with a middle-button extension.", "", M_ms, I_serial, CS7 | STD_FLG, {0x40, 0x40, 0x40, 0x00}, 3, 1, 0, 0, 0}, {"acecad", "Acecad tablet absolute mode(Sumagrapics MM-Series mode)", "", M_summa, I_summa, STD_FLG, {0x80, 0x80, 0x00, 0x00}, 7, 1, 0, 1, 0}, {"bare", "Unadorned ms protocol. Needed with some 2-buttons mice.", "Microsoft", M_bare, I_serial, CS7 | STD_FLG, {0x40, 0x40, 0x40, 0x00}, 3, 1, 0, 0, 0}, {"bm", "Micro$oft busmice and compatible devices.", "BusMouse", M_bm, I_empty, STD_FLG, /* bm is sun */ {0xf8, 0x80, 0x00, 0x00}, 3, 3, 0, 0, 0}, {"brw", "Fellowes Browser - 4 buttons (and a wheel) (dual protocol?)", "", M_brw, I_pnp, CS7 | STD_FLG, {0xc0, 0x40, 0xc0, 0x00}, 4, 1, 0, 0, 0}, {"cal", "Calcomp UltraSlate", "", M_calus, I_calus, CS8 | CSTOPB | STD_FLG, {0x80, 0x80, 0x80, 0x00}, 6, 6, 0, 1, 0}, {"calr", "Calcomp UltraSlate - relative mode", "", M_calus_rel, I_calus, CS8 | CSTOPB | STD_FLG, {0x80, 0x80, 0x80, 0x00}, 6, 6, 0, 0, 0}, {"etouch", "EloTouch touch-screens (only button-1 events, by now)", "", M_etouch, I_etouch, STD_FLG, {0xFF, 0x55, 0xFF, 0x54}, 7, 1, 0, 1, NULL}, #ifdef HAVE_LINUX_INPUT_H {"evdev", "Linux Event Device", "", M_evdev, I_empty, STD_FLG, {0x00, 0x00, 0x00, 0x00} , 16, 16, 0, 0, NULL}, #endif /* HAVE_LINUX_INPUT_H */ {"exps2", "IntelliMouse Explorer (ps2) - 3 buttons, wheel unused", "ExplorerPS/2", M_imps2, I_exps2, STD_FLG, {0xc0, 0x00, 0x00, 0x00}, 4, 1, 0, 0, 0}, #ifdef HAVE_LINUX_JOYSTICK_H {"js", "Joystick mouse emulation", "Joystick", M_js, NULL, 0, {0xFC, 0x00, 0x00, 0x00}, 12, 12, 0, 0, 0}, #endif {"genitizer", "\"Genitizer\" tablet, in relative mode.", "", M_geni, I_serial, CS8|PARENB|PARODD, {0x80, 0x80, 0x00, 0x00}, 3, 1, 0, 0, 0}, {"gunze", "Gunze touch-screens (only button-1 events, by now)", "", M_gunze, I_gunze, STD_FLG, {0xF9, 0x50, 0xF0, 0x30}, 11, 1, 0, 1, NULL}, {"imps2","Microsoft Intellimouse (ps2)-autodetect 2/3 buttons,wheel unused", "", M_imps2, I_imps2, STD_FLG, {0xC0, 0x00, 0x00, 0x00}, 4, 1, 0, 0, R_imps2}, {"logi", "Used in some Logitech devices (only serial).", "Logitech", M_logi, I_logi, CS8 | CSTOPB | STD_FLG, {0xe0, 0x80, 0x80, 0x00}, 3, 3, 0, 0, 0}, {"logim", "Turn logitech into Mouse-Systems-Compatible.", "", M_logimsc, I_serial, CS8 | CSTOPB | STD_FLG, {0xf8, 0x80, 0x00, 0x00}, 5, 1, 0, 0, 0}, {"mm", "MM series. Probably an old protocol...", "MMSeries", M_mm, I_serial, CS8 | PARENB|PARODD | STD_FLG, {0xe0, 0x80, 0x80, 0x00}, 3, 1, 0, 0, 0}, {"ms3", "Microsoft Intellimouse (serial) - 3 buttons, wheel unused", "", M_ms3, I_pnp, CS7 | STD_FLG, {0xc0, 0x40, 0xc0, 0x00}, 4, 1, 0, 0, R_ms3}, {"ms+", "Like 'ms', but allows dragging with the middle button.", "", M_ms_plus, I_serial, CS7 | STD_FLG, {0x40, 0x40, 0x40, 0x00}, 3, 1, 0, 0, 0}, {"ms+lr", "'ms+', but you can reset m by pressing lr (see man page).", "", M_ms_plus_lr, I_serial, CS7 | STD_FLG, {0x40, 0x40, 0x40, 0x00}, 3, 1, 0, 0, 0}, {"msc", "Mouse-Systems-Compatible (5bytes). Most 3-button mice.", "MouseSystems", M_msc, I_serial, CS8 | CSTOPB | STD_FLG, {0xf8, 0x80, 0x00, 0x00}, 5, 1, 0, 0, R_msc}, {"mtouch", "MicroTouch touch-screens (only button-1 events, by now)", "", M_mtouch, I_mtouch, STD_FLG, {0x80, 0x80, 0x80, 0x00}, 5, 1, 0, 1, NULL}, {"ncr", "Ncr3125pen, found on some laptops", "", M_ncr, NULL, STD_FLG, {0x08, 0x08, 0x00, 0x00}, 7, 7, 0, 1, 0}, {"netmouse","Genius NetMouse (ps2) - 2 buttons and 2 buttons 'up'/'down'.", "", M_netmouse, I_netmouse, CS7 | STD_FLG, {0xc0, 0x00, 0x00, 0x00}, 4, 1, 0, 0, 0}, {"pnp", "Plug and pray. New mice may not run with '-t ms'.", "", M_bare, I_pnp, CS7 | STD_FLG, {0x40, 0x40, 0x40, 0x00}, 3, 1, 0, 0, 0}, {"ps2", "Busmice of the ps/2 series. Most busmice, actually.", "PS/2", M_ps2, I_ps2, STD_FLG, {0xc0, 0x00, 0x00, 0x00}, 3, 1, 0, 0, R_ps2}, {"sun", "'msc' protocol, but only 3 bytes per packet.", "", M_sun, I_serial, CS8 | CSTOPB | STD_FLG, {0xf8, 0x80, 0x00, 0x00}, 3, 1, 0, 0, R_sun}, {"summa", "Summagraphics or Genius tablet absolute mode(MM-Series)", "", M_summa, I_summa, STD_FLG, {0x80, 0x80, 0x00, 0x00}, 5, 1, 0, 1, R_summa}, {"syn", "The \"Synaptics\" serial TouchPad.", "synaptics", M_synaptics_serial, I_serial, CS7 | STD_FLG, {0x40, 0x40, 0x40, 0x00}, 6, 6, 1, 0, 0}, {"synps2", "The \"Synaptics\" PS/2 TouchPad", "synaptics_ps2", M_synaptics_ps2, I_synps2, STD_FLG, {0x80, 0x80, 0x00, 0x00}, 6, 1, 1, 0, 0}, {"twid", "Twidddler keyboard", "", M_twid, I_twid, CS8 | STD_FLG, {0x80, 0x00, 0x80, 0x80}, 5, 1, 0, 0, 0}, {"vsxxxaa", "The DEC VSXXX-AA/GA serial mouse on DEC workstations.", "", M_vsxxx_aa, I_serial, CS8 | PARENB | PARODD | STD_FLG, {0xe0, 0x80, 0x80, 0x00}, 3, 1, 0, 0, 0}, {"wacom","Wacom Protocol IV Tablets: Pen+Mouse, relative+absolute mode", "", M_wacom, I_wacom, STD_FLG, {0x80, 0x80, 0x80, 0x00}, 7, 1, 0, 0, 0}, {"wp", "Genius WizardPad tablet", "wizardpad", M_wp, I_wp, STD_FLG, {0xFA, 0x42, 0x00, 0x00}, 10, 1, 0, 1, 0}, {"", "", "", NULL, NULL, 0, {0x00, 0x00, 0x00, 0x00}, 0, 0, 0, 0, 0} }; /*------------------------------------------------------------------------*/ /* and the help */ int M_listTypes(void) { Gpm_Type *type; printf(GPM_MESS_VERSION "\n"); printf(GPM_MESS_AVAIL_MYT); for (type=mice; type->fun; type++) printf(GPM_MESS_SYNONYM, type->repeat_fun?'*':' ', type->name, type->desc, type->synonyms); putchar('\n'); return 1; /* to exit() */ } /* indent: use three spaces. no tab. not two or four. three */ /* Local Variables: */ /* c-indent-level: 3 */ /* End: */