/
SDL_QuartzWM.m
448 lines (360 loc) · 14 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2003 Sam Lantinga
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
20
slouken@libsdl.org
21
*/
22
#include "SDL_config.h"
23
24
#include "SDL_QuartzVideo.h"
25
#include "SDL_QuartzWM.h"
26
27
28
void QZ_FreeWMCursor (_THIS, WMcursor *cursor) {
29
30
31
if ( cursor != NULL ) {
[ cursor->nscursor release ];
32
free (cursor);
33
}
34
35
}
36
WMcursor* QZ_CreateWMCursor (_THIS, Uint8 *data, Uint8 *mask,
37
38
int w, int h, int hot_x, int hot_y) {
WMcursor *cursor;
39
40
41
42
43
44
45
46
NSBitmapImageRep *imgrep;
NSImage *img;
unsigned char *planes[5];
int i;
NSAutoreleasePool *pool;
pool = [ [ NSAutoreleasePool alloc ] init ];
47
/* Allocate the cursor memory */
48
cursor = (WMcursor *)SDL_malloc(sizeof(WMcursor));
49
if (cursor == NULL) goto outOfMemory;
50
51
52
53
54
/* create the image representation and get the pointers to its storage */
imgrep = [ [ [ NSBitmapImageRep alloc ] initWithBitmapDataPlanes: NULL pixelsWide: w pixelsHigh: h bitsPerSample: 1 samplesPerPixel: 2 hasAlpha: YES isPlanar: YES colorSpaceName: NSDeviceBlackColorSpace bytesPerRow: (w+7)/8 bitsPerPixel: 0 ] autorelease ];
if (imgrep == nil) goto outOfMemory;
[ imgrep getBitmapDataPlanes: planes ];
55
56
57
58
59
/* copy data and mask, extending the mask to all black pixels because the inversion effect doesn't work with Cocoa's alpha-blended cursors */
for (i = 0; i < (w+7)/8*h; i++) {
planes[0][i] = data[i];
planes[1][i] = mask[i] | data[i];
60
}
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/* create image and cursor */
img = [ [ [ NSImage alloc ] initWithSize: NSMakeSize(w, h) ] autorelease ];
if (img == nil) goto outOfMemory;
[ img addRepresentation: imgrep ];
if (system_version < 0x1030) { /* on 10.2, cursors must be 16*16 */
if (w > 16 || h > 16) { /* too big: scale it down */
[ img setScalesWhenResized: YES ];
hot_x = hot_x*16/w;
hot_y = hot_y*16/h;
}
else { /* too small (or just right): extend it (from the bottom left corner, so hot_y must be adjusted) */
hot_y += 16 - h;
}
[ img setSize: NSMakeSize(16, 16) ];
76
}
77
78
cursor->nscursor = [ [ NSCursor alloc ] initWithImage: img hotSpot: NSMakePoint(hot_x, hot_y) ];
if (cursor->nscursor == nil) goto outOfMemory;
79
80
[ pool release ];
81
return(cursor);
82
83
84
85
86
87
outOfMemory:
[ pool release ];
if (cursor != NULL) SDL_free(cursor);
SDL_OutOfMemory();
return(NULL);
88
89
}
90
91
void QZ_UpdateCursor (_THIS) {
BOOL state;
92
93
94
95
96
97
98
99
100
101
102
103
104
if (cursor_should_be_visible || !(SDL_GetAppState() & SDL_APPMOUSEFOCUS)) {
state = YES;
} else {
state = NO;
}
if (state != cursor_visible) {
if (state) {
[ NSCursor unhide ];
} else {
[ NSCursor hide ];
}
cursor_visible = state;
105
106
107
}
}
108
BOOL QZ_IsMouseInWindow (_THIS) {
109
if (qz_window == nil || (mode_flags & SDL_FULLSCREEN)) return YES; /*fullscreen*/
110
111
112
113
114
else {
NSPoint p = [ qz_window mouseLocationOutsideOfEventStream ];
p.y -= 1.0f; /* Apparently y goes from 1 to h, not from 0 to h-1 (i.e. the "location of the mouse" seems to be defined as "the location of the top left corner of the mouse pointer's hot pixel" */
return NSPointInRect(p, [ window_view frame ]);
}
115
116
}
117
int QZ_ShowWMCursor (_THIS, WMcursor *cursor) {
118
119
if ( cursor == NULL) {
120
121
if ( cursor_should_be_visible ) {
cursor_should_be_visible = NO;
122
QZ_ChangeGrabState (this, QZ_HIDECURSOR);
123
}
124
QZ_UpdateCursor(this);
125
126
}
else {
127
128
129
130
131
132
if (qz_window ==nil || (mode_flags & SDL_FULLSCREEN)) {
[ cursor->nscursor set ];
}
else {
[ qz_window invalidateCursorRectsForView: [ qz_window contentView ] ];
}
133
134
if ( ! cursor_should_be_visible ) {
cursor_should_be_visible = YES;
135
QZ_ChangeGrabState (this, QZ_SHOWCURSOR);
136
}
137
QZ_UpdateCursor(this);
138
139
140
141
142
}
return 1;
}
143
144
145
146
147
148
149
/*
Coordinate conversion functions, for convenience
Cocoa sets the origin at the lower left corner of the window/screen
SDL, CoreGraphics/WindowServer, and QuickDraw use the origin at the upper left corner
The routines were written so they could be called before SetVideoMode() has finished;
this might have limited usefulness at the moment, but the extra cost is trivial.
*/
150
151
/* Convert Cocoa screen coordinate to Cocoa window coordinate */
152
void QZ_PrivateGlobalToLocal (_THIS, NSPoint *p) {
153
154
155
156
157
158
*p = [ qz_window convertScreenToBase:*p ];
}
/* Convert Cocoa window coordinate to Cocoa screen coordinate */
159
void QZ_PrivateLocalToGlobal (_THIS, NSPoint *p) {
160
161
162
163
164
*p = [ qz_window convertBaseToScreen:*p ];
}
/* Convert SDL coordinate to Cocoa coordinate */
165
void QZ_PrivateSDLToCocoa (_THIS, NSPoint *p) {
166
167
168
if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */
169
p->y = CGDisplayPixelsHigh (display_id) - p->y;
170
}
171
else {
172
173
*p = [ window_view convertPoint:*p toView: nil ];
174
175
176
177
178
/* We need a workaround in OpenGL mode */
if ( SDL_VideoSurface->flags & SDL_OPENGL ) {
p->y = [window_view frame].size.height - p->y;
}
179
180
181
}
}
182
/* Convert Cocoa coordinate to SDL coordinate */
183
void QZ_PrivateCocoaToSDL (_THIS, NSPoint *p) {
184
185
186
if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */
187
p->y = CGDisplayPixelsHigh (display_id) - p->y;
188
189
}
else {
190
191
192
*p = [ window_view convertPoint:*p fromView: nil ];
193
/* We need a workaround in OpenGL mode */
194
if ( SDL_VideoSurface != NULL && (SDL_VideoSurface->flags & SDL_OPENGL) ) {
195
p->y = [window_view frame].size.height - p->y;
196
}
197
}
198
199
200
}
/* Convert SDL coordinate to window server (CoreGraphics) coordinate */
201
CGPoint QZ_PrivateSDLToCG (_THIS, NSPoint *p) {
202
203
204
205
CGPoint cgp;
if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
int height;
QZ_PrivateSDLToCocoa (this, p);
QZ_PrivateLocalToGlobal (this, p);
height = CGDisplayPixelsHigh (display_id);
p->y = height - p->y;
}
cgp.x = p->x;
cgp.y = p->y;
return cgp;
}
222
#if 0 /* Dead code */
223
/* Convert window server (CoreGraphics) coordinate to SDL coordinate */
224
void QZ_PrivateCGToSDL (_THIS, NSPoint *p) {
225
226
227
228
229
230
231
232
233
234
235
236
237
if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */
int height;
/* Convert CG Global to Cocoa Global */
height = CGDisplayPixelsHigh (display_id);
p->y = height - p->y;
QZ_PrivateGlobalToLocal (this, p);
QZ_PrivateCocoaToSDL (this, p);
}
}
238
#endif /* Dead code */
239
240
void QZ_PrivateWarpCursor (_THIS, int x, int y) {
241
242
243
244
245
NSPoint p;
CGPoint cgp;
p = NSMakePoint (x, y);
246
247
248
249
250
cgp = QZ_PrivateSDLToCG (this, &p);
/* this is the magic call that fixes cursor "freezing" after warp */
CGSetLocalEventsSuppressionInterval (0.0);
CGWarpMouseCursorPosition (cgp);
251
252
}
253
void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y) {
254
255
/* Only allow warping when in foreground */
256
if ( ! [ NSApp isActive ] )
257
258
259
return;
/* Do the actual warp */
260
if (grab_state != QZ_INVISIBLE_GRAB) QZ_PrivateWarpCursor (this, x, y);
261
262
263
/* Generate the mouse moved event */
SDL_PrivateMouseMotion (0, 0, x, y);
264
265
}
266
267
void QZ_MoveWMCursor (_THIS, int x, int y) { }
void QZ_CheckMouseMode (_THIS) { }
268
269
void QZ_SetCaption (_THIS, const char *title, const char *icon) {
270
271
if ( qz_window != nil ) {
272
273
NSString *string;
if ( title != NULL ) {
274
string = [ [ NSString alloc ] initWithUTF8String:title ];
275
[ qz_window setTitle:string ];
276
277
278
[ string release ];
}
if ( icon != NULL ) {
279
string = [ [ NSString alloc ] initWithUTF8String:icon ];
280
[ qz_window setMiniwindowTitle:string ];
281
282
283
[ string release ];
}
}
284
285
}
286
void QZ_SetIcon (_THIS, SDL_Surface *icon, Uint8 *mask)
287
{
288
289
290
291
NSBitmapImageRep *imgrep;
NSImage *img;
SDL_Surface *mergedSurface;
NSAutoreleasePool *pool;
292
293
294
295
Uint8 *pixels;
SDL_bool iconSrcAlpha;
Uint8 iconAlphaValue;
int i, j, maskPitch, index;
296
297
298
pool = [ [ NSAutoreleasePool alloc ] init ];
299
300
301
302
imgrep = [ [ [ NSBitmapImageRep alloc ] initWithBitmapDataPlanes: NULL pixelsWide: icon->w pixelsHigh: icon->h bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES isPlanar: NO colorSpaceName: NSDeviceRGBColorSpace bytesPerRow: 4*icon->w bitsPerPixel: 32 ] autorelease ];
if (imgrep == nil) goto freePool;
pixels = [ imgrep bitmapData ];
SDL_memset(pixels, 0, 4*icon->w*icon->h); /* make the background, which will survive in colorkeyed areas, completely transparent */
303
304
305
306
307
308
309
310
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define BYTEORDER_DEPENDENT_RGBA_MASKS 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF
#else
#define BYTEORDER_DEPENDENT_RGBA_MASKS 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000
#endif
mergedSurface = SDL_CreateRGBSurfaceFrom(pixels, icon->w, icon->h, 32, 4*icon->w, BYTEORDER_DEPENDENT_RGBA_MASKS);
if (mergedSurface == NULL) goto freePool;
311
312
313
314
315
316
317
318
319
/* blit, with temporarily cleared SRCALPHA flag because we want to copy, not alpha-blend */
iconSrcAlpha = ((icon->flags & SDL_SRCALPHA) != 0);
iconAlphaValue = icon->format->alpha;
SDL_SetAlpha(icon, 0, 255);
SDL_BlitSurface(icon, NULL, mergedSurface, NULL);
if (iconSrcAlpha) SDL_SetAlpha(icon, SDL_SRCALPHA, iconAlphaValue);
SDL_FreeSurface(mergedSurface);
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/* apply mask, source alpha, and premultiply color values by alpha */
maskPitch = (icon->w+7)/8;
for (i = 0; i < icon->h; i++) {
for (j = 0; j < icon->w; j++) {
index = i*4*icon->w + j*4;
if (!(mask[i*maskPitch + j/8] & (128 >> j%8))) {
pixels[index + 3] = 0;
}
else {
if (iconSrcAlpha) {
if (icon->format->Amask == 0) pixels[index + 3] = icon->format->alpha;
}
else {
pixels[index + 3] = 255;
}
}
if (pixels[index + 3] < 255) {
pixels[index + 0] = (Uint16)pixels[index + 0]*pixels[index + 3]/255;
pixels[index + 1] = (Uint16)pixels[index + 1]*pixels[index + 3]/255;
pixels[index + 2] = (Uint16)pixels[index + 2]*pixels[index + 3]/255;
341
342
}
}
343
344
}
345
346
img = [ [ [ NSImage alloc ] initWithSize: NSMakeSize(icon->w, icon->h) ] autorelease ];
if (img == nil) goto freePool;
347
348
349
[ img addRepresentation: imgrep ];
[ NSApp setApplicationIconImage:img ];
350
freePool:
351
[ pool release ];
352
353
}
354
int QZ_IconifyWindow (_THIS) {
355
356
357
if ( ! [ qz_window isMiniaturized ] ) {
[ qz_window miniaturize:nil ];
358
359
360
361
if ( ! [ qz_window isMiniaturized ] ) {
SDL_SetError ("window iconification failed");
return 0;
}
362
363
364
return 1;
}
else {
365
SDL_SetError ("window already iconified");
366
367
368
369
370
return 0;
}
}
/*
371
int QZ_GetWMInfo (_THIS, SDL_SysWMinfo *info) {
372
info->nsWindowPtr = qz_window;
373
374
375
return 0;
}*/
376
void QZ_ChangeGrabState (_THIS, int action) {
377
378
379
380
381
382
383
/*
Figure out what the next state should be based on the action.
Ignore actions that can't change the current state.
*/
if ( grab_state == QZ_UNGRABBED ) {
if ( action == QZ_ENABLE_GRAB ) {
384
if ( cursor_should_be_visible )
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
grab_state = QZ_VISIBLE_GRAB;
else
grab_state = QZ_INVISIBLE_GRAB;
}
}
else if ( grab_state == QZ_VISIBLE_GRAB ) {
if ( action == QZ_DISABLE_GRAB )
grab_state = QZ_UNGRABBED;
else if ( action == QZ_HIDECURSOR )
grab_state = QZ_INVISIBLE_GRAB;
}
else {
assert( grab_state == QZ_INVISIBLE_GRAB );
if ( action == QZ_DISABLE_GRAB )
grab_state = QZ_UNGRABBED;
else if ( action == QZ_SHOWCURSOR )
grab_state = QZ_VISIBLE_GRAB;
}
/* now apply the new state */
if (grab_state == QZ_UNGRABBED) {
CGAssociateMouseAndMouseCursorPosition (1);
}
else if (grab_state == QZ_VISIBLE_GRAB) {
CGAssociateMouseAndMouseCursorPosition (1);
}
else {
assert( grab_state == QZ_INVISIBLE_GRAB );
QZ_PrivateWarpCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2);
CGAssociateMouseAndMouseCursorPosition (0);
}
}
422
SDL_GrabMode QZ_GrabInput (_THIS, SDL_GrabMode grab_mode) {
423
424
425
426
427
428
429
430
431
432
433
434
435
int doGrab = grab_mode & SDL_GRAB_ON;
/*int fullscreen = grab_mode & SDL_GRAB_FULLSCREEN;*/
if ( this->screen == NULL ) {
SDL_SetError ("QZ_GrabInput: screen is NULL");
return SDL_GRAB_OFF;
}
if ( ! video_set ) {
/*SDL_SetError ("QZ_GrabInput: video is not set, grab will take effect on mode switch"); */
current_grab_mode = grab_mode;
return grab_mode; /* Will be set later on mode switch */
436
}
437
438
439
440
441
442
if ( grab_mode != SDL_GRAB_QUERY ) {
if ( doGrab )
QZ_ChangeGrabState (this, QZ_ENABLE_GRAB);
else
QZ_ChangeGrabState (this, QZ_DISABLE_GRAB);
443
444
445
446
current_grab_mode = doGrab ? SDL_GRAB_ON : SDL_GRAB_OFF;
}
447
return current_grab_mode;
448
}