src/video/x11/SDL_x11mouse.c
author Sam Lantinga <slouken@lokigames.com>
Thu, 26 Apr 2001 16:45:43 +0000
changeset 0 74212992fb08
child 78 e9582f471c02
permissions -rw-r--r--
Initial revision
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001  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@devolution.com
    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 	/* If the mouse is hidden and input is grabbed, we use relative mode */
   233 	if ( !(SDL_cursorstate & CURSOR_VISIBLE) &&
   234 	     (this->input_grab != SDL_GRAB_OFF) &&
   235              (SDL_GetAppState() & SDL_APPACTIVE) ) {
   236 		if ( ! mouse_relative ) {
   237 			X11_EnableDGAMouse(this);
   238 			if ( ! (using_dga & DGA_MOUSE) ) {
   239 				char *xmouse_accel;
   240 
   241 				SDL_GetMouseState(&mouse_last.x, &mouse_last.y);
   242 				/* Use as raw mouse mickeys as possible */
   243 				XGetPointerControl(SDL_Display,
   244 						&mouse_accel.numerator, 
   245 						&mouse_accel.denominator,
   246 						&mouse_accel.threshold);
   247 				xmouse_accel=getenv("SDL_VIDEO_X11_MOUSEACCEL");
   248 				if ( xmouse_accel ) {
   249 					SetMouseAccel(this, xmouse_accel);
   250 				}
   251 			}
   252 			mouse_relative = 1;
   253 		}
   254 	} else {
   255 		if ( mouse_relative ) {
   256 			if ( using_dga & DGA_MOUSE ) {
   257 				X11_DisableDGAMouse(this);
   258 			} else {
   259 				XChangePointerControl(SDL_Display, True, True,
   260 						mouse_accel.numerator, 
   261 						mouse_accel.denominator,
   262 						mouse_accel.threshold);
   263 			}
   264 			mouse_relative = 0;
   265 		}
   266 	}
   267 }
   268 void X11_CheckMouseMode(_THIS)
   269 {
   270 	SDL_Lock_EventThread();
   271 	X11_CheckMouseModeNoLock(this);
   272 	SDL_Unlock_EventThread();
   273 }