This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_QuartzWM.m
492 lines (403 loc) · 13.5 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
25
#include "SDL_QuartzVideo.h"
26
27
28
struct WMcursor
{
29
NSCursor *nscursor;
30
31
};
32
void
33
QZ_FreeWMCursor(_THIS, WMcursor * cursor)
34
{
35
36
37
38
39
if (cursor != NULL) {
[cursor->nscursor release];
free(cursor);
}
40
41
}
42
WMcursor *
43
44
QZ_CreateWMCursor(_THIS, Uint8 * data, Uint8 * mask,
int w, int h, int hot_x, int hot_y)
45
{
46
WMcursor *cursor;
47
48
49
50
51
NSBitmapImageRep *imgrep;
NSImage *img;
unsigned char *planes[5];
int i;
NSAutoreleasePool *pool;
52
53
pool =[[NSAutoreleasePool alloc] init];
54
55
56
57
58
/* Allocate the cursor memory */
cursor = (WMcursor *) SDL_malloc(sizeof(WMcursor));
if (cursor == NULL)
goto outOfMemory;
59
60
61
62
63
64
/* 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];
65
66
67
68
69
/* 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];
70
}
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/* 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)];
86
}
87
88
89
90
cursor->nscursor =[[NSCursor alloc] initWithImage: img hotSpot:NSMakePoint(hot_x,
hot_y)];
if (cursor->nscursor == nil)
goto outOfMemory;
91
92
[pool release];
93
return (cursor);
94
95
96
97
98
99
100
outOfMemory:
[pool release];
if (cursor != NULL)
SDL_free(cursor);
SDL_OutOfMemory();
return (NULL);
101
102
}
103
void
104
QZ_ShowMouse(_THIS)
105
{
106
if (!cursor_visible) {
107
[NSCursor unhide];
108
109
110
111
cursor_visible = YES;
}
}
112
void
113
QZ_HideMouse(_THIS)
114
{
115
if ((SDL_GetAppState() & SDL_APPMOUSEFOCUS) && cursor_visible) {
116
[NSCursor hide];
117
118
119
120
cursor_visible = NO;
}
}
121
BOOL
122
QZ_IsMouseInWindow(_THIS)
123
124
125
{
if (qz_window == nil)
return YES; /*fullscreen */
126
else {
127
128
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" */
129
return NSPointInRect(p,[window_view frame]);
130
}
131
132
}
133
int
134
QZ_ShowWMCursor(_THIS, WMcursor * cursor)
135
{
136
137
138
if (cursor == NULL) {
if (cursor_should_be_visible) {
139
QZ_HideMouse(this);
140
cursor_should_be_visible = NO;
141
QZ_ChangeGrabState(this, QZ_HIDECURSOR);
142
}
143
} else {
144
[cursor->nscursor set];
145
if (!cursor_should_be_visible) {
146
QZ_ShowMouse(this);
147
cursor_should_be_visible = YES;
148
QZ_ChangeGrabState(this, QZ_SHOWCURSOR);
149
150
151
152
153
154
}
}
return 1;
}
155
156
157
158
159
160
161
/*
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.
*/
162
163
/* Convert Cocoa screen coordinate to Cocoa window coordinate */
164
void
165
QZ_PrivateGlobalToLocal(_THIS, NSPoint * p)
166
{
167
168
*p =[qz_window convertScreenToBase:*p];
169
170
171
172
}
/* Convert Cocoa window coordinate to Cocoa screen coordinate */
173
void
174
QZ_PrivateLocalToGlobal(_THIS, NSPoint * p)
175
{
176
177
*p =[qz_window convertBaseToScreen:*p];
178
179
180
}
/* Convert SDL coordinate to Cocoa coordinate */
181
void
182
QZ_PrivateSDLToCocoa(_THIS, NSPoint * p)
183
184
{
185
if (CGDisplayIsCaptured(display_id)) { /* capture signals fullscreen */
186
187
p->y = CGDisplayPixelsHigh(display_id) - p->y;
188
189
190
191
} else {
*p =[window_view convertPoint: *p toView:nil];
192
/* We need a workaround in OpenGL mode */
193
if (SDL_VideoSurface->flags & SDL_OPENGL) {
194
p->y =[window_view frame].size.height - p->y;
195
}
196
197
198
}
}
199
/* Convert Cocoa coordinate to SDL coordinate */
200
void
201
QZ_PrivateCocoaToSDL(_THIS, NSPoint * p)
202
203
{
204
if (CGDisplayIsCaptured(display_id)) { /* capture signals fullscreen */
205
206
p->y = CGDisplayPixelsHigh(display_id) - p->y;
207
208
209
} else {
*p =[window_view convertPoint: *p fromView:nil];
210
211
/* We need a workaround in OpenGL mode */
212
if (SDL_VideoSurface != NULL
213
&& (SDL_VideoSurface->flags & SDL_OPENGL)) {
214
p->y =[window_view frame].size.height - p->y;
215
}
216
}
217
218
219
}
/* Convert SDL coordinate to window server (CoreGraphics) coordinate */
220
CGPoint
221
QZ_PrivateSDLToCG(_THIS, NSPoint * p)
222
223
{
224
CGPoint cgp;
225
226
if (!CGDisplayIsCaptured(display_id)) { /* not captured => not fullscreen => local coord */
227
228
int height;
229
230
231
QZ_PrivateSDLToCocoa(this, p);
QZ_PrivateLocalToGlobal(this, p);
232
233
height = CGDisplayPixelsHigh(display_id);
234
235
p->y = height - p->y;
}
236
237
238
cgp.x = p->x;
cgp.y = p->y;
239
240
241
242
return cgp;
}
243
#if 0 /* Dead code */
244
/* Convert window server (CoreGraphics) coordinate to SDL coordinate */
245
void
246
QZ_PrivateCGToSDL(_THIS, NSPoint * p)
247
248
{
249
if (!CGDisplayIsCaptured(display_id)) { /* not captured => not fullscreen => local coord */
250
251
252
253
int height;
/* Convert CG Global to Cocoa Global */
254
height = CGDisplayPixelsHigh(display_id);
255
256
p->y = height - p->y;
257
258
QZ_PrivateGlobalToLocal(this, p);
QZ_PrivateCocoaToSDL(this, p);
259
260
}
}
261
#endif /* Dead code */
262
263
void
264
QZ_PrivateWarpCursor(_THIS, int x, int y)
265
266
{
267
268
NSPoint p;
CGPoint cgp;
269
270
271
p = NSMakePoint(x, y);
cgp = QZ_PrivateSDLToCG(this, &p);
272
273
/* this is the magic call that fixes cursor "freezing" after warp */
274
275
CGSetLocalEventsSuppressionInterval(0.0);
CGWarpMouseCursorPosition(cgp);
276
277
}
278
void
279
QZ_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
280
{
281
282
/* Only allow warping when in foreground */
283
if (![NSApp isActive])
284
return;
285
286
/* Do the actual warp */
287
if (grab_state != QZ_INVISIBLE_GRAB)
288
QZ_PrivateWarpCursor(this, x, y);
289
290
/* Generate the mouse moved event */
291
SDL_PrivateMouseMotion(0, 0, x, y);
292
293
}
294
void
295
QZ_MoveWMCursor(_THIS, int x, int y)
296
297
298
{
}
void
299
QZ_CheckMouseMode(_THIS)
300
301
{
}
302
303
void
304
QZ_SetCaption(_THIS, const char *title, const char *icon)
305
{
306
307
if (qz_window != nil) {
308
NSString *string;
309
310
311
312
if (title != NULL) {
string =[[NSString alloc] initWithUTF8String:title];
[qz_window setTitle:string];
[string release];
313
}
314
315
316
317
if (icon != NULL) {
string =[[NSString alloc] initWithUTF8String:icon];
[qz_window setMiniwindowTitle:string];
[string release];
318
319
}
}
320
321
}
322
void
323
QZ_SetIcon(_THIS, SDL_Surface * icon, Uint8 * mask)
324
{
325
326
327
328
NSBitmapImageRep *imgrep;
NSImage *img;
SDL_Surface *mergedSurface;
NSAutoreleasePool *pool;
329
330
331
332
Uint8 *pixels;
SDL_bool iconSrcAlpha;
Uint8 iconAlphaValue;
int i, j, maskPitch, index;
333
334
335
336
337
338
339
pool =[[NSAutoreleasePool alloc] init];
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];
340
SDL_memset(pixels, 0, 4 * icon->w * icon->h); /* make the background, which will survive in colorkeyed areas, completely transparent */
341
342
343
344
345
346
#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
347
mergedSurface =
348
349
SDL_CreateRGBSurfaceFrom(pixels, icon->w, icon->h, 32, 4 * icon->w,
BYTEORDER_DEPENDENT_RGBA_MASKS);
350
351
352
if (mergedSurface == NULL)
goto freePool;
353
354
355
/* blit, with temporarily cleared SRCALPHA flag because we want to copy, not alpha-blend */
iconSrcAlpha = ((icon->flags & SDL_SRCALPHA) != 0);
iconAlphaValue = icon->format->alpha;
356
357
SDL_SetAlpha(icon, 0, 255);
SDL_BlitSurface(icon, NULL, mergedSurface, NULL);
358
if (iconSrcAlpha)
359
SDL_SetAlpha(icon, SDL_SRCALPHA, iconAlphaValue);
360
361
SDL_FreeSurface(mergedSurface);
362
363
/* apply mask, source alpha, and premultiply color values by alpha */
364
maskPitch = (icon->w + 7) / 8;
365
366
for (i = 0; i < icon->h; i++) {
for (j = 0; j < icon->w; j++) {
367
368
index = i * 4 * icon->w + j * 4;
if (!(mask[i * maskPitch + j / 8] & (128 >> j % 8))) {
369
pixels[index + 3] = 0;
370
} else {
371
if (iconSrcAlpha) {
372
373
374
if (icon->format->Amask == 0)
pixels[index + 3] = icon->format->alpha;
} else {
375
376
377
378
pixels[index + 3] = 255;
}
}
if (pixels[index + 3] < 255) {
379
380
381
382
383
384
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;
385
386
}
}
387
}
388
389
390
img =[[[NSImage alloc] initWithSize:NSMakeSize(icon->w,
icon->h)] autorelease];
391
392
393
394
395
396
397
if (img == nil)
goto freePool;
[img addRepresentation:imgrep];
[NSApp setApplicationIconImage:img];
freePool:
[pool release];
398
399
}
400
int
401
QZ_IconifyWindow(_THIS)
402
{
403
404
405
if (![qz_window isMiniaturized]) {
[qz_window miniaturize:nil];
406
return 1;
407
} else {
408
SDL_SetError("window already iconified");
409
410
411
412
413
return 0;
}
}
/*
414
int QZ_GetWMInfo (_THIS, SDL_SysWMinfo *info) {
415
info->nsWindowPtr = qz_window;
416
417
418
return 0;
}*/
419
void
420
QZ_ChangeGrabState(_THIS, int action)
421
{
422
423
/*
424
425
426
427
428
429
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) {
if (cursor_should_be_visible)
430
431
432
433
grab_state = QZ_VISIBLE_GRAB;
else
grab_state = QZ_INVISIBLE_GRAB;
}
434
435
} else if (grab_state == QZ_VISIBLE_GRAB) {
if (action == QZ_DISABLE_GRAB)
436
grab_state = QZ_UNGRABBED;
437
else if (action == QZ_HIDECURSOR)
438
grab_state = QZ_INVISIBLE_GRAB;
439
} else {
440
assert(grab_state == QZ_INVISIBLE_GRAB);
441
442
if (action == QZ_DISABLE_GRAB)
443
grab_state = QZ_UNGRABBED;
444
else if (action == QZ_SHOWCURSOR)
445
446
grab_state = QZ_VISIBLE_GRAB;
}
447
448
449
/* now apply the new state */
if (grab_state == QZ_UNGRABBED) {
450
451
CGAssociateMouseAndMouseCursorPosition(1);
452
453
} else if (grab_state == QZ_VISIBLE_GRAB) {
454
CGAssociateMouseAndMouseCursorPosition(1);
455
} else {
456
assert(grab_state == QZ_INVISIBLE_GRAB);
457
458
459
460
QZ_PrivateWarpCursor(this, SDL_VideoSurface->w / 2,
SDL_VideoSurface->h / 2);
CGAssociateMouseAndMouseCursorPosition(0);
461
462
463
}
}
464
SDL_GrabMode
465
QZ_GrabInput(_THIS, SDL_GrabMode grab_mode)
466
{
467
468
int doGrab = grab_mode & SDL_GRAB_ON;
469
/*int fullscreen = grab_mode & SDL_GRAB_FULLSCREEN; */
470
471
if (this->screen == NULL) {
472
SDL_SetError("QZ_GrabInput: screen is NULL");
473
474
return SDL_GRAB_OFF;
}
475
476
if (!video_set) {
477
478
479
/*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 */
480
}
481
482
483
if (grab_mode != SDL_GRAB_QUERY) {
if (doGrab)
484
QZ_ChangeGrabState(this, QZ_ENABLE_GRAB);
485
else
486
QZ_ChangeGrabState(this, QZ_DISABLE_GRAB);
487
488
489
490
current_grab_mode = doGrab ? SDL_GRAB_ON : SDL_GRAB_OFF;
}
491
return current_grab_mode;
492
}