src/video/quartz/SDL_QuartzEvents.m
author Sam Lantinga <slouken@libsdl.org>
Sun, 15 Jul 2007 17:22:39 +0000
branchSDL-1.2
changeset 4072 6930cd5ab933
parent 4070 b8f2db95145e
child 4093 7ea10b80eda3
permissions -rw-r--r--
Final fix for bug #373

------- Comment #13 From Christian Walther 2007-07-15 10:04:56 [reply] -------
Created an attachment (id=229) [details]
patch to reinstate the SDL cursor after an NSApplicationActivatedEvent

Oops, you're right. My testcursor.c has diverged so far from the original that
I missed that. In fact, now that I think about it, that issue isn't even
addressed by the cursor rectangle patch - it is only concerned with windowed
mode (and fixes the problem there as a side effect). The problem in fullscreen
is still the same as before. How about the attachment for a fix? (I can confirm
that [NSCursor set] doesn't seem to have any effect when called from
QZ_DoActivate().)
     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 
    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;
   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     } else {
   274         numChars = 0;
   275     }
   276 
   277     if (numChars == 0) {
   278       
   279         key.scancode = [ event keyCode ];
   280         key.sym      = keymap [ key.scancode ];
   281         key.unicode  = 0;
   282         key.mod      = KMOD_NONE;
   283 
   284         SDL_PrivateKeyboard (state, &key);
   285     }
   286     else if (numChars >= 1) {
   287 
   288         key.scancode = [ event keyCode ];
   289         key.sym      = keymap [ key.scancode ];
   290         key.unicode  = [ chars characterAtIndex:0 ];
   291         key.mod      = KMOD_NONE;
   292 
   293         SDL_PrivateKeyboard (state, &key);
   294       
   295         for (i = 1; i < numChars; i++) {
   296 
   297             key.scancode = 0;
   298             key.sym      = 0;
   299             key.unicode  = [ chars characterAtIndex:i];
   300             key.mod      = KMOD_NONE;
   301 
   302             SDL_PrivateKeyboard (state, &key);
   303         }
   304     }
   305     
   306     if (SDL_getenv ("SDL_ENABLEAPPEVENTS"))
   307         [ NSApp sendEvent:event ];
   308 }
   309 
   310 /* This is the original behavior, before support was added for 
   311  * differentiating between left and right versions of the keys.
   312  */
   313 static void QZ_DoUnsidedModifiers (_THIS, unsigned int newMods) {
   314 
   315     const int mapping[] = { SDLK_CAPSLOCK, SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA };
   316 
   317     int i;
   318     int bit;
   319     SDL_keysym key;
   320     
   321     key.scancode    = 0;
   322     key.sym         = SDLK_UNKNOWN;
   323     key.unicode     = 0;
   324     key.mod         = KMOD_NONE;
   325 
   326     /* Iterate through the bits, testing each against the current modifiers */
   327     for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
   328 
   329         unsigned int currentMask, newMask;
   330 
   331         currentMask = current_mods & bit;
   332         newMask     = newMods & bit;
   333 
   334         if ( currentMask &&
   335              currentMask != newMask ) {     /* modifier up event */
   336 
   337              key.sym = mapping[i];
   338              /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
   339              if (bit == NSAlphaShiftKeyMask)
   340                   SDL_PrivateKeyboard (SDL_PRESSED, &key);
   341              SDL_PrivateKeyboard (SDL_RELEASED, &key);
   342         }
   343         else if ( newMask &&
   344                   currentMask != newMask ) {     /* modifier down event */
   345         
   346              key.sym = mapping[i];
   347              SDL_PrivateKeyboard (SDL_PRESSED, &key);
   348              /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
   349              if (bit == NSAlphaShiftKeyMask)
   350                   SDL_PrivateKeyboard (SDL_RELEASED, &key);
   351         }
   352     }
   353 }
   354 
   355 /* This is a helper function for QZ_HandleModifierSide. This 
   356  * function reverts back to behavior before the distinction between
   357  * sides was made.
   358  */
   359 static void QZ_HandleNonDeviceModifier ( _THIS, unsigned int device_independent_mask, unsigned int newMods, unsigned int key_sym) {
   360     unsigned int currentMask, newMask;
   361     SDL_keysym key;
   362     
   363     key.scancode    = 0;
   364     key.sym         = key_sym;
   365     key.unicode     = 0;
   366     key.mod         = KMOD_NONE;
   367     
   368     /* Isolate just the bits we care about in the depedent bits so we can 
   369      * figure out what changed
   370      */ 
   371     currentMask = current_mods & device_independent_mask;
   372     newMask     = newMods & device_independent_mask;
   373     
   374     if ( currentMask &&
   375          currentMask != newMask ) {     /* modifier up event */
   376          SDL_PrivateKeyboard (SDL_RELEASED, &key);
   377     }
   378     else if ( newMask &&
   379           currentMask != newMask ) {     /* modifier down event */
   380           SDL_PrivateKeyboard (SDL_PRESSED, &key);
   381     }
   382 }
   383 
   384 /* This is a helper function for QZ_HandleModifierSide. 
   385  * This function sets the actual SDL_PrivateKeyboard event.
   386  */
   387 static void QZ_HandleModifierOneSide ( _THIS, unsigned int newMods,
   388                                        unsigned int key_sym, 
   389                                        unsigned int sided_device_dependent_mask ) {
   390     
   391     SDL_keysym key;
   392     unsigned int current_dep_mask, new_dep_mask;
   393     
   394     key.scancode    = 0;
   395     key.sym         = key_sym;
   396     key.unicode     = 0;
   397     key.mod         = KMOD_NONE;
   398     
   399     /* Isolate just the bits we care about in the depedent bits so we can 
   400      * figure out what changed
   401      */ 
   402     current_dep_mask = current_mods & sided_device_dependent_mask;
   403     new_dep_mask     = newMods & sided_device_dependent_mask;
   404     
   405     /* We now know that this side bit flipped. But we don't know if
   406      * it went pressed to released or released to pressed, so we must 
   407      * find out which it is.
   408      */
   409     if( new_dep_mask &&
   410         current_dep_mask != new_dep_mask ) { 
   411         /* Modifier down event */
   412         SDL_PrivateKeyboard (SDL_PRESSED, &key);
   413     }
   414     else /* Modifier up event */ {
   415         SDL_PrivateKeyboard (SDL_RELEASED, &key);
   416     }
   417 }
   418 
   419 /* This is a helper function for QZ_DoSidedModifiers.
   420  * This function will figure out if the modifier key is the left or right side, 
   421  * e.g. left-shift vs right-shift. 
   422  */
   423 static void QZ_HandleModifierSide ( _THIS, int device_independent_mask, 
   424                                     unsigned int newMods, 
   425                                     unsigned int left_key_sym, 
   426                                     unsigned int right_key_sym,
   427                                     unsigned int left_device_dependent_mask, 
   428                                     unsigned int right_device_dependent_mask ) {
   429     unsigned int device_dependent_mask = 0;
   430     unsigned int diff_mod = 0;
   431     
   432     device_dependent_mask = left_device_dependent_mask | right_device_dependent_mask;
   433     /* On the basis that the device independent mask is set, but there are 
   434      * no device dependent flags set, we'll assume that we can't detect this 
   435      * keyboard and revert to the unsided behavior.
   436      */
   437     if ( (device_dependent_mask & newMods) == 0 ) {
   438         /* Revert to the old behavior */
   439         QZ_HandleNonDeviceModifier ( this, device_independent_mask, newMods, left_key_sym );
   440         return;
   441     }
   442         
   443     /* XOR the previous state against the new state to see if there's a change */
   444     diff_mod = (device_dependent_mask & current_mods)
   445         ^ (device_dependent_mask & newMods);
   446 
   447     if ( diff_mod ) {
   448         /* A change in state was found. Isolate the left and right bits 
   449          * to handle them separately just in case the values can simulataneously
   450          * change or if the bits don't both exist.
   451          */
   452         if ( left_device_dependent_mask & diff_mod ) {
   453             QZ_HandleModifierOneSide ( this, newMods, left_key_sym, left_device_dependent_mask );
   454         }
   455         if ( right_device_dependent_mask & diff_mod ) {
   456             QZ_HandleModifierOneSide ( this, newMods, right_key_sym, right_device_dependent_mask );
   457         }
   458     }
   459 }
   460    
   461 /* This is a helper function for QZ_DoSidedModifiers.
   462  * This function will release a key press in the case that 
   463  * it is clear that the modifier has been released (i.e. one side 
   464  * can't still be down).
   465  */
   466 static void QZ_ReleaseModifierSide ( _THIS, 
   467                                      unsigned int device_independent_mask, 
   468                                      unsigned int newMods,
   469                                      unsigned int left_key_sym, 
   470                                      unsigned int right_key_sym,
   471                                      unsigned int left_device_dependent_mask, 
   472                                      unsigned int right_device_dependent_mask ) {
   473     unsigned int device_dependent_mask = 0;
   474     SDL_keysym key;
   475     
   476     key.scancode    = 0;
   477     key.sym         = SDLK_UNKNOWN;
   478     key.unicode     = 0;
   479     key.mod         = KMOD_NONE;
   480     
   481     device_dependent_mask = left_device_dependent_mask | right_device_dependent_mask;
   482     /* On the basis that the device independent mask is set, but there are 
   483      * no device dependent flags set, we'll assume that we can't detect this 
   484      * keyboard and revert to the unsided behavior.
   485      */
   486     if ( (device_dependent_mask & current_mods) == 0 ) {
   487         /* In this case, we can't detect the keyboard, so use the left side 
   488          * to represent both, and release it. 
   489          */
   490         key.sym = left_key_sym;
   491         SDL_PrivateKeyboard (SDL_RELEASED, &key);
   492 
   493         return;
   494     }
   495         
   496         
   497     /* 
   498      * This could have been done in an if-else case because at this point,
   499      * we know that all keys have been released when calling this function. 
   500      * But I'm being paranoid so I want to handle each separately,
   501      * so I hope this doesn't cause other problems.
   502      */
   503     if ( left_device_dependent_mask & current_mods ) {
   504         key.sym = left_key_sym;
   505         SDL_PrivateKeyboard (SDL_RELEASED, &key);
   506     }
   507     if ( right_device_dependent_mask & current_mods ) {
   508         key.sym = right_key_sym;
   509         SDL_PrivateKeyboard (SDL_RELEASED, &key);
   510     }
   511 }
   512 
   513 /* This is a helper function for QZ_DoSidedModifiers.
   514  * This function handles the CapsLock case.
   515  */
   516 static void QZ_HandleCapsLock (_THIS, unsigned int newMods) {
   517     unsigned int currentMask, newMask;
   518     SDL_keysym key;
   519     
   520     key.scancode    = 0;
   521     key.sym         = SDLK_CAPSLOCK;
   522     key.unicode     = 0;
   523     key.mod         = KMOD_NONE;
   524     
   525     currentMask = current_mods & NSAlphaShiftKeyMask;
   526     newMask     = newMods & NSAlphaShiftKeyMask;
   527 
   528     if ( currentMask &&
   529          currentMask != newMask ) {     /* modifier up event */
   530          /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
   531          SDL_PrivateKeyboard (SDL_PRESSED, &key);
   532          SDL_PrivateKeyboard (SDL_RELEASED, &key);
   533     }
   534     else if ( newMask &&
   535               currentMask != newMask ) {     /* modifier down event */
   536         /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
   537         SDL_PrivateKeyboard (SDL_PRESSED, &key);
   538         SDL_PrivateKeyboard (SDL_RELEASED, &key);
   539     }
   540 }
   541 
   542 /* This function will handle the modifier keys and also determine the 
   543  * correct side of the key.
   544  */
   545 static void QZ_DoSidedModifiers (_THIS, unsigned int newMods) {
   546 	/* Set up arrays for the key syms for the left and right side. */
   547     const unsigned int left_mapping[]  = { SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA };
   548     const unsigned int right_mapping[] = { SDLK_RSHIFT, SDLK_RCTRL, SDLK_RALT, SDLK_RMETA };
   549 	/* Set up arrays for the device dependent masks with indices that 
   550      * correspond to the _mapping arrays 
   551      */
   552     const unsigned int left_device_mapping[]  = { NX_DEVICELSHIFTKEYMASK, NX_DEVICELCTLKEYMASK, NX_DEVICELALTKEYMASK, NX_DEVICELCMDKEYMASK };
   553     const unsigned int right_device_mapping[] = { NX_DEVICERSHIFTKEYMASK, NX_DEVICERCTLKEYMASK, NX_DEVICERALTKEYMASK, NX_DEVICERCMDKEYMASK };
   554 
   555     unsigned int i;
   556     unsigned int bit;
   557     
   558     /* Handle CAPSLOCK separately because it doesn't have a left/right side */
   559     QZ_HandleCapsLock ( this, newMods );
   560         
   561     /* Iterate through the bits, testing each against the current modifiers */
   562     for (i = 0, bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
   563 		
   564         unsigned int currentMask, newMask;
   565 		
   566         currentMask = current_mods & bit;
   567         newMask     = newMods & bit;
   568 		
   569         /* If the bit is set, we must always examine it because the left
   570          * and right side keys may alternate or both may be pressed.
   571          */
   572         if ( newMask ) {
   573             QZ_HandleModifierSide ( this, bit, newMods, 
   574                                        left_mapping[i],
   575                                        right_mapping[i],
   576                                        left_device_mapping[i],
   577                                        right_device_mapping[i] );
   578         }
   579         /* If the state changed from pressed to unpressed, we must examine
   580             * the device dependent bits to release the correct keys.
   581             */
   582         else if ( currentMask &&
   583                   currentMask != newMask ) { /* modifier up event */
   584                   QZ_ReleaseModifierSide ( this, bit, newMods,
   585                                            left_mapping[i],
   586                                            right_mapping[i],
   587                                            left_device_mapping[i],
   588                                            right_device_mapping[i] );
   589         }
   590     }
   591 }
   592 
   593 /* This function is called to handle the modifiers.
   594  * It will try to distinguish between the left side and right side 
   595  * of the keyboard for those modifiers that qualify if the 
   596  * operating system version supports it. Otherwise, the code 
   597  * will not try to make the distinction.
   598  */
   599 static void QZ_DoModifiers (_THIS, unsigned int newMods) {
   600 	
   601     if (current_mods == newMods)
   602     	return;
   603     
   604     /* 
   605      * Starting with Panther (10.3.0), the ability to distinguish between 
   606      * left side and right side modifiers is available.
   607      */
   608     if( system_version >= 0x1030 ) {
   609         QZ_DoSidedModifiers (this, newMods);
   610     }
   611     else {
   612         QZ_DoUnsidedModifiers (this, newMods);
   613     }
   614     
   615     current_mods = newMods;
   616 }
   617 
   618 static void QZ_GetMouseLocation (_THIS, NSPoint *p) {
   619     *p = [ NSEvent mouseLocation ]; /* global coordinates */
   620     if (qz_window)
   621         QZ_PrivateGlobalToLocal (this, p);
   622     QZ_PrivateCocoaToSDL (this, p);
   623 }
   624 
   625 void QZ_DoActivate (_THIS) {
   626 
   627     SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS | (QZ_IsMouseInWindow (this) ? SDL_APPMOUSEFOCUS : 0));
   628 
   629     QZ_UpdateCursor(this);
   630 
   631     /* Regrab input, only if it was previously grabbed */
   632     if ( current_grab_mode == SDL_GRAB_ON ) {
   633         
   634         /* Restore cursor location if input was grabbed */
   635         QZ_PrivateWarpCursor (this, cursor_loc.x, cursor_loc.y);
   636         QZ_ChangeGrabState (this, QZ_ENABLE_GRAB);
   637     }
   638     else {
   639         /* Update SDL's mouse location */
   640         NSPoint p;
   641         QZ_GetMouseLocation (this, &p);
   642         SDL_PrivateMouseMotion (0, 0, p.x, p.y);
   643     }
   644 }
   645 
   646 void QZ_DoDeactivate (_THIS) {
   647     
   648     SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS | SDL_APPMOUSEFOCUS);
   649 
   650     /* Get the current cursor location, for restore on activate */
   651     QZ_GetMouseLocation (this, &cursor_loc);
   652     
   653     /* Reassociate mouse and cursor */
   654     CGAssociateMouseAndMouseCursorPosition (1);
   655 
   656     QZ_UpdateCursor(this);
   657 }
   658 
   659 void QZ_SleepNotificationHandler (void * refcon,
   660                                   io_service_t service,
   661                                   natural_t messageType,
   662                                   void * messageArgument )
   663 {
   664      SDL_VideoDevice *this = (SDL_VideoDevice*)refcon;
   665      
   666      switch(messageType)
   667      {
   668          case kIOMessageSystemWillSleep:
   669              IOAllowPowerChange(power_connection, (long) messageArgument);
   670              break;
   671          case kIOMessageCanSystemSleep:
   672              IOAllowPowerChange(power_connection, (long) messageArgument);
   673              break;
   674          case kIOMessageSystemHasPoweredOn:
   675             /* awake */
   676             SDL_PrivateExpose();
   677             break;
   678      }
   679 }
   680 
   681 void QZ_RegisterForSleepNotifications (_THIS)
   682 {
   683      CFRunLoopSourceRef rls;
   684      IONotificationPortRef thePortRef;
   685      io_object_t notifier;
   686 
   687      power_connection = IORegisterForSystemPower (this, &thePortRef, QZ_SleepNotificationHandler, &notifier);
   688 
   689      if (power_connection == 0)
   690          NSLog(@"SDL: QZ_SleepNotificationHandler() IORegisterForSystemPower failed.");
   691 
   692      rls = IONotificationPortGetRunLoopSource (thePortRef);
   693      CFRunLoopAddSource (CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
   694      CFRelease (rls);
   695 }
   696 
   697 
   698 /* Try to map Quartz mouse buttons to SDL's lingo... */
   699 static int QZ_OtherMouseButtonToSDL(int button)
   700 {
   701     switch (button)
   702     {
   703         case 0:
   704             return(SDL_BUTTON_LEFT);   /* 1 */
   705         case 1:
   706             return(SDL_BUTTON_RIGHT);  /* 3 */
   707         case 2:
   708             return(SDL_BUTTON_MIDDLE); /* 2 */
   709     }
   710 
   711     /* >= 3: skip 4 & 5, since those are the SDL mousewheel buttons. */
   712     return(button + 3);
   713 }
   714 
   715 
   716 void QZ_PumpEvents (_THIS)
   717 {
   718     static Uint32 screensaverTicks = 0;
   719     Uint32 nowTicks;
   720     CGMouseDelta dx, dy;
   721 
   722     NSDate *distantPast;
   723     NSEvent *event;
   724     NSRect winRect;
   725     NSAutoreleasePool *pool;
   726 
   727     if (!SDL_VideoSurface)
   728         return;  /* don't do anything if there's no screen surface. */
   729 
   730     /* Update activity every five seconds to prevent screensaver. --ryan. */
   731     if (!allow_screensaver) {
   732         nowTicks = SDL_GetTicks();
   733         if ((nowTicks - screensaverTicks) > 5000)
   734         {
   735             UpdateSystemActivity(UsrActivity);
   736             screensaverTicks = nowTicks;
   737         }
   738     }
   739 
   740     pool = [ [ NSAutoreleasePool alloc ] init ];
   741     distantPast = [ NSDate distantPast ];
   742 
   743     winRect = NSMakeRect (0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h);
   744     
   745     /* while grabbed, accumulate all mouse moved events into one SDL mouse event */
   746     dx = 0;
   747     dy = 0;
   748     
   749     do {
   750     
   751         /* Poll for an event. This will not block */
   752         event = [ NSApp nextEventMatchingMask:NSAnyEventMask
   753                                     untilDate:distantPast
   754                                     inMode: NSDefaultRunLoopMode dequeue:YES ];
   755         if (event != nil) {
   756 
   757             int button;
   758             unsigned int type;
   759             BOOL isInGameWin;
   760             
   761             #define DO_MOUSE_DOWN(button) do {                                               \
   762                             if ( SDL_GetAppState() & SDL_APPMOUSEFOCUS ) {                   \
   763                                 SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);          \
   764                                 expect_mouse_up |= 1<<button;                                \
   765                             }                                                                \
   766                             [ NSApp sendEvent:event ];                                       \
   767             } while(0)
   768             
   769             #define DO_MOUSE_UP(button) do {                                            \
   770                             if ( expect_mouse_up & (1<<button) ) {                      \
   771                                 SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);    \
   772                                 expect_mouse_up &= ~(1<<button);                        \
   773                             }                                                           \
   774                             [ NSApp sendEvent:event ];                                  \
   775             } while(0)
   776             
   777             type = [ event type ];
   778             isInGameWin = QZ_IsMouseInWindow (this);
   779 
   780             QZ_DoModifiers(this, [ event modifierFlags ] );
   781 
   782             switch (type) {
   783                 case NSLeftMouseDown:
   784                     if ( SDL_getenv("SDL_HAS3BUTTONMOUSE") ) {
   785                         DO_MOUSE_DOWN (SDL_BUTTON_LEFT);
   786                     } else {
   787                         if ( NSCommandKeyMask & current_mods ) {
   788                             last_virtual_button = SDL_BUTTON_RIGHT;
   789                             DO_MOUSE_DOWN (SDL_BUTTON_RIGHT);
   790                         }
   791                         else if ( NSAlternateKeyMask & current_mods ) {
   792                             last_virtual_button = SDL_BUTTON_MIDDLE;
   793                             DO_MOUSE_DOWN (SDL_BUTTON_MIDDLE);
   794                         }
   795                         else {
   796                             DO_MOUSE_DOWN (SDL_BUTTON_LEFT);
   797                         }
   798                     }
   799                     break;
   800 
   801                 case NSLeftMouseUp:
   802                     if ( last_virtual_button != 0 ) {
   803                         DO_MOUSE_UP (last_virtual_button);
   804                         last_virtual_button = 0;
   805                     }
   806                     else {
   807                         DO_MOUSE_UP (SDL_BUTTON_LEFT);
   808                     }
   809                     break;
   810 
   811                 case NSOtherMouseDown:
   812                 case NSRightMouseDown:
   813                     button = QZ_OtherMouseButtonToSDL([ event buttonNumber ]);
   814                     DO_MOUSE_DOWN (button);
   815                     break;
   816 
   817                 case NSOtherMouseUp:
   818                 case NSRightMouseUp:
   819                     button = QZ_OtherMouseButtonToSDL([ event buttonNumber ]);
   820                     DO_MOUSE_UP (button);
   821                     break;
   822 
   823                 case NSSystemDefined:
   824                     /*
   825                         Future: up to 32 "mouse" buttons can be handled.
   826                         if ([event subtype] == 7) {
   827                             unsigned int buttons;
   828                             buttons = [ event data2 ];
   829                     */
   830                     break;
   831                 case NSLeftMouseDragged:
   832                 case NSRightMouseDragged:
   833                 case NSOtherMouseDragged: /* usually middle mouse dragged */
   834                 case NSMouseMoved:
   835                     if ( grab_state == QZ_INVISIBLE_GRAB ) {
   836                 
   837                         /*
   838                             If input is grabbed+hidden, the cursor doesn't move,
   839                             so we have to call the lowlevel window server
   840                             function. This is less accurate but works OK.                         
   841                         */
   842                         CGMouseDelta dx1, dy1;
   843                         CGGetLastMouseDelta (&dx1, &dy1);
   844                         dx += dx1;
   845                         dy += dy1;
   846                     }
   847                     else {
   848                         
   849                         /*
   850                             Get the absolute mouse location. This is not the
   851                             mouse location after the currently processed event,
   852                             but the *current* mouse location, i.e. after all
   853                             pending events. This means that if there are
   854                             multiple mouse moved events in the queue, we make
   855                             multiple identical calls to SDL_PrivateMouseMotion(),
   856                             but that's no problem since the latter only
   857                             generates SDL events for nonzero movements. In my
   858                             experience on PBG4/10.4.8, this rarely happens anyway.
   859                         */
   860                         NSPoint p;
   861                         QZ_GetMouseLocation (this, &p);
   862                         SDL_PrivateMouseMotion (0, 0, p.x, p.y);
   863                     }
   864                     
   865                     /* 
   866                         Handle grab input+cursor visible by warping the cursor back
   867                         into the game window. This still generates a mouse moved event,
   868                         but not as a result of the warp (so it's in the right direction).
   869                     */
   870                     if ( grab_state == QZ_VISIBLE_GRAB && !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 
   894                         if (grab_state == QZ_INVISIBLE_GRAB)
   895                             /*The cursor has left the window even though it is
   896                               disassociated from the mouse (and therefore
   897                               shouldn't move): this can happen with Wacom
   898                               tablets, and it effectively breaks the grab, since
   899                               mouse down events now go to background
   900                               applications. The only possibility to avoid this
   901                               seems to be talking to the tablet driver
   902                               (AppleEvents) to constrain its mapped area to the
   903                               window, which may not be worth the effort. For
   904                               now, handle the condition more gracefully than
   905                               before by reassociating cursor and mouse until the
   906                               cursor enters the window again, making it obvious
   907                               to the user that the grab is broken.*/
   908                             CGAssociateMouseAndMouseCursorPosition (1);
   909 
   910                         QZ_UpdateCursor(this);
   911                     }
   912                     else
   913                     if ( isInGameWin && (SDL_GetAppState() & (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS)) == SDL_APPINPUTFOCUS ) {
   914                     
   915                         SDL_PrivateAppActive (1, SDL_APPMOUSEFOCUS);
   916 
   917                         QZ_UpdateCursor(this);
   918 
   919                         if (grab_state == QZ_INVISIBLE_GRAB) { /*see comment above*/
   920                             QZ_PrivateWarpCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2);
   921                             CGAssociateMouseAndMouseCursorPosition (0);
   922                         }
   923                     }
   924                     break;
   925                 case NSScrollWheel:
   926                     if ( isInGameWin ) {
   927                         float dy, dx;
   928                         Uint8 button;
   929                         dy = [ event deltaY ];
   930                         dx = [ event deltaX ];
   931                         if ( dy > 0.0 || dx > 0.0 ) /* Scroll up */
   932                             button = SDL_BUTTON_WHEELUP;
   933                         else /* Scroll down */
   934                             button = SDL_BUTTON_WHEELDOWN;
   935                         /* For now, wheel is sent as a quick down+up */
   936                         SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);
   937                         SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);
   938                     }
   939                     break;
   940                 case NSKeyUp:
   941                     QZ_DoKey (this, SDL_RELEASED, event);
   942                     break;
   943                 case NSKeyDown:
   944                     QZ_DoKey (this, SDL_PRESSED, event);
   945                     break;
   946                 case NSFlagsChanged:
   947                     break;
   948                 case NSAppKitDefined:
   949                     [ NSApp sendEvent:event ];
   950                     if ([ event subtype ] == NSApplicationActivatedEventType && (mode_flags & SDL_FULLSCREEN)) {
   951                         /* 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 */
   952                         SDL_Cursor *sdlc = SDL_GetCursor();
   953                         if (sdlc != NULL && sdlc->wm_cursor != NULL) {
   954                             [ sdlc->wm_cursor->nscursor set ];
   955                         }
   956                     }
   957                     break;
   958                     /* case NSApplicationDefined: break; */
   959                     /* case NSPeriodic: break; */
   960                     /* case NSCursorUpdate: break; */
   961                 default:
   962                     [ NSApp sendEvent:event ];
   963             }
   964         }
   965     } while (event != nil);
   966     
   967     /* handle accumulated mouse moved events */
   968     if (dx != 0 || dy != 0)
   969         SDL_PrivateMouseMotion (0, 1, dx, dy);
   970     
   971     [ pool release ];
   972 }
   973 
   974 void QZ_UpdateMouse (_THIS)
   975 {
   976     NSPoint p;
   977     QZ_GetMouseLocation (this, &p);
   978     SDL_PrivateAppActive (QZ_IsMouseInWindow (this), SDL_APPMOUSEFOCUS);
   979     SDL_PrivateMouseMotion (0, 0, p.x, p.y);
   980 }