|  | @@ -10,6 +10,7 @@
 | 
	
		
			
				|  |  |  #include <X11/Xos.h>
 | 
	
		
			
				|  |  |  #include <X11/keysymdef.h>
 | 
	
		
			
				|  |  |  #include <X11/XKBlib.h>
 | 
	
		
			
				|  |  | +#include <X11/extensions/XInput2.h>
 | 
	
		
			
				|  |  |  #include <GL/glx.h>
 | 
	
		
			
				|  |  |  //#include <GL/glext.h>
 | 
	
		
			
				|  |  |  //#include <GL/glu.h>
 | 
	
	
		
			
				|  | @@ -34,6 +35,66 @@ typedef void (*glXSwapIntervalEXTProc)(Display *dpy, GLXDrawable drawable, int i
 | 
	
		
			
				|  |  |  glXCreateContextAttribsARBProc glXCreateContextAttribsF;
 | 
	
		
			
				|  |  |  glXSwapIntervalEXTProc glXSwapIntervalEXTF;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void la_PrintWacomValuators(Display *display, XIAnyClassInfo **classes, int num_classes){
 | 
	
		
			
				|  |  | +    int i;
 | 
	
		
			
				|  |  | +    for (i = 0; i < num_classes; i++) {
 | 
	
		
			
				|  |  | +        if (classes[i]->type == XIValuatorClass) {
 | 
	
		
			
				|  |  | +            XIValuatorClassInfo *v = (XIValuatorClassInfo*)classes[i];
 | 
	
		
			
				|  |  | +            logPrint("        Valuator %d: '%s'\n", v->number, (v->label) ?  XGetAtomName(display, v->label) : "No label");
 | 
	
		
			
				|  |  | +            logPrint("            Range: %f - %f\n", v->min, v->max);
 | 
	
		
			
				|  |  | +            logPrint("            Resolution: %d units/m\n", v->resolution);
 | 
	
		
			
				|  |  | +            logPrint("            Mode: %s\n", v->mode == XIModeAbsolute ? "absolute": "relative");
 | 
	
		
			
				|  |  | +            if (v->mode == XIModeAbsolute) logPrint("            Current value: %f\n", v->value);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +static void la_RegisterWacomEventMasks(Display *display, int deviceid)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	XIDeviceInfo *info, *dev;
 | 
	
		
			
				|  |  | +	int i, ndevices;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	dev = XIQueryDevice(display, deviceid, &ndevices);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    XIEventMask *mask=memAcquireSimple(sizeof(XIEventMask));
 | 
	
		
			
				|  |  | +    mask->deviceid = deviceid;
 | 
	
		
			
				|  |  | +    mask->mask_len = XIMaskLen(XI_RawMotion);
 | 
	
		
			
				|  |  | +    mask->mask=memAcquireSimple(mask->mask_len);
 | 
	
		
			
				|  |  | +    memset(mask->mask, 0, mask->mask_len);
 | 
	
		
			
				|  |  | +	XISetMask(mask->mask, XI_RawMotion);
 | 
	
		
			
				|  |  | +    XISelectEvents(display, DefaultRootWindow(display), mask, 1);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	logPrint("    Device Name: '%s' (%d)\n", dev->name, dev->deviceid);
 | 
	
		
			
				|  |  | +	//la_PrintWacomValuators(display, dev->classes, dev->num_classes);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +static void la_ScanWacomDevices(Display *display, int deviceid){
 | 
	
		
			
				|  |  | +    XIDeviceInfo *info, *dev;
 | 
	
		
			
				|  |  | +    int ndevices;
 | 
	
		
			
				|  |  | +    int i; char * word;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    int _event, _error;
 | 
	
		
			
				|  |  | +    if (!XQueryExtension(MAIN.dpy, "XInputExtension", &MAIN.xi_opcode, &_event, &_error)) {
 | 
	
		
			
				|  |  | +        logPrint("X Input extension not available, wacom pressure events are not available.\n"); return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    info = XIQueryDevice(display, deviceid, &ndevices);
 | 
	
		
			
				|  |  | +    for(i = 0; i < ndevices; i++) {
 | 
	
		
			
				|  |  | +        dev = &info[i];
 | 
	
		
			
				|  |  | +        word = strtok (dev->name," ");
 | 
	
		
			
				|  |  | +        while (1) {
 | 
	
		
			
				|  |  | +            word = strtok (NULL, " "); if (!word) break;
 | 
	
		
			
				|  |  | +            if (strcmp("stylus", word) == 0) MAIN.WacomDeviceStylus = dev->deviceid;
 | 
	
		
			
				|  |  | +            elif (strcmp("eraser", word) == 0) MAIN.WacomDeviceEraser = dev->deviceid;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if(MAIN.WacomDeviceStylus || MAIN.WacomDeviceEraser){
 | 
	
		
			
				|  |  | +        logPrintNew("Found wacom devices:\n");
 | 
	
		
			
				|  |  | +        if(MAIN.WacomDeviceStylus) la_RegisterWacomEventMasks(display, MAIN.WacomDeviceStylus);
 | 
	
		
			
				|  |  | +        if(MAIN.WacomDeviceEraser) la_RegisterWacomEventMasks(display, MAIN.WacomDeviceEraser);
 | 
	
		
			
				|  |  | +	}else{
 | 
	
		
			
				|  |  | +        logPrintNew("No wacom pen device connected.\n");
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  Window la_CreateWindowX11(int x, int y, int w, int h, char *title, int SyncToVBlank, GLXContext* r_glc){
 | 
	
		
			
				|  |  |      XSetWindowAttributes swa;
 | 
	
		
			
				|  |  |      XWindowAttributes wa;
 | 
	
	
		
			
				|  | @@ -233,6 +294,8 @@ int laGetReady(){
 | 
	
		
			
				|  |  |          exit(0);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    la_ScanWacomDevices(MAIN.dpy,XIAllDevices);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      static int visual_attribs[] =
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  |        GLX_X_RENDERABLE    , True,
 | 
	
	
		
			
				|  | @@ -604,6 +667,10 @@ void la_SaveEvent(Window hwnd, laEvent *e, int use_last_pos){
 | 
	
		
			
				|  |  |              e->x = rx; e->y = ry;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    e->Pressure=MAIN.PointerIsEraser?MAIN.EraserPressure:MAIN.StylusPressure;
 | 
	
		
			
				|  |  | +    e->AngleX=MAIN.PointerIsEraser?MAIN.EraserAngleX:MAIN.StylusAngleX;
 | 
	
		
			
				|  |  | +    e->AngleY=MAIN.PointerIsEraser?MAIN.EraserAngleY:MAIN.StylusAngleY;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      lstAppendItem(el, (laListItem *)e);
 | 
	
		
			
				|  |  |      laMappingRequestEval();
 | 
	
		
			
				|  |  |  };
 | 
	
	
		
			
				|  | @@ -6076,6 +6143,21 @@ void laFinalizeOperators(){
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +//=================
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void la_RegisterRawMotions(XIRawEvent *event)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    double *valuator = event->valuators.values;
 | 
	
		
			
				|  |  | +    int IsStylus=event->deviceid==MAIN.WacomDeviceStylus;
 | 
	
		
			
				|  |  | +    if(!IsStylus) MAIN.PointerIsEraser = 1; else MAIN.PointerIsEraser = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if(XIMaskIsSet(event->valuators.mask, 2)){ if(IsStylus) MAIN.StylusPressure=valuator[2]/65536; else MAIN.EraserPressure=valuator[2]/65536; }
 | 
	
		
			
				|  |  | +    if(XIMaskIsSet(event->valuators.mask, 3)){ if(IsStylus) MAIN.StylusAngleX=valuator[3]; else MAIN.EraserAngleX=valuator[3]; }
 | 
	
		
			
				|  |  | +    if(XIMaskIsSet(event->valuators.mask, 4)){ if(IsStylus) MAIN.StylusAngleY=valuator[4]; else MAIN.EraserAngleY=valuator[4]; }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int la_UpdateOperatorHints(laWindow* w){
 | 
	
		
			
				|  |  |      laSafeString* ss=0;
 | 
	
		
			
				|  |  |      for(laOperator* o=w->Operators.pFirst;o;o=o->Item.pNext){
 | 
	
	
		
			
				|  | @@ -6245,9 +6327,14 @@ int la_ProcessSysMessage(){
 | 
	
		
			
				|  |  |      if(!MAIN.IdleTriggered && MAIN.TimeAccum-MAIN.IdleStart>MAIN.IdleTime) SendIdle=1;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      while(XPending(MAIN.dpy)){
 | 
	
		
			
				|  |  | +        XGenericEventCookie *cookie = &e.xcookie;
 | 
	
		
			
				|  |  |          XNextEvent(MAIN.dpy, &e);
 | 
	
		
			
				|  |  |          if (XFilterEvent(&e, None)) continue;
 | 
	
		
			
				|  |  |          SendIdle=0; MAIN.IdleStart=MAIN.TimeAccum; MAIN.IdleTriggered=0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (cookie->type == GenericEvent && cookie->extension == MAIN.xi_opcode && XGetEventData(MAIN.dpy, cookie)){
 | 
	
		
			
				|  |  | +            if (cookie->evtype == XI_RawMotion) la_RegisterRawMotions(cookie->data);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |              
 | 
	
		
			
				|  |  |          switch(e.type){
 | 
	
		
			
				|  |  |          case ConfigureNotify:
 | 
	
	
		
			
				|  | @@ -6262,6 +6349,7 @@ int la_ProcessSysMessage(){
 | 
	
		
			
				|  |  |              break;
 | 
	
		
			
				|  |  |          case ButtonPress:
 | 
	
		
			
				|  |  |              type=LA_MOUSEDOWN;
 | 
	
		
			
				|  |  | +            printf("down\n");
 | 
	
		
			
				|  |  |              if(e.xbutton.button==1){type|=LA_KEY_MOUSE_LEFT;}
 | 
	
		
			
				|  |  |              elif(e.xbutton.button==2){type|=LA_KEY_MOUSE_MIDDLE;}
 | 
	
		
			
				|  |  |              elif(e.xbutton.button==3){type|=LA_KEY_MOUSE_RIGHT;}
 | 
	
	
		
			
				|  | @@ -6337,6 +6425,7 @@ int la_ProcessSysMessage(){
 | 
	
		
			
				|  |  |          default:
 | 
	
		
			
				|  |  |              break;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        XFreeEventData(MAIN.dpy, cookie);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      for(laWindow* w=MAIN.Windows.pFirst;w;w=w->Item.pNext){
 |