src/video/quartz/SDL_QuartzEvents.m
author Sam Lantinga
Sun, 22 Jun 2014 11:08:33 -0700
branchSDL-1.2
changeset 8911 606600f0f6d7
parent 6150 5f0b3693ab60
permissions -rw-r--r--
Fixed bug 2560 - Crash on any input

Alex Marshall

On all of my OSX machines running 10.9 (I posted OS as Mac OS X (All) due to there not being a 10.9 selection), I get a crash on any input using SDL 1.2. I've had this issue in both HG 1.2 and final release 1.2 on the website.

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