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