src/video/quartz/SDL_QuartzEvents.m
author Sam Lantinga <slouken@libsdl.org>
Sat, 05 Oct 2002 05:11:20 +0000
changeset 502 80a3d09bab29
parent 501 74262d2647ca
child 511 79c189f5bd76
permissions -rw-r--r--
Fixed mouse wheel events on MacOS X
     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 "SDL_QuartzKeys.h"
    23 
    24 static void     QZ_InitOSKeymap (_THIS) {
    25     const void *KCHRPtr;
    26     UInt32 state;
    27     UInt32 value;
    28     int i;
    29     int world = SDLK_WORLD_0;
    30 
    31     for ( i=0; i<SDL_TABLESIZE(keymap); ++i )
    32         keymap[i] = SDLK_UNKNOWN;
    33 
    34     /* This keymap is almost exactly the same as the OS 9 one */
    35     keymap[QZ_ESCAPE] = SDLK_ESCAPE;
    36     keymap[QZ_F1] = SDLK_F1;
    37     keymap[QZ_F2] = SDLK_F2;
    38     keymap[QZ_F3] = SDLK_F3;
    39     keymap[QZ_F4] = SDLK_F4;
    40     keymap[QZ_F5] = SDLK_F5;
    41     keymap[QZ_F6] = SDLK_F6;
    42     keymap[QZ_F7] = SDLK_F7;
    43     keymap[QZ_F8] = SDLK_F8;
    44     keymap[QZ_F9] = SDLK_F9;
    45     keymap[QZ_F10] = SDLK_F10;
    46     keymap[QZ_F11] = SDLK_F11;
    47     keymap[QZ_F12] = SDLK_F12;
    48     keymap[QZ_PRINT] = SDLK_PRINT;
    49     keymap[QZ_SCROLLOCK] = SDLK_SCROLLOCK;
    50     keymap[QZ_PAUSE] = SDLK_PAUSE;
    51     keymap[QZ_POWER] = SDLK_POWER;
    52     keymap[QZ_BACKQUOTE] = SDLK_BACKQUOTE;
    53     keymap[QZ_1] = SDLK_1;
    54     keymap[QZ_2] = SDLK_2;
    55     keymap[QZ_3] = SDLK_3;
    56     keymap[QZ_4] = SDLK_4;
    57     keymap[QZ_5] = SDLK_5;
    58     keymap[QZ_6] = SDLK_6;
    59     keymap[QZ_7] = SDLK_7;
    60     keymap[QZ_8] = SDLK_8;
    61     keymap[QZ_9] = SDLK_9;
    62     keymap[QZ_0] = SDLK_0;
    63     keymap[QZ_MINUS] = SDLK_MINUS;
    64     keymap[QZ_EQUALS] = SDLK_EQUALS;
    65     keymap[QZ_BACKSPACE] = SDLK_BACKSPACE;
    66     keymap[QZ_INSERT] = SDLK_INSERT;
    67     keymap[QZ_HOME] = SDLK_HOME;
    68     keymap[QZ_PAGEUP] = SDLK_PAGEUP;
    69     keymap[QZ_NUMLOCK] = SDLK_NUMLOCK;
    70     keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS;
    71     keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE;
    72     keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
    73     keymap[QZ_TAB] = SDLK_TAB;
    74     keymap[QZ_q] = SDLK_q;
    75     keymap[QZ_w] = SDLK_w;
    76     keymap[QZ_e] = SDLK_e;
    77     keymap[QZ_r] = SDLK_r;
    78     keymap[QZ_t] = SDLK_t;
    79     keymap[QZ_y] = SDLK_y;
    80     keymap[QZ_u] = SDLK_u;
    81     keymap[QZ_i] = SDLK_i;
    82     keymap[QZ_o] = SDLK_o;
    83     keymap[QZ_p] = SDLK_p;
    84     keymap[QZ_LEFTBRACKET] = SDLK_LEFTBRACKET;
    85     keymap[QZ_RIGHTBRACKET] = SDLK_RIGHTBRACKET;
    86     keymap[QZ_BACKSLASH] = SDLK_BACKSLASH;
    87     keymap[QZ_DELETE] = SDLK_DELETE;
    88     keymap[QZ_END] = SDLK_END;
    89     keymap[QZ_PAGEDOWN] = SDLK_PAGEDOWN;
    90     keymap[QZ_KP7] = SDLK_KP7;
    91     keymap[QZ_KP8] = SDLK_KP8;
    92     keymap[QZ_KP9] = SDLK_KP9;
    93     keymap[QZ_KP_MINUS] = SDLK_KP_MINUS;
    94     keymap[QZ_CAPSLOCK] = SDLK_CAPSLOCK;
    95     keymap[QZ_a] = SDLK_a;
    96     keymap[QZ_s] = SDLK_s;
    97     keymap[QZ_d] = SDLK_d;
    98     keymap[QZ_f] = SDLK_f;
    99     keymap[QZ_g] = SDLK_g;
   100     keymap[QZ_h] = SDLK_h;
   101     keymap[QZ_j] = SDLK_j;
   102     keymap[QZ_k] = SDLK_k;
   103     keymap[QZ_l] = SDLK_l;
   104     keymap[QZ_SEMICOLON] = SDLK_SEMICOLON;
   105     keymap[QZ_QUOTE] = SDLK_QUOTE;
   106     keymap[QZ_RETURN] = SDLK_RETURN;
   107     keymap[QZ_KP4] = SDLK_KP4;
   108     keymap[QZ_KP5] = SDLK_KP5;
   109     keymap[QZ_KP6] = SDLK_KP6;
   110     keymap[QZ_KP_PLUS] = SDLK_KP_PLUS;
   111     keymap[QZ_LSHIFT] = SDLK_LSHIFT;
   112     keymap[QZ_z] = SDLK_z;
   113     keymap[QZ_x] = SDLK_x;
   114     keymap[QZ_c] = SDLK_c;
   115     keymap[QZ_v] = SDLK_v;
   116     keymap[QZ_b] = SDLK_b;
   117     keymap[QZ_n] = SDLK_n;
   118     keymap[QZ_m] = SDLK_m;
   119     keymap[QZ_COMMA] = SDLK_COMMA;
   120     keymap[QZ_PERIOD] = SDLK_PERIOD;
   121     keymap[QZ_SLASH] = SDLK_SLASH;
   122     keymap[QZ_UP] = SDLK_UP;
   123     keymap[QZ_KP1] = SDLK_KP1;
   124     keymap[QZ_KP2] = SDLK_KP2;
   125     keymap[QZ_KP3] = SDLK_KP3;
   126     keymap[QZ_KP_ENTER] = SDLK_KP_ENTER;
   127     keymap[QZ_LCTRL] = SDLK_LCTRL;
   128     keymap[QZ_LALT] = SDLK_LALT;
   129     keymap[QZ_LMETA] = SDLK_LMETA;
   130     keymap[QZ_SPACE] = SDLK_SPACE;
   131     keymap[QZ_LEFT] = SDLK_LEFT;
   132     keymap[QZ_DOWN] = SDLK_DOWN;
   133     keymap[QZ_RIGHT] = SDLK_RIGHT;
   134     keymap[QZ_KP0] = SDLK_KP0;
   135     keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD;
   136     keymap[QZ_IBOOK_ENTER] = SDLK_KP_ENTER;
   137     keymap[QZ_IBOOK_RIGHT] = SDLK_RIGHT;
   138     keymap[QZ_IBOOK_DOWN] = SDLK_DOWN;
   139     keymap[QZ_IBOOK_UP]      = SDLK_UP;
   140     keymap[QZ_IBOOK_LEFT] = SDLK_LEFT;
   141 
   142     /* 
   143         Up there we setup a static scancode->keysym map. However, it will not
   144         work very well on international keyboard. Hence we now query MacOS
   145         for its own keymap to adjust our own mapping table. However, this is
   146         basically only useful for ascii char keys. This is also the reason
   147         why we keep the static table, too.
   148      */
   149 
   150     /* Get a pointer to the systems cached KCHR */
   151     KCHRPtr = (void *)GetScriptManagerVariable(smKCHRCache);
   152     if (KCHRPtr)
   153     {
   154         /* Loop over all 127 possible scan codes */
   155         for (i = 0; i < 0x7F; i++)
   156         {
   157             /* We pretend a clean start to begin with (i.e. no dead keys active */
   158             state = 0;
   159 
   160             /* Now translate the key code to a key value */
   161             value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
   162 
   163             /* If the state become 0, it was a dead key. We need to translate again,
   164                 passing in the new state, to get the actual key value */
   165             if (state != 0)
   166                 value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
   167 
   168             /* Now we should have an ascii value, or 0. Try to figure out to which SDL symbol it maps */
   169             if (value >= 128)     /* Some non-ASCII char, map it to SDLK_WORLD_* */
   170                 keymap[i] = world++;
   171             else if (value >= 32)     /* non-control ASCII char */
   172                 keymap[i] = value;
   173         }
   174     }
   175 
   176     /* 
   177         The keypad codes are re-setup here, because the loop above cannot
   178         distinguish between a key on the keypad and a regular key. We maybe
   179         could get around this problem in another fashion: NSEvent's flags
   180         include a "NSNumericPadKeyMask" bit; we could check that and modify
   181         the symbol we return on the fly. However, this flag seems to exhibit
   182         some weird behaviour related to the num lock key
   183     */
   184     keymap[QZ_KP0] = SDLK_KP0;
   185     keymap[QZ_KP1] = SDLK_KP1;
   186     keymap[QZ_KP2] = SDLK_KP2;
   187     keymap[QZ_KP3] = SDLK_KP3;
   188     keymap[QZ_KP4] = SDLK_KP4;
   189     keymap[QZ_KP5] = SDLK_KP5;
   190     keymap[QZ_KP6] = SDLK_KP6;
   191     keymap[QZ_KP7] = SDLK_KP7;
   192     keymap[QZ_KP8] = SDLK_KP8;
   193     keymap[QZ_KP9] = SDLK_KP9;
   194     keymap[QZ_KP_MINUS] = SDLK_KP_MINUS;
   195     keymap[QZ_KP_PLUS] = SDLK_KP_PLUS;
   196     keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD;
   197     keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS;
   198     keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE;
   199     keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
   200     keymap[QZ_KP_ENTER] = SDLK_KP_ENTER;
   201 }
   202 
   203 static void QZ_DoKey (_THIS, int state, NSEvent *event) {
   204 
   205     NSString *chars;
   206     int i;
   207     SDL_keysym key;
   208 
   209     /* 
   210         An event can contain multiple characters
   211         I'll ignore this fact for now, since there 
   212         is only one virtual key code per event, so
   213         no good way to handle this.
   214     */
   215     chars = [ event characters ];
   216     for (i =0; i < 1 /*[ chars length ] */; i++) {
   217 
   218         key.scancode = [ event keyCode ];
   219         key.sym         = keymap [ key.scancode ];
   220         key.unicode     = [ chars characterAtIndex:i];
   221         key.mod         = KMOD_NONE;
   222 
   223         SDL_PrivateKeyboard (state, &key);
   224     }
   225 }
   226 
   227 static void QZ_DoModifiers (_THIS, unsigned int newMods) {
   228 
   229     const int mapping[] = { SDLK_CAPSLOCK, SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA } ;
   230 
   231     int i;
   232     int bit;
   233     SDL_keysym key;
   234 
   235     key.scancode = 0;
   236     key.sym         = SDLK_UNKNOWN;
   237     key.unicode     = 0;
   238     key.mod         = KMOD_NONE;
   239 
   240     /* Iterate through the bits, testing each against the current modifiers */
   241     for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
   242 
   243         unsigned int currentMask, newMask;
   244 
   245         currentMask = current_mods & bit;
   246         newMask     = newMods & bit;
   247 
   248         if ( currentMask &&
   249              currentMask != newMask ) {     /* modifier up event */
   250 
   251              key.sym = mapping[i];
   252              /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
   253              if (bit == NSAlphaShiftKeyMask)
   254              SDL_PrivateKeyboard (SDL_PRESSED, &key);
   255              SDL_PrivateKeyboard (SDL_RELEASED, &key);
   256         }
   257         else if ( newMask &&
   258                   currentMask != newMask ) {     /* modifier down event */
   259         
   260              key.sym = mapping[i];
   261              SDL_PrivateKeyboard (SDL_PRESSED, &key);
   262              /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
   263              if (bit == NSAlphaShiftKeyMask)
   264                   SDL_PrivateKeyboard (SDL_RELEASED, &key);
   265         }
   266     }
   267 
   268     current_mods = newMods;
   269 }
   270 
   271 static void QZ_DoActivate (_THIS)
   272 {
   273     in_foreground = YES;
   274 
   275     /* Regrab the mouse, only if it was previously grabbed */
   276     if ( current_grab_mode == SDL_GRAB_ON ) {
   277         QZ_WarpWMCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2);
   278         CGAssociateMouseAndMouseCursorPosition (0);
   279     }
   280 
   281     /* Hide the mouse cursor if inside the app window */
   282     if (!QZ_cursor_visible) {
   283         HideCursor ();
   284     }
   285 
   286     SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS);
   287 }
   288 
   289 static void QZ_DoDeactivate (_THIS) {
   290 
   291     in_foreground = NO;
   292 
   293     /* Ungrab mouse if it is grabbed */
   294     if ( current_grab_mode == SDL_GRAB_ON ) {
   295         CGAssociateMouseAndMouseCursorPosition (1);
   296     }
   297 
   298     /* Show the mouse cursor */
   299     if (!QZ_cursor_visible) {
   300         ShowCursor ();
   301     }
   302 
   303     SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS);
   304 }
   305 
   306 static void QZ_PumpEvents (_THIS)
   307 {
   308     int firstMouseEvent;
   309     CGMouseDelta dx, dy;
   310 
   311     NSDate *distantPast;
   312     NSEvent *event;
   313     NSRect winRect;
   314     NSRect titleBarRect;
   315     NSAutoreleasePool *pool;
   316 
   317     pool = [ [ NSAutoreleasePool alloc ] init ];
   318     distantPast = [ NSDate distantPast ];
   319 
   320     winRect = NSMakeRect (0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h);
   321     titleBarRect = NSMakeRect (0, SDL_VideoSurface->h, SDL_VideoSurface->w,
   322                                 SDL_VideoSurface->h + 22);
   323     
   324     /* send the first mouse event in absolute coordinates */
   325     firstMouseEvent = 1;
   326     
   327     /* accumulate any additional mouse moved events into one SDL mouse event */
   328     dx = 0;
   329     dy = 0;
   330     
   331     do {
   332     
   333         /* Poll for an event. This will not block */
   334         event = [ NSApp nextEventMatchingMask:NSAnyEventMask
   335                                     untilDate:distantPast
   336                                     inMode: NSDefaultRunLoopMode dequeue:YES ];
   337     
   338         if (event != nil) {
   339 
   340             unsigned int type;
   341             BOOL isForGameWin;
   342     
   343             #define DO_MOUSE_DOWN(button, sendToWindow) do {                                 \
   344                             if ( in_foreground ) {                                           \
   345                                 if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) ||           \
   346                                     NSPointInRect([event locationInWindow], winRect) )       \
   347                                         SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);  \
   348                             }                                                                \
   349                             else {                                                           \
   350                                 QZ_DoActivate (this);                                        \
   351                             }                                                                \
   352                             [ NSApp sendEvent:event ];                                       \
   353             } while(0)
   354             
   355             #define DO_MOUSE_UP(button, sendToWindow) do {                      \
   356                             if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) ||  \
   357                     !NSPointInRect([event locationInWindow], titleBarRect) )    \
   358                         SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);    \
   359                             [ NSApp sendEvent:event ];                          \
   360             } while(0)
   361             
   362             type = [ event type ];
   363             isForGameWin = (qz_window == [ event window ]);
   364             switch (type) {
   365             
   366                 case NSLeftMouseDown:
   367                     if ( NSCommandKeyMask & current_mods ) {
   368                         last_virtual_button = 3;
   369                         DO_MOUSE_DOWN (3, 0);
   370                     }
   371                     else if ( NSAlternateKeyMask & current_mods ) {
   372                         last_virtual_button = 2;
   373                         DO_MOUSE_DOWN (2, 0);
   374                     }
   375                     else {
   376                         DO_MOUSE_DOWN (1, 1);
   377                     }
   378                     break;
   379                 case NSOtherMouseDown: DO_MOUSE_DOWN (2, 0); break;
   380                 case NSRightMouseDown: DO_MOUSE_DOWN (3, 0); break;
   381                 case NSLeftMouseUp:
   382                     if ( last_virtual_button != 0 ) {
   383                         DO_MOUSE_UP (last_virtual_button, 0);
   384                         last_virtual_button = 0;
   385                     }
   386                     else {
   387                         DO_MOUSE_UP (1, 1);
   388                     }
   389                     break;
   390                 case NSOtherMouseUp:   DO_MOUSE_UP (2, 0);     break;
   391                 case NSRightMouseUp:   DO_MOUSE_UP (3, 0);     break;
   392                 case NSSystemDefined:
   393                     /*
   394                         Future: up to 32 "mouse" buttons can be handled.
   395                         if ([event subtype] == 7) {
   396                             unsigned int buttons;
   397                             buttons = [ event data2 ];
   398                     */
   399                     break;
   400                 case NSLeftMouseDragged:
   401                 case NSRightMouseDragged:
   402                 case NSOtherMouseDragged: /* usually middle mouse dragged */
   403                 case NSMouseMoved:
   404                     if (current_grab_mode == SDL_GRAB_ON) {
   405                 
   406                         /*
   407                             If input is grabbed, the cursor doesn't move,
   408                             so we have to call the lowlevel window server
   409                             function. This is less accurate but works OK.                         
   410                         */
   411                         CGMouseDelta dx1, dy1;
   412                         CGGetLastMouseDelta (&dx1, &dy1);
   413                         dx += dx1;
   414                         dy += dy1;
   415                     }
   416                     else if (warp_flag) {
   417                 
   418                         /*
   419                             If we just warped the mouse, the cursor is frozen for a while.
   420                             So we have to use the lowlevel function until it
   421                             unfreezes. This really helps apps that continuously
   422                             warp the mouse to keep it in the game window. Developers should
   423                             really use GrabInput, but our GrabInput freezes the HW cursor,
   424                             which doesn't cut it for some apps.
   425                         */
   426                         Uint32 ticks;
   427                 
   428                         ticks = SDL_GetTicks();
   429                         if (ticks - warp_ticks < 150) {
   430                 
   431                             CGMouseDelta dx1, dy1;
   432                             CGGetLastMouseDelta (&dx1, &dy1);
   433                             dx += dx1;
   434                             dy += dy1;
   435                         }
   436                         else {
   437                 
   438                             warp_flag = 0;
   439                         }
   440                     }
   441                     else if (firstMouseEvent) {
   442                         
   443                         /*
   444                             Get the first mouse event in a possible
   445                             sequence of mouse moved events. Since we
   446                             use absolute coordinates, this serves to
   447                             compensate any inaccuracy in deltas, and
   448                             provides the first known mouse position,
   449                             since everything after this uses deltas
   450                         */
   451                         NSPoint p = [ event locationInWindow ];
   452                         QZ_PrivateCocoaToSDL(this, &p);
   453                         
   454                         SDL_PrivateMouseMotion (0, 0, p.x, p.y);
   455 
   456                         firstMouseEvent = 0;
   457                     }
   458                     else {
   459                     
   460                         /*
   461                             Get the amount moved since the last drag or move event,
   462                             add it on for one big move event at the end.
   463                         */
   464                         dx += [ event deltaX ];
   465                         dy += [ event deltaY ];
   466                     }
   467                     break;
   468                 case NSScrollWheel:
   469                     if (NSPointInRect([ event locationInWindow ], winRect)) {
   470                         float dy;
   471                         Uint8 button;
   472                         dy = [ event deltaY ];
   473                         if ( dy > 0.0 ) /* Scroll up */
   474                             button = SDL_BUTTON_WHEELUP;
   475                         else /* Scroll down */
   476                             button = SDL_BUTTON_WHEELDOWN;
   477 			/* For now, wheel is sent as a quick down+up */
   478                         SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0);
   479                         SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0);
   480                     }
   481                     break;
   482                 case NSKeyUp:
   483                     QZ_DoKey (this, SDL_RELEASED, event);
   484                     break;
   485                 case NSKeyDown:
   486                     QZ_DoKey (this, SDL_PRESSED, event);
   487                     break;
   488                 case NSFlagsChanged:
   489                     QZ_DoModifiers(this, [ event modifierFlags ] );
   490                     break;
   491                 case NSAppKitDefined:
   492                     switch ( [ event subtype ] ) {
   493                         case NSApplicationActivatedEventType:
   494                             QZ_DoActivate (this);
   495                             break;
   496                         case NSApplicationDeactivatedEventType:
   497                             QZ_DoDeactivate (this);
   498                             break;
   499                     }
   500                     [ NSApp sendEvent:event ];
   501                     break;
   502                     /* case NSApplicationDefined: break; */
   503                     /* case NSPeriodic: break; */
   504                     /* case NSCursorUpdate: break; */
   505                 default:
   506                     [ NSApp sendEvent:event ];
   507             }
   508         }
   509     } while (event != nil);
   510     
   511     /* handle accumulated mouse moved events */
   512     if (dx != 0 || dy != 0)
   513         SDL_PrivateMouseMotion (0, 1, dx, dy);
   514     
   515     [ pool release ];
   516 }