1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012  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@libsdl.org
21*/
22#include "SDL_config.h"
23
24#include "SDL_QuartzVideo.h"
25#include "SDL_QuartzWM.h"
26
27#include <IOKit/IOMessage.h> /* For wake from sleep detection */
28#include <IOKit/pwr_mgt/IOPMLib.h> /* For wake from sleep detection */
29#include "SDL_QuartzKeys.h"
30
31/*
32 * On Leopard, this is missing from the 64-bit headers
33 */
34#if defined(__LP64__) && !defined(__POWER__)
35/*
36 * Workaround for a bug in the 10.5 SDK: By accident, OSService.h does
37 * not include Power.h at all when compiling in 64bit mode. This has
38 * been fixed in 10.6, but for 10.5, we manually define UsrActivity
39 * to ensure compilation works.
40 */
41#define UsrActivity 1
42#endif
43
44/*
45 * In Panther, this header defines device dependent masks for
46 * right side keys. These definitions only exist in Panther, but
47 * the header seems to exist at least in Jaguar and probably earlier
48 * versions of the OS, so this should't break anything.
49 */
50#include <IOKit/hidsystem/IOLLEvent.h>
51/*
52 * These are not defined before Panther. To keep the code compiling
53 * on systems without these, I will define if they don't exist.
54 */
55#ifndef NX_DEVICERCTLKEYMASK
56    #define NX_DEVICELCTLKEYMASK    0x00000001
57#endif
58#ifndef NX_DEVICELSHIFTKEYMASK
59    #define NX_DEVICELSHIFTKEYMASK  0x00000002
60#endif
61#ifndef NX_DEVICERSHIFTKEYMASK
62    #define NX_DEVICERSHIFTKEYMASK  0x00000004
63#endif
64#ifndef NX_DEVICELCMDKEYMASK
65    #define NX_DEVICELCMDKEYMASK    0x00000008
66#endif
67#ifndef NX_DEVICERCMDKEYMASK
68    #define NX_DEVICERCMDKEYMASK    0x00000010
69#endif
70#ifndef NX_DEVICELALTKEYMASK
71    #define NX_DEVICELALTKEYMASK    0x00000020
72#endif
73#ifndef NX_DEVICERALTKEYMASK
74    #define NX_DEVICERALTKEYMASK    0x00000040
75#endif
76#ifndef NX_DEVICERCTLKEYMASK
77    #define NX_DEVICERCTLKEYMASK    0x00002000
78#endif
79
80void     QZ_InitOSKeymap (_THIS) {
81    BOOL saw_layout = NO;
82    UInt32 state;
83    UInt32 value;
84    Uint16 i;
85    int world = SDLK_WORLD_0;
86
87    for ( i=0; i<SDL_TABLESIZE(keymap); ++i )
88        keymap[i] = SDLK_UNKNOWN;
89
90    /* This keymap is almost exactly the same as the OS 9 one */
91    keymap[QZ_ESCAPE] = SDLK_ESCAPE;
92    keymap[QZ_F1] = SDLK_F1;
93    keymap[QZ_F2] = SDLK_F2;
94    keymap[QZ_F3] = SDLK_F3;
95    keymap[QZ_F4] = SDLK_F4;
96    keymap[QZ_F5] = SDLK_F5;
97    keymap[QZ_F6] = SDLK_F6;
98    keymap[QZ_F7] = SDLK_F7;
99    keymap[QZ_F8] = SDLK_F8;
100    keymap[QZ_F9] = SDLK_F9;
101    keymap[QZ_F10] = SDLK_F10;
102    keymap[QZ_F11] = SDLK_F11;
103    keymap[QZ_F12] = SDLK_F12;
104    keymap[QZ_F13] = SDLK_F13;
105    keymap[QZ_F14] = SDLK_F14;
106    keymap[QZ_F15] = SDLK_F15;
107/*
108    keymap[QZ_PRINT] = SDLK_PRINT;
109    keymap[QZ_SCROLLOCK] = SDLK_SCROLLOCK;
110    keymap[QZ_PAUSE] = SDLK_PAUSE;
111*/
112    keymap[QZ_POWER] = SDLK_POWER;
113    keymap[QZ_BACKQUOTE] = SDLK_BACKQUOTE;
114    keymap[QZ_1] = SDLK_1;
115    keymap[QZ_2] = SDLK_2;
116    keymap[QZ_3] = SDLK_3;
117    keymap[QZ_4] = SDLK_4;
118    keymap[QZ_5] = SDLK_5;
119    keymap[QZ_6] = SDLK_6;
120    keymap[QZ_7] = SDLK_7;
121    keymap[QZ_8] = SDLK_8;
122    keymap[QZ_9] = SDLK_9;
123    keymap[QZ_0] = SDLK_0;
124    keymap[QZ_MINUS] = SDLK_MINUS;
125    keymap[QZ_EQUALS] = SDLK_EQUALS;
126    keymap[QZ_BACKSPACE] = SDLK_BACKSPACE;
127    keymap[QZ_INSERT] = SDLK_INSERT;
128    keymap[QZ_HOME] = SDLK_HOME;
129    keymap[QZ_PAGEUP] = SDLK_PAGEUP;
130    keymap[QZ_NUMLOCK] = SDLK_NUMLOCK;
131    keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS;
132    keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE;
133    keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
134    keymap[QZ_TAB] = SDLK_TAB;
135    keymap[QZ_q] = SDLK_q;
136    keymap[QZ_w] = SDLK_w;
137    keymap[QZ_e] = SDLK_e;
138    keymap[QZ_r] = SDLK_r;
139    keymap[QZ_t] = SDLK_t;
140    keymap[QZ_y] = SDLK_y;
141    keymap[QZ_u] = SDLK_u;
142    keymap[QZ_i] = SDLK_i;
143    keymap[QZ_o] = SDLK_o;
144    keymap[QZ_p] = SDLK_p;
145    keymap[QZ_LEFTBRACKET] = SDLK_LEFTBRACKET;
146    keymap[QZ_RIGHTBRACKET] = SDLK_RIGHTBRACKET;
147    keymap[QZ_BACKSLASH] = SDLK_BACKSLASH;
148    keymap[QZ_DELETE] = SDLK_DELETE;
149    keymap[QZ_END] = SDLK_END;
150    keymap[QZ_PAGEDOWN] = SDLK_PAGEDOWN;
151    keymap[QZ_KP7] = SDLK_KP7;
152    keymap[QZ_KP8] = SDLK_KP8;
153    keymap[QZ_KP9] = SDLK_KP9;
154    keymap[QZ_KP_MINUS] = SDLK_KP_MINUS;
155    keymap[QZ_CAPSLOCK] = SDLK_CAPSLOCK;
156    keymap[QZ_a] = SDLK_a;
157    keymap[QZ_s] = SDLK_s;
158    keymap[QZ_d] = SDLK_d;
159    keymap[QZ_f] = SDLK_f;
160    keymap[QZ_g] = SDLK_g;
161    keymap[QZ_h] = SDLK_h;
162    keymap[QZ_j] = SDLK_j;
163    keymap[QZ_k] = SDLK_k;
164    keymap[QZ_l] = SDLK_l;
165    keymap[QZ_SEMICOLON] = SDLK_SEMICOLON;
166    keymap[QZ_QUOTE] = SDLK_QUOTE;
167    keymap[QZ_RETURN] = SDLK_RETURN;
168    keymap[QZ_KP4] = SDLK_KP4;
169    keymap[QZ_KP5] = SDLK_KP5;
170    keymap[QZ_KP6] = SDLK_KP6;
171    keymap[QZ_KP_PLUS] = SDLK_KP_PLUS;
172    keymap[QZ_LSHIFT] = SDLK_LSHIFT;
173    keymap[QZ_RSHIFT] = SDLK_RSHIFT;
174    keymap[QZ_z] = SDLK_z;
175    keymap[QZ_x] = SDLK_x;
176    keymap[QZ_c] = SDLK_c;
177    keymap[QZ_v] = SDLK_v;
178    keymap[QZ_b] = SDLK_b;
179    keymap[QZ_n] = SDLK_n;
180    keymap[QZ_m] = SDLK_m;
181    keymap[QZ_COMMA] = SDLK_COMMA;
182    keymap[QZ_PERIOD] = SDLK_PERIOD;
183    keymap[QZ_SLASH] = SDLK_SLASH;
184    keymap[QZ_UP] = SDLK_UP;
185    keymap[QZ_KP1] = SDLK_KP1;
186    keymap[QZ_KP2] = SDLK_KP2;
187    keymap[QZ_KP3] = SDLK_KP3;
188    keymap[QZ_KP_ENTER] = SDLK_KP_ENTER;
189    keymap[QZ_LCTRL] = SDLK_LCTRL;
190    keymap[QZ_LALT] = SDLK_LALT;
191    keymap[QZ_LMETA] = SDLK_LMETA;
192    keymap[QZ_RCTRL] = SDLK_RCTRL;
193    keymap[QZ_RALT] = SDLK_RALT;
194    keymap[QZ_RMETA] = SDLK_RMETA;
195    keymap[QZ_SPACE] = SDLK_SPACE;
196    keymap[QZ_LEFT] = SDLK_LEFT;
197    keymap[QZ_DOWN] = SDLK_DOWN;
198    keymap[QZ_RIGHT] = SDLK_RIGHT;
199    keymap[QZ_KP0] = SDLK_KP0;
200    keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD;
201    keymap[QZ_IBOOK_ENTER] = SDLK_KP_ENTER;
202    keymap[QZ_IBOOK_RIGHT] = SDLK_RIGHT;
203    keymap[QZ_IBOOK_DOWN] = SDLK_DOWN;
204    keymap[QZ_IBOOK_UP]      = SDLK_UP;
205    keymap[QZ_IBOOK_LEFT] = SDLK_LEFT;
206
207    /*
208        Up there we setup a static scancode->keysym map. However, it will not
209        work very well on international keyboard. Hence we now query MacOS
210        for its own keymap to adjust our own mapping table. However, this is
211        basically only useful for ascii char keys. This is also the reason
212        why we keep the static table, too.
213     */
214
215#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
216    if (TISCopyCurrentKeyboardLayoutInputSource != NULL) {
217        TISInputSourceRef src = TISCopyCurrentKeyboardLayoutInputSource();
218        if (src != NULL) {
219            CFDataRef data = (CFDataRef)
220                TISGetInputSourceProperty(src,
221                    kTISPropertyUnicodeKeyLayoutData);
222            if (data != NULL) {
223                const UCKeyboardLayout *layout = (const UCKeyboardLayout *)
224                    CFDataGetBytePtr(data);
225                if (layout != NULL) {
226                    const UInt32 kbdtype = LMGetKbdType();
227                    saw_layout = YES;
228
229                    /* Loop over all 127 possible scan codes */
230                    for (i = 0; i < 0x7F; i++) {
231                        UniChar buf[16];
232                        UniCharCount count = 0;
233
234                        /* We pretend a clean start to begin with (i.e. no dead keys active */
235                        state = 0;
236
237                        if (UCKeyTranslate(layout, i, kUCKeyActionDown, 0, kbdtype,
238                                           0, &state, 16, &count, buf) != noErr) {
239                            continue;
240                        }
241
242                        /* If the state become 0, it was a dead key. We need to
243                           translate again, passing in the new state, to get
244                           the actual key value */
245                        if (state != 0) {
246                            if (UCKeyTranslate(layout, i, kUCKeyActionDown, 0, kbdtype,
247                                               0, &state, 16, &count, buf) != noErr) {
248                                continue;
249                            }
250                        }
251
252                        if (count != 1) {
253                            continue;  /* no multi-char. Use SDL 1.3 instead. :) */
254                        }
255
256                        value = (UInt32) buf[0];
257                        if (value >= 128) {
258                            /* Some non-ASCII char, map it to SDLK_WORLD_* */
259                            if (world < 0xFF) {
260                                keymap[i] = world++;
261                            }
262                        } else if (value >= 32) {     /* non-control ASCII char */
263                            keymap[i] = value;
264                        }
265                    }
266                }
267            }
268            CFRelease(src);
269        }
270    }
271#endif
272
273#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1050)
274    if (!saw_layout) {
275        /* Get a pointer to the systems cached KCHR */
276        const void *KCHRPtr = (const void *)GetScriptManagerVariable(smKCHRCache);
277        if (KCHRPtr)
278        {
279            /* Loop over all 127 possible scan codes */
280            for (i = 0; i < 0x7F; i++)
281            {
282                /* We pretend a clean start to begin with (i.e. no dead keys active */
283                state = 0;
284
285                /* Now translate the key code to a key value */
286                value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
287
288                /* If the state become 0, it was a dead key. We need to translate again,
289                    passing in the new state, to get the actual key value */
290                if (state != 0)
291                    value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
292
293                /* Now we should have an ascii value, or 0. Try to figure out to which SDL symbol it maps */
294                if (value >= 128) {     /* Some non-ASCII char, map it to SDLK_WORLD_* */
295                    if (world < 0xFF) {
296                        keymap[i] = world++;
297                    }
298                } else if (value >= 32) {     /* non-control ASCII char */
299                    keymap[i] = value;
300                }
301            }
302        }
303    }
304#endif
305
306    /*
307        The keypad codes are re-setup here, because the loop above cannot
308        distinguish between a key on the keypad and a regular key. We maybe
309        could get around this problem in another fashion: NSEvent's flags
310        include a "NSNumericPadKeyMask" bit; we could check that and modify
311        the symbol we return on the fly. However, this flag seems to exhibit
312        some weird behaviour related to the num lock key
313    */
314    keymap[QZ_KP0] = SDLK_KP0;
315    keymap[QZ_KP1] = SDLK_KP1;
316    keymap[QZ_KP2] = SDLK_KP2;
317    keymap[QZ_KP3] = SDLK_KP3;
318    keymap[QZ_KP4] = SDLK_KP4;
319    keymap[QZ_KP5] = SDLK_KP5;
320    keymap[QZ_KP6] = SDLK_KP6;
321    keymap[QZ_KP7] = SDLK_KP7;
322    keymap[QZ_KP8] = SDLK_KP8;
323    keymap[QZ_KP9] = SDLK_KP9;
324    keymap[QZ_KP_MINUS] = SDLK_KP_MINUS;
325    keymap[QZ_KP_PLUS] = SDLK_KP_PLUS;
326    keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD;
327    keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS;
328    keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE;
329    keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
330    keymap[QZ_KP_ENTER] = SDLK_KP_ENTER;
331}
332
333static void QZ_DoKey (_THIS, int state, NSEvent *event) {
334
335    NSString *chars = NULL;
336    unsigned int i, numChars;
337    SDL_keysym key;
338
339    /*
340        A key event can contain multiple characters,
341        or no characters at all. In most cases, it
342        will contain a single character. If it contains
343        0 characters, we'll use 0 as the unicode. If it
344        contains multiple characters, we'll use 0 as
345        the scancode/keysym.
346    */
347    if (SDL_TranslateUNICODE && state == SDL_PRESSED) {
348        [field_edit interpretKeyEvents:[NSArray arrayWithObject:event]];
349        chars = [ event characters ];
350        numChars = [ chars length ];
351        if (numChars > 0)
352            [field_edit setString:@""];
353    } else {
354        numChars = 0;
355    }
356
357    if (numChars == 0) {
358
359        key.scancode = [ event keyCode ];
360        key.sym      = keymap [ key.scancode ];
361        key.unicode  = 0;
362        key.mod      = KMOD_NONE;
363
364        SDL_PrivateKeyboard (state, &key);
365    }
366    else if (numChars >= 1) {
367
368        key.scancode = [ event keyCode ];
369        key.sym      = keymap [ key.scancode ];
370        key.unicode  = [ chars characterAtIndex:0 ];
371        key.mod      = KMOD_NONE;
372
373        SDL_PrivateKeyboard (state, &key);
374
375        for (i = 1; i < numChars; i++) {
376
377            key.scancode = 0;
378            key.sym      = 0;
379            key.unicode  = [ chars characterAtIndex:i];
380            key.mod      = KMOD_NONE;
381
382            SDL_PrivateKeyboard (state, &key);
383        }
384    }
385
386    if (SDL_getenv ("SDL_ENABLEAPPEVENTS"))
387        [ NSApp sendEvent:event ];
388}
389
390/* This is the original behavior, before support was added for
391 * differentiating between left and right versions of the keys.
392 */
393static void QZ_DoUnsidedModifiers (_THIS, unsigned int newMods) {
394
395    const int mapping[] = { SDLK_CAPSLOCK, SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA };
396
397    int i;
398    int bit;
399    SDL_keysym key;
400
401    key.scancode    = 0;
402    key.sym         = SDLK_UNKNOWN;
403    key.unicode     = 0;
404    key.mod         = KMOD_NONE;
405
406    /* Iterate through the bits, testing each against the current modifiers */
407    for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
408
409        unsigned int currentMask, newMask;
410
411        currentMask = current_mods & bit;
412        newMask     = newMods & bit;
413
414        if ( currentMask &&
415             currentMask != newMask ) {     /* modifier up event */
416
417             key.sym = mapping[i];
418             /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
419             if (bit == NSAlphaShiftKeyMask)
420                  SDL_PrivateKeyboard (SDL_PRESSED, &key);
421             SDL_PrivateKeyboard (SDL_RELEASED, &key);
422        }
423        else if ( newMask &&
424                  currentMask != newMask ) {     /* modifier down event */
425
426             key.sym = mapping[i];
427             SDL_PrivateKeyboard (SDL_PRESSED, &key);
428             /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
429             if (bit == NSAlphaShiftKeyMask)
430                  SDL_PrivateKeyboard (SDL_RELEASED, &key);
431        }
432    }
433}
434
435/* This is a helper function for QZ_HandleModifierSide. This
436 * function reverts back to behavior before the distinction between
437 * sides was made.
438 */
439static void QZ_HandleNonDeviceModifier ( _THIS, unsigned int device_independent_mask, unsigned int newMods, unsigned int key_sym) {
440    unsigned int currentMask, newMask;
441    SDL_keysym key;
442
443    key.scancode    = 0;
444    key.sym         = key_sym;
445    key.unicode     = 0;
446    key.mod         = KMOD_NONE;
447
448    /* Isolate just the bits we care about in the depedent bits so we can
449     * figure out what changed
450     */
451    currentMask = current_mods & device_independent_mask;
452    newMask     = newMods & device_independent_mask;
453
454    if ( currentMask &&
455         currentMask != newMask ) {     /* modifier up event */
456         SDL_PrivateKeyboard (SDL_RELEASED, &key);
457    }
458    else if ( newMask &&
459          currentMask != newMask ) {     /* modifier down event */
460          SDL_PrivateKeyboard (SDL_PRESSED, &key);
461    }
462}
463
464/* This is a helper function for QZ_HandleModifierSide.
465 * This function sets the actual SDL_PrivateKeyboard event.
466 */
467static void QZ_HandleModifierOneSide ( _THIS, unsigned int newMods,
468                                       unsigned int key_sym,
469                                       unsigned int sided_device_dependent_mask ) {
470
471    SDL_keysym key;
472    unsigned int current_dep_mask, new_dep_mask;
473
474    key.scancode    = 0;
475    key.sym         = key_sym;
476    key.unicode     = 0;
477    key.mod         = KMOD_NONE;
478
479    /* Isolate just the bits we care about in the depedent bits so we can
480     * figure out what changed
481     */
482    current_dep_mask = current_mods & sided_device_dependent_mask;
483    new_dep_mask     = newMods & sided_device_dependent_mask;
484
485    /* We now know that this side bit flipped. But we don't know if
486     * it went pressed to released or released to pressed, so we must
487     * find out which it is.
488     */
489    if( new_dep_mask &&
490        current_dep_mask != new_dep_mask ) {
491        /* Modifier down event */
492        SDL_PrivateKeyboard (SDL_PRESSED, &key);
493    }
494    else /* Modifier up event */ {
495        SDL_PrivateKeyboard (SDL_RELEASED, &key);
496    }
497}
498
499/* This is a helper function for QZ_DoSidedModifiers.
500 * This function will figure out if the modifier key is the left or right side,
501 * e.g. left-shift vs right-shift.
502 */
503static void QZ_HandleModifierSide ( _THIS, int device_independent_mask,
504                                    unsigned int newMods,
505                                    unsigned int left_key_sym,
506                                    unsigned int right_key_sym,
507                                    unsigned int left_device_dependent_mask,
508                                    unsigned int right_device_dependent_mask ) {
509    unsigned int device_dependent_mask = 0;
510    unsigned int diff_mod = 0;
511
512    device_dependent_mask = left_device_dependent_mask | right_device_dependent_mask;
513    /* On the basis that the device independent mask is set, but there are
514     * no device dependent flags set, we'll assume that we can't detect this
515     * keyboard and revert to the unsided behavior.
516     */
517    if ( (device_dependent_mask & newMods) == 0 ) {
518        /* Revert to the old behavior */
519        QZ_HandleNonDeviceModifier ( this, device_independent_mask, newMods, left_key_sym );
520        return;
521    }
522
523    /* XOR the previous state against the new state to see if there's a change */
524    diff_mod = (device_dependent_mask & current_mods)
525        ^ (device_dependent_mask & newMods);
526
527    if ( diff_mod ) {
528        /* A change in state was found. Isolate the left and right bits
529         * to handle them separately just in case the values can simulataneously
530         * change or if the bits don't both exist.
531         */
532        if ( left_device_dependent_mask & diff_mod ) {
533            QZ_HandleModifierOneSide ( this, newMods, left_key_sym, left_device_dependent_mask );
534        }
535        if ( right_device_dependent_mask & diff_mod ) {
536            QZ_HandleModifierOneSide ( this, newMods, right_key_sym, right_device_dependent_mask );
537        }
538    }
539}
540
541/* This is a helper function for QZ_DoSidedModifiers.
542 * This function will release a key press in the case that
543 * it is clear that the modifier has been released (i.e. one side
544 * can't still be down).
545 */
546static void QZ_ReleaseModifierSide ( _THIS,
547                                     unsigned int device_independent_mask,
548                                     unsigned int newMods,
549                                     unsigned int left_key_sym,
550                                     unsigned int right_key_sym,
551                                     unsigned int left_device_dependent_mask,
552                                     unsigned int right_device_dependent_mask ) {
553    unsigned int device_dependent_mask = 0;
554    SDL_keysym key;
555
556    key.scancode    = 0;
557    key.sym         = SDLK_UNKNOWN;
558    key.unicode     = 0;
559    key.mod         = KMOD_NONE;
560
561    device_dependent_mask = left_device_dependent_mask | right_device_dependent_mask;
562    /* On the basis that the device independent mask is set, but there are
563     * no device dependent flags set, we'll assume that we can't detect this
564     * keyboard and revert to the unsided behavior.
565     */
566    if ( (device_dependent_mask & current_mods) == 0 ) {
567        /* In this case, we can't detect the keyboard, so use the left side
568         * to represent both, and release it.
569         */
570        key.sym = left_key_sym;
571        SDL_PrivateKeyboard (SDL_RELEASED, &key);
572
573        return;
574    }
575
576
577    /*
578     * This could have been done in an if-else case because at this point,
579     * we know that all keys have been released when calling this function.
580     * But I'm being paranoid so I want to handle each separately,
581     * so I hope this doesn't cause other problems.
582     */
583    if ( left_device_dependent_mask & current_mods ) {
584        key.sym = left_key_sym;
585        SDL_PrivateKeyboard (SDL_RELEASED, &key);
586    }
587    if ( right_device_dependent_mask & current_mods ) {
588        key.sym = right_key_sym;
589        SDL_PrivateKeyboard (SDL_RELEASED, &key);
590    }
591}
592
593/* This is a helper function for QZ_DoSidedModifiers.
594 * This function handles the CapsLock case.
595 */
596static void QZ_HandleCapsLock (_THIS, unsigned int newMods) {
597    unsigned int currentMask, newMask;
598    SDL_keysym key;
599
600    key.scancode    = 0;
601    key.sym         = SDLK_CAPSLOCK;
602    key.unicode     = 0;
603    key.mod         = KMOD_NONE;
604
605    currentMask = current_mods & NSAlphaShiftKeyMask;
606    newMask     = newMods & NSAlphaShiftKeyMask;
607
608    if ( currentMask &&
609         currentMask != newMask ) {     /* modifier up event */
610         /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
611         SDL_PrivateKeyboard (SDL_PRESSED, &key);
612         SDL_PrivateKeyboard (SDL_RELEASED, &key);
613    }
614    else if ( newMask &&
615              currentMask != newMask ) {     /* modifier down event */
616        /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
617        SDL_PrivateKeyboard (SDL_PRESSED, &key);
618        SDL_PrivateKeyboard (SDL_RELEASED, &key);
619    }
620}
621
622/* This function will handle the modifier keys and also determine the
623 * correct side of the key.
624 */
625static void QZ_DoSidedModifiers (_THIS, unsigned int newMods) {
626	/* Set up arrays for the key syms for the left and right side. */
627    const unsigned int left_mapping[]  = { SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA };
628    const unsigned int right_mapping[] = { SDLK_RSHIFT, SDLK_RCTRL, SDLK_RALT, SDLK_RMETA };
629	/* Set up arrays for the device dependent masks with indices that
630     * correspond to the _mapping arrays
631     */
632    const unsigned int left_device_mapping[]  = { NX_DEVICELSHIFTKEYMASK, NX_DEVICELCTLKEYMASK, NX_DEVICELALTKEYMASK, NX_DEVICELCMDKEYMASK };
633    const unsigned int right_device_mapping[] = { NX_DEVICERSHIFTKEYMASK, NX_DEVICERCTLKEYMASK, NX_DEVICERALTKEYMASK, NX_DEVICERCMDKEYMASK };
634
635    unsigned int i;
636    unsigned int bit;
637
638    /* Handle CAPSLOCK separately because it doesn't have a left/right side */
639    QZ_HandleCapsLock ( this, newMods );
640
641    /* Iterate through the bits, testing each against the current modifiers */
642    for (i = 0, bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
643
644        unsigned int currentMask, newMask;
645
646        currentMask = current_mods & bit;
647        newMask     = newMods & bit;
648
649        /* If the bit is set, we must always examine it because the left
650         * and right side keys may alternate or both may be pressed.
651         */
652        if ( newMask ) {
653            QZ_HandleModifierSide ( this, bit, newMods,
654                                       left_mapping[i],
655                                       right_mapping[i],
656                                       left_device_mapping[i],
657                                       right_device_mapping[i] );
658        }
659        /* If the state changed from pressed to unpressed, we must examine
660            * the device dependent bits to release the correct keys.
661            */
662        else if ( currentMask &&
663                  currentMask != newMask ) { /* modifier up event */
664                  QZ_ReleaseModifierSide ( this, bit, newMods,
665                                           left_mapping[i],
666                                           right_mapping[i],
667                                           left_device_mapping[i],
668                                           right_device_mapping[i] );
669        }
670    }
671}
672
673/* This function is called to handle the modifiers.
674 * It will try to distinguish between the left side and right side
675 * of the keyboard for those modifiers that qualify if the
676 * operating system version supports it. Otherwise, the code
677 * will not try to make the distinction.
678 */
679static void QZ_DoModifiers (_THIS, unsigned int newMods) {
680
681    if (current_mods == newMods)
682    	return;
683
684    /*
685     * Starting with Panther (10.3.0), the ability to distinguish between
686     * left side and right side modifiers is available.
687     */
688    if( system_version >= 0x1030 ) {
689        QZ_DoSidedModifiers (this, newMods);
690    }
691    else {
692        QZ_DoUnsidedModifiers (this, newMods);
693    }
694
695    current_mods = newMods;
696}
697
698static void QZ_GetMouseLocation (_THIS, NSPoint *p) {
699    *p = [ NSEvent mouseLocation ]; /* global coordinates */
700    if (qz_window)
701        QZ_PrivateGlobalToLocal (this, p);
702    QZ_PrivateCocoaToSDL (this, p);
703}
704
705void QZ_DoActivate (_THIS) {
706
707    SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS | (QZ_IsMouseInWindow (this) ? SDL_APPMOUSEFOCUS : 0));
708
709    QZ_UpdateCursor(this);
710
711    /* Regrab input, only if it was previously grabbed */
712    if ( current_grab_mode == SDL_GRAB_ON ) {
713
714        /* Restore cursor location if input was grabbed */
715        QZ_PrivateWarpCursor (this, cursor_loc.x, cursor_loc.y);
716        QZ_ChangeGrabState (this, QZ_ENABLE_GRAB);
717    }
718    else {
719        /* Update SDL's mouse location */
720        NSPoint p;
721        QZ_GetMouseLocation (this, &p);
722        SDL_PrivateMouseMotion (0, 0, p.x, p.y);
723    }
724
725    QZ_UpdateCursor(this);
726}
727
728void QZ_DoDeactivate (_THIS) {
729
730    SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS | SDL_APPMOUSEFOCUS);
731
732    /* Get the current cursor location, for restore on activate */
733    QZ_GetMouseLocation (this, &cursor_loc);
734
735    /* Reassociate mouse and cursor */
736    CGAssociateMouseAndMouseCursorPosition (1);
737
738    QZ_UpdateCursor(this);
739}
740
741void QZ_SleepNotificationHandler (void * refcon,
742                                  io_service_t service,
743                                  natural_t messageType,
744                                  void * messageArgument )
745{
746     SDL_VideoDevice *this = (SDL_VideoDevice*)refcon;
747
748     switch(messageType)
749     {
750         case kIOMessageSystemWillSleep:
751             IOAllowPowerChange(power_connection, (long) messageArgument);
752             break;
753         case kIOMessageCanSystemSleep:
754             IOAllowPowerChange(power_connection, (long) messageArgument);
755             break;
756         case kIOMessageSystemHasPoweredOn:
757            /* awake */
758            SDL_PrivateExpose();
759            break;
760     }
761}
762
763void QZ_RegisterForSleepNotifications (_THIS)
764{
765     CFRunLoopSourceRef rls;
766     IONotificationPortRef thePortRef;
767     io_object_t notifier;
768
769     power_connection = IORegisterForSystemPower (this, &thePortRef, QZ_SleepNotificationHandler, &notifier);
770
771     if (power_connection == 0)
772         NSLog(@"SDL: QZ_SleepNotificationHandler() IORegisterForSystemPower failed.");
773
774     rls = IONotificationPortGetRunLoopSource (thePortRef);
775     CFRunLoopAddSource (CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
776     CFRelease (rls);
777}
778
779
780/* Try to map Quartz mouse buttons to SDL's lingo... */
781static int QZ_OtherMouseButtonToSDL(int button)
782{
783    switch (button)
784    {
785        case 0:
786            return(SDL_BUTTON_LEFT);   /* 1 */
787        case 1:
788            return(SDL_BUTTON_RIGHT);  /* 3 */
789        case 2:
790            return(SDL_BUTTON_MIDDLE); /* 2 */
791    }
792
793    /* >= 3: skip 4 & 5, since those are the SDL mousewheel buttons. */
794    return(button + 3);
795}
796
797
798void QZ_PumpEvents (_THIS)
799{
800    int32_t dx, dy;
801
802    NSDate *distantPast;
803    NSEvent *event;
804    NSRect winRect;
805    NSAutoreleasePool *pool;
806
807    if (!SDL_VideoSurface)
808        return;  /* don't do anything if there's no screen surface. */
809
810    /* Update activity every five seconds to prevent screensaver. --ryan. */
811    if (!allow_screensaver) {
812        static Uint32 screensaverTicks;
813        Uint32 nowTicks = SDL_GetTicks();
814        if ((nowTicks - screensaverTicks) > 5000)
815        {
816            UpdateSystemActivity(UsrActivity);
817            screensaverTicks = nowTicks;
818        }
819    }
820
821    pool = [ [ NSAutoreleasePool alloc ] init ];
822    distantPast = [ NSDate distantPast ];
823
824    winRect = NSMakeRect (0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h);
825
826    /* while grabbed, accumulate all mouse moved events into one SDL mouse event */
827    dx = 0;
828    dy = 0;
829
830    do {
831
832        /* Poll for an event. This will not block */
833        event = [ NSApp nextEventMatchingMask:NSAnyEventMask
834                                    untilDate:distantPast
835                                    inMode: NSDefaultRunLoopMode dequeue:YES ];
836        if (event != nil) {
837
838            int button;
839            unsigned int type;
840            BOOL isInGameWin;
841
842            #define DO_MOUSE_DOWN(button) do {                                               \
843                            if ( SDL_GetAppState() & SDL_APPMOUSEFOCUS ) {                   \
844                                SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);          \
845                                expect_mouse_up |= 1<<button;                                \
846                            }                                                                \
847                            [ NSApp sendEvent:event ];                                       \
848            } while(0)
849
850            #define DO_MOUSE_UP(button) do {                                            \
851                            if ( expect_mouse_up & (1<<button) ) {                      \
852                                SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);    \
853                                expect_mouse_up &= ~(1<<button);                        \
854                            }                                                           \
855                            [ NSApp sendEvent:event ];                                  \
856            } while(0)
857
858            type = [ event type ];
859            isInGameWin = QZ_IsMouseInWindow (this);
860
861            QZ_DoModifiers(this, [ event modifierFlags ] );
862
863            switch (type) {
864                case NSLeftMouseDown:
865                    if ( SDL_getenv("SDL_HAS3BUTTONMOUSE") ) {
866                        DO_MOUSE_DOWN (SDL_BUTTON_LEFT);
867                    } else {
868                        if ( NSCommandKeyMask & current_mods ) {
869                            last_virtual_button = SDL_BUTTON_RIGHT;
870                            DO_MOUSE_DOWN (SDL_BUTTON_RIGHT);
871                        }
872                        else if ( NSAlternateKeyMask & current_mods ) {
873                            last_virtual_button = SDL_BUTTON_MIDDLE;
874                            DO_MOUSE_DOWN (SDL_BUTTON_MIDDLE);
875                        }
876                        else {
877                            DO_MOUSE_DOWN (SDL_BUTTON_LEFT);
878                        }
879                    }
880                    break;
881
882                case NSLeftMouseUp:
883                    if ( last_virtual_button != 0 ) {
884                        DO_MOUSE_UP (last_virtual_button);
885                        last_virtual_button = 0;
886                    }
887                    else {
888                        DO_MOUSE_UP (SDL_BUTTON_LEFT);
889                    }
890                    break;
891
892                case NSOtherMouseDown:
893                case NSRightMouseDown:
894                    button = QZ_OtherMouseButtonToSDL([ event buttonNumber ]);
895                    DO_MOUSE_DOWN (button);
896                    break;
897
898                case NSOtherMouseUp:
899                case NSRightMouseUp:
900                    button = QZ_OtherMouseButtonToSDL([ event buttonNumber ]);
901                    DO_MOUSE_UP (button);
902                    break;
903
904                case NSSystemDefined:
905                    /*
906                        Future: up to 32 "mouse" buttons can be handled.
907                        if ([event subtype] == 7) {
908                            unsigned int buttons;
909                            buttons = [ event data2 ];
910                    */
911                    break;
912                case NSLeftMouseDragged:
913                case NSRightMouseDragged:
914                case NSOtherMouseDragged: /* usually middle mouse dragged */
915                case NSMouseMoved:
916                    if ( grab_state == QZ_INVISIBLE_GRAB ) {
917
918                        /*
919                            If input is grabbed+hidden, the cursor doesn't move,
920                            so we have to call the lowlevel window server
921                            function. This is less accurate but works OK.
922                        */
923                        int32_t dx1, dy1;
924                        CGGetLastMouseDelta (&dx1, &dy1);
925                        dx += dx1;
926                        dy += dy1;
927                    }
928                    else {
929
930                        /*
931                            Get the absolute mouse location. This is not the
932                            mouse location after the currently processed event,
933                            but the *current* mouse location, i.e. after all
934                            pending events. This means that if there are
935                            multiple mouse moved events in the queue, we make
936                            multiple identical calls to SDL_PrivateMouseMotion(),
937                            but that's no problem since the latter only
938                            generates SDL events for nonzero movements. In my
939                            experience on PBG4/10.4.8, this rarely happens anyway.
940                        */
941                        NSPoint p;
942                        QZ_GetMouseLocation (this, &p);
943                        SDL_PrivateMouseMotion (0, 0, p.x, p.y);
944                    }
945
946                    /*
947                        Handle grab input+cursor visible by warping the cursor back
948                        into the game window. This still generates a mouse moved event,
949                        but not as a result of the warp (so it's in the right direction).
950                    */
951                    if ( grab_state == QZ_VISIBLE_GRAB && !isInGameWin ) {
952
953                        NSPoint p;
954                        QZ_GetMouseLocation (this, &p);
955
956                        if ( p.x < 0.0 )
957                            p.x = 0.0;
958
959                        if ( p.y < 0.0 )
960                            p.y = 0.0;
961
962                        if ( p.x >= winRect.size.width )
963                            p.x = winRect.size.width-1;
964
965                        if ( p.y >= winRect.size.height )
966                            p.y = winRect.size.height-1;
967
968                        QZ_PrivateWarpCursor (this, p.x, p.y);
969                    }
970                    else
971                    if ( !isInGameWin && (SDL_GetAppState() & SDL_APPMOUSEFOCUS) ) {
972
973                        SDL_PrivateAppActive (0, SDL_APPMOUSEFOCUS);
974
975                        if (grab_state == QZ_INVISIBLE_GRAB)
976                            /*The cursor has left the window even though it is
977                              disassociated from the mouse (and therefore
978                              shouldn't move): this can happen with Wacom
979                              tablets, and it effectively breaks the grab, since
980                              mouse down events now go to background
981                              applications. The only possibility to avoid this
982                              seems to be talking to the tablet driver
983                              (AppleEvents) to constrain its mapped area to the
984                              window, which may not be worth the effort. For
985                              now, handle the condition more gracefully than
986                              before by reassociating cursor and mouse until the
987                              cursor enters the window again, making it obvious
988                              to the user that the grab is broken.*/
989                            CGAssociateMouseAndMouseCursorPosition (1);
990
991                        QZ_UpdateCursor(this);
992                    }
993                    else
994                    if ( isInGameWin && (SDL_GetAppState() & (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS)) == SDL_APPINPUTFOCUS ) {
995
996                        SDL_PrivateAppActive (1, SDL_APPMOUSEFOCUS);
997
998                        QZ_UpdateCursor(this);
999
1000                        if (grab_state == QZ_INVISIBLE_GRAB) { /*see comment above*/
1001                            QZ_PrivateWarpCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2);
1002                            CGAssociateMouseAndMouseCursorPosition (0);
1003                        }
1004                    }
1005                    break;
1006                case NSScrollWheel:
1007                    if ( isInGameWin ) {
1008                        float dy, dx;
1009                        Uint8 button;
1010                        dy = [ event deltaY ];
1011                        dx = [ event deltaX ];
1012                        if ( dy > 0.0 ) /* Scroll up */
1013                            button = SDL_BUTTON_WHEELUP;
1014                        else if ( dy < 0.0 ) /* Scroll down */
1015                            button = SDL_BUTTON_WHEELDOWN;
1016                        else
1017                            break; /* Horizontal scroll */
1018                        /* For now, wheel is sent as a quick down+up */
1019                        SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);
1020                        SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);
1021                    }
1022                    break;
1023                case NSKeyUp:
1024                    QZ_DoKey (this, SDL_RELEASED, event);
1025                    break;
1026                case NSKeyDown:
1027                    QZ_DoKey (this, SDL_PRESSED, event);
1028                    break;
1029                case NSFlagsChanged:
1030                    break;
1031                case NSAppKitDefined:
1032                    [ NSApp sendEvent:event ];
1033                    if ([ event subtype ] == NSApplicationActivatedEventType && (mode_flags & SDL_FULLSCREEN)) {
1034                        /* the default handling of this event seems to reset any cursor set by [NSCursor set] (used by SDL_SetCursor() in fullscreen mode) to the default system arrow cursor */
1035                        SDL_Cursor *sdlc = SDL_GetCursor();
1036                        if (sdlc != NULL && sdlc->wm_cursor != NULL) {
1037                            [ sdlc->wm_cursor->nscursor set ];
1038                        }
1039                    }
1040                    break;
1041                    /* case NSApplicationDefined: break; */
1042                    /* case NSPeriodic: break; */
1043                    /* case NSCursorUpdate: break; */
1044                default:
1045                    [ NSApp sendEvent:event ];
1046            }
1047        }
1048    } while (event != nil);
1049
1050    /* handle accumulated mouse moved events */
1051    if (dx != 0 || dy != 0)
1052        SDL_PrivateMouseMotion (0, 1, dx, dy);
1053
1054    [ pool release ];
1055}
1056
1057void QZ_UpdateMouse (_THIS)
1058{
1059    NSPoint p;
1060    QZ_GetMouseLocation (this, &p);
1061    SDL_PrivateAppActive (QZ_IsMouseInWindow (this), SDL_APPMOUSEFOCUS);
1062    SDL_PrivateMouseMotion (0, 0, p.x, p.y);
1063}
1064