/
SDL_QuartzWM.m
418 lines (330 loc) · 12.4 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
29
30
struct WMcursor {
Cursor curs;
};
31
void QZ_FreeWMCursor (_THIS, WMcursor *cursor) {
32
33
34
35
36
37
if ( cursor != NULL )
free (cursor);
}
/* Use the Carbon cursor routines for now */
38
WMcursor* QZ_CreateWMCursor (_THIS, Uint8 *data, Uint8 *mask,
39
40
41
42
43
int w, int h, int hot_x, int hot_y) {
WMcursor *cursor;
int row, bytes;
/* Allocate the cursor memory */
44
cursor = (WMcursor *)SDL_malloc(sizeof(WMcursor));
45
46
47
48
if ( cursor == NULL ) {
SDL_OutOfMemory();
return(NULL);
}
49
SDL_memset(cursor, 0, sizeof(*cursor));
50
51
52
53
54
55
56
if (w > 16)
w = 16;
if (h > 16)
h = 16;
57
58
59
bytes = (w+7)/8;
for ( row=0; row<h; ++row ) {
60
SDL_memcpy(&cursor->curs.data[row], data, bytes);
61
62
63
data += bytes;
}
for ( row=0; row<h; ++row ) {
64
SDL_memcpy(&cursor->curs.mask[row], mask, bytes);
65
66
67
68
69
mask += bytes;
}
cursor->curs.hotSpot.h = hot_x;
cursor->curs.hotSpot.v = hot_y;
70
return(cursor);
71
72
}
73
74
75
76
77
78
79
80
void QZ_ShowMouse (_THIS) {
if (!cursor_visible) {
[ NSCursor unhide ];
cursor_visible = YES;
}
}
void QZ_HideMouse (_THIS) {
81
if ((SDL_GetAppState() & SDL_APPMOUSEFOCUS) && cursor_visible) {
82
83
84
85
86
[ NSCursor hide ];
cursor_visible = NO;
}
}
87
BOOL QZ_IsMouseInWindow (_THIS) {
88
if (qz_window == nil) return YES; /*fullscreen*/
89
90
91
92
93
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 ]);
}
94
95
}
96
int QZ_ShowWMCursor (_THIS, WMcursor *cursor) {
97
98
if ( cursor == NULL) {
99
100
101
if ( cursor_should_be_visible ) {
QZ_HideMouse (this);
cursor_should_be_visible = NO;
102
QZ_ChangeGrabState (this, QZ_HIDECURSOR);
103
104
105
106
}
}
else {
SetCursor(&cursor->curs);
107
108
109
if ( ! cursor_should_be_visible ) {
QZ_ShowMouse (this);
cursor_should_be_visible = YES;
110
QZ_ChangeGrabState (this, QZ_SHOWCURSOR);
111
112
113
114
115
116
}
}
return 1;
}
117
118
119
120
121
122
123
/*
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.
*/
124
125
/* Convert Cocoa screen coordinate to Cocoa window coordinate */
126
void QZ_PrivateGlobalToLocal (_THIS, NSPoint *p) {
127
128
129
130
131
132
*p = [ qz_window convertScreenToBase:*p ];
}
/* Convert Cocoa window coordinate to Cocoa screen coordinate */
133
void QZ_PrivateLocalToGlobal (_THIS, NSPoint *p) {
134
135
136
137
138
*p = [ qz_window convertBaseToScreen:*p ];
}
/* Convert SDL coordinate to Cocoa coordinate */
139
void QZ_PrivateSDLToCocoa (_THIS, NSPoint *p) {
140
141
142
if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */
143
p->y = CGDisplayPixelsHigh (display_id) - p->y;
144
}
145
else {
146
147
*p = [ window_view convertPoint:*p toView: nil ];
148
149
150
151
152
/* We need a workaround in OpenGL mode */
if ( SDL_VideoSurface->flags & SDL_OPENGL ) {
p->y = [window_view frame].size.height - p->y;
}
153
154
155
}
}
156
/* Convert Cocoa coordinate to SDL coordinate */
157
void QZ_PrivateCocoaToSDL (_THIS, NSPoint *p) {
158
159
160
if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */
161
p->y = CGDisplayPixelsHigh (display_id) - p->y;
162
163
}
else {
164
165
166
*p = [ window_view convertPoint:*p fromView: nil ];
167
/* We need a workaround in OpenGL mode */
168
if ( SDL_VideoSurface != NULL && (SDL_VideoSurface->flags & SDL_OPENGL) ) {
169
p->y = [window_view frame].size.height - p->y;
170
}
171
}
172
173
174
}
/* Convert SDL coordinate to window server (CoreGraphics) coordinate */
175
CGPoint QZ_PrivateSDLToCG (_THIS, NSPoint *p) {
176
177
178
179
CGPoint cgp;
if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
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;
}
196
#if 0 /* Dead code */
197
/* Convert window server (CoreGraphics) coordinate to SDL coordinate */
198
void QZ_PrivateCGToSDL (_THIS, NSPoint *p) {
199
200
201
202
203
204
205
206
207
208
209
210
211
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);
}
}
212
#endif /* Dead code */
213
214
void QZ_PrivateWarpCursor (_THIS, int x, int y) {
215
216
217
218
219
NSPoint p;
CGPoint cgp;
p = NSMakePoint (x, y);
220
221
222
223
224
cgp = QZ_PrivateSDLToCG (this, &p);
/* this is the magic call that fixes cursor "freezing" after warp */
CGSetLocalEventsSuppressionInterval (0.0);
CGWarpMouseCursorPosition (cgp);
225
226
}
227
void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y) {
228
229
/* Only allow warping when in foreground */
230
if ( ! [ NSApp isActive ] )
231
232
233
return;
/* Do the actual warp */
234
if (grab_state != QZ_INVISIBLE_GRAB) QZ_PrivateWarpCursor (this, x, y);
235
236
237
/* Generate the mouse moved event */
SDL_PrivateMouseMotion (0, 0, x, y);
238
239
}
240
241
void QZ_MoveWMCursor (_THIS, int x, int y) { }
void QZ_CheckMouseMode (_THIS) { }
242
243
void QZ_SetCaption (_THIS, const char *title, const char *icon) {
244
245
if ( qz_window != nil ) {
246
247
NSString *string;
if ( title != NULL ) {
248
string = [ [ NSString alloc ] initWithUTF8String:title ];
249
[ qz_window setTitle:string ];
250
251
252
[ string release ];
}
if ( icon != NULL ) {
253
string = [ [ NSString alloc ] initWithUTF8String:icon ];
254
[ qz_window setMiniwindowTitle:string ];
255
256
257
[ string release ];
}
}
258
259
}
260
void QZ_SetIcon (_THIS, SDL_Surface *icon, Uint8 *mask)
261
{
262
263
264
265
NSBitmapImageRep *imgrep;
NSImage *img;
SDL_Surface *mergedSurface;
NSAutoreleasePool *pool;
266
267
268
269
Uint8 *pixels;
SDL_bool iconSrcAlpha;
Uint8 iconAlphaValue;
int i, j, maskPitch, index;
270
271
272
pool = [ [ NSAutoreleasePool alloc ] init ];
273
274
275
276
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 */
277
278
279
280
281
282
283
284
#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;
285
286
287
288
289
290
291
292
293
/* 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);
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/* 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;
315
316
}
}
317
318
}
319
320
img = [ [ [ NSImage alloc ] initWithSize: NSMakeSize(icon->w, icon->h) ] autorelease ];
if (img == nil) goto freePool;
321
322
323
[ img addRepresentation: imgrep ];
[ NSApp setApplicationIconImage:img ];
324
freePool:
325
[ pool release ];
326
327
}
328
int QZ_IconifyWindow (_THIS) {
329
330
331
if ( ! [ qz_window isMiniaturized ] ) {
[ qz_window miniaturize:nil ];
332
333
334
return 1;
}
else {
335
SDL_SetError ("window already iconified");
336
337
338
339
340
return 0;
}
}
/*
341
int QZ_GetWMInfo (_THIS, SDL_SysWMinfo *info) {
342
info->nsWindowPtr = qz_window;
343
344
345
return 0;
}*/
346
void QZ_ChangeGrabState (_THIS, int action) {
347
348
349
350
351
352
353
/*
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 ) {
354
if ( cursor_should_be_visible )
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
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);
}
}
392
SDL_GrabMode QZ_GrabInput (_THIS, SDL_GrabMode grab_mode) {
393
394
395
396
397
398
399
400
401
402
403
404
405
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 */
406
}
407
408
409
410
411
412
if ( grab_mode != SDL_GRAB_QUERY ) {
if ( doGrab )
QZ_ChangeGrabState (this, QZ_ENABLE_GRAB);
else
QZ_ChangeGrabState (this, QZ_DISABLE_GRAB);
413
414
415
416
current_grab_mode = doGrab ? SDL_GRAB_ON : SDL_GRAB_OFF;
}
417
return current_grab_mode;
418
}