/* * Ivypointer * * Copyright (C) 2007 * DGAC / DSNA / DTI / R&D * * Main and only file * * Authors: Benjamin Tissoires * * Please refer to file version.h for the * copyright notice regarding this software */ //#define _MPX_ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #if X_DISPLAY_MISSING # error X11 is required to build xtest #else # include # include # include //# include //# include # define XK_MISCELLANY # define XK_XKB_KEYS # include # ifndef IsXExtensionKeyboard # define IsXExtensionKeyboard 3 # endif # ifndef IsXExtensionPointer # define IsXExtensionPointer 4 # endif # if HAVE_X11_EXTENSIONS_XTEST_H # include # else # error The XTest extension is required to build xtest # endif # if HAVE_X11_EXTENSIONS_XINERAMA_H // Xinerama.h may lack extern "C" for inclusion by C++ extern "C" { # include } # endif # if HAVE_XKB_EXTENSION # include # endif #endif #include #include #include #include "getopt.h" #define REGEXP_CHANGE_GEOMETRY_SCREEN "^screen_geometry_event device_id=%s x0=(.*) y0=(.*) x1=(.*) y1=(.*)" #define REGEXP_LOCK_UNLOCK "^lock_event device_id=%s status=(lock|unlock)" #define REGEXP_MOVE "^TelepointerMotion Id=%s X=(.*) Y=(.*)" #define REGEXP_DRAG "^TelepointerDrag Id=%s X=(.*) Y=(.*)" #define REGEXP_REL_MOVE "^TelepointerRelativeMotion Id=%s X=(.*) Y=(.*)" #define REGEXP_BUTTON "^TelepointerButton Id=%s Button=(.*) status=(.*)" #define CHAMP_X_TELEPOINTEUR 1 #define CHAMP_Y_TELEPOINTEUR 2 #define REGEXP_POINTER "^pointer_event device_id=%s x=(.*) y=(.*) presure=(.*) tilt_x=(.*) tilt_y=(.*) wheel=(.*) predicted_x=(.*) predicted_y=(.*) type=(.*) serial_number=(.*) time=(.*) hires_x=(.*) hires_y=(.*) proximity=(.*)" #define REGEXP_PAD "^pad_event device_id=%s button=(.*) status=(.*) time=(.*)" #define REGEXP_SLIDER "^slider_event device_id=%s value=(.*) side=(.*) time=(.*)" #define REGEXP_BUTTON_WACOM "^button_event device_id=%s button=(.*) status=(.*) x=(.*) y=(.*) presure=(.*) tilt_x=(.*) tilt_y=(.*) wheel=(.*) predicted_x=(.*) predicted_y=(.*) type=(.*) serial_number=(.*) time=(.*) hires_x=(.*) hires_y=(.*) proximity=(.*)" #define CHAMP_X_WACOM 12 #define CHAMP_Y_WACOM 13 #define CHAMP_X_WACOM_BUTTON 14 #define CHAMP_Y_WACOM_BUTTON 15 #define CHAMP_X_WACOM_PREDICTIVE 7 #define CHAMP_Y_WACOM_PREDICTIVE 8 #define CHAMP_X_WACOM_PREDICTIVE_BUTTON 9 #define CHAMP_Y_WACOM_PREDICTIVE_BUTTON 10 #define TIME_CLICK 200 #define TIME_BETWEEN_CLICK 200 static void printHelpMsg(const char *name); static Display* display = NULL; #ifdef _MPX_ static XDevice* dev = NULL; //#else //# error plop #endif static float width,height; static float coords_input[4] = {0.0,0.0,100.0,100.0}; static float width_input,height_input, offsetx, offsety; static float horiz_ratio; // width / width_input static float vert_ratio; // height / height_input static int dragged = 0; static int relative = 0; static int switch_mode = 0; static int locked = 0; static int predictive_mode = 0; static int champX; static int champY; static int champXButton; static int champYButton; static float old_x = 0; static float old_y = 0; static int device_input_id_given = 0; static Display* openDisplay(const char* displayName) { // get the DISPLAY if (displayName == NULL) { displayName = getenv("DISPLAY"); if (displayName == NULL) { displayName = ":0.0"; } } // open the display printf("XOpenDisplay(\"%s\")\n", displayName); display = XOpenDisplay(displayName); if (display == NULL) { printf("EE : can't open display \"%s\"\n", displayName); return NULL; // throw XScreenUnavailable(60.0); } // verify the availability of the XTest extension int majorOpcode, firstEvent, firstError; if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) { printf("XTEST extension not available"); XCloseDisplay(display); return NULL; } XTestGrabControl(display, True); #if HAVE_XKB_EXTENSION { m_xkb = false; int major = XkbMajorVersion, minor = XkbMinorVersion; if (XkbLibraryVersion(&major, &minor)) { int opcode, firstError; if (XkbQueryExtension(display, &opcode, &m_xkbEventBase, &firstError, &major, &minor)) { m_xkb = true; XkbSelectEvents(display, XkbUseCoreKbd, XkbMapNotifyMask, XkbMapNotifyMask); XkbSelectEventDetails(display, XkbUseCoreKbd, XkbStateNotifyMask, XkbGroupStateMask, XkbGroupStateMask); } } } #endif return display; } #ifdef _MPX_ static XDevice *openDevice(Display * dpy, int id){ return XOpenDevice(dpy, id); } #endif // -------------------------------------------------------------------------- // tools // -------------------------------------------------------------------------- static int valid(float x, float y) { return !locked && x>=coords_input[0] && x<=coords_input[2] && y>=coords_input[1] && y<=coords_input[3]; } // -------------------------------------------------------------------------- // X11 functions // -------------------------------------------------------------------------- static void fakeMouseButton(const unsigned int xButton, Bool press, int x, int y) { if (xButton != 0) { #ifndef _MPX_ XTestFakeButtonEvent(display, xButton, press ? True : False, CurrentTime); #else int axes[2] = {x,y}; XTestFakeDeviceButtonEvent(display, dev, xButton, press ? True : False, axes, 2, CurrentTime); #endif XFlush(display); } } static void fakeMouseMove(int x, int y) { // printf("x = %d,y = %d;\n ",x,y); #ifndef _MPX_ XTestFakeMotionEvent(display, DefaultScreen(display), x, y, CurrentTime); #else int axes[2] = {x,y}; XTestFakeDeviceMotionEvent(display, dev, False, 0, axes, 2, CurrentTime); #endif //XWarpPointer(display, None, RootWindow(display, 0), 0, 0, 0, 0, x, y); XFlush(display); } static void fakeMouseRelativeMove(int dx, int dy) { #ifndef _MPX_ XTestFakeRelativeMotionEvent(display, dx, dy, CurrentTime); #else int axes[2] = {dx,dy}; XTestFakeDeviceMotionEvent(display, dev, True, 0, axes, 2, CurrentTime); #endif //XWarpPointer(display, None, None, 0, 0, 0, 0, dx, dy); XFlush(display); } // -------------------------------------------------------------------------- // Geometry callback // -------------------------------------------------------------------------- static void CallbackScreen(IvyClientPtr app, void *user_data, int argc, char *argv[]) { // char *id = argv[0]; float x0 = atof(argv[1-device_input_id_given]); float y0 = atof(argv[2-device_input_id_given]); float x1 = atof(argv[3-device_input_id_given]); float y1 = atof(argv[4-device_input_id_given]); if (x1 > x0 && y1 > y0 ){ offsetx = x0; offsety = y0; width = x1-x0; height = y1-y0; horiz_ratio = width / width_input; vert_ratio = height / height_input; printf("screen : %f+%f x %f+%f\n",offsetx,width,offsety,height); } else { printf("bad coords received : %.2f,%.2f %.2f,%.2f\n",x0,y0,x1,y1); } } // -------------------------------------------------------------------------- // Lock/Unlock callback // -------------------------------------------------------------------------- static void CallbackLockUnlock(IvyClientPtr app, void *user_data, int argc, char *argv[]) { locked = strcmp(argv[1-device_input_id_given],"lock"); } // -------------------------------------------------------------------------- // Telepointer callbacks // -------------------------------------------------------------------------- static void CallbackMove (IvyClientPtr app, void *user_data, int argc, char *argv[]) { // printf ("%s sent (size : %d)",IvyGetApplicationName(app),argc); // for (i = 0; i < argc; i++) // printf(" '%s'",argv[i]); float x = atof(argv[champX]); float y = atof(argv[champY]); if (!valid(x,y)){ // ignore outside events return; } x = offsetx + x * horiz_ratio; y = offsety + y * vert_ratio; // printf("x = %f,y = %f\n",x,y); old_x = x; old_y = y; fakeMouseMove((int)x,(int)y); if (dragged){ fakeMouseButton(1,0,x,y); dragged = 0; } } static void CallbackDrag (IvyClientPtr app, void *user_data, int argc, char *argv[]) { // char *id = argv[0]; float x = atof(argv[champX]); float y = atof(argv[champY]); if (!valid(x,y)){ // ignore outside events return; } x = offsetx + x * horiz_ratio; y = offsety + y * vert_ratio; // printf("x = %f,y = %f\n",x,y); old_x = x; old_y = y; fakeMouseMove((int)x,(int)y); if (!dragged){ fakeMouseButton(1,1,x,y); dragged = 1; } } static void CallbackRelMove (IvyClientPtr app, void *user_data, int argc, char *argv[]) { // char *id = argv[0]; float dx = atof(argv[1-device_input_id_given]); float dy = atof(argv[2-device_input_id_given]); fakeMouseRelativeMove((int)dx,(int)dy); } static void CallbackButton (IvyClientPtr app, void *user_data, int argc, char *argv[]) { // char *id = argv[0]; int b = atoi(argv[1-device_input_id_given]); int press = atoi(argv[2-device_input_id_given]); if (!valid(old_x,old_y)){ // ignore outside events return; } fakeMouseButton(b,press,0,0); } // -------------------------------------------------------------------------- // Wacom callbacks // -------------------------------------------------------------------------- static void CallbackPointer (IvyClientPtr app, void *user_data, int argc, char *argv[]) { // char *id = argv[0]; float x = atof(argv[champX]); // predicted position float y = atof(argv[champY]); // predicted position int time = atoi(argv[11-device_input_id_given]); int presure = atoi(argv[3-device_input_id_given]); static int old_time = 0; static int should_click = 0; static int clicked = 0; if (!valid(x,y)){ // ignore outside events return; } if (!predictive_mode) { // in predictive mode, we don't allow the geometry to be changed x = offsetx + x * horiz_ratio; y = offsety + y * vert_ratio; } if (!relative) { //absolute mode fakeMouseMove((int)x,(int)y); } else { //relative mode int dx = (int)(x-old_x); int dy = (int)(y-old_y); int dt = time-old_time; // if (old_time) // printf("time=%d, old_time=%d, dt=%d\n",time, old_time, dt); if (presure > 0) { if (!should_click){ old_time = time; dt = 0; should_click = 1; // printf("armement du timeout\n"); } else { if (should_click ==1 && dt > TIME_CLICK){ should_click = 2; // printf("timeout\n"); } } fakeMouseRelativeMove(dx,dy); } else { if (should_click == 1) { // printf("dt = %d\n",dt); if (clicked) { //double click // printf("ck\n"); fakeMouseButton(1,0,x,y); } // printf("cli\n"); clicked = 1; old_time = time; fakeMouseButton(1,1,x,y); } should_click = 0; if (clicked) { if (dt > TIME_BETWEEN_CLICK){ // printf("ck\n"); fakeMouseButton(1,0,x,y); clicked = 0; } } else { old_time = 0; } } } // release (common of both modes) if (dragged && presure==0){ fakeMouseButton(1,0,x,y); dragged = 0; } old_x = x; old_y = y; } static void CallbackPad (IvyClientPtr app, void *user_data, int argc, char *argv[]) { if (!valid(old_x,old_y)){ // ignore outside events return; } int b = atoi(argv[1-device_input_id_given]); int press = 0; // if (!b) // return if (strcmp(argv[2-device_input_id_given],"up")){ press = 1; } // printf("Pad button=%d pressed%d event received\n",b, press); if (switch_mode && (b==11 || b==15 || (press && (b==12 || b==16)))){ //inversion du mode relative = !relative; } else { fakeMouseButton(b,press,0,0); } } static void CallbackSlider (IvyClientPtr app, void *user_data, int argc, char *argv[]) { //TODO // char *id = argv[0]; // int b = atoi(argv[1]); // int press = atoi(argv[2]); // if (!b) // return // fakeMouseButton(b,press); } static void CallbackButtonWacom (IvyClientPtr app, void *user_data, int argc, char *argv[]) { // char *id = argv[0]; int b = atoi(argv[1-device_input_id_given]); if (b == 1 && relative) // gestion propre du bouton 1 return; int press = strcmp(argv[2-device_input_id_given],"up"); float x = atof(argv[champXButton]); float y = atof(argv[champYButton]); if (!valid(x,y)){ // ignore outside events return; } fakeMouseButton(b,press,0,0); } static int extractValues(char optarg[], float* output, const char name[]){ int i = 0; int j = 0; int last = 0; float coupures[4] = {0,0,0,0}; while(optarg[i]){ if (optarg[i] == ','){ coupures[j] = i; j++; if (j > 3) { printHelpMsg(name); exit(1); } } i++; } coupures[j] = i; j = 0; last = 0; for (i=0; i< 4; i++){ int length = coupures[i]-last; char buf [1024] = ""; int k; for (k = 0; k < length; k++) { buf[k] = optarg[last+k]; } output[i] = atof(buf); last = coupures[i]+1; } return 1; } static void printHelpMsg(const char *name){ const char* helpmsg = "[options] [regexps]\n\t-b bus\t\tdefines the Ivy bus to which to connect to, defaults to 127:2010\n" "\t-t\t\tuse data from ModeManager instead of xinput_wacom\n" "\t-C x0,y0,x1,y1\tspecify the coords of the screen used\n" "\t-d device\tuse \"device\" as the source of the events\n" "\t-r\tuse relative mode by default\n" "\t-s\tenable the ability to dynamically switch between relative and absolute\n" "\t-p\tuse predictive mode (disable geometry)\n" "\t-v\t\tprints the ivy relase number\n\n" ; printf("usage: %s %s",name,helpmsg); } // -------------------------------------------------------------------------- // Main // -------------------------------------------------------------------------- int main(int argc, char *argv[]) { const char* bus = 0; char busbuf [1024] = ""; char device_input_id [1024] = "(.*)"; int wacom = 1; float user_coordsO = 0; float coordsO[4] = {0,0,0,0}; int c; int id = -1; while ((c = getopt(argc, argv, "vb:td:C:rpi:s")) != EOF) switch (c) { case 'b': strcpy (busbuf, optarg); bus = busbuf; break; case 'v': printf("ivy c library version %d.%d\n",IVYMAJOR_VERSION,IVYMINOR_VERSION); break; case 'r': relative = 1; break; case 'd': strcpy (device_input_id, optarg); device_input_id_given = 1; break; case 't': wacom = 0; break; case 'p': predictive_mode = 1; break; case 'C': user_coordsO = extractValues(optarg, coordsO, argv[0]); break; case 'i': id = atoi(optarg); break; case 's': switch_mode = 1; break; default: printf("bad option : %s\n",(char*)&c); printHelpMsg(argv[0]); exit(1); } Display* d = openDisplay(NULL); if (!d){ return 1; } #ifdef _MPX_ if (id >=0){ printf("opening device id=%d\n",id); dev = openDevice(d,id); if (!dev) { printf("unable to open device id=%d\n",id); } } #endif if (!predictive_mode && user_coordsO){ offsetx = coordsO[0]; offsety = coordsO[1]; width = coordsO[2]-coordsO[0]; height = coordsO[3]-coordsO[1]; } else { offsetx = 0; offsety = 0; width = DisplayWidth(d, DefaultScreen(d)); height = DisplayHeight(d, DefaultScreen(d)); } printf("screen : %.2f+%.2f x %.2f+%.2f\n",offsetx,width,offsety,height); // verifications if (width < 2 || height < 2 ){ printf("screen geometry : %.2f %.2f\n",width,height); printHelpMsg(argv[0]); exit(1); } if (wacom) { if (predictive_mode){ coords_input[2] = width; coords_input[3] = height; } else { coords_input[2] = 1.0; coords_input[3] = 1.0; } //~ print_coords_input(); if (!predictive_mode) { champX = CHAMP_X_WACOM; champY = CHAMP_Y_WACOM; champXButton = CHAMP_X_WACOM_BUTTON; champYButton = CHAMP_Y_WACOM_BUTTON; } else { champX = CHAMP_X_WACOM_PREDICTIVE; champY = CHAMP_Y_WACOM_PREDICTIVE; champXButton = CHAMP_X_WACOM_PREDICTIVE_BUTTON; champYButton = CHAMP_Y_WACOM_PREDICTIVE_BUTTON; } } else { champX = CHAMP_X_TELEPOINTEUR; champY = CHAMP_Y_TELEPOINTEUR; } champX -= device_input_id_given; champY -= device_input_id_given; champXButton -= device_input_id_given; champYButton -= device_input_id_given; width_input = coords_input[2]-coords_input[0]; height_input = coords_input[3]-coords_input[1]; horiz_ratio = width / width_input; vert_ratio = height / height_input; MsgRcvPtr ptrMove,ptrRelMove,ptrButton,ptrGeometry,ptrLockUnlock; IvyInit("IvyPointer","IvyPointer Ready",NULL,NULL,NULL,NULL); char regexp[2048] = ""; if (!wacom) { // telepointer callbacks sprintf(regexp, REGEXP_MOVE, device_input_id); ptrMove=IvyBindMsg(CallbackMove,&ptrMove,REGEXP_MOVE, device_input_id); printf("bound to %s\n",regexp); sprintf(regexp, REGEXP_DRAG, device_input_id); ptrMove=IvyBindMsg(CallbackDrag,&ptrMove, REGEXP_DRAG, device_input_id); printf("bound to %s\n",regexp); sprintf(regexp, REGEXP_REL_MOVE, device_input_id); ptrMove=IvyBindMsg(CallbackRelMove,&ptrRelMove, REGEXP_REL_MOVE, device_input_id); printf("bound to %s\n",regexp); sprintf(regexp, REGEXP_BUTTON, device_input_id); ptrMove=IvyBindMsg(CallbackButton,&ptrButton, REGEXP_BUTTON, device_input_id); printf("bound to %s\n",regexp); } else { //wacom callbacks sprintf(regexp, REGEXP_POINTER, device_input_id); ptrMove=IvyBindMsg(CallbackPointer,&ptrMove, REGEXP_POINTER, device_input_id); printf("bound to %s\n",regexp); // ptrMove=IvyBindMsg(CallbackSlider,&ptrMove,REGEXP_SLIDER); // printf("bound to %s\n",REGEXP_SLIDER); sprintf(regexp, REGEXP_PAD, device_input_id); ptrMove=IvyBindMsg(CallbackPad,&ptrButton, REGEXP_PAD, device_input_id); printf("bound to %s\n",regexp); sprintf(regexp, REGEXP_BUTTON_WACOM, device_input_id); ptrMove=IvyBindMsg(CallbackButtonWacom,&ptrButton, REGEXP_BUTTON_WACOM, device_input_id); printf("bound to %s\n",regexp); } if (!predictive_mode) { sprintf(regexp, REGEXP_CHANGE_GEOMETRY_SCREEN, device_input_id); ptrGeometry=IvyBindMsg(CallbackScreen,&ptrGeometry, REGEXP_CHANGE_GEOMETRY_SCREEN, device_input_id); printf("bound to %s\n",regexp); } sprintf(regexp, REGEXP_LOCK_UNLOCK, device_input_id); ptrLockUnlock=IvyBindMsg(CallbackLockUnlock,&ptrLockUnlock, REGEXP_LOCK_UNLOCK, device_input_id); printf("bound to %s\n",regexp); IvyStart(bus); #if (IVYMAJOR_VERSION == 3) && (IVYMINOR_VERSION < 9) IvyMainLoop(NULL, NULL); #else IvyMainLoop(); #endif return 0; }