src/video/x11/SDL_x11mouse.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 22 Mar 2006 05:00:59 +0000
changeset 1575 3ba88cb7eb1b
parent 1402 d910939febfa
child 1657 5b0805ceb50f
permissions -rw-r--r--
Updated dynamic X11 code. See details in Bugzilla #170.
     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 	Cursor x_cursor;
    37 };
    38 
    39 
    40 void X11_FreeWMCursor(_THIS, WMcursor *cursor)
    41 {
    42 	if ( SDL_Display != NULL ) {
    43 		SDL_Lock_EventThread();
    44 		XFreeCursor(SDL_Display, cursor->x_cursor);
    45 		XSync(SDL_Display, False);
    46 		SDL_Unlock_EventThread();
    47 	}
    48 	SDL_free(cursor);
    49 }
    50 
    51 WMcursor *X11_CreateWMCursor(_THIS,
    52 		Uint8 *data, Uint8 *mask, int w, int h, int hot_x, int hot_y)
    53 {
    54 	WMcursor *cursor;
    55 	XGCValues GCvalues;
    56 	GC        GCcursor;
    57 	XImage *data_image, *mask_image;
    58 	Pixmap  data_pixmap, mask_pixmap;
    59 	int       clen, i;
    60 	char     *x_data, *x_mask;
    61 	static XColor black = {  0,  0,  0,  0 };
    62 	static XColor white = { 0xffff, 0xffff, 0xffff, 0xffff };
    63 
    64 	/* Allocate the cursor memory */
    65 	cursor = (WMcursor *)SDL_malloc(sizeof(WMcursor));
    66 	if ( cursor == NULL ) {
    67 		SDL_OutOfMemory();
    68 		return(NULL);
    69 	}
    70 
    71 	/* Mix the mask and the data */
    72 	clen = (w/8)*h;
    73 	x_data = (char *)SDL_malloc(clen);
    74 	if ( x_data == NULL ) {
    75 		SDL_free(cursor);
    76 		SDL_OutOfMemory();
    77 		return(NULL);
    78 	}
    79 	x_mask = (char *)SDL_malloc(clen);
    80 	if ( x_mask == NULL ) {
    81 		SDL_free(cursor);
    82 		SDL_free(x_data);
    83 		SDL_OutOfMemory();
    84 		return(NULL);
    85 	}
    86 	for ( i=0; i<clen; ++i ) {
    87 		/* The mask is OR'd with the data to turn inverted color
    88 		   pixels black since inverted color cursors aren't supported
    89 		   under X11.
    90 		 */
    91 		x_mask[i] = data[i] | mask[i];
    92 		x_data[i] = data[i];
    93 	}
    94 
    95 	/* Prevent the event thread from running while we use the X server */
    96 	SDL_Lock_EventThread();
    97 
    98 	/* Create the data image */
    99 	data_image = XCreateImage(SDL_Display, 
   100 			DefaultVisual(SDL_Display, SDL_Screen),
   101 					1, XYBitmap, 0, x_data, w, h, 8, w/8);
   102 	data_image->byte_order = MSBFirst;
   103 	data_image->bitmap_bit_order = MSBFirst;
   104 	data_pixmap = XCreatePixmap(SDL_Display, SDL_Root, w, h, 1);
   105 
   106 	/* Create the data mask */
   107 	mask_image = XCreateImage(SDL_Display, 
   108 			DefaultVisual(SDL_Display, SDL_Screen),
   109 					1, XYBitmap, 0, x_mask, w, h, 8, w/8);
   110 	mask_image->byte_order = MSBFirst;
   111 	mask_image->bitmap_bit_order = MSBFirst;
   112 	mask_pixmap = XCreatePixmap(SDL_Display, SDL_Root, w, h, 1);
   113 
   114 	/* Create the graphics context */
   115 	GCvalues.function = GXcopy;
   116 	GCvalues.foreground = ~0;
   117 	GCvalues.background =  0;
   118 	GCvalues.plane_mask = AllPlanes;
   119 	GCcursor = XCreateGC(SDL_Display, data_pixmap,
   120 			(GCFunction|GCForeground|GCBackground|GCPlaneMask),
   121 								&GCvalues);
   122 
   123 	/* Blit the images to the pixmaps */
   124 	XPutImage(SDL_Display, data_pixmap, GCcursor, data_image,
   125 							0, 0, 0, 0, w, h);
   126 	XPutImage(SDL_Display, mask_pixmap, GCcursor, mask_image,
   127 							0, 0, 0, 0, w, h);
   128 	XFreeGC(SDL_Display, GCcursor);
   129 	/* These free the x_data and x_mask memory pointers */
   130 	XDestroyImage(data_image);
   131 	XDestroyImage(mask_image);
   132 
   133 	/* Create the cursor */
   134 	cursor->x_cursor = XCreatePixmapCursor(SDL_Display, data_pixmap,
   135 				mask_pixmap, &black, &white, hot_x, hot_y);
   136 	XFreePixmap(SDL_Display, data_pixmap);
   137 	XFreePixmap(SDL_Display, mask_pixmap);
   138 
   139 	/* Release the event thread */
   140 	XSync(SDL_Display, False);
   141 	SDL_Unlock_EventThread();
   142 
   143 	return(cursor);
   144 }
   145 
   146 int X11_ShowWMCursor(_THIS, WMcursor *cursor)
   147 {
   148 	/* Don't do anything if the display is gone */
   149 	if ( SDL_Display == NULL ) {
   150 		return(0);
   151 	}
   152 
   153 	/* Set the X11 cursor cursor, or blank if cursor is NULL */
   154 	if ( SDL_Window ) {
   155 		SDL_Lock_EventThread();
   156 		if ( cursor == NULL ) {
   157 			if ( SDL_BlankCursor != NULL ) {
   158 				XDefineCursor(SDL_Display, SDL_Window,
   159 					SDL_BlankCursor->x_cursor);
   160 			}
   161 		} else {
   162 			XDefineCursor(SDL_Display, SDL_Window, cursor->x_cursor);
   163 		}
   164 		XSync(SDL_Display, False);
   165 		SDL_Unlock_EventThread();
   166 	}
   167 	return(1);
   168 }
   169 
   170 void X11_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
   171 {
   172 	if ( using_dga & DGA_MOUSE ) {
   173 		SDL_PrivateMouseMotion(0, 0, x, y);
   174 	} else if ( mouse_relative) {
   175 		/*	RJR: March 28, 2000
   176 			leave physical cursor at center of screen if
   177 			mouse hidden and grabbed */
   178 		SDL_PrivateMouseMotion(0, 0, x, y);
   179 	} else {
   180 		SDL_Lock_EventThread();
   181 		XWarpPointer(SDL_Display, None, SDL_Window, 0, 0, 0, 0, x, y);
   182 		XSync(SDL_Display, False);
   183 		SDL_Unlock_EventThread();
   184 	}
   185 }
   186 
   187 /* Sets the mouse acceleration from a string of the form:
   188 	2/1/0
   189    The first number is the numerator, followed by the acceleration
   190    denumenator and threshold.
   191 */
   192 static void SetMouseAccel(_THIS, const char *accel_param)
   193 {
   194 	int i;
   195 	size_t len;
   196 	int accel_value[3];
   197 	char *mouse_param, *mouse_param_buf, *pin;
   198 
   199 	len = SDL_strlen(accel_param)+1;
   200 	mouse_param_buf = SDL_stack_alloc(char, len);
   201 	if ( ! mouse_param_buf ) {
   202 		return;
   203 	}
   204 	SDL_strlcpy(mouse_param_buf, accel_param, len);
   205 	mouse_param = mouse_param_buf;
   206 
   207 	for ( i=0; (i < 3) && mouse_param; ++i ) {
   208 		pin = SDL_strchr(mouse_param, '/');
   209 		if ( pin ) {
   210 			*pin = '\0';
   211 		}
   212 		accel_value[i] = atoi(mouse_param);
   213 		if ( pin ) {
   214 			mouse_param = pin+1;
   215 		} else {
   216 			mouse_param = NULL;
   217 		}
   218 	}
   219 	if ( mouse_param_buf ) {
   220 		XChangePointerControl(SDL_Display, True, True,
   221 			accel_value[0], accel_value[1], accel_value[2]);
   222 		SDL_free(mouse_param_buf);
   223 	}
   224 }
   225 
   226 /* Check to see if we need to enter or leave mouse relative mode */
   227 void X11_CheckMouseModeNoLock(_THIS)
   228 {
   229 	char *env_override;
   230 	int enable_relative = 1;
   231 
   232 	/* Allow the user to override the relative mouse mode.
   233 	   They almost never want to do this, as it seriously affects
   234 	   applications that rely on continuous relative mouse motion.
   235 	*/
   236 	env_override = SDL_getenv("SDL_MOUSE_RELATIVE");
   237 	if ( env_override ) {
   238 		enable_relative = atoi(env_override);
   239 	}
   240 
   241 	/* If the mouse is hidden and input is grabbed, we use relative mode */
   242 	if ( enable_relative &&
   243 	     !(SDL_cursorstate & CURSOR_VISIBLE) &&
   244 	     (this->input_grab != SDL_GRAB_OFF) &&
   245              (SDL_GetAppState() & SDL_APPACTIVE) ) {
   246 		if ( ! mouse_relative ) {
   247 			X11_EnableDGAMouse(this);
   248 			if ( ! (using_dga & DGA_MOUSE) ) {
   249 				char *xmouse_accel;
   250 
   251 				SDL_GetMouseState(&mouse_last.x, &mouse_last.y);
   252 				/* Use as raw mouse mickeys as possible */
   253 				XGetPointerControl(SDL_Display,
   254 						&mouse_accel.numerator, 
   255 						&mouse_accel.denominator,
   256 						&mouse_accel.threshold);
   257 				xmouse_accel=SDL_getenv("SDL_VIDEO_X11_MOUSEACCEL");
   258 				if ( xmouse_accel ) {
   259 					SetMouseAccel(this, xmouse_accel);
   260 				}
   261 			}
   262 			mouse_relative = 1;
   263 		}
   264 	} else {
   265 		if ( mouse_relative ) {
   266 			if ( using_dga & DGA_MOUSE ) {
   267 				X11_DisableDGAMouse(this);
   268 			} else {
   269 				XChangePointerControl(SDL_Display, True, True,
   270 						mouse_accel.numerator, 
   271 						mouse_accel.denominator,
   272 						mouse_accel.threshold);
   273 			}
   274 			mouse_relative = 0;
   275 		}
   276 	}
   277 }
   278 void X11_CheckMouseMode(_THIS)
   279 {
   280 	SDL_Lock_EventThread();
   281 	X11_CheckMouseModeNoLock(this);
   282 	SDL_Unlock_EventThread();
   283 }