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
X11_FreeWMCursor(_THIS,WMcursor * cursor)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
X11_CreateWMCursor(_THIS,Uint8 * data,Uint8 * mask,int w,int h,int hot_x,int hot_y)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
X11_ShowWMCursor(_THIS,WMcursor * cursor)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
X11_WarpWMCursor(_THIS,Uint16 x,Uint16 y)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 */
SetMouseAccel(_THIS,const char * accel_param)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 ( i == 3 ) {
220 XChangePointerControl(SDL_Display, True, True,
221 accel_value[0], accel_value[1], accel_value[2]);
222 }
223 SDL_stack_free(mouse_param_buf);
224 }
225
226 /* Check to see if we need to enter or leave mouse relative mode */
X11_CheckMouseModeNoLock(_THIS)227 void X11_CheckMouseModeNoLock(_THIS)
228 {
229 const Uint8 full_focus = (SDL_APPACTIVE|SDL_APPINPUTFOCUS|SDL_APPMOUSEFOCUS);
230 char *env_override;
231 int enable_relative = 1;
232
233 /* Allow the user to override the relative mouse mode.
234 They almost never want to do this, as it seriously affects
235 applications that rely on continuous relative mouse motion.
236 */
237 env_override = SDL_getenv("SDL_MOUSE_RELATIVE");
238 if ( env_override ) {
239 enable_relative = atoi(env_override);
240 }
241
242 /* If the mouse is hidden and input is grabbed, we use relative mode */
243 if ( enable_relative &&
244 !(SDL_cursorstate & CURSOR_VISIBLE) &&
245 (this->input_grab != SDL_GRAB_OFF) &&
246 (SDL_GetAppState() & full_focus) == full_focus ) {
247 if ( ! mouse_relative ) {
248 X11_EnableDGAMouse(this);
249 if ( ! (using_dga & DGA_MOUSE) ) {
250 char *xmouse_accel;
251
252 SDL_GetMouseState(&mouse_last.x, &mouse_last.y);
253 /* Use as raw mouse mickeys as possible */
254 XGetPointerControl(SDL_Display,
255 &mouse_accel.numerator,
256 &mouse_accel.denominator,
257 &mouse_accel.threshold);
258 xmouse_accel=SDL_getenv("SDL_VIDEO_X11_MOUSEACCEL");
259 if ( xmouse_accel ) {
260 SetMouseAccel(this, xmouse_accel);
261 }
262 }
263 mouse_relative = 1;
264 }
265 } else {
266 if ( mouse_relative ) {
267 if ( using_dga & DGA_MOUSE ) {
268 X11_DisableDGAMouse(this);
269 } else {
270 XChangePointerControl(SDL_Display, True, True,
271 mouse_accel.numerator,
272 mouse_accel.denominator,
273 mouse_accel.threshold);
274 }
275 mouse_relative = 0;
276 }
277 }
278 }
X11_CheckMouseMode(_THIS)279 void X11_CheckMouseMode(_THIS)
280 {
281 SDL_Lock_EventThread();
282 X11_CheckMouseModeNoLock(this);
283 SDL_Unlock_EventThread();
284 }
285