src/video/x11/SDL_x11mouse.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 10 Jul 2006 21:04:37 +0000
changeset 1895 c121d94672cb
parent 1740 db7e15a99cb3
permissions -rw-r--r--
SDL 1.2 is moving to a branch, and SDL 1.3 is becoming the head.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include <X11/Xlib.h>
    25 #include <X11/Xutil.h>
    26 
    27 #include "SDL_mouse.h"
    28 #include "../../events/SDL_events_c.h"
    29 #include "../SDL_cursor_c.h"
    30 #include "SDL_x11dga_c.h"
    31 #include "SDL_x11mouse_c.h"
    32 
    33 
    34 /* The implementation dependent data for the window manager cursor */
    35 struct WMcursor
    36 {
    37     Cursor x_cursor;
    38 };
    39 
    40 
    41 void
    42 X11_FreeWMCursor(_THIS, WMcursor * cursor)
    43 {
    44     if (SDL_Display != NULL) {
    45         SDL_Lock_EventThread();
    46         XFreeCursor(SDL_Display, cursor->x_cursor);
    47         XSync(SDL_Display, False);
    48         SDL_Unlock_EventThread();
    49     }
    50     SDL_free(cursor);
    51 }
    52 
    53 WMcursor *
    54 X11_CreateWMCursor(_THIS,
    55                    Uint8 * data, Uint8 * mask, int w, int h, int hot_x,
    56                    int hot_y)
    57 {
    58     WMcursor *cursor;
    59     XGCValues GCvalues;
    60     GC GCcursor;
    61     XImage *data_image, *mask_image;
    62     Pixmap data_pixmap, mask_pixmap;
    63     int clen, i;
    64     char *x_data, *x_mask;
    65     static XColor black = { 0, 0, 0, 0 };
    66     static XColor white = { 0xffff, 0xffff, 0xffff, 0xffff };
    67 
    68     /* Allocate the cursor memory */
    69     cursor = (WMcursor *) SDL_malloc(sizeof(WMcursor));
    70     if (cursor == NULL) {
    71         SDL_OutOfMemory();
    72         return (NULL);
    73     }
    74 
    75     /* Mix the mask and the data */
    76     clen = (w / 8) * h;
    77     x_data = (char *) SDL_malloc(clen);
    78     if (x_data == NULL) {
    79         SDL_free(cursor);
    80         SDL_OutOfMemory();
    81         return (NULL);
    82     }
    83     x_mask = (char *) SDL_malloc(clen);
    84     if (x_mask == NULL) {
    85         SDL_free(cursor);
    86         SDL_free(x_data);
    87         SDL_OutOfMemory();
    88         return (NULL);
    89     }
    90     for (i = 0; i < clen; ++i) {
    91         /* The mask is OR'd with the data to turn inverted color
    92            pixels black since inverted color cursors aren't supported
    93            under X11.
    94          */
    95         x_mask[i] = data[i] | mask[i];
    96         x_data[i] = data[i];
    97     }
    98 
    99     /* Prevent the event thread from running while we use the X server */
   100     SDL_Lock_EventThread();
   101 
   102     /* Create the data image */
   103     data_image = XCreateImage(SDL_Display,
   104                               DefaultVisual(SDL_Display, SDL_Screen),
   105                               1, XYBitmap, 0, x_data, w, h, 8, w / 8);
   106     data_image->byte_order = MSBFirst;
   107     data_image->bitmap_bit_order = MSBFirst;
   108     data_pixmap = XCreatePixmap(SDL_Display, SDL_Root, w, h, 1);
   109 
   110     /* Create the data mask */
   111     mask_image = XCreateImage(SDL_Display,
   112                               DefaultVisual(SDL_Display, SDL_Screen),
   113                               1, XYBitmap, 0, x_mask, w, h, 8, w / 8);
   114     mask_image->byte_order = MSBFirst;
   115     mask_image->bitmap_bit_order = MSBFirst;
   116     mask_pixmap = XCreatePixmap(SDL_Display, SDL_Root, w, h, 1);
   117 
   118     /* Create the graphics context */
   119     GCvalues.function = GXcopy;
   120     GCvalues.foreground = ~0;
   121     GCvalues.background = 0;
   122     GCvalues.plane_mask = AllPlanes;
   123     GCcursor = XCreateGC(SDL_Display, data_pixmap,
   124                          (GCFunction | GCForeground | GCBackground |
   125                           GCPlaneMask), &GCvalues);
   126 
   127     /* Blit the images to the pixmaps */
   128     XPutImage(SDL_Display, data_pixmap, GCcursor, data_image,
   129               0, 0, 0, 0, w, h);
   130     XPutImage(SDL_Display, mask_pixmap, GCcursor, mask_image,
   131               0, 0, 0, 0, w, h);
   132     XFreeGC(SDL_Display, GCcursor);
   133     /* These free the x_data and x_mask memory pointers */
   134     XDestroyImage(data_image);
   135     XDestroyImage(mask_image);
   136 
   137     /* Create the cursor */
   138     cursor->x_cursor = XCreatePixmapCursor(SDL_Display, data_pixmap,
   139                                            mask_pixmap, &black, &white,
   140                                            hot_x, hot_y);
   141     XFreePixmap(SDL_Display, data_pixmap);
   142     XFreePixmap(SDL_Display, mask_pixmap);
   143 
   144     /* Release the event thread */
   145     XSync(SDL_Display, False);
   146     SDL_Unlock_EventThread();
   147 
   148     return (cursor);
   149 }
   150 
   151 int
   152 X11_ShowWMCursor(_THIS, WMcursor * cursor)
   153 {
   154     /* Don't do anything if the display is gone */
   155     if (SDL_Display == NULL) {
   156         return (0);
   157     }
   158 
   159     /* Set the X11 cursor cursor, or blank if cursor is NULL */
   160     if (SDL_Window) {
   161         SDL_Lock_EventThread();
   162         if (cursor == NULL) {
   163             if (SDL_BlankCursor != NULL) {
   164                 XDefineCursor(SDL_Display, SDL_Window,
   165                               SDL_BlankCursor->x_cursor);
   166             }
   167         } else {
   168             XDefineCursor(SDL_Display, SDL_Window, cursor->x_cursor);
   169         }
   170         XSync(SDL_Display, False);
   171         SDL_Unlock_EventThread();
   172     }
   173     return (1);
   174 }
   175 
   176 void
   177 X11_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
   178 {
   179     if (using_dga & DGA_MOUSE) {
   180         SDL_PrivateMouseMotion(0, 0, x, y);
   181     } else if (mouse_relative) {
   182         /*      RJR: March 28, 2000
   183            leave physical cursor at center of screen if
   184            mouse hidden and grabbed */
   185         SDL_PrivateMouseMotion(0, 0, x, y);
   186     } else {
   187         SDL_Lock_EventThread();
   188         XWarpPointer(SDL_Display, None, SDL_Window, 0, 0, 0, 0, x, y);
   189         XSync(SDL_Display, False);
   190         SDL_Unlock_EventThread();
   191     }
   192 }
   193 
   194 /* Sets the mouse acceleration from a string of the form:
   195 	2/1/0
   196    The first number is the numerator, followed by the acceleration
   197    denumenator and threshold.
   198 */
   199 static void
   200 SetMouseAccel(_THIS, const char *accel_param)
   201 {
   202     int i;
   203     size_t len;
   204     int accel_value[3];
   205     char *mouse_param, *mouse_param_buf, *pin;
   206 
   207     len = SDL_strlen(accel_param) + 1;
   208     mouse_param_buf = SDL_stack_alloc(char, len);
   209     if (!mouse_param_buf) {
   210         return;
   211     }
   212     SDL_strlcpy(mouse_param_buf, accel_param, len);
   213     mouse_param = mouse_param_buf;
   214 
   215     for (i = 0; (i < 3) && mouse_param; ++i) {
   216         pin = SDL_strchr(mouse_param, '/');
   217         if (pin) {
   218             *pin = '\0';
   219         }
   220         accel_value[i] = atoi(mouse_param);
   221         if (pin) {
   222             mouse_param = pin + 1;
   223         } else {
   224             mouse_param = NULL;
   225         }
   226     }
   227     if (mouse_param_buf) {
   228         XChangePointerControl(SDL_Display, True, True,
   229                               accel_value[0], accel_value[1], accel_value[2]);
   230         SDL_free(mouse_param_buf);
   231     }
   232 }
   233 
   234 /* Check to see if we need to enter or leave mouse relative mode */
   235 void
   236 X11_CheckMouseModeNoLock(_THIS)
   237 {
   238     const Uint8 full_focus =
   239         (SDL_APPACTIVE | SDL_APPINPUTFOCUS | SDL_APPMOUSEFOCUS);
   240     char *env_override;
   241     int enable_relative = 1;
   242 
   243     /* Allow the user to override the relative mouse mode.
   244        They almost never want to do this, as it seriously affects
   245        applications that rely on continuous relative mouse motion.
   246      */
   247     env_override = SDL_getenv("SDL_MOUSE_RELATIVE");
   248     if (env_override) {
   249         enable_relative = atoi(env_override);
   250     }
   251 
   252     /* If the mouse is hidden and input is grabbed, we use relative mode */
   253     if (enable_relative &&
   254         !(SDL_cursorstate & CURSOR_VISIBLE) &&
   255         (SDL_CurrentWindow.input_grab != SDL_GRAB_OFF) &&
   256         (SDL_GetAppState() & full_focus) == full_focus) {
   257         if (!mouse_relative) {
   258             X11_EnableDGAMouse(this);
   259             if (!(using_dga & DGA_MOUSE)) {
   260                 char *xmouse_accel;
   261 
   262                 SDL_GetMouseState(&mouse_last.x, &mouse_last.y);
   263                 /* Use as raw mouse mickeys as possible */
   264                 XGetPointerControl(SDL_Display,
   265                                    &mouse_accel.numerator,
   266                                    &mouse_accel.denominator,
   267                                    &mouse_accel.threshold);
   268                 xmouse_accel = SDL_getenv("SDL_VIDEO_X11_MOUSEACCEL");
   269                 if (xmouse_accel) {
   270                     SetMouseAccel(this, xmouse_accel);
   271                 }
   272             }
   273             mouse_relative = 1;
   274         }
   275     } else {
   276         if (mouse_relative) {
   277             if (using_dga & DGA_MOUSE) {
   278                 X11_DisableDGAMouse(this);
   279             } else {
   280                 XChangePointerControl(SDL_Display, True, True,
   281                                       mouse_accel.numerator,
   282                                       mouse_accel.denominator,
   283                                       mouse_accel.threshold);
   284             }
   285             mouse_relative = 0;
   286         }
   287     }
   288 }
   289 void
   290 X11_CheckMouseMode(_THIS)
   291 {
   292     SDL_Lock_EventThread();
   293     X11_CheckMouseModeNoLock(this);
   294     SDL_Unlock_EventThread();
   295 }
   296 
   297 /* vi: set ts=4 sw=4 expandtab: */