src/video/quartz/SDL_QuartzEvents.m
author Ryan C. Gordon <icculus@icculus.org>
Tue, 27 May 2003 07:33:11 +0000
changeset 624 fb78cadbfeb8
parent 619 bf816ce70144
child 631 52864d66d168
permissions -rw-r--r--
More-than-three mouse button support for Quartz target.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002    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 <stdlib.h>	// For getenv()
    23 #include <IOKit/IOMessage.h> // For wake from sleep detection
    24 #include <IOKit/pwr_mgt/IOPMLib.h> // For wake from sleep detection
    25 #include "SDL_QuartzKeys.h"
    26 
    27 static void     QZ_InitOSKeymap (_THIS) {
    28     const void *KCHRPtr;
    29     UInt32 state;
    30     UInt32 value;
    31     int i;
    32     int world = SDLK_WORLD_0;
    33 
    34     for ( i=0; i<SDL_TABLESIZE(keymap); ++i )
    35         keymap[i] = SDLK_UNKNOWN;
    36 
    37     /* This keymap is almost exactly the same as the OS 9 one */
    38     keymap[QZ_ESCAPE] = SDLK_ESCAPE;
    39     keymap[QZ_F1] = SDLK_F1;
    40     keymap[QZ_F2] = SDLK_F2;
    41     keymap[QZ_F3] = SDLK_F3;
    42     keymap[QZ_F4] = SDLK_F4;
    43     keymap[QZ_F5] = SDLK_F5;
    44     keymap[QZ_F6] = SDLK_F6;
    45     keymap[QZ_F7] = SDLK_F7;
    46     keymap[QZ_F8] = SDLK_F8;
    47     keymap[QZ_F9] = SDLK_F9;
    48     keymap[QZ_F10] = SDLK_F10;
    49     keymap[QZ_F11] = SDLK_F11;
    50     keymap[QZ_F12] = SDLK_F12;
    51     keymap[QZ_PRINT] = SDLK_PRINT;
    52     keymap[QZ_SCROLLOCK] = SDLK_SCROLLOCK;
    53     keymap[QZ_PAUSE] = SDLK_PAUSE;
    54     keymap[QZ_POWER] = SDLK_POWER;
    55     keymap[QZ_BACKQUOTE] = SDLK_BACKQUOTE;
    56     keymap[QZ_1] = SDLK_1;
    57     keymap[QZ_2] = SDLK_2;
    58     keymap[QZ_3] = SDLK_3;
    59     keymap[QZ_4] = SDLK_4;
    60     keymap[QZ_5] = SDLK_5;
    61     keymap[QZ_6] = SDLK_6;
    62     keymap[QZ_7] = SDLK_7;
    63     keymap[QZ_8] = SDLK_8;
    64     keymap[QZ_9] = SDLK_9;
    65     keymap[QZ_0] = SDLK_0;
    66     keymap[QZ_MINUS] = SDLK_MINUS;
    67     keymap[QZ_EQUALS] = SDLK_EQUALS;
    68     keymap[QZ_BACKSPACE] = SDLK_BACKSPACE;
    69     keymap[QZ_INSERT] = SDLK_INSERT;
    70     keymap[QZ_HOME] = SDLK_HOME;
    71     keymap[QZ_PAGEUP] = SDLK_PAGEUP;
    72     keymap[QZ_NUMLOCK] = SDLK_NUMLOCK;
    73     keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS;
    74     keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE;
    75     keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
    76     keymap[QZ_TAB] = SDLK_TAB;
    77     keymap[QZ_q] = SDLK_q;
    78     keymap[QZ_w] = SDLK_w;
    79     keymap[QZ_e] = SDLK_e;
    80     keymap[QZ_r] = SDLK_r;
    81     keymap[QZ_t] = SDLK_t;
    82     keymap[QZ_y] = SDLK_y;
    83     keymap[QZ_u] = SDLK_u;
    84     keymap[QZ_i] = SDLK_i;
    85     keymap[QZ_o] = SDLK_o;
    86     keymap[QZ_p] = SDLK_p;
    87     keymap[QZ_LEFTBRACKET] = SDLK_LEFTBRACKET;
    88     keymap[QZ_RIGHTBRACKET] = SDLK_RIGHTBRACKET;
    89     keymap[QZ_BACKSLASH] = SDLK_BACKSLASH;
    90     keymap[QZ_DELETE] = SDLK_DELETE;
    91     keymap[QZ_END] = SDLK_END;
    92     keymap[QZ_PAGEDOWN] = SDLK_PAGEDOWN;
    93     keymap[QZ_KP7] = SDLK_KP7;
    94     keymap[QZ_KP8] = SDLK_KP8;
    95     keymap[QZ_KP9] = SDLK_KP9;
    96     keymap[QZ_KP_MINUS] = SDLK_KP_MINUS;
    97     keymap[QZ_CAPSLOCK] = SDLK_CAPSLOCK;
    98     keymap[QZ_a] = SDLK_a;
    99     keymap[QZ_s] = SDLK_s;
   100     keymap[QZ_d] = SDLK_d;
   101     keymap[QZ_f] = SDLK_f;
   102     keymap[QZ_g] = SDLK_g;
   103     keymap[QZ_h] = SDLK_h;
   104     keymap[QZ_j] = SDLK_j;
   105     keymap[QZ_k] = SDLK_k;
   106     keymap[QZ_l] = SDLK_l;
   107     keymap[QZ_SEMICOLON] = SDLK_SEMICOLON;
   108     keymap[QZ_QUOTE] = SDLK_QUOTE;
   109     keymap[QZ_RETURN] = SDLK_RETURN;
   110     keymap[QZ_KP4] = SDLK_KP4;
   111     keymap[QZ_KP5] = SDLK_KP5;
   112     keymap[QZ_KP6] = SDLK_KP6;
   113     keymap[QZ_KP_PLUS] = SDLK_KP_PLUS;
   114     keymap[QZ_LSHIFT] = SDLK_LSHIFT;
   115     keymap[QZ_z] = SDLK_z;
   116     keymap[QZ_x] = SDLK_x;
   117     keymap[QZ_c] = SDLK_c;
   118     keymap[QZ_v] = SDLK_v;
   119     keymap[QZ_b] = SDLK_b;
   120     keymap[QZ_n] = SDLK_n;
   121     keymap[QZ_m] = SDLK_m;
   122     keymap[QZ_COMMA] = SDLK_COMMA;
   123     keymap[QZ_PERIOD] = SDLK_PERIOD;
   124     keymap[QZ_SLASH] = SDLK_SLASH;
   125     keymap[QZ_UP] = SDLK_UP;
   126     keymap[QZ_KP1] = SDLK_KP1;
   127     keymap[QZ_KP2] = SDLK_KP2;
   128     keymap[QZ_KP3] = SDLK_KP3;
   129     keymap[QZ_KP_ENTER] = SDLK_KP_ENTER;
   130     keymap[QZ_LCTRL] = SDLK_LCTRL;
   131     keymap[QZ_LALT] = SDLK_LALT;
   132     keymap[QZ_LMETA] = SDLK_LMETA;
   133     keymap[QZ_SPACE] = SDLK_SPACE;
   134     keymap[QZ_LEFT] = SDLK_LEFT;
   135     keymap[QZ_DOWN] = SDLK_DOWN;
   136     keymap[QZ_RIGHT] = SDLK_RIGHT;
   137     keymap[QZ_KP0] = SDLK_KP0;
   138     keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD;
   139     keymap[QZ_IBOOK_ENTER] = SDLK_KP_ENTER;
   140     keymap[QZ_IBOOK_RIGHT] = SDLK_RIGHT;
   141     keymap[QZ_IBOOK_DOWN] = SDLK_DOWN;
   142     keymap[QZ_IBOOK_UP]      = SDLK_UP;
   143     keymap[QZ_IBOOK_LEFT] = SDLK_LEFT;
   144 
   145     /* 
   146         Up there we setup a static scancode->keysym map. However, it will not
   147         work very well on international keyboard. Hence we now query MacOS
   148         for its own keymap to adjust our own mapping table. However, this is
   149         basically only useful for ascii char keys. This is also the reason
   150         why we keep the static table, too.
   151      */
   152 
   153     /* Get a pointer to the systems cached KCHR */
   154     KCHRPtr = (void *)GetScriptManagerVariable(smKCHRCache);
   155     if (KCHRPtr)
   156     {
   157         /* Loop over all 127 possible scan codes */
   158         for (i = 0; i < 0x7F; i++)
   159         {
   160             /* We pretend a clean start to begin with (i.e. no dead keys active */
   161             state = 0;
   162 
   163             /* Now translate the key code to a key value */
   164             value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
   165 
   166             /* If the state become 0, it was a dead key. We need to translate again,
   167                 passing in the new state, to get the actual key value */
   168             if (state != 0)
   169                 value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
   170 
   171             /* Now we should have an ascii value, or 0. Try to figure out to which SDL symbol it maps */
   172             if (value >= 128)     /* Some non-ASCII char, map it to SDLK_WORLD_* */
   173                 keymap[i] = world++;
   174             else if (value >= 32)     /* non-control ASCII char */
   175                 keymap[i] = value;
   176         }
   177     }
   178 
   179     /* 
   180         The keypad codes are re-setup here, because the loop above cannot
   181         distinguish between a key on the keypad and a regular key. We maybe
   182         could get around this problem in another fashion: NSEvent's flags
   183         include a "NSNumericPadKeyMask" bit; we could check that and modify
   184         the symbol we return on the fly. However, this flag seems to exhibit
   185         some weird behaviour related to the num lock key
   186     */
   187     keymap[QZ_KP0] = SDLK_KP0;
   188     keymap[QZ_KP1] = SDLK_KP1;
   189     keymap[QZ_KP2] = SDLK_KP2;
   190     keymap[QZ_KP3] = SDLK_KP3;
   191     keymap[QZ_KP4] = SDLK_KP4;
   192     keymap[QZ_KP5] = SDLK_KP5;
   193     keymap[QZ_KP6] = SDLK_KP6;
   194     keymap[QZ_KP7] = SDLK_KP7;
   195     keymap[QZ_KP8] = SDLK_KP8;
   196     keymap[QZ_KP9] = SDLK_KP9;
   197     keymap[QZ_KP_MINUS] = SDLK_KP_MINUS;
   198     keymap[QZ_KP_PLUS] = SDLK_KP_PLUS;
   199     keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD;
   200     keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS;
   201     keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE;
   202     keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
   203     keymap[QZ_KP_ENTER] = SDLK_KP_ENTER;
   204 }
   205 
   206 static void QZ_DoKey (_THIS, int state, NSEvent *event) {
   207 
   208     NSString *chars;
   209     unsigned int numChars;
   210     SDL_keysym key;
   211     
   212     /* 
   213         A key event can contain multiple characters,
   214         or no characters at all. In most cases, it
   215         will contain a single character. If it contains
   216         0 characters, we'll use 0 as the unicode. If it
   217         contains multiple characters, we'll use 0 as
   218         the scancode/keysym.
   219     */
   220     chars = [ event characters ];
   221     numChars = [ chars length ];
   222 
   223     if (numChars == 1) {
   224 
   225         key.scancode = [ event keyCode ];
   226         key.sym      = keymap [ key.scancode ];
   227         key.unicode  = [ chars characterAtIndex:0 ];
   228         key.mod      = KMOD_NONE;
   229 
   230         SDL_PrivateKeyboard (state, &key);
   231     }
   232     else if (numChars == 0) {
   233       
   234         key.scancode = [ event keyCode ];
   235         key.sym      = keymap [ key.scancode ];
   236         key.unicode  = 0;
   237         key.mod      = KMOD_NONE;
   238 
   239         SDL_PrivateKeyboard (state, &key);
   240     }
   241     else /* (numChars > 1) */ {
   242       
   243         int i;
   244         for (i = 0; i < numChars; i++) {
   245 
   246             key.scancode = 0;
   247             key.sym      = 0;
   248             key.unicode  = [ chars characterAtIndex:i];
   249             key.mod      = KMOD_NONE;
   250 
   251             SDL_PrivateKeyboard (state, &key);
   252         }
   253     }
   254 }
   255 
   256 static void QZ_DoModifiers (_THIS, unsigned int newMods) {
   257 
   258     const int mapping[] = { SDLK_CAPSLOCK, SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA } ;
   259 
   260     int i;
   261     int bit;
   262     SDL_keysym key;
   263 
   264     key.scancode = 0;
   265     key.sym         = SDLK_UNKNOWN;
   266     key.unicode     = 0;
   267     key.mod         = KMOD_NONE;
   268 
   269     /* Iterate through the bits, testing each against the current modifiers */
   270     for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
   271 
   272         unsigned int currentMask, newMask;
   273 
   274         currentMask = current_mods & bit;
   275         newMask     = newMods & bit;
   276 
   277         if ( currentMask &&
   278              currentMask != newMask ) {     /* modifier up event */
   279 
   280              key.sym = mapping[i];
   281              /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
   282              if (bit == NSAlphaShiftKeyMask)
   283              SDL_PrivateKeyboard (SDL_PRESSED, &key);
   284              SDL_PrivateKeyboard (SDL_RELEASED, &key);
   285         }
   286         else if ( newMask &&
   287                   currentMask != newMask ) {     /* modifier down event */
   288         
   289              key.sym = mapping[i];
   290              SDL_PrivateKeyboard (SDL_PRESSED, &key);
   291              /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
   292              if (bit == NSAlphaShiftKeyMask)
   293                   SDL_PrivateKeyboard (SDL_RELEASED, &key);
   294         }
   295     }
   296 
   297     current_mods = newMods;
   298 }
   299 
   300 static void QZ_DoActivate (_THIS)
   301 {
   302     in_foreground = YES;
   303     
   304     /* Hide the cursor if it was hidden by SDL_ShowCursor() */
   305     if (!cursor_visible && !cursor_hidden) {
   306         HideCursor ();
   307         cursor_hidden = YES;
   308     }
   309 
   310     /* Regrab input, only if it was previously grabbed */
   311     if ( current_grab_mode == SDL_GRAB_ON ) {
   312         
   313         /* Restore cursor location if input was grabbed */
   314         QZ_PrivateWarpCursor (this, cursor_loc.x, cursor_loc.y);
   315         QZ_ChangeGrabState (this, QZ_ENABLE_GRAB);
   316     }
   317 
   318     SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS);
   319 }
   320 
   321 static void QZ_DoDeactivate (_THIS) {
   322 
   323     in_foreground = NO;
   324 
   325     /* Get the current cursor location, for restore on activate */
   326     cursor_loc = [ NSEvent mouseLocation ]; /* global coordinates */
   327     if (qz_window)
   328         QZ_PrivateGlobalToLocal (this, &cursor_loc);
   329     QZ_PrivateCocoaToSDL (this, &cursor_loc);
   330     
   331     /* Reassociate mouse and cursor */
   332     CGAssociateMouseAndMouseCursorPosition (1);
   333 
   334     /* Show the cursor if it was hidden by SDL_ShowCursor() */
   335     if (!cursor_visible && cursor_hidden) {
   336         ShowCursor ();
   337         cursor_hidden = NO;
   338     }
   339 
   340     SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS);
   341 }
   342 
   343 void QZ_SleepNotificationHandler (void * refcon,
   344                                   io_service_t service,
   345                                   natural_t messageType,
   346                                   void * messageArgument )
   347 {
   348      SDL_VideoDevice *this = (SDL_VideoDevice*)refcon;
   349      
   350      switch(messageType)
   351      {
   352          case kIOMessageSystemWillSleep:
   353              IOAllowPowerChange(power_connection, (long) messageArgument);
   354              break;
   355          case kIOMessageCanSystemSleep:
   356              IOAllowPowerChange(power_connection, (long) messageArgument);
   357              break;
   358          case kIOMessageSystemHasPoweredOn:
   359 			/* awake */
   360             SDL_PrivateExpose();
   361             break;
   362      }
   363 }
   364 
   365 static void QZ_RegisterForSleepNotifications (_THIS)
   366 {
   367      CFRunLoopSourceRef rls;
   368      IONotificationPortRef thePortRef;
   369      io_object_t notifier;
   370 
   371      power_connection = IORegisterForSystemPower (this, &thePortRef, QZ_SleepNotificationHandler, &notifier);
   372 
   373      if (power_connection == 0)
   374          NSLog(@"SDL: QZ_SleepNotificationHandler() IORegisterForSystemPower failed.");
   375 
   376      rls = IONotificationPortGetRunLoopSource (thePortRef);
   377      CFRunLoopAddSource (CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
   378      CFRelease (rls);
   379 }
   380 
   381 
   382 // Try to map Quartz mouse buttons to SDL's lingo...
   383 static int QZ_OtherMouseButtonToSDL(int button)
   384 {
   385     switch (button)
   386     {
   387         case 0:
   388             return(SDL_BUTTON_LEFT);   // 1
   389         case 1:
   390             return(SDL_BUTTON_RIGHT);  // 3
   391         case 2:
   392             return(SDL_BUTTON_MIDDLE); // 2
   393     }
   394 
   395     // >= 3: skip 4 & 5, since those are the SDL mousewheel buttons.
   396     return(button + 3);
   397 }
   398 
   399 
   400 static void QZ_PumpEvents (_THIS)
   401 {
   402     int firstMouseEvent;
   403     CGMouseDelta dx, dy;
   404 
   405     NSDate *distantPast;
   406     NSEvent *event;
   407     NSRect winRect;
   408     NSRect titleBarRect;
   409     NSAutoreleasePool *pool;
   410 
   411     /* Update activity every five seconds to prevent screensaver. --ryan. */
   412     static Uint32 screensaverTicks = 0;
   413     Uint32 nowTicks = SDL_GetTicks();
   414     if ((nowTicks - screensaverTicks) > 5000)
   415     {
   416         UpdateSystemActivity(UsrActivity);
   417         screensaverTicks = nowTicks;
   418     }
   419 
   420     pool = [ [ NSAutoreleasePool alloc ] init ];
   421     distantPast = [ NSDate distantPast ];
   422 
   423     winRect = NSMakeRect (0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h);
   424     titleBarRect = NSMakeRect (0, SDL_VideoSurface->h, SDL_VideoSurface->w,
   425                                 SDL_VideoSurface->h + 22);
   426     
   427     /* send the first mouse event in absolute coordinates */
   428     firstMouseEvent = 1;
   429     
   430     /* accumulate any additional mouse moved events into one SDL mouse event */
   431     dx = 0;
   432     dy = 0;
   433     
   434     do {
   435     
   436         /* Poll for an event. This will not block */
   437         event = [ NSApp nextEventMatchingMask:NSAnyEventMask
   438                                     untilDate:distantPast
   439                                     inMode: NSDefaultRunLoopMode dequeue:YES ];
   440         if (event != nil) {
   441 
   442             int button;
   443             unsigned int type;
   444             BOOL isForGameWin;
   445             BOOL isInGameWin;
   446             
   447             #define DO_MOUSE_DOWN(button) do {                                               \
   448                             if ( in_foreground ) {                                           \
   449                                 if ( isInGameWin ) {                                         \
   450                                     SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);      \
   451                                     expect_mouse_up |= 1<<button;                            \
   452                                 }                                                            \
   453                             }                                                                \
   454                             else {                                                           \
   455                                 QZ_DoActivate (this);                                        \
   456                             }                                                                \
   457                             [ NSApp sendEvent:event ];                                       \
   458             } while(0)
   459             
   460             #define DO_MOUSE_UP(button) do {                                            \
   461                             if ( expect_mouse_up & (1<<button) ) {                      \
   462                                 SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);    \
   463                                 expect_mouse_up &= ~(1<<button);                        \
   464                             }                                                           \
   465                             [ NSApp sendEvent:event ];                                  \
   466             } while(0)
   467             
   468             type = [ event type ];
   469             isForGameWin = (qz_window == [ event window ]);
   470             isInGameWin = NSPointInRect([event locationInWindow], winRect);
   471             switch (type) {
   472                 case NSLeftMouseDown:
   473                     if ( getenv("SDL_HAS3BUTTONMOUSE") ) {
   474                         DO_MOUSE_DOWN (SDL_BUTTON_LEFT);
   475                     } else {
   476                         if ( NSCommandKeyMask & current_mods ) {
   477                             last_virtual_button = SDL_BUTTON_RIGHT;
   478                             DO_MOUSE_DOWN (SDL_BUTTON_RIGHT);
   479                         }
   480                         else if ( NSAlternateKeyMask & current_mods ) {
   481                             last_virtual_button = SDL_BUTTON_MIDDLE;
   482                             DO_MOUSE_DOWN (SDL_BUTTON_MIDDLE);
   483                         }
   484                         else {
   485                             DO_MOUSE_DOWN (SDL_BUTTON_LEFT);
   486                         }
   487                     }
   488                     break;
   489 
   490                 case NSLeftMouseUp:
   491                     if ( last_virtual_button != 0 ) {
   492                         DO_MOUSE_UP (last_virtual_button);
   493                         last_virtual_button = 0;
   494                     }
   495                     else {
   496                         DO_MOUSE_UP (SDL_BUTTON_LEFT);
   497                     }
   498                     break;
   499 
   500                 case NSOtherMouseDown:
   501                 case NSRightMouseDown:
   502                     button = QZ_OtherMouseButtonToSDL([ event buttonNumber ]);
   503                     DO_MOUSE_DOWN (button);
   504                     break;
   505 
   506                 case NSOtherMouseUp:
   507                 case NSRightMouseUp:
   508                     button = QZ_OtherMouseButtonToSDL([ event buttonNumber ]);
   509                     DO_MOUSE_UP (button);
   510                     break;
   511 
   512                 case NSSystemDefined:
   513                     /*
   514                         Future: up to 32 "mouse" buttons can be handled.
   515                         if ([event subtype] == 7) {
   516                             unsigned int buttons;
   517                             buttons = [ event data2 ];
   518                     */
   519                     break;
   520                 case NSLeftMouseDragged:
   521                 case NSRightMouseDragged:
   522                 case NSOtherMouseDragged: /* usually middle mouse dragged */
   523                 case NSMouseMoved:
   524                     if ( grab_state == QZ_INVISIBLE_GRAB ) {
   525                 
   526                         /*
   527                             If input is grabbed+hidden, the cursor doesn't move,
   528                             so we have to call the lowlevel window server
   529                             function. This is less accurate but works OK.                         
   530                         */
   531                         CGMouseDelta dx1, dy1;
   532                         CGGetLastMouseDelta (&dx1, &dy1);
   533                         dx += dx1;
   534                         dy += dy1;
   535                     }
   536                     else if (firstMouseEvent) {
   537                         
   538                         /*
   539                             Get the first mouse event in a possible
   540                             sequence of mouse moved events. Since we
   541                             use absolute coordinates, this serves to
   542                             compensate any inaccuracy in deltas, and
   543                             provides the first known mouse position,
   544                             since everything after this uses deltas
   545                         */
   546                         NSPoint p = [ event locationInWindow ];
   547                         QZ_PrivateCocoaToSDL (this, &p);
   548                         SDL_PrivateMouseMotion (0, 0, p.x, p.y);
   549                         firstMouseEvent = 0;
   550                     }
   551                     else {
   552                     
   553                         /*
   554                             Get the amount moved since the last drag or move event,
   555                             add it on for one big move event at the end.
   556                         */
   557                         dx += [ event deltaX ];
   558                         dy += [ event deltaY ];
   559                     }
   560                     
   561                     /* 
   562                         Handle grab input+cursor visible by warping the cursor back
   563                         into the game window. This still generates a mouse moved event,
   564                         but not as a result of the warp (so it's in the right direction).
   565                     */
   566                     if ( grab_state == QZ_VISIBLE_GRAB &&
   567                          !isInGameWin ) {
   568                        
   569                         NSPoint p = [ event locationInWindow ]; 
   570                         QZ_PrivateCocoaToSDL (this, &p);
   571 
   572                         if ( p.x < 0.0 ) 
   573                             p.x = 0.0;
   574                         
   575                         if ( p.y < 0.0 ) 
   576                             p.y = 0.0;
   577                         
   578                         if ( p.x >= winRect.size.width ) 
   579                             p.x = winRect.size.width-1;
   580                         
   581                         if ( p.y >= winRect.size.height ) 
   582                             p.y = winRect.size.height-1;
   583                         
   584                         QZ_PrivateWarpCursor (this, p.x, p.y);
   585                     }
   586                     break;
   587                 case NSScrollWheel:
   588                     if ( isInGameWin ) {
   589                         float dy;
   590                         Uint8 button;
   591                         dy = [ event deltaY ];
   592                         if ( dy > 0.0 ) /* Scroll up */
   593                             button = SDL_BUTTON_WHEELUP;
   594                         else /* Scroll down */
   595                             button = SDL_BUTTON_WHEELDOWN;
   596                         /* For now, wheel is sent as a quick down+up */
   597                         SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);
   598                         SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);
   599                     }
   600                     break;
   601                 case NSKeyUp:
   602                     QZ_DoKey (this, SDL_RELEASED, event);
   603                     break;
   604                 case NSKeyDown:
   605                     QZ_DoKey (this, SDL_PRESSED, event);
   606                     break;
   607                 case NSFlagsChanged:
   608                     QZ_DoModifiers(this, [ event modifierFlags ] );
   609                     break;
   610                 case NSAppKitDefined:
   611                     switch ( [ event subtype ] ) {
   612                         case NSApplicationActivatedEventType:
   613                             QZ_DoActivate (this);
   614                             break;
   615                         case NSApplicationDeactivatedEventType:
   616                             QZ_DoDeactivate (this);
   617                             break;
   618                     }
   619                     [ NSApp sendEvent:event ];
   620                     break;
   621                     /* case NSApplicationDefined: break; */
   622                     /* case NSPeriodic: break; */
   623                     /* case NSCursorUpdate: break; */
   624                 default:
   625                     [ NSApp sendEvent:event ];
   626             }
   627         }
   628     } while (event != nil);
   629     
   630     /* handle accumulated mouse moved events */
   631     if (dx != 0 || dy != 0)
   632         SDL_PrivateMouseMotion (0, 1, dx, dy);
   633     
   634     [ pool release ];
   635 }