view src/video/x11/SDL_x11mouse.c @ 1944:91f41fc124a7

The AAlib driver is fun, but not worth rewriting for SDL 1.3
author Sam Lantinga <slouken@libsdl.org>
date Wed, 26 Jul 2006 03:24:27 +0000
parents c121d94672cb
children
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include "SDL_mouse.h"
#include "../../events/SDL_events_c.h"
#include "../SDL_cursor_c.h"
#include "SDL_x11dga_c.h"
#include "SDL_x11mouse_c.h"


/* The implementation dependent data for the window manager cursor */
struct WMcursor
{
    Cursor x_cursor;
};


void
X11_FreeWMCursor(_THIS, WMcursor * cursor)
{
    if (SDL_Display != NULL) {
        SDL_Lock_EventThread();
        XFreeCursor(SDL_Display, cursor->x_cursor);
        XSync(SDL_Display, False);
        SDL_Unlock_EventThread();
    }
    SDL_free(cursor);
}

WMcursor *
X11_CreateWMCursor(_THIS,
                   Uint8 * data, Uint8 * mask, int w, int h, int hot_x,
                   int hot_y)
{
    WMcursor *cursor;
    XGCValues GCvalues;
    GC GCcursor;
    XImage *data_image, *mask_image;
    Pixmap data_pixmap, mask_pixmap;
    int clen, i;
    char *x_data, *x_mask;
    static XColor black = { 0, 0, 0, 0 };
    static XColor white = { 0xffff, 0xffff, 0xffff, 0xffff };

    /* Allocate the cursor memory */
    cursor = (WMcursor *) SDL_malloc(sizeof(WMcursor));
    if (cursor == NULL) {
        SDL_OutOfMemory();
        return (NULL);
    }

    /* Mix the mask and the data */
    clen = (w / 8) * h;
    x_data = (char *) SDL_malloc(clen);
    if (x_data == NULL) {
        SDL_free(cursor);
        SDL_OutOfMemory();
        return (NULL);
    }
    x_mask = (char *) SDL_malloc(clen);
    if (x_mask == NULL) {
        SDL_free(cursor);
        SDL_free(x_data);
        SDL_OutOfMemory();
        return (NULL);
    }
    for (i = 0; i < clen; ++i) {
        /* The mask is OR'd with the data to turn inverted color
           pixels black since inverted color cursors aren't supported
           under X11.
         */
        x_mask[i] = data[i] | mask[i];
        x_data[i] = data[i];
    }

    /* Prevent the event thread from running while we use the X server */
    SDL_Lock_EventThread();

    /* Create the data image */
    data_image = XCreateImage(SDL_Display,
                              DefaultVisual(SDL_Display, SDL_Screen),
                              1, XYBitmap, 0, x_data, w, h, 8, w / 8);
    data_image->byte_order = MSBFirst;
    data_image->bitmap_bit_order = MSBFirst;
    data_pixmap = XCreatePixmap(SDL_Display, SDL_Root, w, h, 1);

    /* Create the data mask */
    mask_image = XCreateImage(SDL_Display,
                              DefaultVisual(SDL_Display, SDL_Screen),
                              1, XYBitmap, 0, x_mask, w, h, 8, w / 8);
    mask_image->byte_order = MSBFirst;
    mask_image->bitmap_bit_order = MSBFirst;
    mask_pixmap = XCreatePixmap(SDL_Display, SDL_Root, w, h, 1);

    /* Create the graphics context */
    GCvalues.function = GXcopy;
    GCvalues.foreground = ~0;
    GCvalues.background = 0;
    GCvalues.plane_mask = AllPlanes;
    GCcursor = XCreateGC(SDL_Display, data_pixmap,
                         (GCFunction | GCForeground | GCBackground |
                          GCPlaneMask), &GCvalues);

    /* Blit the images to the pixmaps */
    XPutImage(SDL_Display, data_pixmap, GCcursor, data_image,
              0, 0, 0, 0, w, h);
    XPutImage(SDL_Display, mask_pixmap, GCcursor, mask_image,
              0, 0, 0, 0, w, h);
    XFreeGC(SDL_Display, GCcursor);
    /* These free the x_data and x_mask memory pointers */
    XDestroyImage(data_image);
    XDestroyImage(mask_image);

    /* Create the cursor */
    cursor->x_cursor = XCreatePixmapCursor(SDL_Display, data_pixmap,
                                           mask_pixmap, &black, &white,
                                           hot_x, hot_y);
    XFreePixmap(SDL_Display, data_pixmap);
    XFreePixmap(SDL_Display, mask_pixmap);

    /* Release the event thread */
    XSync(SDL_Display, False);
    SDL_Unlock_EventThread();

    return (cursor);
}

int
X11_ShowWMCursor(_THIS, WMcursor * cursor)
{
    /* Don't do anything if the display is gone */
    if (SDL_Display == NULL) {
        return (0);
    }

    /* Set the X11 cursor cursor, or blank if cursor is NULL */
    if (SDL_Window) {
        SDL_Lock_EventThread();
        if (cursor == NULL) {
            if (SDL_BlankCursor != NULL) {
                XDefineCursor(SDL_Display, SDL_Window,
                              SDL_BlankCursor->x_cursor);
            }
        } else {
            XDefineCursor(SDL_Display, SDL_Window, cursor->x_cursor);
        }
        XSync(SDL_Display, False);
        SDL_Unlock_EventThread();
    }
    return (1);
}

void
X11_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
{
    if (using_dga & DGA_MOUSE) {
        SDL_PrivateMouseMotion(0, 0, x, y);
    } else if (mouse_relative) {
        /*      RJR: March 28, 2000
           leave physical cursor at center of screen if
           mouse hidden and grabbed */
        SDL_PrivateMouseMotion(0, 0, x, y);
    } else {
        SDL_Lock_EventThread();
        XWarpPointer(SDL_Display, None, SDL_Window, 0, 0, 0, 0, x, y);
        XSync(SDL_Display, False);
        SDL_Unlock_EventThread();
    }
}

/* Sets the mouse acceleration from a string of the form:
	2/1/0
   The first number is the numerator, followed by the acceleration
   denumenator and threshold.
*/
static void
SetMouseAccel(_THIS, const char *accel_param)
{
    int i;
    size_t len;
    int accel_value[3];
    char *mouse_param, *mouse_param_buf, *pin;

    len = SDL_strlen(accel_param) + 1;
    mouse_param_buf = SDL_stack_alloc(char, len);
    if (!mouse_param_buf) {
        return;
    }
    SDL_strlcpy(mouse_param_buf, accel_param, len);
    mouse_param = mouse_param_buf;

    for (i = 0; (i < 3) && mouse_param; ++i) {
        pin = SDL_strchr(mouse_param, '/');
        if (pin) {
            *pin = '\0';
        }
        accel_value[i] = atoi(mouse_param);
        if (pin) {
            mouse_param = pin + 1;
        } else {
            mouse_param = NULL;
        }
    }
    if (mouse_param_buf) {
        XChangePointerControl(SDL_Display, True, True,
                              accel_value[0], accel_value[1], accel_value[2]);
        SDL_free(mouse_param_buf);
    }
}

/* Check to see if we need to enter or leave mouse relative mode */
void
X11_CheckMouseModeNoLock(_THIS)
{
    const Uint8 full_focus =
        (SDL_APPACTIVE | SDL_APPINPUTFOCUS | SDL_APPMOUSEFOCUS);
    char *env_override;
    int enable_relative = 1;

    /* Allow the user to override the relative mouse mode.
       They almost never want to do this, as it seriously affects
       applications that rely on continuous relative mouse motion.
     */
    env_override = SDL_getenv("SDL_MOUSE_RELATIVE");
    if (env_override) {
        enable_relative = atoi(env_override);
    }

    /* If the mouse is hidden and input is grabbed, we use relative mode */
    if (enable_relative &&
        !(SDL_cursorstate & CURSOR_VISIBLE) &&
        (SDL_CurrentWindow.input_grab != SDL_GRAB_OFF) &&
        (SDL_GetAppState() & full_focus) == full_focus) {
        if (!mouse_relative) {
            X11_EnableDGAMouse(this);
            if (!(using_dga & DGA_MOUSE)) {
                char *xmouse_accel;

                SDL_GetMouseState(&mouse_last.x, &mouse_last.y);
                /* Use as raw mouse mickeys as possible */
                XGetPointerControl(SDL_Display,
                                   &mouse_accel.numerator,
                                   &mouse_accel.denominator,
                                   &mouse_accel.threshold);
                xmouse_accel = SDL_getenv("SDL_VIDEO_X11_MOUSEACCEL");
                if (xmouse_accel) {
                    SetMouseAccel(this, xmouse_accel);
                }
            }
            mouse_relative = 1;
        }
    } else {
        if (mouse_relative) {
            if (using_dga & DGA_MOUSE) {
                X11_DisableDGAMouse(this);
            } else {
                XChangePointerControl(SDL_Display, True, True,
                                      mouse_accel.numerator,
                                      mouse_accel.denominator,
                                      mouse_accel.threshold);
            }
            mouse_relative = 0;
        }
    }
}
void
X11_CheckMouseMode(_THIS)
{
    SDL_Lock_EventThread();
    X11_CheckMouseModeNoLock(this);
    SDL_Unlock_EventThread();
}

/* vi: set ts=4 sw=4 expandtab: */