src/video/x11/SDL_x11mouse.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 05 Nov 2005 19:53:37 +0000
changeset 1168 045f186426e1
parent 888 07def9d03315
child 1312 c9b51268668f
permissions -rw-r--r--
Dynamically load X11 libraries like we currently do for alsa, esd, etc.
This allows you to run an SDL program on a system without Xlib, since it'll
just report the x11 target unavailable at runtime.
     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 		pXFreeCursor(SDL_Display, cursor->x_cursor);
    54 		pXSync(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 = pXCreateImage(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 = pXCreatePixmap(SDL_Display, SDL_Root, w, h, 1);
   114 
   115 	/* Create the data mask */
   116 	mask_image = pXCreateImage(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 = pXCreatePixmap(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 = pXCreateGC(SDL_Display, data_pixmap,
   129 			(GCFunction|GCForeground|GCBackground|GCPlaneMask),
   130 								&GCvalues);
   131 
   132 	/* Blit the images to the pixmaps */
   133 	pXPutImage(SDL_Display, data_pixmap, GCcursor, data_image,
   134 							0, 0, 0, 0, w, h);
   135 	pXPutImage(SDL_Display, mask_pixmap, GCcursor, mask_image,
   136 							0, 0, 0, 0, w, h);
   137 	pXFreeGC(SDL_Display, GCcursor);
   138 	/* These free the x_data and x_mask memory pointers */
   139 	pXDestroyImage(data_image);
   140 	pXDestroyImage(mask_image);
   141 
   142 	/* Create the cursor */
   143 	cursor->x_cursor = pXCreatePixmapCursor(SDL_Display, data_pixmap,
   144 				mask_pixmap, &black, &white, hot_x, hot_y);
   145 	pXFreePixmap(SDL_Display, data_pixmap);
   146 	pXFreePixmap(SDL_Display, mask_pixmap);
   147 
   148 	/* Release the event thread */
   149 	pXSync(SDL_Display, False);
   150 	SDL_Unlock_EventThread();
   151 
   152 	return(cursor);
   153 }
   154 
   155 int X11_ShowWMCursor(_THIS, WMcursor *cursor)
   156 {
   157 	/* Don't do anything if the display is gone */
   158 	if ( SDL_Display == NULL ) {
   159 		return(0);
   160 	}
   161 
   162 	/* Set the X11 cursor cursor, or blank if cursor is NULL */
   163 	if ( SDL_Window ) {
   164 		SDL_Lock_EventThread();
   165 		if ( cursor == NULL ) {
   166 			if ( SDL_BlankCursor != NULL ) {
   167 				pXDefineCursor(SDL_Display, SDL_Window,
   168 					SDL_BlankCursor->x_cursor);
   169 			}
   170 		} else {
   171 			pXDefineCursor(SDL_Display, SDL_Window, cursor->x_cursor);
   172 		}
   173 		pXSync(SDL_Display, False);
   174 		SDL_Unlock_EventThread();
   175 	}
   176 	return(1);
   177 }
   178 
   179 void X11_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
   180 {
   181 	if ( using_dga & DGA_MOUSE ) {
   182 		SDL_PrivateMouseMotion(0, 0, x, y);
   183 	} else if ( mouse_relative) {
   184 		/*	RJR: March 28, 2000
   185 			leave physical cursor at center of screen if
   186 			mouse hidden and grabbed */
   187 		SDL_PrivateMouseMotion(0, 0, x, y);
   188 	} else {
   189 		SDL_Lock_EventThread();
   190 		pXWarpPointer(SDL_Display, None, SDL_Window, 0, 0, 0, 0, x, y);
   191 		pXSync(SDL_Display, False);
   192 		SDL_Unlock_EventThread();
   193 	}
   194 }
   195 
   196 /* Sets the mouse acceleration from a string of the form:
   197 	2/1/0
   198    The first number is the numerator, followed by the acceleration
   199    denumenator and threshold.
   200 */
   201 static void SetMouseAccel(_THIS, const char *accel_param)
   202 {
   203 	int i;
   204 	int accel_value[3];
   205 	char *mouse_param, *mouse_param_buf, *pin;
   206 
   207 	mouse_param_buf = (char *)malloc(strlen(accel_param)+1);
   208 	if ( ! mouse_param_buf ) {
   209 		return;
   210 	}
   211 	strcpy(mouse_param_buf, accel_param);
   212 	mouse_param = mouse_param_buf;
   213 
   214 	for ( i=0; (i < 3) && mouse_param; ++i ) {
   215 		pin = strchr(mouse_param, '/');
   216 		if ( pin ) {
   217 			*pin = '\0';
   218 		}
   219 		accel_value[i] = atoi(mouse_param);
   220 		if ( pin ) {
   221 			mouse_param = pin+1;
   222 		} else {
   223 			mouse_param = NULL;
   224 		}
   225 	}
   226 	if ( mouse_param_buf ) {
   227 		pXChangePointerControl(SDL_Display, True, True,
   228 			accel_value[0], accel_value[1], accel_value[2]);
   229 		free(mouse_param_buf);
   230 	}
   231 }
   232 
   233 /* Check to see if we need to enter or leave mouse relative mode */
   234 void X11_CheckMouseModeNoLock(_THIS)
   235 {
   236 	char *env_override;
   237 	int enable_relative = 1;
   238 
   239 	/* Allow the user to override the relative mouse mode.
   240 	   They almost never want to do this, as it seriously affects
   241 	   applications that rely on continuous relative mouse motion.
   242 	*/
   243 	env_override = getenv("SDL_MOUSE_RELATIVE");
   244 	if ( env_override ) {
   245 		enable_relative = atoi(env_override);
   246 	}
   247 
   248 	/* If the mouse is hidden and input is grabbed, we use relative mode */
   249 	if ( enable_relative &&
   250 	     !(SDL_cursorstate & CURSOR_VISIBLE) &&
   251 	     (this->input_grab != SDL_GRAB_OFF) &&
   252              (SDL_GetAppState() & SDL_APPACTIVE) ) {
   253 		if ( ! mouse_relative ) {
   254 			X11_EnableDGAMouse(this);
   255 			if ( ! (using_dga & DGA_MOUSE) ) {
   256 				char *xmouse_accel;
   257 
   258 				SDL_GetMouseState(&mouse_last.x, &mouse_last.y);
   259 				/* Use as raw mouse mickeys as possible */
   260 				pXGetPointerControl(SDL_Display,
   261 						&mouse_accel.numerator, 
   262 						&mouse_accel.denominator,
   263 						&mouse_accel.threshold);
   264 				xmouse_accel=getenv("SDL_VIDEO_X11_MOUSEACCEL");
   265 				if ( xmouse_accel ) {
   266 					SetMouseAccel(this, xmouse_accel);
   267 				}
   268 			}
   269 			mouse_relative = 1;
   270 		}
   271 	} else {
   272 		if ( mouse_relative ) {
   273 			if ( using_dga & DGA_MOUSE ) {
   274 				X11_DisableDGAMouse(this);
   275 			} else {
   276 				pXChangePointerControl(SDL_Display, True, True,
   277 						mouse_accel.numerator, 
   278 						mouse_accel.denominator,
   279 						mouse_accel.threshold);
   280 			}
   281 			mouse_relative = 0;
   282 		}
   283 	}
   284 }
   285 void X11_CheckMouseMode(_THIS)
   286 {
   287 	SDL_Lock_EventThread();
   288 	X11_CheckMouseModeNoLock(this);
   289 	SDL_Unlock_EventThread();
   290 }