src/video/x11/SDL_x11mouse.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 06 Mar 2002 11:23:08 +0000
changeset 297 f6ffac90895c
parent 252 e8157fcb3114
child 527 5c74ac147358
permissions -rw-r--r--
Updated copyright information for 2002
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  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 		x += (this->screen->offset % this->screen->pitch) /
   181 		      this->screen->format->BytesPerPixel;
   182 		y += (this->screen->offset / this->screen->pitch);
   183 		SDL_PrivateMouseMotion(0, 0, x, y);
   184 	} else {
   185 		SDL_Lock_EventThread();
   186 		XWarpPointer(SDL_Display, None, SDL_Window, 0, 0, 0, 0, x, y);
   187 		XSync(SDL_Display, False);
   188 		SDL_Unlock_EventThread();
   189 	}
   190 }
   191 
   192 /* Sets the mouse acceleration from a string of the form:
   193 	2/1/0
   194    The first number is the numerator, followed by the acceleration
   195    denumenator and threshold.
   196 */
   197 static void SetMouseAccel(_THIS, const char *accel_param)
   198 {
   199 	int i;
   200 	int accel_value[3];
   201 	char *mouse_param, *mouse_param_buf, *pin;
   202 
   203 	mouse_param_buf = (char *)malloc(strlen(accel_param)+1);
   204 	if ( ! mouse_param_buf ) {
   205 		return;
   206 	}
   207 	strcpy(mouse_param_buf, accel_param);
   208 	mouse_param = mouse_param_buf;
   209 
   210 	for ( i=0; (i < 3) && mouse_param; ++i ) {
   211 		pin = strchr(mouse_param, '/');
   212 		if ( pin ) {
   213 			*pin = '\0';
   214 		}
   215 		accel_value[i] = atoi(mouse_param);
   216 		if ( pin ) {
   217 			mouse_param = pin+1;
   218 		} else {
   219 			mouse_param = NULL;
   220 		}
   221 	}
   222 	if ( mouse_param_buf ) {
   223 		XChangePointerControl(SDL_Display, True, True,
   224 			accel_value[0], accel_value[1], accel_value[2]);
   225 		free(mouse_param_buf);
   226 	}
   227 }
   228 
   229 /* Check to see if we need to enter or leave mouse relative mode */
   230 void X11_CheckMouseModeNoLock(_THIS)
   231 {
   232 	char *env_override;
   233 	int enable_relative = 1;
   234 
   235 	/* Allow the user to override the relative mouse mode.
   236 	   They almost never want to do this, as it seriously affects
   237 	   applications that rely on continuous relative mouse motion.
   238 	*/
   239 	env_override = getenv("SDL_MOUSE_RELATIVE");
   240 	if ( env_override ) {
   241 		enable_relative = atoi(env_override);
   242 	}
   243 
   244 	/* If the mouse is hidden and input is grabbed, we use relative mode */
   245 	if ( enable_relative &&
   246 	     !(SDL_cursorstate & CURSOR_VISIBLE) &&
   247 	     (this->input_grab != SDL_GRAB_OFF) &&
   248              (SDL_GetAppState() & SDL_APPACTIVE) ) {
   249 		if ( ! mouse_relative ) {
   250 			X11_EnableDGAMouse(this);
   251 			if ( ! (using_dga & DGA_MOUSE) ) {
   252 				char *xmouse_accel;
   253 
   254 				SDL_GetMouseState(&mouse_last.x, &mouse_last.y);
   255 				/* Use as raw mouse mickeys as possible */
   256 				XGetPointerControl(SDL_Display,
   257 						&mouse_accel.numerator, 
   258 						&mouse_accel.denominator,
   259 						&mouse_accel.threshold);
   260 				xmouse_accel=getenv("SDL_VIDEO_X11_MOUSEACCEL");
   261 				if ( xmouse_accel ) {
   262 					SetMouseAccel(this, xmouse_accel);
   263 				}
   264 			}
   265 			mouse_relative = 1;
   266 		}
   267 	} else {
   268 		if ( mouse_relative ) {
   269 			if ( using_dga & DGA_MOUSE ) {
   270 				X11_DisableDGAMouse(this);
   271 			} else {
   272 				XChangePointerControl(SDL_Display, True, True,
   273 						mouse_accel.numerator, 
   274 						mouse_accel.denominator,
   275 						mouse_accel.threshold);
   276 			}
   277 			mouse_relative = 0;
   278 		}
   279 	}
   280 }
   281 void X11_CheckMouseMode(_THIS)
   282 {
   283 	SDL_Lock_EventThread();
   284 	X11_CheckMouseModeNoLock(this);
   285 	SDL_Unlock_EventThread();
   286 }