Home | History | Annotate | Download | only in quartz
      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 (at) 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 
     67 void     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 
    255 static 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  */
    317 static 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  */
    363 static 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  */
    391 static 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  */
    427 static 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  */
    470 static 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  */
    520 static 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  */
    549 static 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  */
    603 static 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 
    622 static 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 
    629 void 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 
    650 void 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 
    663 void 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 
    685 void 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... */
    703 static 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 
    720 void 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 
    978 void 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