view src/video/x11/SDL_x11mouse.c @ 1662:782fd950bd46 SDL-1.3

Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API. WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid. The code is now run through a consistent indent format: indent -i4 -nut -nsc -br -ce The headers are being converted to automatically generate doxygen documentation.
author Sam Lantinga <slouken@libsdl.org>
date Sun, 28 May 2006 13:04:16 +0000
parents 5b0805ceb50f
children 4da1ee79c9af
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: */