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

wcmCommon.c

/*
 * Copyright 1995-2004 by Frederic Lepied, France. <Lepied@XFree86.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is  hereby granted without fee, provided that
 * the  above copyright   notice appear  in   all  copies and  that both  that
 * copyright  notice   and   this  permission   notice  appear  in  supporting
 * documentation, and that   the  name of  Frederic   Lepied not  be  used  in
 * advertising or publicity pertaining to distribution of the software without
 * specific,  written      prior  permission.     Frederic  Lepied   makes  no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * FREDERIC  LEPIED DISCLAIMS ALL   WARRANTIES WITH REGARD  TO  THIS SOFTWARE,
 * INCLUDING ALL IMPLIED   WARRANTIES OF MERCHANTABILITY  AND   FITNESS, IN NO
 * EVENT  SHALL FREDERIC  LEPIED BE   LIABLE   FOR ANY  SPECIAL, INDIRECT   OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA  OR PROFITS, WHETHER  IN  AN ACTION OF  CONTRACT,  NEGLIGENCE OR OTHER
 * TORTIOUS  ACTION, ARISING    OUT OF OR   IN  CONNECTION  WITH THE USE    OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "xf86Wacom.h"

      WacomDeviceClass* wcmDeviceClasses[] =
      {
            /* Current USB implementation requires LINUX_INPUT */
            #ifdef LINUX_INPUT
            &gWacomUSBDevice,
            #endif

            &gWacomISDV4Device,
            &gWacomSerialDevice,
            NULL
      };

/*****************************************************************************
 * Static functions
 ****************************************************************************/
 
static void transPressureCurve(WacomDevicePtr pDev, WacomDeviceStatePtr pState);
static void commonDispatchDevice(WacomCommonPtr common, unsigned int channel, 
      const WacomChannelPtr pChannel);
static void resetSampleCounter(const WacomChannelPtr pChannel);
static void sendAButton(LocalDevicePtr local, int button, int mask,
            int rx, int ry, int rz, int rtx, int rty, int rrot,
            int rth, int rwheel);
 
/*****************************************************************************
 * xf86WcmSetScreen --
 *   set to the proper screen according to the converted (x,y).
 *   this only supports for horizontal setup now.
 *   need to know screen's origin (x,y) to support 
 *   combined horizontal and vertical setups
 ****************************************************************************/

static void xf86WcmSetScreen(LocalDevicePtr local, int *v0, int *v1)
{
      WacomDevicePtr priv = (WacomDevicePtr) local->private;
      int screenToSet = 0;
      int totalWidth = 0, maxHeight = 0, leftPadding = 0;
      int i, x, y;
      double sizeX = priv->bottomX - priv->topX - 2*priv->tvoffsetX;
      double sizeY = priv->bottomY - priv->topY - 2*priv->tvoffsetY;

      DBG(6, ErrorF("xf86WcmSetScreen\n"));

      /* set factorX and factorY for single screen setup since
       * Top X Y and Bottom X Y can be changed while driver is running
       */
      if (screenInfo.numScreens == 1 || !priv->common->wcmMMonitor)
      {
            if (priv->twinview != TV_NONE && (priv->flags & ABSOLUTE_FLAG))
            {
                  if (priv->screen_no == -1)
                  {
                        if (priv->twinview == TV_LEFT_RIGHT)
                        {
                              if (*v0 > priv->bottomX - priv->tvoffsetX && *v0 <= priv->bottomX)
                                    priv->currentScreen = 1;
                              if (*v0 > priv->topX && *v0 <= priv->topX + priv->tvoffsetX)
                                    priv->currentScreen = 0;
                        }
                        if (priv->twinview == TV_ABOVE_BELOW)
                        {
                              if (*v1 > priv->bottomY - priv->tvoffsetY && *v1 <= priv->bottomY)
                                    priv->currentScreen = 1;
                              if (*v1 > priv->topY && *v1 <= priv->topY + priv->tvoffsetY)
                                    priv->currentScreen = 0;
                        }
                  }
                  else
                        priv->currentScreen = priv->screen_no;
                  priv->factorX = priv->tvResolution[2*priv->currentScreen] / sizeX;
                  priv->factorY = priv->tvResolution[2*priv->currentScreen+1] / sizeY;
            }
            else
            {
                  /* tool on the tablet when driver starts */
                  if (miPointerCurrentScreen())
                        priv->currentScreen = miPointerCurrentScreen()->myNum;
                  priv->factorX = screenInfo.screens[priv->currentScreen]->width / sizeX;
                  priv->factorY = screenInfo.screens[priv->currentScreen]->height / sizeY;
            }
            return;
      }

      if (!(local->flags & (XI86_ALWAYS_CORE | XI86_CORE_POINTER))) return;
      if (!(priv->flags & ABSOLUTE_FLAG))
      {
            /* screenToSet lags by one event, but not that important */
            screenToSet = miPointerCurrentScreen()->myNum;
            priv->factorX = screenInfo.screens[screenToSet]->width / sizeX;
            priv->factorY = screenInfo.screens[screenToSet]->height / sizeY;
            priv->currentScreen = screenToSet;
            return;
      }

      /* YHJ - these don't need to be calculated every time */
      for (i = 0; i < priv->numScreen; i++)
      {
            totalWidth += screenInfo.screens[i]->width;
            if (maxHeight < screenInfo.screens[i]->height)
                  maxHeight = screenInfo.screens[i]->height;
      }
      /* YHJ - looks nasty. sorry. */     
      if (priv->screen_no == -1)
      {
            for (i = 0; i < priv->numScreen; i++)
            {
                  if (*v0 * totalWidth <= (leftPadding + 
                        screenInfo.screens[i]->width) * sizeX)
                  {
                        screenToSet = i;
                        break;
                  }
                  leftPadding += screenInfo.screens[i]->width;
            }
      }
#ifdef PANORAMIX
      else if (!noPanoramiXExtension && priv->common->wcmGimp)
      {
            screenToSet = priv->screen_no;
            for (i = 0; i < screenToSet; i++)
                  leftPadding += screenInfo.screens[i]->width;
            *v0 = (sizeX * leftPadding + *v0
                  * screenInfo.screens[screenToSet]->width) /
                  (double)totalWidth + 0.5;
            *v1 = *v1 * screenInfo.screens[screenToSet]->height /
                  (double)maxHeight + 0.5;
      }

      if (!noPanoramiXExtension && priv->common->wcmGimp)
      {
            priv->factorX = totalWidth/sizeX;
            priv->factorY = maxHeight/sizeY;
            x = (*v0 - sizeX
                  * leftPadding / totalWidth) * priv->factorX + 0.5;
            y = *v1 * priv->factorY + 0.5;
            
            if (x >= screenInfo.screens[screenToSet]->width)
                  x = screenInfo.screens[screenToSet]->width - 1;
            if (y >= screenInfo.screens[screenToSet]->height)
                  y = screenInfo.screens[screenToSet]->height - 1;
      }
      else
#endif
      {
            if (priv->screen_no == -1)
                  *v0 = (*v0 * totalWidth - sizeX * leftPadding)
                        / screenInfo.screens[screenToSet]->width;
            else
                  screenToSet = priv->screen_no;
            priv->factorX = screenInfo.screens[screenToSet]->width / sizeX;
            priv->factorY = screenInfo.screens[screenToSet]->height / sizeY;

            x = *v0 * priv->factorX + 0.5;
            y = *v1 * priv->factorY + 0.5;
      }

      xf86XInputSetScreen(local, screenToSet, x, y);
      DBG(10, ErrorF("xf86WcmSetScreen current=%d ToSet=%d\n", 
            priv->currentScreen, screenToSet));
      priv->currentScreen = screenToSet;
}

/*****************************************************************************
 * xf86WcmSendButtons --
 *   Send button events by comparing the current button mask with the
 *   previous one.
 ****************************************************************************/

static void xf86WcmSendButtons(LocalDevicePtr local, int buttons,
            int rx, int ry, int rz, int rtx, int rty, int rrot,
            int rth, int rwheel)
{
      int button, mask, bsent = 0;
      WacomDevicePtr priv = (WacomDevicePtr) local->private;
      WacomCommonPtr common = priv->common;
      DBG(6, ErrorF("xf86WcmSendButtons buttons=%d for %s\n", buttons, local->name));

      /* Tablet PC buttons. */
      if ( common->wcmTPCButton && !IsCursor(priv) && !IsPad(priv) )
      {
            if ( buttons & 1 )
            {
                  if ( !(priv->flags & TPCBUTTONS_FLAG) )
                  {
                        priv->flags |= TPCBUTTONS_FLAG;

                        bsent = 0;

                        /* send all pressed buttons down */
                        for (button=2; button<=16; button++)
                        {
                              mask = 1 << (button-1);
                              if ( buttons & mask ) 
                              {
                                    bsent = 1;
                                    /* set to the configured button */
                                    sendAButton(local, priv->button[button-1], 1, rx, ry, 
                                          rz, rtx, rty, rrot, rth, rwheel);
                              }
                        }
                        
                        /* only send button one when nothing else was sent 
                         * There is a bug in XFree86 for combined left click and 
                         * other button. It'll lost left up when releases.
                         * This should be removed if XFree86 fixes the problem.
                         */
                        if ( !bsent && (buttons & 1) )
                        {
                              priv->flags |= TPCBUTTONONE_FLAG;
                              sendAButton(local, priv->button[0], 1, rx, ry, 
                                    rz, rtx, rty, rrot, rth, rwheel);
                        }
                  }
                  else
                  {
                        bsent = 0;
                        for (button=2; button<=16; button++)
                        {
                              mask = 1 << (button-1);
                              if ((mask & priv->oldButtons) != (mask & buttons))
                              {
                                    /* Send button one up before any button down is sent.
                                     * There is a bug in XFree86 for combined left click and 
                                     * other button. It'll lost left up when releases.
                                     * This should be removed if XFree86 fixes the problem.
                                     */
                                    if (priv->flags & TPCBUTTONONE_FLAG && !bsent)
                                    {
                                          priv->flags &= ~TPCBUTTONONE_FLAG;
                                          sendAButton(local, 1, 0, rx, ry, rz, 
                                                rtx, rty, rrot, rth, rwheel);
                                          bsent = 1;
                                    }
                                    /* set to the configured buttons */
                                    sendAButton(local, priv->button[button-1], mask & buttons, 
                                          rx, ry, rz, rtx, rty, rrot, rth, rwheel);
                              }
                        }
                  }
            }
            else if ( priv->flags & TPCBUTTONS_FLAG )
            {
                  priv->flags &= ~TPCBUTTONS_FLAG;

                  /* send all pressed buttons up */
                  for (button=2; button<=16; button++)
                  {
                        mask = 1 << (button-1);
                        if ((mask & priv->oldButtons) != (mask & buttons) || (mask & buttons) )
                        {
                              /* set to the configured button */
                              sendAButton(local, priv->button[button-1], 0, rx, ry, 
                                    rz, rtx, rty, rrot, rth, rwheel);
                        }
                  }
                  /* This is also part of the workaround of the XFree86 bug mentioned above
                   */
                  if (priv->flags & TPCBUTTONONE_FLAG)
                  {
                        priv->flags &= ~TPCBUTTONONE_FLAG;
                        sendAButton(local, 1, 0, rx, ry, rz, rtx, rty, rrot, rth, rwheel);
                  }
            }
      }
      else  /* normal buttons */
      {
            for (button=1; button<=16; button++)
            {
                  mask = 1 << (button-1);
                  if ((mask & priv->oldButtons) != (mask & buttons))
                  {
                        /* set to the configured button */
                        sendAButton(local, priv->button[button-1], mask & buttons, rx, ry, 
                              rz, rtx, rty, rrot, rth, rwheel);
                  }
            }
      }
}

/*****************************************************************************
 * sendAButton --
 *   Send one button event, called by xf86WcmSendButtons
 ****************************************************************************/

static void sendAButton(LocalDevicePtr local, int button, int mask,
            int rx, int ry, int rz, int rtx, int rty, int rrot,
            int rth, int rwheel)
{
      WacomDevicePtr priv = (WacomDevicePtr) local->private;
      WacomCommonPtr common = priv->common;
      int is_absolute = priv->flags & ABSOLUTE_FLAG, nbutton;

      DBG(4, ErrorF("xf86WcmSendButtons TPCButton(%s) button=%d state=%d, for %s\n", 
            common->wcmTPCButton ? "on" : "off", button, mask, local->name));

      /* translate into Left Double Click */
      if (button == 17)
      {
            nbutton = 1;
            if ( mask )
            {
                  /* Left button down */
                  if (IsCursor(priv))
                        xf86PostButtonEvent(local->dev, is_absolute, nbutton, 
                              1, 0, 6, rx, ry, rz, rrot, rth, rwheel);
                  else
                        xf86PostButtonEvent(local->dev, is_absolute, nbutton,
                              1, 0, 6, rx, ry, rz, rtx, rty, rwheel);
                  /* Left button up */
                  if (IsCursor(priv))
                        xf86PostButtonEvent(local->dev, is_absolute, nbutton, 
                              0, 0, 6, rx, ry, rz, rrot, rth, rwheel);
                  else
                        xf86PostButtonEvent(local->dev, is_absolute, nbutton, 
                              0, 0, 6, rx, ry, rz, rtx, rty, rwheel);
            }

            /* Left button down/up upon mask is 1/0 */
            if (IsCursor(priv))
                  xf86PostButtonEvent(local->dev, is_absolute, nbutton, 
                        mask != 0, 0, 6, rx, ry, rz, rrot, rth, rwheel);
            else
                  xf86PostButtonEvent(local->dev, is_absolute, nbutton, 
                        mask != 0, 0, 6, rx, ry, rz, rtx, rty, rwheel);
      }
      /* switch absolute or relative (Mode Toggle) */
      if ( button == 19 && mask )
      {
            if (is_absolute)
            {
                  priv->flags &= ~ABSOLUTE_FLAG;
                  xf86ReplaceStrOption(local->options, "Mode", "Relative");
            }
            else
            {
                  priv->flags |= ABSOLUTE_FLAG;
                  xf86ReplaceStrOption(local->options, "Mode", "Absolute");
            }
      }
      if (button < 17)
      {
            if (IsCursor(priv))
                  xf86PostButtonEvent(local->dev, is_absolute, button,
                        mask != 0, 0, 6, rx, ry, rz, rrot, rth, rwheel);
            else
                  xf86PostButtonEvent(local->dev, is_absolute, button,
                        mask != 0, 0, 6, rx, ry, rz, rtx, rty, rwheel);
      }
}

/*****************************************************************************
 * xf86WcmSendEvents --
 *   Send events according to the device state.
 ****************************************************************************/

void xf86WcmSendEvents(LocalDevicePtr local, const WacomDeviceState* ds, unsigned int channel)
{
      int type = ds->device_type;
      int is_button = !!(ds->buttons);
      int is_proximity = ds->proximity;
      int x = ds->x;
      int y = ds->y;
      int z = ds->pressure;
      int buttons = ds->buttons;
      int tx = ds->tiltx;
      int ty = ds->tilty;
      int rot = ds->rotation;
      int throttle = ds->throttle;
      int wheel = ds->abswheel;

      WacomDevicePtr priv = (WacomDevicePtr) local->private;
      WacomCommonPtr common = priv->common;
      int rx, ry, rz, rtx, rty, rrot, rth, rw, no_jitter;
      double param, relacc;
      int is_core_pointer, is_absolute, doffsetX=0, doffsetY=0;
      int aboveBelowSwitch = (priv->twinview == TV_ABOVE_BELOW)
            ? ((y < priv->topY) ? -1 : ((priv->bottomY < y) ? 1 : 0)) : 0;
      int leftRightSwitch = (priv->twinview == TV_LEFT_RIGHT)
            ? ((x < priv->topX) ? -1 : ((priv->bottomX < x) ? 1 : 0)) : 0;

      /* use tx and ty to report stripx and stripy */
      if (type == PAD_ID)
      {
            tx = ds->stripx;
            ty = ds->stripy;
      }

      DBG(7, ErrorF("[%s] prox=%s x=%d y=%d z=%d "
            "b=%s b=%d tx=%d ty=%d wl=%d rot=%d th=%d\n",
            (type == STYLUS_ID) ? "stylus" :
                  (type == CURSOR_ID) ? "cursor" : 
                  (type == ERASER_ID) ? "eraser" : "pad",
            is_proximity ? "true" : "false",
            x, y, z, is_button ? "true" : "false", buttons,
            tx, ty, wheel, rot, throttle));

      is_absolute = (priv->flags & ABSOLUTE_FLAG);
      is_core_pointer = xf86IsCorePointer(local->dev);

      if ( is_proximity || x || y || z || buttons || tx || ty || wheel )
      {
            switch ( leftRightSwitch )
            {
                  case -1:
                        doffsetX = 0;
                        break;
                  case 1:
                        doffsetX = priv->bottomX - priv->topX - 2*priv->tvoffsetX;
                        break;
            }
            switch ( aboveBelowSwitch )
            {
                  case -1:
                        doffsetY = 0;
                        break;
                  case 1:
                        doffsetY = priv->bottomY - priv->topY - 2*priv->tvoffsetY;
                        break;
            }
      }

      x += doffsetX;
      y += doffsetY;

      DBG(6, ErrorF("[%s] %s prox=%d\tx=%d\ty=%d\tz=%d\t"
            "button=%s\tbuttons=%d\t on channel=%d\n",
            local->name,
            is_absolute ? "abs" : "rel",
            is_proximity,
            x, y, z,
            is_button ? "true" : "false", buttons, channel));

      /* sets rx and ry according to the mode */
      if (is_absolute)
      {
            if (priv->twinview == TV_NONE)
            {
                  rx = x > priv->bottomX ? priv->bottomX - priv->topX :
                        x < priv->topX ? 0 : x - priv->topX;
                  ry = y > priv->bottomY ? priv->bottomY - priv->topY :
                        y < priv->topY ? 0 : y - priv->topY;
            }
            else
            {
                  rx = x;
                  ry = y;
            }
            rz = z;
            rtx = tx;
            rty = ty;
            rrot = rot;
            rth = throttle;
            rw = wheel;
      }
      else
      {
            if (priv->oldProximity)
            {
                  /* unify acceleration in both directions */
                  rx = (x - priv->oldX) * priv->factorY / priv->factorX;
                  ry = y - priv->oldY;
            }
            else
            {
                  rx = 0;
                  ry = 0;
            }

            /* don't apply speed for fairly small increments */
            no_jitter = priv->speed * 3;
            relacc = (MAX_ACCEL-priv->accel)*(MAX_ACCEL-priv->accel);
            if (ABS(rx) > no_jitter)
            {
                  param = priv->speed;

                  /* apply acceleration only when priv->speed > DEFAULT_SPEED */
                  if (priv->speed > DEFAULT_SPEED )
                  {
                        param += priv->accel > 0 ? abs(rx)/relacc : 0;
                        /* don't apply acceleration when too fast. */
                        if (param < 20.00)
                        {
                              rx *= param;
                        }
                  }
            }
            if (ABS(ry) > no_jitter)
            {
                  param = priv->speed;
                  /* apply acceleration only when priv->speed > DEFAULT_SPEED */
                  if (priv->speed > DEFAULT_SPEED )
                  {
                        param += priv->accel > 0 ? abs(ry)/relacc : 0;
                        /* don't apply acceleration when too fast. */
                        if (param < 20.00)
                        {
                              ry *= param;
                        }
                  }
            }
            rz = z - priv->oldZ;
            rtx = tx - priv->oldTiltX;
            rty = ty - priv->oldTiltY;
            rrot = rot - priv->oldRot;
            rth = throttle - priv->oldThrottle;
            rw = wheel - priv->oldWheel;
      }

      /* for multiple monitor support, we need to set the proper 
       * screen and modify the axes before posting events */
      if( !(priv->flags & BUTTONS_ONLY_FLAG) || !channel )
      {
            xf86WcmSetScreen(local, &rx, &ry);
      }

      /* coordinates are ready we can send events */
      if (is_proximity)
      {
            if (!priv->oldProximity)
            {
                  if (IsCursor(priv))
                        xf86PostProximityEvent(
                              local->dev, 1, 0, 6,
                              rx, ry, rz, rrot,
                              rth, rw);
                  else
                        xf86PostProximityEvent(
                              local->dev, 1, 0, 6,
                              rx, ry, rz, rtx, rty,
                              rw);
            }

            /* don't move the cursor if it only supports buttons or
             * if it's the second tool in dual input case.
             * More complicated dual input, such as only sylus/puck moves 
             * the cursor or both tools can move the cursor will be 
             * supported when needed */
            if( !(priv->flags & BUTTONS_ONLY_FLAG) || !channel )
            {
                  if (IsCursor(priv))
                        xf86PostMotionEvent(local->dev,
                              is_absolute, 0, 6, rx, ry, rz,
                              rrot, rth, rw);
                  else
                        xf86PostMotionEvent(local->dev,
                              is_absolute, 0, 6, rx, ry, rz,
                              rtx, rty, rw);
            }

            if (priv->oldButtons != buttons)
            {
                  xf86WcmSendButtons (local, buttons, rx, ry, rz,
                              rtx, rty, rrot, rth, rw);
            }

            /* simulate button 4 and 5 for relative wheel */
            if ( ds->relwheel )
            {
                  int fakeButton = ds->relwheel > 0 ? 5 : 4;
                  int i;
                  for (i=0; i<abs(ds->relwheel); i++)
                  {
                        xf86PostButtonEvent(local->dev,
                              is_absolute,
                              fakeButton, 1, 0, 6, rx, ry, rz,
                              rrot, rth, rw);
                        xf86PostButtonEvent(local->dev,
                              is_absolute,
                              fakeButton, 0, 0, 6, rx, ry, rz,
                              rrot, rth, rw);
                  }
            }
      }

      /* not in proximity */
      else
      {
            /* reports button up when the device has been
             * down and becomes out of proximity */
            if (priv->oldButtons)
            {
                  buttons = 0;
                  xf86WcmSendButtons (local, buttons, rx, ry, rz,
                        rtx, rty, rrot, rth, rw);
            }
            if (!is_core_pointer)
            {
                  /* macro button management */
                  if (common->wcmProtocolLevel == 4 && buttons)
                  {
                        int macro = z / 2;

                        DBG(6, ErrorF("macro=%d buttons=%d "
                              "wacom_map[%d]=%lx\n",
                              macro, buttons, macro,
                              gWacomModule.keymap[macro]));

                        /* First available Keycode begins at 8
                         * therefore macro+7 */

                        /* key down */
                        if (IsCursor(priv))
                              xf86PostKeyEvent(local->dev,macro+7,1,
                                    is_absolute,0,6,
                                    0,0,buttons,rrot,rth,
                                    rw);
                        else
                              xf86PostKeyEvent(local->dev,macro+7,1,
                                    is_absolute,0,6,
                                    0,0,buttons,rtx,rty,rw);

                        /* key up */
                        if (IsCursor(priv))
                              xf86PostKeyEvent(local->dev,macro+7,0,
                                    is_absolute,0,6,
                                    0,0,buttons,rrot,rth,
                                    rw);
                        else
                              xf86PostKeyEvent(local->dev,macro+7,0,
                                    is_absolute,0,6,
                                    0,0,buttons,rtx,rty,rw);

                  }
            }
            if (priv->oldProximity)
            {
                  if (IsCursor(priv))
                        xf86PostProximityEvent(local->dev,
                                    0, 0, 6, rx, ry, rz,
                                    rrot, rth, rw);
                  else
                        xf86PostProximityEvent(local->dev,
                                    0, 0, 6, rx, ry, rz,
                                    rtx, rty, rw);
            }
      } /* not in proximity */

      priv->oldProximity = is_proximity;
      priv->oldButtons = buttons;
      priv->oldWheel = wheel;
      priv->oldX = x;
      priv->oldY = y;
      priv->oldZ = z;
      priv->oldTiltX = tx;
      priv->oldTiltY = ty;
      priv->oldStripX = ds->stripx;
      priv->oldStripY = ds->stripy;
      priv->oldRot = rot;
      priv->oldThrottle = throttle;
}

/*****************************************************************************
 * xf86WcmSuppress --
 *  Determine whether device state has changed enough - return 1
 *  if not.
 ****************************************************************************/

static int xf86WcmSuppress(int suppress, const WacomDeviceState* dsOrig,
      const WacomDeviceState* dsNew)
{
      /* NOTE: Suppression value of zero disables suppression. */

      if (dsOrig->buttons != dsNew->buttons) return 0;
      if (dsOrig->proximity != dsNew->proximity) return 0;
      if (ABS(dsOrig->x - dsNew->x) > suppress) return 0;
      if (ABS(dsOrig->y - dsNew->y) > suppress) return 0;
      if (ABS(dsOrig->tiltx - dsNew->tiltx) > suppress) return 0;
      if (ABS(dsOrig->tilty - dsNew->tilty) > suppress) return 0;
      if (ABS(dsOrig->stripx - dsNew->stripx) > suppress) return 0;
      if (ABS(dsOrig->stripy - dsNew->stripy) > suppress) return 0;
      if (ABS(dsOrig->pressure - dsNew->pressure) > suppress) return 0;
      if (ABS(dsOrig->throttle - dsNew->throttle) > suppress) return 0;

      if (ABS(dsOrig->rotation - dsNew->rotation) > suppress ||
            (1800 - ABS(dsNew->rotation - dsOrig->rotation)) > suppress)
            return 0;

      /* look for change in absolute wheel
       * position or any relative wheel movement */
      if ((ABS(dsOrig->abswheel - dsNew->abswheel) > suppress) ||
            (dsNew->relwheel != 0)) return 0;

      return 1;
}

/*****************************************************************************
 * xf86WcmOpen --
 ****************************************************************************/

Bool xf86WcmOpen(LocalDevicePtr local)
{
      WacomDevicePtr priv = (WacomDevicePtr)local->private;
      WacomCommonPtr common = priv->common;
      WacomDeviceClass** ppDevCls;

      DBG(1, ErrorF("opening %s\n", common->wcmDevice));

      local->fd = xf86WcmOpenTablet(local);
      if (local->fd < 0)
      {
            ErrorF("Error opening %s : %s\n", common->wcmDevice,
                  strerror(errno));
            return !Success;
      }

      /* Detect device class; default is serial device */
      for (ppDevCls=wcmDeviceClasses; *ppDevCls!=NULL; ++ppDevCls)
      {
            if ((*ppDevCls)->Detect(local))
            {
                  common->wcmDevCls = *ppDevCls;
                  break;
            }
      }

      /* Initialize the tablet */
      return common->wcmDevCls->Init(local);
}

/* reset raw data counters for filters */
static void resetSampleCounter(const WacomChannelPtr pChannel)
{
      /* if out of proximity, reset hardware filter */
      if (!pChannel->valid.state.proximity)
      {
            pChannel->nSamples = 0;
            pChannel->rawFilter.npoints = 0;
            pChannel->rawFilter.statex = 0;
            pChannel->rawFilter.statey = 0;
      }
}

/*****************************************************************************
 * xf86WcmEvent -
 *   Handles suppression, transformation, filtering, and event dispatch.
 ****************************************************************************/

void xf86WcmEvent(WacomCommonPtr common, unsigned int channel,
      const WacomDeviceState* pState)
{
      WacomDeviceState* pLast;
      WacomDeviceState ds;
      WacomChannelPtr pChannel;

      /* tool on the tablet when driver starts */
      if (!miPointerCurrentScreen())
      {
            DBG(6, ErrorF("xf86WcmEvent: Wacom driver can not get Current Screen ID\n"));
            DBG(6, ErrorF("Please remove Wacom tool from the tablet.\n"));
            return;
      }

      /* sanity check the channel */
      if (channel >= MAX_CHANNELS)
            return;
      
      pChannel = common->wcmChannel + channel;
      pLast = &pChannel->valid.state;

      /* we must copy the state because certain types of filtering
       * will need to change the values (ie. for error correction) */
      ds = *pState;

      /* timestamp the state for velocity and acceleration analysis */
      ds.sample = (int)GetTimeInMillis();

      DBG(10, ErrorF("xf86WcmEvent: c=%d i=%d t=%d s=%x x=%d y=%d b=%d "
            "p=%d rz=%d tx=%d ty=%d aw=%d rw=%d t=%d df=%d px=%d st=%d\n",
            channel,
            ds.device_id,
            ds.device_type,
            ds.serial_num,
            ds.x, ds.y, ds.buttons,
            ds.pressure, ds.rotation, ds.tiltx,
            ds.tilty, ds.abswheel, ds.relwheel, ds.throttle,
            ds.discard_first, ds.proximity, ds.sample));

      DBG(11, ErrorF("filter %d, %p\n",RAW_FILTERING(common),
            (void *)common->wcmModel->FilterRaw));

      /* Discard the first 2 USB packages due to events delay */
      if ( (pChannel->nSamples < 2) && (common->wcmDevCls == &gWacomUSBDevice) )
      {
            DBG(11, ErrorF("discarded %dth USB data.\n", pChannel->nSamples));
            /* store channel device state for later use */
            memmove(pChannel->valid.states + 1,
                  pChannel->valid.states,
                  sizeof(WacomDeviceState) * (MAX_SAMPLES - 1));
            ++pChannel->nSamples;
            return; /* discard */
      }

      /* Filter raw data, fix hardware defects, perform error correction */
      if (RAW_FILTERING(common) && common->wcmModel->FilterRaw)
      {
            if (common->wcmModel->FilterRaw(common,pChannel,&ds))
            {
                  DBG(10, ErrorF("Raw filtering discarded data.\n"));
                  resetSampleCounter(pChannel);
                  return; /* discard */
            }
      }

      /* Discard unwanted data */
      if (xf86WcmSuppress(common->wcmSuppress, pLast, &ds) && pChannel->rawFilter.npoints == 4)
      {
            /* If throttle is not in use, discard data. */
            if (ABS(ds.throttle) < common->wcmSuppress)
            {
                  resetSampleCounter(pChannel);
                  return;
            }

            /* Otherwise, we need this event for time-rate-of-change
             * values like the throttle-to-relative-wheel filter.
             * To eliminate position change events, we reset all values
             * to last unsuppressed position. */

            ds = *pLast;
            RESET_RELATIVE(ds);
      }

      /* JEJ - Do not move this code without discussing it with me.
       * The device state is invariant of any filtering performed below.
       * Changing the device state after this point can and will cause
       * a feedback loop resulting in oscillations, error amplification,
       * unnecessary quantization, and other annoying effects. */

      /* save channel device state and device to which last event went */
      memmove(pChannel->valid.states + 1,
            pChannel->valid.states,
            sizeof(WacomDeviceState) * (MAX_SAMPLES - 1));
      pChannel->valid.state = ds; /*save last raw sample */
      if (pChannel->rawFilter.npoints < 4) ++pChannel->nSamples;

      commonDispatchDevice(common,channel,pChannel);
      resetSampleCounter(pChannel);
}

static void commonDispatchDevice(WacomCommonPtr common, unsigned int channel,
      const WacomChannelPtr pChannel)
{
      int id, idx;
      WacomDevicePtr priv;
      LocalDevicePtr pDev = NULL;
      LocalDevicePtr pLastDev = pChannel->pDev;
      WacomDeviceState* ds = &pChannel->valid.states[0];
      WacomDeviceState* pLast = &pChannel->valid.states[1];

      DBG(10, ErrorF("commonDispatchEvents\n"));

      if (!ds->device_type)
      {
            /* defaults to cursor if tool is on the tablet when X starts */
            ds->device_type = CURSOR_ID;
            ds->proximity = 1;
            if (ds->serial_num)
                  for (idx=0; idx<common->wcmNumDevices; idx++)
                  {
                        priv = common->wcmDevices[idx]->private;
                        if (ds->serial_num == priv->serial)
                        {
                              ds->device_type = DEVICE_ID(priv->flags);
                              break;
                        }
                  }
      }

      /* Find the device the current events are meant for */
      for (idx=0; idx<common->wcmNumDevices; idx++)
      {
            priv = common->wcmDevices[idx]->private;
            id = DEVICE_ID(priv->flags);

            if (id == ds->device_type &&
                  ((!priv->serial) || (ds->serial_num == priv->serial)))
            {
                  if ((priv->topX <= ds->x && priv->bottomX > ds->x &&
                        priv->topY <= ds->y && priv->bottomY > ds->y))
                  {
                        DBG(11, ErrorF("tool id=%d for %s\n",
                              id, common->wcmDevices[idx]->name));
                        pDev = common->wcmDevices[idx];
                        break;
                  }
                  /* Fallback to allow the cursor to move
                   * smoothly along screen edges */
                  else if (priv->oldProximity)
                  {
                        pDev = common->wcmDevices[idx];
                  }
            }
      }

      DBG(11, ErrorF("commonDispatchEvents: %p %p\n",(void *)pDev,(void *)pLastDev));

      /* if the logical device of the same physical tool has changed,
       * send proximity out to the previous one */
      if (pLastDev && (pLastDev != pDev) &&
            (pLast->serial_num == ds->serial_num))
      {
            pLast->proximity = 0;
            xf86WcmSendEvents(pLastDev, pLast, channel);
      }

      /* if a device matched criteria, handle filtering per device
       * settings, and send event to XInput */
      if (pDev)
      {
            WacomDeviceState filtered = pChannel->valid.state;
            WacomDevicePtr priv = pDev->private;

            /* Device transformations come first */

            /* button 1 Threshold test */
            int button = 1;
            if ( !IsCursor(priv) )
            {
                  if (filtered.pressure < common->wcmThreshold )
                        filtered.buttons &= ~button;
                  else
                        filtered.buttons |= button;
                  /* transform pressure */
                  transPressureCurve(priv,&filtered);
            }

            /* User-requested filtering comes next */

            /* User-requested transformations come last */

            #if 0

            /* not quite ready for prime-time;
             * it needs to be possible to disable,
             * and returning throttle to zero does
             * not reset the wheel, yet. */

            int sampleTime, ticks;

            /* get the sample time */
            sampleTime = GetTimeInMillis(); 
            
            ticks = ThrottleToRate(ds->throttle);

            /* throttle filter */
            if (!ticks)
            {
                  priv->throttleLimit = -1;
            }
            else if ((priv->throttleStart > sampleTime) ||
                  (priv->throttleLimit == -1))
            {
                  priv->throttleStart = sampleTime;
                  priv->throttleLimit = sampleTime + ticks;
            }
            else if (priv->throttleLimit < sampleTime)
            {
                  DBG(6, ErrorF("LIMIT REACHED: s=%d l=%d n=%d v=%d "
                        "N=%d\n", priv->throttleStart,
                        priv->throttleLimit, sampleTime,
                        ds->throttle, sampleTime + ticks));

                  ds->relwheel = (ds->throttle > 0) ? 1 :
                              (ds->throttle < 0) ? -1 : 0;

                  priv->throttleStart = sampleTime;
                  priv->throttleLimit = sampleTime + ticks;
            }
            else
                  priv->throttleLimit = priv->throttleStart + ticks;

            #endif /* throttle */

            /* force out-prox when height is greater than 112. 
             * This only applies to USB protocol V tablets
             * which aimed at improving relative movement support. 
             */
            if (filtered.distance > 112 && !(priv->flags & ABSOLUTE_FLAG) && !channel )
            {
                  ds->proximity = 0;
                  filtered.proximity = 0;
            }

            xf86WcmSendEvents(pDev, &filtered, channel);
      }

      /* otherwise, if no device matched... */
      else
      {
            DBG(11, ErrorF("no device matches with id=%d, serial=%d\n",
                        ds->device_type, ds->serial_num));
      }

      /* save the last device */
      pChannel->pDev = pDev;
}

/*****************************************************************************
 * xf86WcmInitTablet -- common initialization for all tablets
 ****************************************************************************/

int xf86WcmInitTablet(WacomCommonPtr common, WacomModelPtr model,
      int fd, const char* id, float version)
{
      /* Initialize the tablet */
      model->Initialize(common,fd,id,version);

      /* Get tablet resolution */
      if (model->GetResolution)
            model->GetResolution(common,fd);

      /* Get tablet range */
      if (model->GetRanges && (model->GetRanges(common,fd) != Success))
            return !Success;
      
      /* Default threshold value if not set */
      if (common->wcmThreshold <= 0)
      {
            /* Threshold for counting pressure as a button */
            common->wcmThreshold = common->wcmMaxZ * 3 / 50;
            ErrorF("%s Wacom using pressure threshold of %d for button 1\n",
                  XCONFIG_PROBED, common->wcmThreshold);
      }

      /* Reset tablet to known state */
      if (model->Reset && (model->Reset(common,fd) != Success))
      {
            ErrorF("Wacom xf86WcmWrite error : %s\n", strerror(errno));
            return !Success;
      }

      /* Enable tilt mode, if requested and available */
      if ((common->wcmFlags & TILT_REQUEST_FLAG) && model->EnableTilt)
      {
            if (model->EnableTilt(common,fd) != Success)
                  return !Success;
      }

      /* Enable hardware suppress, if requested and available */
      if ((common->wcmSuppress != 0) && model->EnableSuppress)
      {
            if (model->EnableSuppress(common,fd) != Success)
                  return !Success;
      }

      /* change the serial speed, if requested */
      if (common->wcmLinkSpeed != 9600)
      {
            if (model->SetLinkSpeed)
            {
                  if (model->SetLinkSpeed(common,fd) != Success)
                        return !Success;
            }
            else
            {
                  ErrorF("Tablet does not support setting link "
                        "speed, or not yet implemented\n");
            }
      }

      /* output tablet state as probed */
      if (xf86Verbose)
            ErrorF("%s Wacom %s tablet speed=%d maxX=%d maxY=%d maxZ=%d "
                  "resX=%d resY=%d suppress=%d tilt=%s\n",
                  XCONFIG_PROBED,
                  model->name, common->wcmLinkSpeed,
                  common->wcmMaxX, common->wcmMaxY, common->wcmMaxZ,
                  common->wcmResolX, common->wcmResolY,
                  common->wcmSuppress,
                  HANDLE_TILT(common) ? "enabled" : "disabled");
  
      /* start the tablet data */
      if (model->Start && (model->Start(common,fd) != Success))
            return !Success;

      /*set the model */
      common->wcmModel = model;

      return Success;
}

/*****************************************************************************
** Transformations
*****************************************************************************/

static void transPressureCurve(WacomDevicePtr pDev, WacomDeviceStatePtr pState)
{
      if (pDev->pPressCurve)
      {
            int p = pState->pressure;

            /* clip */
            p = (p < 0) ? 0 : (p > pDev->common->wcmMaxZ) ?
                  pDev->common->wcmMaxZ : p;

            /* rescale pressure to FILTER_PRESSURE_RES */
            p = (p * FILTER_PRESSURE_RES) / pDev->common->wcmMaxZ;

            /* apply pressure curve function */
            p = pDev->pPressCurve[p];

            /* scale back to wcmMaxZ */
            pState->pressure = (p * pDev->common->wcmMaxZ) /
                  FILTER_PRESSURE_RES;
      }
}

Generated by  Doxygen 1.6.0   Back to index