Logo Search packages:      
Sourcecode: wacom-tools version File versions

wacomxi.c

/*
 * wacomxi.c -- Add X11 extended input handling capability for wacomcpl.
 *
 * Author         : Ping Cheng
 * Creation date  : 04/05/2003
 *
 * Based on xi.c 1998-99 Patrick Lecoanet --
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This code 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this code; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>

#include <X11/extensions/XInput.h>
/*
 * Included to obtain the number of events used by the xinput
 * extension.
 */
#include <X11/extensions/XIproto.h>

#include "wacomxi.h"

/*
 *----------------------------------------------------------------------
 *
 * This code implements tcl commands that give access
 * to additional input devices manipulation at the tcl level.
 *
 * The command 'bindevent' binds tcl scripts to xinput events
 * occuring in a widget.
 *
 *----------------------------------------------------------------------
 */

#define CORE_KEYBOARD   1
#define CORE_POINTER    2

/*
 * These constants are used to represent fixed
 * event types. These types are computed by
 * mapping event types send by each server. The
 * mapping is done by a table maintained for
 * each server.
 */
#define KEY_EVENT       1
#define BUTTON_EVENT          2
#define MOTION_EVENT          3
#define FOCUS_EVENT           4
#define PROXIMITY_EVENT       5
#define CHANGE_DEVICE_EVENT   6
#define DEVICE_MAPPING_EVENT  7
#define DEVICE_STATE_EVENT    8

/*
 * These constants must be kept in sync with the
 * xi_event_names array.
 */
#define KEY_PRESS       0
#define KEY_RELEASE           1
#define BUTTON_PRESS          2
#define BUTTON_PRESS_GRAB     3
#define BUTTON_PRESS_OWNER_GRAB     4
#define BUTTON_RELEASE        5
#define MOTION                6
#define MOTION_HINT           7
#define BUTTON_MOTION         8
#define B1_MOTION       9
#define B2_MOTION       10
#define B3_MOTION       11
#define B4_MOTION       12
#define B5_MOTION       13
#define FOCUS_IN        14
#define FOCUS_OUT       15
#define PROXIMITY_IN          16
#define PROXIMITY_OUT         17
#define DEVICE_STATE          18
#define DEVICE_MAPPING        19
#define CHANGE_DEVICE         20

#define     NUM_XI_EVENTS           21
#define NUM_EVENTS            256   /* Core limit of the protocol */

#define NUM_SIZE 50

static Tk_Uid xi_event_names[NUM_XI_EVENTS] = {
  "KeyPress",
  "KeyRelease",
  "ButtonPress",
  "ButtonPressGrab",
  "ButtonPressOwnerGrab",
  "ButtonRelease",
  "Motion",
  "MotionHint",
  "ButtonMotion",
  "B1Motion",
  "B2Motion",
  "B3Motion",
  "B4Motion",
  "B5Motion",
  "FocusIn",
  "FocusOut",
  "ProximityIn",
  "ProximityOut",
  "DeviceState",
  "DeviceMapping",
  "ChangeDevice",
};

typedef struct _EventHandlerStruct {
  Tk_EventProc                *proc;
  ClientData                  client_data;
  int                   type;
  XID                   device_id;
  int                   num_classes;
  int                   classes[3];
  Tk_Window             tkwin;
  struct _EventHandlerStruct  *next;
} EventHandlerStruct;

typedef struct _EventScriptRecord {
  struct _DeviceInfoStruct    *device;
  Tk_Uid                event_spec;
  Tk_Window             tkwin;
  Tcl_Interp                  *interp;
  char                        *script;
  struct _EventScriptRecord   *next;
} EventScriptRecord;

typedef struct _AxeInfo {
  int min_value;
  int max_value;
  int resolution;
  int value;
} AxeInfo;

typedef struct _DeviceInfoStruct {
  struct _DisplayInfoStruct *dpy_info;
  XDevice   *xdev;
  Tk_Uid    name;
  XID       id;
  char            core;       /* If the device is the core keyboard or pointer */
  unsigned char   x_index;    /* index of valuator corresponding to core X */
  unsigned char   y_index;    /* index of valuator corresponding to core Y */
  int       num_axes;
  int       num_keys;
  int       num_buttons;
  char            focusable;  /* True if the device can be explicitly focused */
  char            proximity;  /* True if the device can emit proximity events */
  char            feedback;   /* True if the device has some feedback control */
  int       mode;       /* ABSOLUTE or RELATIVE */
  int       history_size;
  Time            last_motion_time;
  
  AxeInfo   *axe_info;
  int       *valuator_cache; /* Used to store valuators if the device reports
                          * more than six axes. The array is allocated
                          * dynamically to match the number of axes. */
  int       event_classes[NUM_XI_EVENTS];
  int       no_event_class;
} DeviceInfoStruct;

typedef struct _DisplayInfoStruct {
  char            has_xdevices;           /* Non zero if some devices are available
                               * (other than core devices). */
  Display   *display;         /* The display that is described. */
  DeviceInfoStruct *devices;        /* The device list */
  int       num_dev;
  char            event_types[NUM_XI_EVENTS];
  char            event_atypes[NUM_EVENTS]; /* Mapping from server local event types
                                 * to absolutes types independant from
                                 * servers. */
  int       event_base;       /* The first event type reported by the
                               * xinput extension on this server. */
  Tcl_HashTable   per_wins;         /* Records for all handlers per windows on
                               * this display. */
  EventHandlerStruct *other_handlers;     /* The handlers associated with events not
                               * reported relative to a window. */
  EventHandlerStruct *frozen_handlers;    /* The handlers on hold because theirs
                               * devices are currently core devices. */
  struct _DisplayInfoStruct *next;
} DisplayInfoStruct;

typedef struct _WindowInfoStruct {
  EventHandlerStruct    *handlers;
  EventScriptRecord     *scripts;
} WindowInfoStruct;

typedef struct _InProgress {
  ClientData            next_handler;     /* Next handler in search. */
  struct _InProgress    *next;            /* Next higher nested search. */
} InProgress;


static DisplayInfoStruct *display_infos = NULL;
static InProgress *pending_handlers = NULL;

static void InvokeEventScript(ClientData client_data, XEvent *e);
static WindowInfoStruct *GetWindowInfo(Tk_Window w, int create);


/*
 *----------------------------------------------------------------------
 *
 * GetDisplayInfo --
 *    Return all known information known about the display.
 *    This gets all the needed informations on the available devices
 *    including their name.
 *    This has the side effect (on the first call) of gathering from
 *    the server, all devices that are hooked on the display.
 *
 *----------------------------------------------------------------------
 */
static DisplayInfoStruct *
GetDisplayInfo(Display  *dpy)
{
    XDeviceInfoPtr      device_list;
    DisplayInfoStruct   *info;
    DeviceInfoStruct    *device;
    int                 i, axe, num_classes;
    XAnyClassPtr  any;
    XKeyInfoPtr         k;
    XButtonInfoPtr      b;
    XValuatorInfoPtr    v;
    int                 dummy = 0;

/*printf("Begin of GetDisplayInfo\n");*/
  
    /*
     * Lookup the display in the already known list.
     */
    info = display_infos;
    while (info) 
    {
      if (dpy == info->display) 
      {
          /*printf("End of GetDisplayInfo\n");*/
          return info;
      }
      else 
      {
          info = info->next;
      }
    }
    /*
     * Nothing found, make a new entry and fill it with all the available info.
     */
    /*
     * First ask if the server has made the xinput extension available.
     */
    info = (DisplayInfoStruct *) ckalloc(sizeof(DisplayInfoStruct));
    info->next = display_infos;
    display_infos = info;
    info->has_xdevices = XQueryExtension(dpy, INAME, &dummy,
                               &info->event_base, &dummy);
    info->display = dpy;
    Tcl_InitHashTable(&info->per_wins, TCL_ONE_WORD_KEYS);
    info->other_handlers = NULL;
    info->frozen_handlers = NULL;
    /*
     * Then ask the device list.
     */
    if (info->has_xdevices) 
    {
      device_list = (XDeviceInfoPtr) XListInputDevices(dpy, &info->num_dev);

      if (info->num_dev) 
      {
          info->devices = (DeviceInfoStruct *) 
                  ckalloc(info->num_dev * sizeof(DeviceInfoStruct));

          dummy = 0;
          for (device = info->devices, i = 0; i < info->num_dev; i++, device++) 
          {
            device->dpy_info = info;
            device->xdev = NULL;
            device->id = device_list[i].id;
            device->name = Tk_GetUid(device_list[i].name);
            device->core = ((device_list[i].use == IsXExtensionDevice) ? 0 :
            ((device_list[i].use == IsXPointer) ? CORE_POINTER : CORE_KEYBOARD));
            device->x_index = 0;
            device->y_index = 1;
            device->num_axes = 0;
            device->num_keys = 0;
            device->num_buttons = 0;
            device->focusable = 0;
            device->proximity = 0;
            device->feedback = 0;
            /*
             * Setup each input class declared by the device.
             */
            if (device_list[i].num_classes > 0) 
            {
                num_classes = device_list[i].num_classes;
                any = (XAnyClassPtr) device_list[i].inputclassinfo;
                while (num_classes--) 
                {
                  switch (any->class) 
                  {
                      case KeyClass:
                        k = (XKeyInfoPtr) any;
                        device->num_keys = k->num_keys;
                        break;
                      case ButtonClass:
                        b = (XButtonInfoPtr) any;
                        device->num_buttons = b->num_buttons;
                        break;
                      case ValuatorClass:
                        v = (XValuatorInfoPtr) any;
                        device->num_axes = v->num_axes;
                        device->axe_info = (AxeInfo*) 
                              ckalloc(sizeof(AxeInfo)*v->num_axes);
                        device->history_size = v->motion_buffer;
                        for (axe = 0; axe < v->num_axes; axe++) 
                        {
                            device->axe_info[axe].min_value = 
                                    v->axes[axe].min_value;
                            device->axe_info[axe].max_value = 
                                    v->axes[axe].max_value;
                            device->axe_info[axe].resolution = 
                                    v->axes[axe].resolution;
                            device->axe_info[axe].value = 0;
                        }
                        break;
                      default:
                        break;
                  }
                  any = (XAnyClassPtr) ((char *) any + any->length);
                }
            }
          }
          XFreeDeviceList(device_list);
      }
      else 
      {
          info->has_xdevices = 0;
      }
    }
    return info;
}

/*
 *----------------------------------------------------------------------
 *
 * LookupDeviceById --
 *    Return a device record from the display and the id of
 *    the device on the display. This is typically used to
 *    retrieve a device record from an event.
 *
 *----------------------------------------------------------------------
 */
static DeviceInfoStruct *
LookupDeviceById(Display      *dpy,
             XID        device_id)
{
    DisplayInfoStruct   *info;
    int                 i;
  
    info = GetDisplayInfo(dpy);
    for (i = 0; i < info->num_dev; i++) 
    {
      if (info->devices[i].id == device_id) 
      {
          return &info->devices[i];
      }
    }
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * UpdateCoreMark --
 *    Extended event handler bound to ChangeDevice events.
 *
 *    It's purpose is to maintain the core bits (CORE_POINTER,
 *    CORE_KEYBOARD) in the device structs when the core devices
 *    change.
 *    This also implies that all handlers registered for the new
 *    core device are put on hold and all handlers put on hold for
 *    the previous core device are revived.
 *
 *----------------------------------------------------------------------
 */
static void
UpdateCoreMark(ClientData     client_data,
             XEvent           *e)
{
    XChangeDeviceNotifyEvent  *cdne = (XChangeDeviceNotifyEvent *) e;
    DeviceInfoStruct          *device, *old_device = NULL;
    DisplayInfoStruct         *info;
    int                       i, event_atype;
    EventHandlerStruct        *handlers, *h_prev, *h_next;
    Tcl_HashEntry       *entry;
    Tcl_HashSearch            search;
    WindowInfoStruct          *pw;
  
/*printf("UpdateCoreMark: type=%d, win=0x%X\n", e->type, (int)e->xany.window);*/

    device = LookupDeviceById(cdne->display, cdne->deviceid);
    info = device->dpy_info;
    for (i = 0; i < info->num_dev; i++) 
    {
      if ((info->devices[i].core == CORE_POINTER) &&
            (cdne->request == NewPointer)) 
      {
          old_device = &info->devices[i];
          old_device->core = 0;
/*printf("old pointer is %s\n", old_device->name);*/
          break;
      }
      else if ((info->devices[i].core == CORE_KEYBOARD) &&
            (cdne->request == NewKeyboard)) 
      {
          old_device = &info->devices[i];
          old_device->core = 0;
          break;
      }
    }
    if (cdne->request == NewKeyboard) 
    {
      device->core = CORE_KEYBOARD;
    }
    else if (cdne->request == NewPointer) 
    {
      device->core = CORE_POINTER;
/*printf("new pointer is %s\n", device->name);*/
    }
  
    /*
     * Try to unfreeze all handlers associated with the previous
     * core device.
     */
    for (handlers = info->frozen_handlers, h_prev = NULL; handlers != NULL;
       handlers = h_next) 
    {
      h_next = handlers->next;
      if (handlers->device_id == old_device->id) 
      {
          if (handlers == info->frozen_handlers) 
          {
            info->frozen_handlers = handlers->next;
          }
          else 
          {
            h_prev->next = handlers->next;
          }
          event_atype = info->event_atypes[handlers->type];
          if ((event_atype == CHANGE_DEVICE_EVENT) ||
                  (event_atype == DEVICE_MAPPING_EVENT) ||
                  (event_atype == DEVICE_STATE_EVENT)) 
          {
            handlers->next = info->other_handlers;
            info->other_handlers = handlers;
          }
          else 
          {
            WindowInfoStruct *pw = GetWindowInfo(handlers->tkwin, 0);   
            handlers->next = pw->handlers;
            pw->handlers = handlers;
          }
      }
      else 
      {
          h_prev = handlers;
      }
    }
    /*
     * Then try to freeze all handlers associated with the new
     * core device. First traverse the other_handlers list and
     * then all the per window handlers.
     */
    for (handlers = info->other_handlers, h_prev = NULL; 
            handlers != NULL; handlers = h_next) 
    {
      h_next = handlers->next;
      if (handlers->device_id == device->id) 
      {
          if (handlers == info->other_handlers) 
          {
            info->other_handlers = handlers->next;
          }
          else 
          {
            h_prev->next = handlers->next;
          }
          handlers->next = info->frozen_handlers;
          info->frozen_handlers = handlers;
      }
      else 
      {
          h_prev = handlers;
      }
    }
    entry = Tcl_FirstHashEntry(&info->per_wins, &search);
    while (entry) 
    {
      pw = (WindowInfoStruct *) Tcl_GetHashValue(entry);
      for (handlers = pw->handlers, h_prev = NULL; 
                  handlers != NULL; handlers = h_next) 
      {
          h_next = handlers->next;
          if (handlers->device_id == device->id) 
          {
            if (handlers == pw->handlers) 
            {
                pw->handlers = handlers->next;
            }
            else 
            {
                h_prev->next = handlers->next;
            }
            handlers->next = info->frozen_handlers;
            info->frozen_handlers = handlers;
          }
          else 
          {
            h_prev = handlers;
          }
      }
      entry = Tcl_NextHashEntry(&search);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * GetDeviceInfo --
 *    Get the device descriptor for the device named 'name' on
 *    the given window's display. The device is opened as needed.
 *    If no such device exists on the display the function returns
 *    NULL.
 *
 *----------------------------------------------------------------------
 */
static DeviceInfoStruct *
GetDeviceInfo(Tk_Window tkwin,      /* Used to get the display */
            Tk_Uid      name)
{
    Display       *dpy = Tk_Display(tkwin);
    DisplayInfoStruct   *info;
    DeviceInfoStruct    *device = NULL;
    XInputClassInfo     *classes;
    int                 i, dummy;

    info = GetDisplayInfo(dpy);
/*printf("display_info=0x%X, display=0x%X\n", info, dpy);*/
    for (i = 0; i < info->num_dev; i++) 
    {
      if (info->devices[i].name == name) 
      {
          device = &info->devices[i];
      }
    }
    if (device) 
    {
      /*
       * Open the xdevice if not already done and not currently
       * a core device.
       */
/*printf("device=%s(0x%X), id=%d\n", device->name, device, (int)device->id);*/
      if (!device->xdev && !device->core) 
      {
          dummy = 0;
          device->xdev = XOpenDevice(dpy, device->id);
          if (device->xdev) 
          {
            /*
             * Process all declared input classes. This code finalizes
             * the work done when getting the device list for the display.
             * Some input classes are not reported until the device is open.
             */
            for (i = 0, classes = device->xdev->classes; 
                  i < device->xdev->num_classes; i++, classes++) 
            {
                switch (classes->input_class) 
                {
                  case ButtonClass:
                      DeviceButtonRelease(device->xdev, 
                        info->event_types[BUTTON_RELEASE],
                        device->event_classes[BUTTON_RELEASE]);
                      DeviceButtonPress(device->xdev, 
                        info->event_types[BUTTON_PRESS],
                        device->event_classes[BUTTON_PRESS]);
                            info->event_atypes[(int)info->
                        event_types[BUTTON_RELEASE]] = BUTTON_EVENT;
                      info->event_atypes[(int)info->
                        event_types[BUTTON_PRESS]] = BUTTON_EVENT;
                      break;
                  case KeyClass:
                      DeviceKeyRelease(device->xdev, 
                        info->event_types[KEY_RELEASE],
                        device->event_classes[KEY_RELEASE]);
                      DeviceKeyPress(device->xdev, 
                        info->event_types[KEY_PRESS],
                        device->event_classes[KEY_PRESS]);
                      info->event_atypes[(int)info->
                        event_types[KEY_RELEASE]] = KEY_EVENT;
                      info->event_atypes[(int)info->
                        event_types[KEY_PRESS]] = KEY_EVENT;
                      break;
                  case ValuatorClass:
                      DeviceMotionNotify(device->xdev, 
                        info->event_types[MOTION],
                        device->event_classes[MOTION]);
                      DevicePointerMotionHint(device->xdev, dummy,
                        device->event_classes[MOTION_HINT]);
                      info->event_types[MOTION_HINT] = 
                        info->event_types[MOTION];
                      info->event_atypes[(int)info->
                        event_types[MOTION]] = MOTION_EVENT;
                      break;
                  case FocusClass:
                      device->focusable = 1;
                      DeviceFocusIn(device->xdev, 
                        info->event_types[FOCUS_IN],
                        device->event_classes[FOCUS_IN]);
                      DeviceFocusOut(device->xdev, 
                        info->event_types[FOCUS_OUT],
                        device->event_classes[FOCUS_OUT]);
                      info->event_atypes[(int)info->
                        event_types[FOCUS_IN]] = FOCUS_EVENT;
                      info->event_atypes[(int)info->
                        event_types[FOCUS_OUT]] = FOCUS_EVENT;
                      break;
                  case ProximityClass:
                      device->proximity = 1;
                      ProximityIn(device->xdev, 
                        info->event_types[PROXIMITY_IN],
                        device->event_classes[PROXIMITY_IN]);
                      ProximityOut(device->xdev, 
                        info->event_types[PROXIMITY_OUT],
                        device->event_classes[PROXIMITY_OUT]);
                      info->event_atypes[(int)info->
                        event_types[PROXIMITY_OUT]] = PROXIMITY_EVENT;
                      info->event_atypes[(int)info->
                        event_types[PROXIMITY_IN]] = PROXIMITY_EVENT;
                      break;
                  case FeedbackClass:
                      device->feedback = 1;
                      break;
                }
            }
            /*
             * If the device can report both valuators and buttons, it is
             * possible to ask for these events. The event type is Motion.
             */
            if (device->num_buttons && device->num_axes) 
            {
                DeviceButtonMotion(device->xdev, dummy,
                  device->event_classes[BUTTON_MOTION]);
                info->event_types[BUTTON_MOTION] = 
                  info->event_types[MOTION];
                DeviceButton1Motion(device->xdev, dummy,
                  device->event_classes[B1_MOTION]);
                info->event_types[B1_MOTION] = 
                  info->event_types[MOTION];
                DeviceButton2Motion(device->xdev, dummy,
                  device->event_classes[B2_MOTION]);
                info->event_types[B2_MOTION] = 
                  info->event_types[MOTION];
                DeviceButton3Motion(device->xdev, dummy,
                  device->event_classes[B3_MOTION]);
                info->event_types[B3_MOTION] = 
                  info->event_types[MOTION];
                DeviceButton4Motion(device->xdev, dummy,
                  device->event_classes[B4_MOTION]);
                info->event_types[B4_MOTION] = 
                  info->event_types[MOTION];
                DeviceButton5Motion(device->xdev, dummy,
                  device->event_classes[B5_MOTION]);
                info->event_types[B5_MOTION] = 
                  info->event_types[MOTION];
            }
            /*
             * Setup the no-event class for this device.
             */
            NoExtensionEvent(device->xdev, dummy, device->no_event_class);
            /*
             * Setup the ChangeDevice, DeviceMapping and DeviceState
             * classes and types.
             */
            ChangeDeviceNotify(device->xdev, 
                  info->event_types[CHANGE_DEVICE],
                  device->event_classes[CHANGE_DEVICE]);
            info->event_atypes[(int)info->
                  event_types[CHANGE_DEVICE]] = CHANGE_DEVICE_EVENT;
            DeviceMappingNotify(device->xdev, 
                  info->event_types[DEVICE_MAPPING],
                  device->event_classes[DEVICE_MAPPING]);
            info->event_atypes[(int)info->
                  event_types[DEVICE_MAPPING]] = DEVICE_MAPPING_EVENT;
            DeviceStateNotify(device->xdev, 
                  info->event_types[DEVICE_STATE],
                  device->event_classes[DEVICE_STATE]);
            info->event_atypes[(int)info->
                  event_types[DEVICE_STATE]] = DEVICE_STATE_EVENT;
            /*
             * If the device has more than six valuators, allocate an
             * array big enough to hold all the valuators of the device.
             */
            if (device->num_axes) 
            {
                device->valuator_cache = (int *) 
                  ckalloc(device->num_axes*sizeof(int));
            }
            else 
            {
                device->valuator_cache = NULL;
            }
            /*
             * Create an event handler on ChangeDevice 
             * to update the core field.
             */
            Tk_CreateXiEventHandler(tkwin, 
                  xi_event_names[CHANGE_DEVICE], 
                  device->name,
                  UpdateCoreMark, NULL);
          }
      }
      if (!device->xdev || device->core) 
      {
          /*
           * Open failed or access denied, return NULL to inform the caller.
           */
          device = NULL;
      }
    }
/*printf("End of GetDeviceInfo\n");*/

  return device;
}

/*
 *----------------------------------------------------------------------
 *
 * GetEventIndex --
 *    Given an event spec uid, the function return the corresponding
 *    event index, needed to retrieve the type and class values.
 *    If the string does not describe a valid event, the function
 *    return -1.
 *
 *----------------------------------------------------------------------
 */
static int
GetEventIndex(Tk_Uid    event_spec)
{
    int           i;

    for (i = 0; i < NUM_XI_EVENTS; i++) 
    {
      if (xi_event_names[i] == event_spec) 
      {
          return i;
      }
    }
    return -1;
}

/*
 *----------------------------------------------------------------------
 *
 * DestroyPerWindow --
 *    Called when a window is destroyed to cleanup the event handlers
 *    that remain registered.
 *    No care is taken to prevent a race with Tk_DispatchXiEvent
 *    since DestroyPerWindow is also triggered as an event handler
 *    and thus should not be active at the same time. This is unfor-
 *    tunately a _FALSE_ guess, DestroyNotify is generated internally
 *    and processed as soon as the destroy command is emitted.
 *
 *----------------------------------------------------------------------
 */
static void
DestroyPerWindow(ClientData   client_data,
             XEvent           *e)
{
    Tk_Window           w = (Tk_Window) client_data;
    DisplayInfoStruct   *info;
    EventScriptRecord   *escripts, *es_next;
    EventHandlerStruct  *handlers, *h_next, *h_prev;
    WindowInfoStruct    *pw;
    Tcl_HashEntry *he;
    InProgress          *ip;
  
    if (e->type == DestroyNotify) 
    {
/*printf("Begin of DestroyPerWindow\n");*/
      info = GetDisplayInfo(e->xany.display);
      he = Tcl_FindHashEntry(&info->per_wins, (char *) w);
      if (he) 
      {
          pw = (WindowInfoStruct *) Tcl_GetHashValue(he);
          for (handlers = pw->handlers; handlers != NULL; handlers = h_next)
          {
            h_next = handlers->next;
            /*
             * Need to see if Tk_DispatchXiEvent is not about to
             * fire this handler. If it is, just abort the whole
             * dispatch thread, the window is gone.
             */
            for (ip = pending_handlers; ip != NULL; ip = ip->next) 
            {
                if (ip->next_handler == (ClientData) handlers) 
                {
                  ip->next_handler = NULL;
                }
            }
            ckfree((char *) handlers);
          }
          for (escripts = pw->scripts; escripts != NULL; escripts = es_next) 
          {
            es_next = escripts->next;
            ckfree(escripts->script);
            ckfree((char *) escripts);
          }
          ckfree((char *) pw);
          Tcl_DeleteHashEntry(he);
      }
      /*
       * Scan through the 'special events' handlers.
       */
      for (handlers = info->other_handlers, h_prev = NULL; 
            handlers != NULL; handlers = h_next) 
      {
          h_next = handlers->next;
          if (handlers->tkwin == w) 
          {
            if (handlers == info->other_handlers) 
            {
                info->other_handlers = h_next;
            }
            else 
            {
                h_prev->next = h_next;
            }
            /*
             * Need to see if Tk_DispatchXiEvent is not about to
             * fire this handler. If it is, point it to the next.
             */
            for (ip = pending_handlers; ip != NULL; ip = ip->next) 
            {
                if (ip->next_handler == (ClientData) handlers) 
                {
                  ip->next_handler = h_next;
                }
            }     
            ckfree((char *) handlers);
          }
          else 
          {
            h_prev = handlers;
          }
      }
      /*
       * Scan through the frozen handlers.
       */
      for (handlers = info->frozen_handlers, h_prev = NULL; 
            handlers != NULL; handlers = h_next) 
      {
          h_next = handlers->next;
          if (handlers->tkwin == w) 
          {
            if (handlers == info->frozen_handlers) 
            {
                info->frozen_handlers = handlers->next;
            }
            else 
            {
                h_prev->next = handlers->next;
            }
            ckfree((char *) handlers);
          }
          else 
          {
            h_prev = handlers;
          }
      }
/*printf("End of DestroyPerWindow\n");*/
    }
}

/*
 *----------------------------------------------------------------------
 *
 * GetWindowInfo --
 *    Return the handlers registered for the window.
 *
 *----------------------------------------------------------------------
 */
static WindowInfoStruct *
GetWindowInfo(Tk_Window w,
            int   create)
{
    DisplayInfoStruct   *info = GetDisplayInfo(Tk_Display(w));
    Tcl_HashEntry *he;
    WindowInfoStruct    *pw;
    int                 new;
  
/*printf("Begin of GetWindowInfo\n");*/
  
    he = Tcl_FindHashEntry(&info->per_wins, (char *) w);
    if (he == NULL) 
    {
      if (create) 
      {
          pw = (WindowInfoStruct *) ckalloc(sizeof(WindowInfoStruct));
          pw->handlers = NULL;
          pw->scripts = NULL;
          he = Tcl_CreateHashEntry(&info->per_wins, (char *) w, &new);
          Tcl_SetHashValue(he, pw);
          Tk_CreateEventHandler(w, StructureNotifyMask, DestroyPerWindow, w);
      }
      else 
      {
          pw = NULL;
      }
/*printf("End of GetWindowInfo\n");*/
      return pw;
    }

/*printf("End of GetWindowInfo\n");*/
    return (WindowInfoStruct *) Tcl_GetHashValue(he);
}

/*
 *----------------------------------------------------------------------
 *
 * SelectEvents --
 *    Traverse all event handlers for a given window and collect
 *    all classes needed for correct event selection. With this,
 *    select the requested events on the window. This may also
 *    unselect as needed all events from a device.
 *
 *----------------------------------------------------------------------
 */
static void
SelectEvents(Tk_Window  w,
           int    no_event_class)
{
    DisplayInfoStruct   *info = GetDisplayInfo(Tk_Display(w));
    WindowInfoStruct    *pw = GetWindowInfo(w, 0);
    EventHandlerStruct  *handlers;
    int                 i, count = 0;
    int                 *classes;
  
/*printf("Begin SelectEvents\n");*/
    if (no_event_class >= 0) 
    {
      count++;
    }
    if (pw) 
    {
      for (handlers = pw->handlers; handlers; handlers = handlers->next) 
      {
          count += handlers->num_classes;
      }
    }
    for (handlers = info->other_handlers; handlers; handlers = handlers->next) 
    {
      if (handlers->tkwin == w) 
      {
          count += handlers->num_classes;
      }
    }

    if (count == 0) return;

    classes = (int *) alloca(count * sizeof(int));
    count = 0;
    if (no_event_class >= 0) 
    {
      classes[count] = no_event_class;
      count++;
    }
    if (pw) 
    {
      for (handlers = pw->handlers; handlers; handlers = handlers->next) 
      {
          for (i = 0; i < handlers->num_classes; i++, count++) 
          {
            classes[count] = handlers->classes[i];
          }
      }
    }
    for (handlers = info->other_handlers; handlers; handlers = handlers->next) 
    {
      if (handlers->tkwin == w) 
      {
          for (i = 0; i < handlers->num_classes; i++, count++) 
          {
            classes[count] = handlers->classes[i];
          }
      }
    }

    XSelectExtensionEvent(Tk_Display(w), Tk_WindowId(w),
                  (XEventClass *) classes, count);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_CreateXiEventHandler --
 *    Public function provided to declare an event handler for
 *    an event from a device on a window. The client can pass a
 *    procedure to be called and a data pointer that will be passed
 *    back to the procedure.
 *
 *    If it is not possible to bind the event the function return
 *    0 else it returns 1.
 *
 *----------------------------------------------------------------------
 */
int
Tk_CreateXiEventHandler(Tk_Window   w,
                  Tk_Uid            event_spec,
                  Tk_Uid            device_spec,
                  Tk_EventProc      *proc,
                  ClientData  client_data)
{
    int                 found, no_window;
    int                 event_index, event_type, event_atype;
    WindowInfoStruct    *pw;
    EventHandlerStruct  *handlers, **handlers_head;
    DeviceInfoStruct    *device;
    DisplayInfoStruct   *info;
  
/*printf("Begin Tk_CreateXiEventHandler\n");*/
  
    device = GetDeviceInfo(w, device_spec);
    info = device->dpy_info;
    event_index = GetEventIndex(event_spec);

    if (event_index < 0) return 0;

    event_type = info->event_types[event_index];
    event_atype = info->event_atypes[event_type];
    if (((event_atype == KEY_EVENT) && !device->num_keys) ||
      ((event_atype == BUTTON_EVENT) && !device->num_buttons) ||
      ((event_atype == MOTION_EVENT) && !device->num_axes) ||
      ((event_atype == FOCUS_EVENT) && !device->focusable) ||
      ((event_atype == PROXIMITY_EVENT) && !device->proximity)) 
    {
      return 0;
    }

    /*
     * Special case for events that do not report a window.
     */
    no_window = ((event_atype == CHANGE_DEVICE_EVENT) ||
             (event_atype == DEVICE_MAPPING_EVENT) ||
             (event_atype == DEVICE_STATE_EVENT));
  
    if (no_window) 
    {
      handlers_head = &info->other_handlers;
    }
    else 
    {
      pw = GetWindowInfo(w, 1);
      handlers_head = &pw->handlers;
    }
  
    /*
     * Lookup if the proc is already installed with the same
     * client_data for the same event and device.
     */
    found = 0;
    for (handlers = *handlers_head ; handlers; handlers = handlers->next) 
    {
      if ((handlers->proc == proc) && 
            (handlers->client_data == client_data) &&
            (handlers->type == event_type) && 
            (handlers->device_id == device->id)) 
      {
          found = 1;
          break;
      }
    }
  
    if (!found) 
    {
      handlers = (EventHandlerStruct *) ckalloc(sizeof(EventHandlerStruct));
      handlers->next = *handlers_head;
      *handlers_head = handlers;
      handlers->proc = proc;
      handlers->client_data = client_data;
      handlers->type = event_type;
      handlers->device_id = device->id;
      handlers->tkwin = w;
    
      if ((event_index == BUTTON_PRESS_GRAB) ||
            (event_index == BUTTON_PRESS_OWNER_GRAB)) 
      {
          handlers->num_classes = 2;
          handlers->classes[0] = device->event_classes[BUTTON_PRESS];
          handlers->classes[1] = device->event_classes[BUTTON_PRESS_GRAB];
          if (event_index == BUTTON_PRESS_OWNER_GRAB) 
          {
            handlers->num_classes++;
            handlers->classes[2] = 
                  device->event_classes[BUTTON_PRESS_OWNER_GRAB]; 
          }
      }
      else if (event_index == MOTION_HINT) 
      {
          handlers->num_classes = 2;
          handlers->classes[0] = device->event_classes[MOTION];
          handlers->classes[1] = device->event_classes[MOTION_HINT];
      }
      else 
      {
          handlers->num_classes = 1;
          handlers->classes[0] = device->event_classes[event_index];
      }
    }

    /*
     * Now we need to check if the widget window is created and
     * if it is, select the right events.
     */
    if (Tk_WindowId(w)) 
    {
      SelectEvents(w, -1);
    }

/*printf("End Tk_CreateXiEventHandler\n");*/
    return 1;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_DeleteXiEventHandler --
 *    Public function provided to suppress an event handler for
 *    an event from a device on a window. The client pass a
 *    procedure and a data pointer to be matched. Only the handler
 *    matching all criteria will be removed.
 *
 *----------------------------------------------------------------------
 */
void
Tk_DeleteXiEventHandler(Tk_Window   w,
                  Tk_Uid            event_spec,
                  Tk_Uid            device_spec,
                  Tk_EventProc      *proc,
                  ClientData  client_data)
{
    WindowInfoStruct    *pw;
    int                 event_index, event_type, event_atype;
    DeviceInfoStruct    *device;
    EventHandlerStruct  *handlers, *prev, *next;
    EventHandlerStruct  **handlers_head;
    InProgress          *ip;
    int                 no_window;
    int                 device_in_use = 0;
  
/*printf("Begin Tk_DeleteXiEventHandler\n");*/
    device = GetDeviceInfo(w, device_spec);
    event_index = GetEventIndex(event_spec);
    event_type = device->dpy_info->event_types[event_index];
    event_atype = device->dpy_info->event_atypes[event_type];

    /*
     * Special case for events that do not report a window.
     */
    no_window = ((event_atype == CHANGE_DEVICE_EVENT) ||
             (event_atype == DEVICE_MAPPING_EVENT) ||
             (event_atype == DEVICE_STATE_EVENT));

    if (no_window) 
    {
      handlers_head = &device->dpy_info->other_handlers;
    }
    else 
    {
      pw = GetWindowInfo(w, 0);

      if ( !pw ) return;

      handlers_head = &pw->handlers;
    }
  
    for (handlers = *handlers_head, prev = NULL;
             handlers != NULL; handlers = next) 
    {
      if ((handlers->proc == proc) && 
            (handlers->client_data == client_data) &&
            (handlers->type == event_type) && 
            (handlers->device_id == device->id)) 
      {
          /* Ok, found the handler we are looking for, but we need
           * to check if Tk_DispatchXiEvent is not about to
           * process the handler. If it is, skip to the next.
           */
          next = handlers->next;

          for (ip = pending_handlers; ip != NULL; ip = ip->next) 
          {
            if (ip->next_handler == (ClientData) handlers) 
            {
                ip->next_handler = (ClientData) next;
            }
          }
          /*
           * Unlink and free the handler.
           */
          if (handlers == *handlers_head) 
          {
            *handlers_head = next;
          }
          else 
          {
            prev->next = next;
          }
          ckfree((char *) handlers);
      }
      else 
      {
          prev = handlers;
          /*
           * Record if the device will be still in use after the
           * removal of this handler.
           */
          next = handlers->next;
          device_in_use = device_in_use || (handlers->device_id == device->id);
      }
    }
    /*
     * Now we need to check if the widget window is created and
     * if it is, select the right events.
     */
/*printf("device in use %s, %d, no_event=%d\n", device->name, device_in_use,
       device->no_event_class);*/
    if (Tk_WindowId(w)) 
    {
      SelectEvents(w, device_in_use ? -1 : device->no_event_class);
    }
/*printf("End Tk_DeleteXiEventHandler\n");*/
}

/*
 *----------------------------------------------------------------------
 *
 * WacomxiGenericEventHandler --
 *    The generic event handler that is used to hook this extension
 *    in Tk event machinery.
 *    If checks if the event is from the xinput extension and
 *    dispatchs it using Tk_DispatchXiEvent on the appropriate
 *    window.
 *
 *----------------------------------------------------------------------
 */
static int
WacomxiGenericEventHandler(ClientData     client_data,
                  XEvent            *event)
{
    DisplayInfoStruct   *info;
  
    info = GetDisplayInfo(event->xany.display);
    if ((event->type >= info->event_base) &&
            (event->type < (info->event_base+IEVENTS))) 
    {
      return Tk_DispatchXiEvent(event);
    }

    /* Not able to dispatch return and let the standard system have a try.
     */
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_DispatchXiEvent --
 *    Public function provided to dispatch an xinput event to a
 *    window. The event must be an xinput event.
 *
 *----------------------------------------------------------------------
 */
int
Tk_DispatchXiEvent(XEvent     *e)
{
    WindowInfoStruct    *pw;
    Tk_Window           tkwin;
    InProgress          ip;
    EventHandlerStruct  *handlers;
    DisplayInfoStruct   *info;
    int                 hit = 0;
  
/*printf("Begin Tk_DispatchXiEvent : %d\n", e->type);*/

    /*
     * Special case needed for ChangeDeviceEvent, DeviceMappingEvent
     * and DeviceStateEvent because the window is not reported.
     */
    if (e->xany.window) 
    {
      tkwin = Tk_IdToWindow(e->xany.display, e->xany.window);

      if ( !tkwin ) return hit;

      pw = GetWindowInfo(tkwin, 0);

      if ( !pw ) return hit;

      handlers = pw->handlers;
    }
    else 
    {
      info = GetDisplayInfo(e->xany.display);
      handlers = info->other_handlers;
    }
  
    ip.next_handler = NULL;
    ip.next = pending_handlers;
    pending_handlers = &ip;
    for ( ; handlers != NULL; ) 
    {
      if ((handlers->type == e->type) &&
            (handlers->device_id == ((XDeviceKeyEvent *) e)->deviceid)) 
      {
          hit = 1;
          ip.next_handler = (ClientData) handlers->next;
          (*(handlers->proc))(handlers->client_data, e);
          handlers = (EventHandlerStruct *) ip.next_handler;
      }
      else 
      {
          handlers = handlers->next; 
      }
    }
    pending_handlers = pending_handlers->next;

/*printf("End Tk_DispatchXiEvent\n");*/
    return hit;
}


/*
 *----------------------------------------------------------------------
 *
 * RemoveEventScript --
 *    Delete a script associated with and event from a device. The
 *    server is asked to stop reporting this event in the window.
 *
 *----------------------------------------------------------------------
 */
static void
RemoveEventScript(Tcl_Interp  *interp,
              Tk_Window tkwin,
              DeviceInfoStruct *device,
              Tk_Uid    event_spec)
{
    WindowInfoStruct    *pw = GetWindowInfo(tkwin, 0);
    EventScriptRecord   *escripts, *prev_escript;

/*printf("Begin RemoveEventScript\n");
printf("for %s\n", event_spec);*/
    /*
     * No window info. The window has probably been destroyed.
     */
    if ( !pw ) return;

    for (escripts = pw->scripts, prev_escript = NULL; 
            escripts != NULL; 
            prev_escript = escripts, escripts = escripts->next) 
    {
      if ((escripts->device == device) &&
            (escripts->event_spec == event_spec) &&
            (escripts->interp == interp)) 
      {
          /*
           * Ok, found the script we are looking for.
           * Unlink the script from the list.
           */
          if (escripts == pw->scripts) 
          {
            pw->scripts = escripts->next;
          }
          else 
          {
            prev_escript->next = escripts->next;
          }
          Tk_DeleteXiEventHandler(tkwin, event_spec, device->name,
                  InvokeEventScript, (ClientData) escripts);
          ckfree(escripts->script);
          ckfree((char *) escripts);
          break;
      }
    }
/*printf("End RemoveEventScript\n");*/
}

/*
 *----------------------------------------------------------------------
 *
 * ExpandPercents --
 *    Do the actual work of expanding the percents in a script. The
 *    code is an adaptation of the function of the same name in
 *    tkBind.c          To be completed
 *
 *----------------------------------------------------------------------
 */

static void
ExpandPercents(Tk_Window      tkwin,
             DeviceInfoStruct *device,
             XEvent           *e,
             char       event_atype,
             char       *script,
             Tcl_DString      *exp_script)
{
    char          *scan;
    char          num_buffer[NUM_SIZE];
    int                 space_needed, cvt_flags;
    unsigned long number;
    int           length, i, x, y, width, height;
    int                 k_b_v_p_flag = 0, k_b_flag = 0;
    unsigned int  device_state = 0;
    int                 axes_count = 0, *axis_data = NULL;
  
/*printf("Begin ExpandPercents\n");*/
    if ((event_atype == KEY_EVENT) || (event_atype == BUTTON_EVENT)) 
    {
      k_b_flag = k_b_v_p_flag = 1;
      device_state = ((XDeviceKeyEvent *) e)->device_state;
      axes_count = ((XDeviceKeyEvent *) e)->axes_count;
      axis_data = ((XDeviceKeyEvent *) e)->axis_data;
    }
    else if (event_atype == MOTION_EVENT) 
    {
      k_b_v_p_flag = 1;
      device_state = ((XDeviceMotionEvent *) e)->device_state;
      axes_count = ((XDeviceMotionEvent *) e)->axes_count;
      axis_data = ((XDeviceMotionEvent *) e)->axis_data;
    }
    else if (event_atype == PROXIMITY_EVENT) 
    {
      k_b_v_p_flag = 1;
      device_state = ((XProximityNotifyEvent *) e)->device_state;
      axes_count = ((XProximityNotifyEvent *) e)->axes_count;
      axis_data = ((XProximityNotifyEvent *) e)->axis_data;
    }

    while (1) 
    {
      /*
       * Find everything up to the next % character and append it
       * to the result string.
       */
      for (scan = script; (*scan != 0) && (*scan != '%'); scan++) 
      {
          /* Empty loop body. */
      }
      if (scan != script) 
      {
          Tcl_DStringAppend(exp_script, script, scan-script);
          script = scan;
      }
      if (*script == 0) 
      {
          break;
      }
      /*
       * Script is on a percent sequence. Process it.
       */
      number = 0;
      scan = "??";
      switch (script[1]) 
      {
          case '#':
            number = e->xany.serial;
            goto do_unsigned_number;
          case '0': 
          case '1': 
          case '2': 
          case '3': 
          case '4':
          case '5': 
          case '6':
          case '7':
          case '8':
          case '9':
            if (k_b_v_p_flag) 
            {
                int     index = script[1]-'0';

                if (index < axes_count) 
                {
                  if (axes_count > 6) 
                  {
                      number = device->valuator_cache[index];
                  }
                  else 
                  {
                      number = axis_data[index];
                  }
                }
            }
            goto do_number;
          case  '*':
            if (event_atype == MOTION_EVENT) 
            {
                DeviceInfoStruct    *devices = device->dpy_info->devices;
                int                 num_devices = device->dpy_info->num_dev;
      
                /*
                 * Go through all known devices and look for a device
                 * id equal to the valuator value. If not found return
                 * the valuator value as an integer.
                 */
                for (i = 0; i < num_devices; i++) 
                {
                  if (devices[i].id == axis_data[0]) 
                  {
                      scan = (char *)devices[i].name;
                      goto do_string;
                  }
                }
                number = axis_data[0];
            }
            goto do_number;
          case 'b':
            if (event_atype == BUTTON_EVENT) 
            {
                number = ((XDeviceButtonEvent *) e)->button;
            }
            goto do_unsigned_number;
          case 'h':
            if (event_atype == MOTION_EVENT) 
            {
                number = ((XDeviceMotionEvent *) e)->is_hint;
            }
            goto do_unsigned_number;
          case 'k':
            if (event_atype == KEY_EVENT) 
            {
                number = ((XDeviceKeyEvent *) e)->keycode;
            }
            goto do_unsigned_number;
          case 's':
            if (k_b_v_p_flag) 
            {
                number = ((XDeviceButtonEvent *) e)->state;
            }
            goto do_unsigned_number;
          case 't':
            if (k_b_v_p_flag) 
            {
                number = ((XDeviceButtonEvent *) e)->time;
            }
            else if (event_atype == FOCUS_EVENT) 
            {
                number = ((XDeviceFocusChangeEvent *) e)->time;
            }
            else 
            {
                /* The other 3 event types: 
                 * Mapping, State & Change are compatible. 
                 */
                number = ((XChangeDeviceNotifyEvent *) e)->time;
            }
            goto do_unsigned_number;
          case 'x':
            if (k_b_v_p_flag) 
            {
                number = ((XDeviceButtonEvent *) e)->x;
            }
            goto do_unsigned_number;
          case 'y':
            if (k_b_v_p_flag) 
            {
                number = ((XDeviceButtonEvent *) e)->y;
            }
            goto do_unsigned_number;
          case 'C':
            if (event_atype == CHANGE_DEVICE_EVENT) 
            {
                XChangeDeviceNotifyEvent *cdne = (XChangeDeviceNotifyEvent *) e;
                if (cdne->request == NewKeyboard) 
                {
                  scan = "NewKeyboard";
                }
                else if (cdne->request == NewPointer) 
                {
                  scan = "NewPointer";
                }
            }
            goto do_string;
          case 'D':
            scan = (char *)device->name;
            goto do_string;
          case 'E':
            number = e->xany.send_event;
            goto do_unsigned_number;
          case 'S':
            if (k_b_v_p_flag) 
            {
                number = device_state;
            }
            goto do_unsigned_number;
          case 'T':
            for (i = 0; i < NUM_XI_EVENTS; i++) 
            {
                if (device->dpy_info->event_types[i] == e->type) 
                {
                  scan = (char *)xi_event_names[i];
                  break;
                }
            }
            goto do_string;
          case 'W':
            if (tkwin != NULL) 
            {
                scan = Tk_PathName(tkwin);
            }
            goto do_string;
          case 'X':
            if (k_b_v_p_flag) 
            {
                number = ((XDeviceButtonEvent *) e)->x_root;
                if (tkwin != NULL)
                {
                  Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
                  number -= x;
                }
            }
            goto do_unsigned_number;
          case 'Y':
            if (k_b_v_p_flag) 
            {
                number = ((XDeviceButtonEvent *) e)->y_root;
                if (tkwin != NULL) 
                {
                  Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
                  number -= y;
                }
            }
            goto do_unsigned_number;
          default:
            num_buffer[0] = script[1];
            num_buffer[1] = '\0';
            scan = num_buffer;
            goto do_string; 
      }

      do_unsigned_number:
          sprintf(num_buffer, "%lu", number);
          scan = num_buffer;
          goto do_string;
    
      do_number:
          sprintf(num_buffer, "%ld", number);
          scan = num_buffer;
    
      do_string:
          space_needed = Tcl_ScanElement(scan, &cvt_flags);
          length = Tcl_DStringLength(exp_script);
          Tcl_DStringSetLength(exp_script, length + space_needed);
          space_needed = Tcl_ConvertElement(scan,
                  Tcl_DStringValue(exp_script) + length,
                  cvt_flags | TCL_DONT_USE_BRACES);
          Tcl_DStringSetLength(exp_script, length + space_needed);
          script += 2;
    }
/*printf("End ExpandPercents\n");*/
}

/*
 *----------------------------------------------------------------------
 *
 * InvokeEventScript --
 *    Invoke a script that has been registered for an event form an
 *    input device. Any % occuring in the script will be replaced by
 *    a string depending on the characters following the percent.
 *    The recognized % sequences are:
 *
 *          %#    The number of the last request processed by the
 *                server.
 *          %0-9  The value of valuator n (0-9).
 *          %*    The name of the device (as encoded in a valuator
 *                by the switch virtual device).
 *          %b    The button number for events related to buttons.
 *          %h    The motion hint field from the event.
 *          %k    The keycode for events related to keys.
 *          %s    The state field from the event (the core that is).
 *          %t    The time field from the event.
 *          %x    The x coord of the core pointer.
 *          %y    The y coord of the core pointer.
 *          %C    The name of the core device that changed (only
 *                for DeviceChange events).
 *          %D    The device name that has emitted the event.
 *          %E    The send_event field from the event.
 *          %S    The device state field from the event.
 *          %T    The type field from the event, as an event name.
 *          %W    The window receiving the event.
 *          %X    The root window x coord of the core pointer.
 *          %Y    The root window y coord of the core pointer.
 *
 *    An unrecognized sequence is replaced by the sequence with
 *    the leading percent removed.
 *
 *----------------------------------------------------------------------
 */
static void
InvokeEventScript(ClientData  client_data,
              XEvent    *e)
{
    EventScriptRecord   *escripts = (EventScriptRecord *) client_data;
    DeviceInfoStruct    *device;
    Tcl_Interp          *interp = escripts->interp;
    int                 result;
    char          event_atype;
    Tcl_DString         exp_script;
    int                 i, try_to_merge = 0;
    int                 first_axis = 0, axes_count = 0;
    int                 *axis_data = NULL;
  
    device = LookupDeviceById(((XDeviceKeyEvent *) e)->display,
                      ((XDeviceKeyEvent *) e)->deviceid);
    event_atype = device->dpy_info->event_atypes[e->type];
    /*
     * If the event is a key, button, motion or proximity event,
     * try to merge events emitted to report more than 6 valuators.
     * It is assumed that an event train can't be mixed with another
     * from the same device.
     */
    if ((event_atype == BUTTON_EVENT) || (event_atype == KEY_EVENT)) 
    {
      first_axis = ((XDeviceKeyEvent *) e)->first_axis;
      axes_count = ((XDeviceKeyEvent *) e)->axes_count;
      axis_data = ((XDeviceKeyEvent *) e)->axis_data;
      try_to_merge = 1;
    }
    else if (event_atype == MOTION_EVENT) 
    {
      first_axis = ((XDeviceMotionEvent *) e)->first_axis;
      axes_count = ((XDeviceMotionEvent *) e)->axes_count;
      axis_data = ((XDeviceMotionEvent *) e)->axis_data;
      try_to_merge = 1;
    }
    else if (event_atype == PROXIMITY_EVENT) 
    {
      first_axis = ((XProximityNotifyEvent *) e)->first_axis;
      axes_count = ((XProximityNotifyEvent *) e)->axes_count;
      axis_data = ((XProximityNotifyEvent *) e)->axis_data;
      try_to_merge = 1;
    }
  
    if (try_to_merge) 
    {
      if (axes_count > 6) 
      {
          /*
           * Cache the axes reported in this event.
           */
          for (i = first_axis; i < axes_count; i++) 
          {
            device->valuator_cache[i] = axis_data[i-first_axis];
          }
          /*
           * If this isn't the last event, wait for the others.
           */
          if (axes_count > (first_axis+6)) return;
      }
    }
  
    Tcl_Preserve((ClientData) interp);
    Tcl_DStringInit(&exp_script);
    ExpandPercents(escripts->tkwin, device, e, event_atype,
             escripts->script, &exp_script);
    /*
     * Get the content of the escript before triggering
     * the script. It may destroy the binding before returning
     * and we might still need the info in case of error.
     */
    result = Tcl_GlobalEval(interp, Tcl_DStringValue(&exp_script));
    Tcl_DStringFree(&exp_script);
    if (result != TCL_OK) 
    {
      Tcl_BackgroundError(interp);
    }
    Tcl_Release((ClientData) interp);
/*printf("End InvokeEventScript\n");*/
}

/*
 *----------------------------------------------------------------------
 *
 * AddEventScript --
 *    Register a script to be invoked when an event is received in a
 *    window from a device. The server is asked to report the event
 *    on the window.
 *
 *    If it is not possible to bind the event the function return
 *    0 else it returns 1.
 *
 *----------------------------------------------------------------------
 */
static int
AddEventScript(Tcl_Interp     *interp,
             Tk_Window  tkwin,
             DeviceInfoStruct *device,
             Tk_Uid           event_spec,
             char       *script)
{
    WindowInfoStruct  *pw = GetWindowInfo(tkwin, 1);
    EventScriptRecord *escripts = NULL;

/*printf("Begin of AddEventScript\n");*/
  
    /*
     * Try to see if a script is already registered with the
     * same combination of device, event and interp.
     */
    for (escripts = pw->scripts; escripts != NULL; escripts = escripts->next) 
    {
      if ((escripts->device == device) &&
            (escripts->event_spec == event_spec) &&
            (escripts->interp == interp)) 
      {
          ckfree(escripts->script);
          escripts->script = NULL;
          break;
      }
    }
    /*
     * The event/device combo has no script already registered. Create
     * a new record and register with the server. The InvokeEventScript
     * function will be called with the script record as parameter when
     * an event of this type/device will be received on the window.
     */
    if (escripts == NULL) 
    {
      escripts = (EventScriptRecord *) ckalloc(sizeof(EventScriptRecord));
      escripts->device = device;
      escripts->tkwin = tkwin;
      escripts->event_spec = event_spec;
      escripts->interp = interp;
      if (!Tk_CreateXiEventHandler(tkwin, event_spec, device->name,
                  InvokeEventScript, (ClientData) escripts)) 
      {
          ckfree((char *) escripts);
          return 0;
      }
      escripts->next = pw->scripts;
      pw->scripts = escripts;
    }
    /*
     * Setup the script in the record.
     */
    escripts->script = ckalloc(strlen(script) + 1);
    strcpy(escripts->script, script);

    return 1;
}

/*
 *----------------------------------------------------------------------
 *
 * WacomxiBindEventCmd --
 *    Implements the 'bindevent' tcl command.
 *
 *----------------------------------------------------------------------
 */
static int
WacomxiBindEventCmd(ClientData      clientData,
             Tcl_Interp *interp,
             int        argc,
             char       **argv)
{
    Tk_Window           mainwin = (Tk_Window) clientData;
    Tk_Window           tkwin;
    DeviceInfoStruct    *device;
    int                 len;
    Tk_Uid        event_spec;
  
    if ((argc != 4) && (argc != 5)) 
    {
      Tcl_AppendResult(interp, "wrong # of arguments, should be \"",
                 argv[0], "win device event ?script?\"", (char *) NULL);
      return TCL_ERROR;
    }
    tkwin = Tk_NameToWindow(interp, argv[1], mainwin);

    if ( !tkwin ) return TCL_ERROR;

    device = GetDeviceInfo(tkwin, Tk_GetUid(argv[2]));
    if (!device) 
    {
      Tcl_AppendResult(interp, "unknown device \"", argv[2],
                 "\" or it is currently a core device", (char *) NULL);
      return TCL_ERROR;    
    }

    len = strlen(argv[3]);
    if ((argv[3][0] != '<') || (argv[3][len-1] != '>')) 
    {
      Tcl_AppendResult(interp,
                 "invalid event specification, should perhaps be <",
                 argv[3], ">", (char *) NULL);
      return TCL_ERROR;
    }
    argv[3][len-1] = 0;
    event_spec = Tk_GetUid(&argv[3][1]);
    argv[3][len-1] = '>';
  
    /*
     * Asked to return the script associated with the widget/event.
     * Lookup the scripts associated with window and in this list
     * the script associated with the given event.
     */
    if (argc == 4) 
    {
      WindowInfoStruct  *pw = GetWindowInfo(tkwin, 1);
      EventScriptRecord *escripts;
    
      for (escripts = pw->scripts; escripts != NULL; escripts = escripts->next) 
      {
          if ((escripts->device == device) &&
                  (escripts->event_spec == event_spec) &&
                  (escripts->interp == interp)) 
          {
            Tcl_SetResult(interp, (char *)escripts->script, TCL_STATIC);
            break;
          }
      }
      return TCL_OK;
    }
    /*
     * The script is the empty string, remove the event binding.
     */
    if (!argv[4][0]) 
    {
      RemoveEventScript(interp, tkwin, device, event_spec);
      return TCL_OK;
    }
    /*
     * Add or override the event binding if the event can be
     * reported by the device. Else report an error.
     */
    if (!AddEventScript(interp, tkwin, device, event_spec, argv[4])) 
    {
      Tcl_AppendResult(interp, "Event \"", argv[3],
                 "\" can't be reported by device \"", argv[2],
                 "\"", (char *) NULL);
      return TCL_ERROR;
    }
/*printf("Added event %s \n", argv[3] );*/
  
    return TCL_OK;
}


int
Libwacomxi_Init(Tcl_Interp    *interp)
{
    static int    setup_done = 0;
    int           i;
  
    if (!Tk_MainWindow(interp)) {
      Tcl_AppendResult(interp, 
                  "... Xinput package need Tk to run.",
                  (char *) NULL);
      return TCL_ERROR;
    }

    if (!setup_done) {
      setup_done = 1;
      Tk_CreateGenericHandler(WacomxiGenericEventHandler, NULL);
    }

    Tcl_CreateCommand(interp, 
                  "wacomxi::bindevent", 
                  (Tcl_CmdProc *)WacomxiBindEventCmd,
                  (ClientData) Tk_MainWindow(interp),
                  (Tcl_CmdDeleteProc *) NULL); 

    /*
     * Convert events names into uids for speed and ease of use.
     */
    for (i = 0; i < NUM_XI_EVENTS; i++) 
    {
      xi_event_names[i] = Tk_GetUid(xi_event_names[i]);
    }

    return Tcl_PkgProvide(interp, "LIBWACOMXI", "1.0");
}

Generated by  Doxygen 1.6.0   Back to index