/*
* mice.c - mouse definitions for gpm-Linux
*
* Copyright (C) 1993 Andrew Haylett <ajh@gec-mrc.co.uk>
* Copyright (C) 1994-2000 Alessandro Rubini <rubini@linux.it>
* Copyright (C) 1998,1999 Ian Zimmerman <itz@rahul.net>
* Copyright (C) 2001-2008 Nico Schottelius <nico-gpm2008 at schottelius.org>
*
* 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h> /* stat() */
#include <sys/time.h> /* select() */
#include <linux/kdev_t.h> /* MAJOR */
#include <linux/keyboard.h>
/* JOYSTICK */
#ifdef HAVE_LINUX_JOYSTICK_H
#include <linux/joystick.h>
#endif
/* EV DEVICE */
#ifdef HAVE_LINUX_INPUT_H
#include <linux/input.h>
#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; i<argc; i++) {
for (p = info; p->type != 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 <pavel@ucw.cz>
*/
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 <runkel@runkeledv.de>,
* Mike Pioskowik 01/2000 <pio.d1mp@nbnet.de>
*/
/* 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<<KG_SHIFT },
{ TW_MOD_C, 1<<KG_CTRL },
{ TW_MOD_A, 1<<KG_ALT },
{ 0, 0}
};
for (ptr = list; ptr->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 <tiger@tyger.org>)*/
#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: */