src/video/quartz/SDL_QuartzEvents.m
author Ryan C. Gordon <icculus@icculus.org>
Mon, 04 Jun 2007 11:22:23 +0000
branchSDL-1.2
changeset 3952 e3c28caea46d
parent 3936 c5c3c772f5aa
child 4009 1146681dbb74
permissions -rw-r--r--
Better handling of multiple queued Cocoa mouse events. Thanks to Christian
Walther for the patch.

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