src/video/x11/SDL_x11mouse.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 04 Jan 2004 16:49:27 +0000
changeset 769 b8d311d90021
parent 527 5c74ac147358
child 888 07def9d03315
permissions -rw-r--r--
Updated copyright information for 2004 (Happy New Year!)
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 #include <stdlib.h>
    29 #include <stdio.h>
    30 #include <string.h>
    31 
    32 #include <X11/Xlib.h>
    33 #include <X11/Xutil.h>
    34 
    35 #include "SDL_error.h"
    36 #include "SDL_mouse.h"
    37 #include "SDL_events_c.h"
    38 #include "SDL_cursor_c.h"
    39 #include "SDL_x11dga_c.h"
    40 #include "SDL_x11mouse_c.h"
    41 
    42 
    43 /* The implementation dependent data for the window manager cursor */
    44 struct WMcursor {
    45 	Cursor x_cursor;
    46 };
    47 
    48 
    49 void X11_FreeWMCursor(_THIS, WMcursor *cursor)
    50 {
    51 	if ( SDL_Display != NULL ) {
    52 		SDL_Lock_EventThread();
    53 		XFreeCursor(SDL_Display, cursor->x_cursor);
    54 		XSync(SDL_Display, False);
    55 		SDL_Unlock_EventThread();
    56 	}
    57 	free(cursor);
    58 }
    59 
    60 WMcursor *X11_CreateWMCursor(_THIS,
    61 		Uint8 *data, Uint8 *mask, int w, int h, int hot_x, int hot_y)
    62 {
    63 	WMcursor *cursor;
    64 	XGCValues GCvalues;
    65 	GC        GCcursor;
    66 	XImage *data_image, *mask_image;
    67 	Pixmap  data_pixmap, mask_pixmap;
    68 	int       clen, i;
    69 	char     *x_data, *x_mask;
    70 	static XColor black = {  0,  0,  0,  0 };
    71 	static XColor white = { 0xffff, 0xffff, 0xffff, 0xffff };
    72 
    73 	/* Allocate the cursor memory */
    74 	cursor = (WMcursor *)malloc(sizeof(WMcursor));
    75 	if ( cursor == NULL ) {
    76 		SDL_OutOfMemory();
    77 		return(NULL);
    78 	}
    79 
    80 	/* Mix the mask and the data */
    81 	clen = (w/8)*h;
    82 	x_data = (char *)malloc(clen);
    83 	if ( x_data == NULL ) {
    84 		free(cursor);
    85 		SDL_OutOfMemory();
    86 		return(NULL);
    87 	}
    88 	x_mask = (char *)malloc(clen);
    89 	if ( x_mask == NULL ) {
    90 		free(cursor);
    91 		free(x_data);
    92 		SDL_OutOfMemory();
    93 		return(NULL);
    94 	}
    95 	for ( i=0; i<clen; ++i ) {
    96 		/* The mask is OR'd with the data to turn inverted color
    97 		   pixels black since inverted color cursors aren't supported
    98 		   under X11.
    99 		 */
   100 		x_mask[i] = data[i] | mask[i];
   101 		x_data[i] = data[i];
   102 	}
   103 
   104 	/* Prevent the event thread from running while we use the X server */
   105 	SDL_Lock_EventThread();
   106 
   107 	/* Create the data image */
   108 	data_image = XCreateImage(SDL_Display, 
   109 			DefaultVisual(SDL_Display, SDL_Screen),
   110 					1, XYBitmap, 0, x_data, w, h, 8, w/8);
   111 	data_image->byte_order = MSBFirst;
   112 	data_image->bitmap_bit_order = MSBFirst;
   113 	data_pixmap = XCreatePixmap(SDL_Display, SDL_Root, w, h, 1);
   114 
   115 	/* Create the data mask */
   116 	mask_image = XCreateImage(SDL_Display, 
   117 			DefaultVisual(SDL_Display, SDL_Screen),
   118 					1, XYBitmap, 0, x_mask, w, h, 8, w/8);
   119 	mask_image->byte_order = MSBFirst;
   120 	mask_image->bitmap_bit_order = MSBFirst;
   121 	mask_pixmap = XCreatePixmap(SDL_Display, SDL_Root, w, h, 1);
   122 
   123 	/* Create the graphics context */
   124 	GCvalues.function = GXcopy;
   125 	GCvalues.foreground = ~0;
   126 	GCvalues.background =  0;
   127 	GCvalues.plane_mask = AllPlanes;
   128 	GCcursor = XCreateGC(SDL_Display, data_pixmap,
   129 			(GCFunction|GCForeground|GCBackground|GCPlaneMask),
   130 								&GCvalues);
   131 
   132 	/* Blit the images to the pixmaps */
   133 	XPutImage(SDL_Display, data_pixmap, GCcursor, data_image,
   134 							0, 0, 0, 0, w, h);
   135 	XPutImage(SDL_Display, mask_pixmap, GCcursor, mask_image,
   136 							0, 0, 0, 0, w, h);
   137 	XFreeGC(SDL_Display, GCcursor);
   138 	/* These free the x_data and x_mask memory pointers */
   139 	XDestroyImage(data_image);
   140 	XDestroyImage(mask_image);
   141 
   142 	/* Create the cursor */
   143 	cursor->x_cursor = XCreatePixmapCursor(SDL_Display, data_pixmap,
   144 				mask_pixmap, &black, &white, hot_x, hot_y);
   145 
   146 	/* Release the event thread */
   147 	XSync(SDL_Display, False);
   148 	SDL_Unlock_EventThread();
   149 
   150 	return(cursor);
   151 }
   152 
   153 int X11_ShowWMCursor(_THIS, WMcursor *cursor)
   154 {
   155 	/* Don't do anything if the display is gone */
   156 	if ( SDL_Display == NULL ) {
   157 		return(0);
   158 	}
   159 
   160 	/* Set the X11 cursor cursor, or blank if cursor is NULL */
   161 	if ( SDL_Window ) {
   162 		SDL_Lock_EventThread();
   163 		if ( cursor == NULL ) {
   164 			if ( SDL_BlankCursor != NULL ) {
   165 				XDefineCursor(SDL_Display, SDL_Window,
   166 					SDL_BlankCursor->x_cursor);
   167 			}
   168 		} else {
   169 			XDefineCursor(SDL_Display, SDL_Window, cursor->x_cursor);
   170 		}
   171 		XSync(SDL_Display, False);
   172 		SDL_Unlock_EventThread();
   173 	}
   174 	return(1);
   175 }
   176 
   177 void 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 SetMouseAccel(_THIS, const char *accel_param)
   200 {
   201 	int i;
   202 	int accel_value[3];
   203 	char *mouse_param, *mouse_param_buf, *pin;
   204 
   205 	mouse_param_buf = (char *)malloc(strlen(accel_param)+1);
   206 	if ( ! mouse_param_buf ) {
   207 		return;
   208 	}
   209 	strcpy(mouse_param_buf, accel_param);
   210 	mouse_param = mouse_param_buf;
   211 
   212 	for ( i=0; (i < 3) && mouse_param; ++i ) {
   213 		pin = strchr(mouse_param, '/');
   214 		if ( pin ) {
   215 			*pin = '\0';
   216 		}
   217 		accel_value[i] = atoi(mouse_param);
   218 		if ( pin ) {
   219 			mouse_param = pin+1;
   220 		} else {
   221 			mouse_param = NULL;
   222 		}
   223 	}
   224 	if ( mouse_param_buf ) {
   225 		XChangePointerControl(SDL_Display, True, True,
   226 			accel_value[0], accel_value[1], accel_value[2]);
   227 		free(mouse_param_buf);
   228 	}
   229 }
   230 
   231 /* Check to see if we need to enter or leave mouse relative mode */
   232 void X11_CheckMouseModeNoLock(_THIS)
   233 {
   234 	char *env_override;
   235 	int enable_relative = 1;
   236 
   237 	/* Allow the user to override the relative mouse mode.
   238 	   They almost never want to do this, as it seriously affects
   239 	   applications that rely on continuous relative mouse motion.
   240 	*/
   241 	env_override = getenv("SDL_MOUSE_RELATIVE");
   242 	if ( env_override ) {
   243 		enable_relative = atoi(env_override);
   244 	}
   245 
   246 	/* If the mouse is hidden and input is grabbed, we use relative mode */
   247 	if ( enable_relative &&
   248 	     !(SDL_cursorstate & CURSOR_VISIBLE) &&
   249 	     (this->input_grab != SDL_GRAB_OFF) &&
   250              (SDL_GetAppState() & SDL_APPACTIVE) ) {
   251 		if ( ! mouse_relative ) {
   252 			X11_EnableDGAMouse(this);
   253 			if ( ! (using_dga & DGA_MOUSE) ) {
   254 				char *xmouse_accel;
   255 
   256 				SDL_GetMouseState(&mouse_last.x, &mouse_last.y);
   257 				/* Use as raw mouse mickeys as possible */
   258 				XGetPointerControl(SDL_Display,
   259 						&mouse_accel.numerator, 
   260 						&mouse_accel.denominator,
   261 						&mouse_accel.threshold);
   262 				xmouse_accel=getenv("SDL_VIDEO_X11_MOUSEACCEL");
   263 				if ( xmouse_accel ) {
   264 					SetMouseAccel(this, xmouse_accel);
   265 				}
   266 			}
   267 			mouse_relative = 1;
   268 		}
   269 	} else {
   270 		if ( mouse_relative ) {
   271 			if ( using_dga & DGA_MOUSE ) {
   272 				X11_DisableDGAMouse(this);
   273 			} else {
   274 				XChangePointerControl(SDL_Display, True, True,
   275 						mouse_accel.numerator, 
   276 						mouse_accel.denominator,
   277 						mouse_accel.threshold);
   278 			}
   279 			mouse_relative = 0;
   280 		}
   281 	}
   282 }
   283 void X11_CheckMouseMode(_THIS)
   284 {
   285 	SDL_Lock_EventThread();
   286 	X11_CheckMouseModeNoLock(this);
   287 	SDL_Unlock_EventThread();
   288 }