src/video/quartz/SDL_QuartzEvents.m
author Ryan C. Gordon <icculus@icculus.org>
Mon, 02 Jan 2006 07:09:52 +0000
changeset 1213 8eb191652834
parent 1212 7663bb0f52c7
child 1338 604d73db6802
permissions -rw-r--r--
Quartz target shouldn't crash if an event thread is used.

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