/*
* mev.c - simple client to print mouse events (gpm-Linux)
*
* Copyright 1994,1995 rubini@linux.it (Alessandro Rubini)
* Copyright (C) 1998 Ian Zimmerman <itz@rahul.net>
*
* 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 client is meant to be used both interactively to check
* that gpm is working, and as a background process to convert gpm events
* to textual strings. I'm using it to handle Linux mouse
* events to emacs
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <linux/keyboard.h> /* to use KG_SHIFT and so on */
#define ALL_KEY_MODS ((1<<KG_SHIFT)|(1<<KG_ALT)|(1<<KG_ALTGR)|(1<<KG_CTRL))
#include "headers/message.h" /* messages */
#include "headers/gpm.h"
#define GPM_NAME "gpm"
#define GPM_DATE "X-Mas 2002"
char *prgname;
struct node {char *name; int flag;};
struct node tableEv[]= {
{"move", GPM_MOVE},
{"drag", GPM_DRAG},
{"down", GPM_DOWN},
{"up", GPM_UP},
{"press", GPM_DOWN},
{"release", GPM_UP},
{"motion", GPM_MOVE | GPM_DRAG},
{"hard", GPM_HARD},
{"any", ~0},
{"all", ~0},
{"none", 0},
{NULL,0}
};
struct node tableMod[]= {
{"shift", 1<<KG_SHIFT},
{"anyAlt", 1<<KG_ALT | 1<<KG_ALTGR},
{"leftAlt", 1<<KG_ALT},
{"rightAlt", 1<<KG_ALTGR},
{"control", 1<<KG_CTRL},
{"any", ~0},
{"all", ~0},
{"none", 0},
{NULL,0}
};
/* provide defaults */
int opt_mask = ~0; /* get everything */
int opt_default = ~GPM_HARD; /* pass everithing unused */
int opt_minMod = 0; /* run always */
int opt_maxMod = ~0; /* with any modifier */
int opt_intrct = 0;
int opt_vc = 0; /* by default get the current vc */
int opt_emacs = 0;
int opt_fit = 0;
int opt_pointer = 0;
/*===================================================================*/
int user_handler(Gpm_Event *event, void *data)
{
if (opt_fit) Gpm_FitEvent(event);
printf("mouse: event 0x%02X, at %2i,%2i (delta %2i,%2i), "
"buttons %i, modifiers 0x%02X\r\n",
event->type,
event->x, event->y,
event->dx, event->dy,
event->buttons, event->modifiers);
if (event->type & (GPM_DRAG|GPM_DOWN)) {
if (0 != opt_pointer) {
GPM_DRAWPOINTER(event);
} /*if*/
} /*if*/
return 0;
}
/*-------------------------------------------------------------------*/
int emacs_handler(Gpm_Event *event, void *data)
{
int i,j;
static int dragX, dragY;
static char buffer[64];
/* itz Mon Mar 23 20:54:54 PST 1998 emacs likes the modifier bits in
alphabetical order, so I'll use a lookup table instead of a loop; it
is faster anyways */
/* static char *s_mod[]={"S-","M-","C-","M-",NULL}; */
static char *s_mod[] = {
"", /* 000 */
"S-", /* 001 */
"M-", /* 002 */
"M-S-", /* 003 */
"C-", /* 004 */
"C-S-", /* 005 */
"C-M-", /* 006 */
"C-M-S-", /* 007 */
/* idea: maybe we should map AltGr to Emacs Alt instead of Meta? */
"M-", /* 010 */
"M-S-", /* 011 */
"M-", /* 012 */
"M-S-", /* 013 */
"C-M-", /* 014 */
"C-M-S-", /* 015 */
"C-M-", /* 016 */
"C-M-S-", /* 017 */
};
/* itz Mon Mar 23 08:23:14 PST 1998 what emacs calls a `drag' event
is our `up' event with coordinates different from the `down'
event. What gpm calls `drag' is just `mouse-movement' to emacs. */
static char *s_type[]={"mouse-movement", "mouse-movement","down-mouse-","mouse-",NULL};
static char *s_button[]={"3","2","1",NULL};
static char *s_multi[]={"double-", "triple-", 0};
static char s_count[]="23";
char count = '1';
struct timeval tv_cur;
long timestamp;
static long dragTime;
/* itz Mon Mar 23 08:27:53 PST 1998 this flag is needed because even
if the final coordinates of a drag are identical to the initial
ones, it is still a drag if there was any movement in between. Sigh. */
static int dragFlag = 0;
gettimeofday(&tv_cur, 0);
timestamp = ((short)tv_cur.tv_sec) * 1000 + (tv_cur.tv_usec / 1000);
if (opt_fit) Gpm_FitEvent(event);
buffer[0]=0;
/* itz Sun Mar 22 19:09:04 PST 1998 Emacs doesn't understand
modifiers on motion events. */
if (!(event->type & (GPM_MOVE|GPM_DRAG))) {
/* modifiers */
strcpy(buffer, s_mod[event->modifiers & ALL_KEY_MODS]);
/* multiple */
for (i=0, j=GPM_DOUBLE; s_multi[i]; i++, j<<=1)
if (event->type & j) {
count = s_count[i];
strcat(buffer,s_multi[i]);
} /*if*/
} /*if*/
if (event->type & GPM_DRAG) {
dragFlag = 1;
} /*if*/
/* itz Mon Mar 23 08:26:33 PST 1998 up-event after movement is a drag. */
if ((event->type & GPM_UP) && dragFlag) {
strcat(buffer, "drag-");
} /*if*/
/* type */
for (i=0, j=GPM_MOVE; s_type[i]; i++, j<<=1)
if (event->type & j)
strcat(buffer,s_type[i]);
/* itz Sun Mar 22 19:09:04 PST 1998 Emacs doesn't understand
modifiers on motion events. */
if (!(event->type & (GPM_MOVE|GPM_DRAG)))
/* button */
for (i=0, j=GPM_B_RIGHT; s_button[i]; i++, j<<=1)
if (event->buttons & j)
strcat(buffer,s_button[i]);
if ((event->type & GPM_UP) && dragFlag) {
printf("(%s ((%i . %i) %ld) %c ((%i . %i) %ld))\n",
buffer,
event->x, event->y, timestamp,
count,
dragX, dragY, dragTime);
} else if (event->type & (GPM_DOWN|GPM_UP)) {
printf("(%s ((%i . %i) %ld) %c)\n",
buffer, event->x, event->y, timestamp, count);
} else if (event->type & (GPM_MOVE|GPM_DRAG)) {
printf("(%s ((%i . %i) %ld))\n",
buffer, event->x, event->y, timestamp);
} /*if*/
if (event->type & GPM_DOWN) {
dragX=event->x;
dragY=event->y;
dragTime=timestamp;
dragFlag = 0;
} /*if*/
if (event->type & (GPM_DRAG|GPM_DOWN)) {
if (0 == opt_pointer) {
GPM_DRAWPOINTER(event);
} /*if*/
} /*if*/
return 0;
}
/*===================================================================*/
int usage(void)
{
//printf( "(" GPM_NAME ") " GPM_RELEASE ", " GPM_DATE "\n"
printf( "(" GPM_NAME ") , " GPM_DATE "\n"
"Usage: %s [options]\n",prgname);
printf(" Valid options are\n"
" -C <number> choose virtual console (beware of it)\n"
" -d <number> choose the default mask\n"
" -e <number> choose the eventMask\n"
" -E emacs-mode\n"
" -i accept commands from stdin\n"
" -f fit drag events inside the screen\n"
" -m <number> minimum modifier mask\n"
" -M <number> maximum modifier mask\n"
" -p show pointer while dragging\n"
" -u user-mode (default)\n"
);
return 1;
}
/*===================================================================*/
#define PARSE_EVENTS 0
#define PARSE_MODIFIERS 1
void getmask(char *arg, int which, int* where)
{
int last=0, value=0;
char *cur;
struct node *table, *n;
int mode = 0; /* 0 = set, 1 = add, 2 = subtract */
if ('+' == arg[0]) {
mode = 1;
++arg;
} else if ('-' == arg[0]) {
mode = 2;
++arg;
}
if (isdigit(arg[0])) {
switch(mode) {
case 0: *where = atoi(arg); break;
case 1: *where |= atoi(arg); break;
case 2: *where &= ~atoi(arg); break;
} /*switch*/
return;
} /*if*/
table= (PARSE_MODIFIERS == which) ? tableMod : tableEv;
while (1)
{
while (*arg && !isalnum(*arg)) arg++; /* skip delimiters */
cur=arg;
while(isalnum(*cur)) cur++; /* scan the word */
if (!*cur) last++;
*cur=0;
for (n=table;n->name;n++)
if (!strcmp(n->name,arg))
{
value |= n->flag;
break;
}
if (!n->name) fprintf(stderr,"%s: Incorrect flag \"%s\"\n",prgname,arg);
if (last) break;
cur++; arg=cur;
}
switch(mode) {
case 0: *where = value; break;
case 1: *where |= value; break;
case 2: *where &= ~value; break;
} /*switch*/
}
/*===================================================================*/
int cmdline(int argc, char **argv, char *options)
{
int opt;
while ((opt = getopt(argc, argv, options)) != -1)
{
switch (opt)
{
/* itz Tue Mar 24 17:11:52 PST 1998 i hate options that do
too much. Made them orthogonal. */
case 'C': sscanf(optarg,"%x",&opt_vc); break;
case 'd': getmask(optarg, PARSE_EVENTS, &opt_default); break;
case 'e': getmask(optarg, PARSE_EVENTS, &opt_mask); break;
case 'E': opt_emacs = 1; break;
case 'i': opt_intrct=1; break;
case 'f': opt_fit=1; break;
case 'm': getmask(optarg, PARSE_MODIFIERS, &opt_minMod); break;
case 'M': getmask(optarg, PARSE_MODIFIERS, &opt_maxMod); break;
case 'p': opt_pointer =1; break;
case 'u': opt_emacs=0; break;
default: return 1;
}
}
return 0;
}
void
do_snapshot()
{
Gpm_Event event;
int i=Gpm_GetSnapshot(&event);
char *s;
if (-1 == i) {
fprintf(stderr,"Warning: cannot get snapshot!\n");
fprintf(stderr,"Have you run \"configure\" and \"make install\"?\n");
return;
} /*if*/
fprintf(stderr,"Mouse has %d buttons\n",i);
fprintf(stderr,"Currently sits at (%d,%d)\n",event.x,event.y);
fprintf(stderr,"The window is %d columns by %d rows\n",event.dx,event.dy);
s=Gpm_GetLibVersion(&i);
fprintf(stderr,"The library is version \"%s\" (%i)\n",s,i);
s=Gpm_GetServerVersion(&i);
fprintf(stderr,"The daemon is version \"%s\" (%i)\n",s,i);
fprintf(stderr,"The current console is %d, with modifiers 0x%02x\n",
event.vc,event.modifiers);
fprintf(stderr,"The button mask is 0x%02X\n",event.buttons);
}
/*===================================================================*/
int interact(char *cmd) /* returns 0 on success and !=0 on error */
{
Gpm_Connect conn;
int argc=0;
char *argv[20];
if (*cmd && cmd[strlen(cmd)-1]=='\n')
cmd[strlen(cmd)-1]='\0';
if (!*cmd) return 0;
/*
* Interaction is accomplished by building an argv and passing it to
* cmdline(), to use the same syntax used to invoke the program
*/
while (argc<19)
{
while(isspace(*cmd)) cmd++;
argv[argc++]=cmd;
while (*cmd && isgraph(*cmd)) cmd++;
if (!*cmd) break;
*cmd=0;
cmd++;
}
argv[argc]=NULL;
if (!strcmp(argv[0],"pop")) {
return (Gpm_Close()==0 ? 1 : 0); /* a different convention on ret values */
} /*if*/
if (!strcmp(argv[0],"info")) {
fprintf(stderr,"The stack of connection info is %i depth\n",gpm_flag);
return 0;
} /*if*/
if (!strcmp(argv[0],"quit")) {
exit(0);
} /*if*/
if (!strcmp(argv[0],"snapshot")) {
do_snapshot();
return 0;
} /*if*/
optind=0; /* scan the entire line */
if (strcmp(argv[0],"push") || cmdline(argc,argv,"d:e:m:M:")) {
fprintf(stderr,"Syntax error in input line\n");
return 0;
} /*if*/
conn.eventMask=opt_mask;
conn.defaultMask=opt_default;
conn.maxMod=opt_maxMod;
conn.minMod=opt_minMod;
if (Gpm_Open(&conn,opt_vc)==-1)
{
fprintf(stderr,"%s: Can't open mouse connection\r\n",argv[0]);
return 1;
}
return 0;
}
/*===================================================================*/
int main(int argc, char **argv)
{
Gpm_Connect conn;
char cmd[128];
Gpm_Handler* my_handler; /* not the real gpm handler! */
fd_set readset;
prgname=argv[0];
if (cmdline(argc,argv,"C:d:e:Efim:M:pu")) exit(usage());
gpm_zerobased = opt_emacs;
conn.eventMask=opt_mask;
conn.defaultMask=opt_default;
conn.maxMod=opt_maxMod;
conn.minMod=opt_minMod;
if (Gpm_Open(&conn,opt_vc) == -1) {
gpm_report(GPM_PR_ERR,"%s: Can't open mouse connection\n",prgname);
exit(1);
} else if (gpm_fd == -2) {
gpm_report(GPM_PR_OOPS,"%s: use rmev to see gpm events in xterm or rxvt\n",prgname);
}
gpm_report(GPM_PR_DEBUG,"STILL RUNNING_1");
my_handler= opt_emacs ? emacs_handler : user_handler;
/* itz Sun Mar 22 09:51:33 PST 1998 needed in case the output is a pipe */
setvbuf(stdout, 0, _IOLBF, 0);
setvbuf(stdin, 0, _IOLBF, 0);
while(1) { /* forever */
FD_ZERO(&readset);
FD_SET(gpm_fd, &readset);
if (opt_intrct) {
FD_SET(STDIN_FILENO, &readset);
}
if (select(gpm_fd+1, &readset, 0, 0, 0) < 0 && errno == EINTR)
continue;
if (FD_ISSET(STDIN_FILENO, &readset)) {
if (0 == fgets(cmd, sizeof(cmd), stdin) || interact(cmd)) {
exit(0); /* ^D typed on input */
} /*if*/
} /*if*/
if (FD_ISSET(gpm_fd, &readset)) {
Gpm_Event evt;
if (Gpm_GetEvent(&evt) > 0) {
my_handler(&evt, 0);
} else {
fprintf(stderr, "mev says : Oops, Gpm_GetEvent()\n");
}
} /*if*/
} /*while*/
/*....................................... Done */
while (Gpm_Close()); /* close all the stack */
exit(0);
}