Edit

IABSD.fr/xenocara/driver/xf86-input-joystick/src/jstk_axis.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2009-05-03 13:54:11
    Hash : 1f845781
    Message : update to xf86-input-joystick 1.4.1

  • driver/xf86-input-joystick/src/jstk_axis.c
  • /*
     * Copyright 2007-2008 by Sascha Hlusiak. <saschahlusiak@freedesktop.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  Sascha   Hlusiak  not  be  used  in
     * advertising or publicity pertaining to distribution of the software without
     * specific,  written      prior  permission.     Sascha   Hlusiak   makes  no
     * representations about the suitability of this software for any purpose.  It
     * is provided "as is" without express or implied warranty.                   
     *                                                                            
     * SASCHA  HLUSIAK  DISCLAIMS ALL   WARRANTIES WITH REGARD  TO  THIS SOFTWARE,
     * INCLUDING ALL IMPLIED   WARRANTIES OF MERCHANTABILITY  AND   FITNESS, IN NO
     * EVENT  SHALL SASCHA  HLUSIAK  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.
     *
     */
    
    
    
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include <xf86Xinput.h>
    #include <xf86_OSproc.h>
    #include <math.h>
    #include <stdlib.h>
    
    #include "jstk.h"
    #include "jstk_axis.h"
    #include "jstk_key.h"
    
    
    /***********************************************************************
     *
     * jstkAxisTimer --
     *
     * The timer that will generate PointerMove-events. Checks every axis
     * and every button for it's mapping.
     * Return 0, when timer can be stopped, because there is no active
     * movement
     *
     ***********************************************************************
     */
    static CARD32
    jstkAxisTimer(OsTimerPtr        timer,
                  CARD32            atime,
                  pointer           arg)
    {
        #define NEXTTIMER 15
        DeviceIntPtr          device = (DeviceIntPtr)arg;
        JoystickDevPtr        priv = (JoystickDevPtr)XI_PRIVATE(device);
    
        int sigstate, i;
        int nexttimer;
        int movex,movey,movezx,movezy;
    
        nexttimer = 0;
        movex = movey = movezx = movezy = 0;
    
        sigstate = xf86BlockSIGIO();
    
        for (i=0; i<MAXAXES; i++) if ((priv->axis[i].value != 0) &&
                                      (priv->axis[i].type != JSTK_TYPE_NONE)) {
            float p1 = 0.0f;     /* Pixels to move cursor */
            float p2 = 0.0f;     /* Pixels to scroll */
            float scale;
            AXIS *axis;
            axis = &priv->axis[i];
    
            nexttimer = NEXTTIMER;
    
            if (priv->axis[i].type == JSTK_TYPE_BYVALUE) {
                /* Calculate scale value, so we get a range from 0 to 32768 */
                scale = (32768.0f / (float)(32768 - axis->deadzone));
    
                /* How many pixels should this axis move the cursor */
                p1 = ((pow((abs((float)axis->value) - (float)axis->deadzone) *
                      scale / 1700.0f, 3.4f)) + 100.0f) *
                     ((float)NEXTTIMER / 40000.0f);
                /* How many "pixels" should this axis scroll */
                p2 = ((pow((abs((float)axis->value) - (float)axis->deadzone) *
                      scale / 1000.0f, 2.5f)) + 200.0f) *
                     ((float)NEXTTIMER / 200000.0f);
            } else if (axis->type == JSTK_TYPE_ACCELERATED) {
                /* Stop to accelerate at a certain speed */
                if (axis->currentspeed < 100.0f)
                   axis->currentspeed = (axis->currentspeed + 3.0f) * 1.07f - 3.0f;
                p1 = axis->currentspeed * (float)NEXTTIMER / 180.0f;
                p2 = p1 / 8.0f;
            }
            if (axis->value < 0) {
                p1 = -p1;
                p2 = -p2;
            }
            p1 *= axis->amplify * priv->amplify;
            p2 *= axis->amplify * priv->amplify;
    
            /* Apply movement to global amount of pixels to move */
            switch (axis->mapping) {
            case JSTK_MAPPING_X:
            case JSTK_MAPPING_Y:
                axis->subpixel += p1;
                break;
            case JSTK_MAPPING_ZX:
            case JSTK_MAPPING_ZY:
            case JSTK_MAPPING_KEY:
                axis->subpixel += p2;
                break;
            default:
                break;
            }
            if ((int)axis->subpixel != 0) {
                switch (axis->mapping) {
                case JSTK_MAPPING_X:
                    movex += (int)axis->subpixel;
                    break;
                case JSTK_MAPPING_Y:
                    movey += (int)axis->subpixel;
                    break;
                case JSTK_MAPPING_ZX:
                    movezx += (int)axis->subpixel;
                    break;
                case JSTK_MAPPING_ZY:
                    movezy += (int)axis->subpixel;
                    break;
    
                case JSTK_MAPPING_KEY: if ((priv->keys_enabled == TRUE) && 
                                      (priv->axis[i].type == JSTK_TYPE_BYVALUE)) {
                    int num;
                    num = abs((int)axis->subpixel);
                    if ((int)axis->subpixel < 0) {
                        for (i=0; i<num; i++) {
                            jstkGenerateKeys(priv->keyboard_device, axis->keys_low, 1);
                            jstkGenerateKeys(priv->keyboard_device, axis->keys_low, 0);
                        }
                    } else {
                        for (i=0; i<num; i++) {
                            jstkGenerateKeys(priv->keyboard_device, axis->keys_high, 1);
                            jstkGenerateKeys(priv->keyboard_device, axis->keys_high, 0);
                        }
                    }
                    break;
                }
                default:
                    break;
                }
                axis->subpixel = axis->subpixel - (int)axis->subpixel;
            }
        }
    
        for (i=0; i<MAXBUTTONS; i++) if (priv->button[i].pressed == 1) {
            float p1;
            float p2;
    
            if (priv->button[i].currentspeed < 100.0f)
                priv->button[i].currentspeed = 
                    (priv->button[i].currentspeed + 3.0f) * 1.07f - 3.0f;
            p1 = priv->button[i].currentspeed * (float)NEXTTIMER / 180.0f *
                priv->button[i].amplify;
            p1 *= priv->amplify;
            p2 = p1 / 8.0f;
    
            /* Apply movement to amount of pixels to move */
            switch (priv->button[i].mapping) {
            case JSTK_MAPPING_X:
            case JSTK_MAPPING_Y:
                priv->button[i].subpixel += p1;
                nexttimer = NEXTTIMER;
                break;
            case JSTK_MAPPING_ZX:
            case JSTK_MAPPING_ZY:
                priv->button[i].subpixel += p2;
                nexttimer = NEXTTIMER;
                break;
            default:
                break;
            }
            if ((int)priv->button[i].subpixel != 0) {
                switch (priv->button[i].mapping) {
                case JSTK_MAPPING_X:
                    movex += (int)priv->button[i].subpixel;
                    break;
                case JSTK_MAPPING_Y:
                    movey += (int)priv->button[i].subpixel;
                    break;
                case JSTK_MAPPING_ZX:
                    movezx += (int)priv->button[i].subpixel;
                    break;
                case JSTK_MAPPING_ZY:
                    movezy += (int)priv->button[i].subpixel;
                    break;
                default:
                    break;
                }
                priv->button[i].subpixel -= (int)priv->button[i].subpixel;
            }
        }
    
        /* Actually move the cursor, if there is enough movement in the buffer */
        if ((movex != 0)||(movey != 0)) {
            xf86PostMotionEvent(device, 0, 0, 2, movex, movey);
        }
    
        /* Generate scrolling events */
        while (movezy >= 1) {  /* down */
            xf86PostButtonEvent(device, 0, 5,
                                1, 0, 0);
            xf86PostButtonEvent(device, 0, 5,
                                0, 0, 0);
            movezy -= 1;
        }
        while (movezy <= -1) { /* up */
            xf86PostButtonEvent(device, 0, 4,
                                1, 0, 0);
            xf86PostButtonEvent(device, 0, 4,
                                0, 0, 0);
            movezy += 1;
        }
    
        while (movezx >= 1) {  /* right */
            xf86PostButtonEvent(device, 0, 7, 
                                1, 0, 0);
            xf86PostButtonEvent(device, 0, 7, 
                                0, 0, 0);
            movezx -= 1;
        }
        while (movezx <= -1) { /* left */
            xf86PostButtonEvent(device, 0, 6,
                                1, 0, 0);
            xf86PostButtonEvent(device, 0, 6,
                                0, 0, 0);
            movezx += 1;
        }
    
        if ((priv->mouse_enabled == FALSE) &&
            (priv->keys_enabled == FALSE))
            nexttimer = 0;
    
        if (nexttimer == 0) { /* No next timer (no subpixel added), stop */
            priv->timerrunning = FALSE;
    
            for (i=0; i<MAXBUTTONS; i++) priv->button[i].subpixel = 0.0f;
            for (i=0; i<MAXAXES; i++) priv->axis[i].subpixel = 0.0f;
    
            DBG(2, ErrorF("Stopping Axis Timer\n"));
        }
        xf86UnblockSIGIO (sigstate);
        return nexttimer;
    }
    
    
    /***********************************************************************
     *
     * jstkStartAxisTimer --
     *
     * Starts the timer for the movement.
     * Will already prepare for moving one pixel, for "tipping" the stick
     *
     ***********************************************************************
     */
    void
    jstkStartAxisTimer(LocalDevicePtr device, int number) 
    {
        int pixel;
        JoystickDevPtr priv = device->private;
    
        if (priv->timerrunning) return;
        priv->timerrunning = TRUE;
    
        pixel = 1;
        if (priv->axis[number].value < 0) pixel = -1;
        priv->axis[number].subpixel += pixel;
    
        DBG(2, ErrorF("Starting Axis Timer (triggered by axis %d)\n", number));
        priv->timer = TimerSet(
            priv->timer, 
            0,         /* Relative */
            1,         /* What about NOW? */
            jstkAxisTimer,
            device->dev);
    }
    
    /***********************************************************************
     *
     * jstkStartButtonAxisTimer --
     *
     * Starts the timer for the movement.
     * Will already prepare for moving one pixel, for "tipping" the stick
     *
     ***********************************************************************
     */
    
    void
    jstkStartButtonAxisTimer(LocalDevicePtr device, int number) 
    {
        int pixel;
        JoystickDevPtr priv = device->private;
    
        if (priv->timerrunning) return;
        priv->timerrunning = TRUE;
    
        pixel = 1;
        if (priv->button[number].amplify < 0) pixel = -1;
        switch (priv->button[number].mapping) {
        case JSTK_MAPPING_X:
        case JSTK_MAPPING_Y:
        case JSTK_MAPPING_ZX:
        case JSTK_MAPPING_ZY:
            priv->button[number].subpixel += pixel;
            break;
        default:
            break;
        }
    
        DBG(2, ErrorF("Starting Axis Timer (triggered by button %d)\n", number));
        priv->timer = TimerSet(
            priv->timer, 
            0,         /* Relative */
            1,         /* What about NOW? */
            jstkAxisTimer,
            device->dev);
    }
    
    /***********************************************************************
     *
     * jstkHandleAbsoluteAxis --
     *
     * Sums up absolute movement of all axes and sets the cursor to the
     * desired Position on the screen. 
     *
     ***********************************************************************
     */
    void
    jstkHandleAbsoluteAxis(LocalDevicePtr device, int number) 
    {
        JoystickDevPtr priv = device->private;
        int i,x,y;
    
        x=0;
        y=0;
    
        for (i=0; i<MAXAXES; i++) 
            if (priv->axis[i].type == JSTK_TYPE_ABSOLUTE)
        {
            float rel;
            int dif;
            
        	rel = 0.0f;
            if (priv->axis[i].value > +priv->axis[i].deadzone)
                rel = (priv->axis[i].value - priv->axis[i].deadzone);
            if (priv->axis[i].value < -priv->axis[i].deadzone)
                rel = (priv->axis[i].value + priv->axis[i].deadzone);
    
            rel = (rel) / (2.0f * (float)(32768 - priv->axis[i].deadzone));
            /* rel contains numbers between -0.5 and +0.5 now */
    
            rel *= priv->axis[i].amplify;
    
            DBG(5, ErrorF("Relative Position of axis %d: %.2f\n", i, rel));
    
            /* Calculate difference to previous position on screen in pixels */
            dif = (int)(rel - priv->axis[i].previousposition + 0.5f);
            if ((dif >= 1)||(dif <= -1)) {
                if (priv->axis[i].mapping == JSTK_MAPPING_X) {
                    x += (dif);
                    priv->axis[i].previousposition += (float)dif;
                }
                if (priv->axis[i].mapping == JSTK_MAPPING_Y) {
                    y += (int)(dif);
                    priv->axis[i].previousposition += (float)dif;
                }
            }
        }
        /* Still move relative, but relative to previous position of the axis */
        if ((x != 0) || (y != 0)) {
            DBG(4, ErrorF("Moving mouse by %dx%d pixels\n", x, y));
            xf86PostMotionEvent(device->dev, 0, 0, 2, x, y);
        }
    }
    
    
    
    
    
    /***********************************************************************
     *
     * jstkPWMAxisTimer --
     *
     * The timer that will generate Key events.
     * The deflection of the axis will control the PERCENT OF TIME the key is
     * down, not the amount of impulses.
     * Return 0, when timer can be stopped.
     *
     ***********************************************************************
     */
    static CARD32
    jstkPWMAxisTimer(OsTimerPtr        timer,
                     CARD32            atime,
                     pointer           arg)
    {
        DeviceIntPtr          device = (DeviceIntPtr)arg;
        JoystickDevPtr        priv = (JoystickDevPtr)XI_PRIVATE(device);
    
        int sigstate, i;
        int nexttimer;
    
        nexttimer = 0;
    
        sigstate = xf86BlockSIGIO();
    
        for (i=0; i<MAXAXES; i++)
            if (priv->axis[i].timer == timer) /* The timer handles only one axis! Find it. */
        {
            AXIS *axis;
            axis = &priv->axis[i];
    
            DBG(8, ErrorF("PWM Axis %d value %d (old %d)\n", i, axis->value, axis->oldvalue));
    
            /* Force key_high down if centered */
            if ((axis->value <= 0) && 
                (axis->oldvalue > 0) &&
                (axis->key_isdown)) 
            {
                DBG(7, ErrorF("PWM Axis %d jumped over. Forcing keys_high up.\n", i));
                jstkGenerateKeys(priv->keyboard_device, 
                    axis->keys_high,
                    0);
                axis->key_isdown = 0;
            }
    
            /* Force key_low down if centered */
            if ((axis->value >= 0) && 
                (axis->oldvalue < 0) &&
                (axis->key_isdown))
            {
                DBG(7, ErrorF("PWM Axis %d jumped over. Forcing keys_low up.\n", i));
                jstkGenerateKeys(priv->keyboard_device, 
                    axis->keys_low,
                    0);
                axis->key_isdown = 0;
            }
    
            if (axis->value == 0)
                nexttimer = 0;
            else {
                float time_on, time_off;
                float scale;
                KEYSCANCODES *keys;
    
                if (axis->value < 0)
                    keys = &axis->keys_low;
                else keys = &axis->keys_high;
    
                /* Calculate next timer */
                time_on = (float)(abs(axis->value) - axis->deadzone) / 32768.0;
                time_on *= (32768.0f / (float)(32768 - axis->deadzone));
    
                time_off = 1.0f - time_on;
    
                time_on  += 0.01f; /* Ugly but ensures we don't divide by 0 */
                time_off += 0.01f;
    
                /* Scale both durations, so the smaller always is 50ms */
                scale = 50.0f * axis->amplify;
    
                if (time_on < time_off)
                    scale /= time_on;
                else scale /= time_off;
    
                time_on  *= scale;
                time_off *= scale;
    
    
                if (time_off > 600.0f) {
                    /* Might as well just have it down forever */
                    DBG(7, ErrorF("PWM Axis %d up time too long (%.0fms). Forcing up)\n", i, time_off));
                    if (axis->key_isdown == 1) {
                        axis->key_isdown = 0;
                        jstkGenerateKeys(priv->keyboard_device,
                            *keys,
                            axis->key_isdown);
                    }
                    nexttimer = 0;
                } else if (time_on > 600.0f) { 
                    /* Might as well just have it up forever */
                    DBG(7, ErrorF("PWM Axis %d down time too long (%.0fms). Forcing down)\n", i, time_on));
                    if (axis->key_isdown == 0) {
                        axis->key_isdown = 1;
                        jstkGenerateKeys(priv->keyboard_device,
                            *keys,
                            axis->key_isdown);
                    }
                    nexttimer = 0;
                } else {
                    /* Flip key state */
                    axis->key_isdown = 1 - axis->key_isdown;
                    jstkGenerateKeys(priv->keyboard_device,
                        *keys,
                        axis->key_isdown);
    
                    DBG(7, ErrorF("PWM Axis %d state=%d (%.0fms down, %.0fms up).\n", i, axis->key_isdown, time_on, time_off));
    
                    nexttimer = axis->key_isdown ? (int)time_on : (int)time_off;
                }
            }
    
            if (nexttimer == 0) { /* No next timer, stop */
                axis->timerrunning = FALSE;
    
                DBG(2, ErrorF("Stopping PWM Axis %d Timer\n", i));
            }
            axis->oldvalue = axis->value;
            break;
        }
    
        xf86UnblockSIGIO (sigstate);
        return nexttimer;
    }
    
    
    /***********************************************************************
     *
     * jstkStartAxisTimer --
     *
     * Starts the timer for the movement.
     * Will already prepare for moving one pixel, for "tipping" the stick
     *
     ***********************************************************************
     */
    void
    jstkHandlePWMAxis(LocalDevicePtr device, int number) 
    {
        JoystickDevPtr priv = device->private;
        if (priv->axis[number].timerrunning) return;
    
        priv->axis[number].timerrunning = TRUE;
    
        DBG(2, ErrorF("Starting PWM Axis Timer (triggered by axis %d, value %d)\n", 
                   number, priv->axis[number].value));
        priv->axis[number].timer = TimerSet(
            priv->axis[number].timer, 
            0,         /* Relative */
            1,         /* What about NOW? */
            jstkPWMAxisTimer,
            device->dev);
    }