Implemented Cocoa key event handling.
authorSam Lantinga <slouken@libsdl.org>
Sun, 30 Jul 2006 05:18:33 +0000
changeset 1959 25d6537feea4
parent 1958 5fc6fb0fb605
child 1960 118daa3a24cc
Implemented Cocoa key event handling.
src/events/SDL_keyboard.c
src/video/cocoa/SDL_cocoaevents.m
src/video/cocoa/SDL_cocoakeyboard.m
src/video/cocoa/SDL_cocoakeys.h
src/video/cocoa/SDL_cocoavideo.h
src/video/cocoa/SDL_cocoavideo.m
src/video/cocoa/SDL_cocoawindow.m
--- a/src/events/SDL_keyboard.c	Sat Jul 29 23:00:15 2006 +0000
+++ b/src/events/SDL_keyboard.c	Sun Jul 30 05:18:33 2006 +0000
@@ -654,6 +654,7 @@
         event.key.keysym.mod = modstate;
         event.key.keysym.unicode = 0;
         event.key.windowID = keyboard->focus;
+        /* FIXME: This doesn't make sense anymore... */
         /*
          * jk 991215 - Added
          */
@@ -688,7 +689,7 @@
         event.text.type = SDL_TEXTINPUT;
         event.text.which = (Uint8) index;
         SDL_strlcpy(event.text.text, text, SDL_arraysize(event.text.text));
-        event.key.windowID = keyboard->focus;
+        event.text.windowID = keyboard->focus;
         posted = (SDL_PushEvent(&event) > 0);
     }
     return (posted);
--- a/src/video/cocoa/SDL_cocoaevents.m	Sat Jul 29 23:00:15 2006 +0000
+++ b/src/video/cocoa/SDL_cocoaevents.m	Sun Jul 30 05:18:33 2006 +0000
@@ -89,9 +89,9 @@
     [appleMenu addItem:[NSMenuItem separatorItem]];
 
     title = [@"Hide " stringByAppendingString:appName];
-    [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
+    [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@/*"h"*/""];
 
-    menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+    menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@/*"h"*/""];
     [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
 
     [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
@@ -99,7 +99,7 @@
     [appleMenu addItem:[NSMenuItem separatorItem]];
 
     title = [@"Quit " stringByAppendingString:appName];
-    [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
+    [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@/*"q"*/""];
     
     /* Put menu into the menubar */
     menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
@@ -116,7 +116,7 @@
     windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
     
     /* "Minimize" item */
-    menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
+    menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@/*"m"*/""];
     [windowMenu addItem:menuItem];
     [menuItem release];
     
@@ -169,7 +169,16 @@
         if ( event == nil ) {
             break;
         }
-        [NSApp sendEvent:event];
+        switch ([event type]) {
+        case NSKeyDown:
+        case NSKeyUp:
+        case NSFlagsChanged:
+            Cocoa_HandleKeyEvent(_this, event);
+            /* Fall through to pass event to NSApp */
+        default:
+            [NSApp sendEvent:event];
+            break;
+        }
     }
     [pool release];
 }
--- a/src/video/cocoa/SDL_cocoakeyboard.m	Sat Jul 29 23:00:15 2006 +0000
+++ b/src/video/cocoa/SDL_cocoakeyboard.m	Sun Jul 30 05:18:33 2006 +0000
@@ -22,20 +22,527 @@
 #include "SDL_config.h"
 
 #include "SDL_cocoavideo.h"
+#include "SDL_cocoakeys.h"
 
 #include "../../events/SDL_keyboard_c.h"
 
+
+#ifndef NX_DEVICERCTLKEYMASK
+    #define NX_DEVICELCTLKEYMASK    0x00000001
+#endif
+#ifndef NX_DEVICELSHIFTKEYMASK
+    #define NX_DEVICELSHIFTKEYMASK  0x00000002
+#endif
+#ifndef NX_DEVICERSHIFTKEYMASK
+    #define NX_DEVICERSHIFTKEYMASK  0x00000004
+#endif
+#ifndef NX_DEVICELCMDKEYMASK
+    #define NX_DEVICELCMDKEYMASK    0x00000008
+#endif
+#ifndef NX_DEVICERCMDKEYMASK
+    #define NX_DEVICERCMDKEYMASK    0x00000010
+#endif
+#ifndef NX_DEVICELALTKEYMASK
+    #define NX_DEVICELALTKEYMASK    0x00000020
+#endif
+#ifndef NX_DEVICERALTKEYMASK
+    #define NX_DEVICERALTKEYMASK    0x00000040
+#endif
+#ifndef NX_DEVICERCTLKEYMASK
+    #define NX_DEVICERCTLKEYMASK    0x00002000
+#endif
+
+static void
+InitKeymap (SDLKey *keymap)
+{
+    const void *KCHRPtr;
+    UInt32 state;
+    UInt32 value;
+    int i;
+
+    for ( i=0; i<256; ++i )
+        keymap[i] = SDLK_UNKNOWN;
+
+    /* This keymap is almost exactly the same as the OS 9 one */
+    keymap[KEY_ESCAPE] = SDLK_ESCAPE;
+    keymap[KEY_F1] = SDLK_F1;
+    keymap[KEY_F2] = SDLK_F2;
+    keymap[KEY_F3] = SDLK_F3;
+    keymap[KEY_F4] = SDLK_F4;
+    keymap[KEY_F5] = SDLK_F5;
+    keymap[KEY_F6] = SDLK_F6;
+    keymap[KEY_F7] = SDLK_F7;
+    keymap[KEY_F8] = SDLK_F8;
+    keymap[KEY_F9] = SDLK_F9;
+    keymap[KEY_F10] = SDLK_F10;
+    keymap[KEY_F11] = SDLK_F11;
+    keymap[KEY_F12] = SDLK_F12;
+    keymap[KEY_PRINT] = SDLK_PRINT;
+    keymap[KEY_SCROLLOCK] = SDLK_SCROLLOCK;
+    keymap[KEY_PAUSE] = SDLK_PAUSE;
+    keymap[KEY_POWER] = SDLK_POWER;
+    keymap[KEY_BACKQUOTE] = SDLK_BACKQUOTE;
+    keymap[KEY_1] = SDLK_1;
+    keymap[KEY_2] = SDLK_2;
+    keymap[KEY_3] = SDLK_3;
+    keymap[KEY_4] = SDLK_4;
+    keymap[KEY_5] = SDLK_5;
+    keymap[KEY_6] = SDLK_6;
+    keymap[KEY_7] = SDLK_7;
+    keymap[KEY_8] = SDLK_8;
+    keymap[KEY_9] = SDLK_9;
+    keymap[KEY_0] = SDLK_0;
+    keymap[KEY_MINUS] = SDLK_MINUS;
+    keymap[KEY_EQUALS] = SDLK_EQUALS;
+    keymap[KEY_BACKSPACE] = SDLK_BACKSPACE;
+    keymap[KEY_INSERT] = SDLK_INSERT;
+    keymap[KEY_HOME] = SDLK_HOME;
+    keymap[KEY_PAGEUP] = SDLK_PAGEUP;
+    keymap[KEY_NUMLOCK] = SDLK_NUMLOCK;
+    keymap[KEY_KP_EQUALS] = SDLK_KP_EQUALS;
+    keymap[KEY_KP_DIVIDE] = SDLK_KP_DIVIDE;
+    keymap[KEY_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
+    keymap[KEY_TAB] = SDLK_TAB;
+    keymap[KEY_q] = SDLK_q;
+    keymap[KEY_w] = SDLK_w;
+    keymap[KEY_e] = SDLK_e;
+    keymap[KEY_r] = SDLK_r;
+    keymap[KEY_t] = SDLK_t;
+    keymap[KEY_y] = SDLK_y;
+    keymap[KEY_u] = SDLK_u;
+    keymap[KEY_i] = SDLK_i;
+    keymap[KEY_o] = SDLK_o;
+    keymap[KEY_p] = SDLK_p;
+    keymap[KEY_LEFTBRACKET] = SDLK_LEFTBRACKET;
+    keymap[KEY_RIGHTBRACKET] = SDLK_RIGHTBRACKET;
+    keymap[KEY_BACKSLASH] = SDLK_BACKSLASH;
+    keymap[KEY_DELETE] = SDLK_DELETE;
+    keymap[KEY_END] = SDLK_END;
+    keymap[KEY_PAGEDOWN] = SDLK_PAGEDOWN;
+    keymap[KEY_KP7] = SDLK_KP7;
+    keymap[KEY_KP8] = SDLK_KP8;
+    keymap[KEY_KP9] = SDLK_KP9;
+    keymap[KEY_KP_MINUS] = SDLK_KP_MINUS;
+    keymap[KEY_CAPSLOCK] = SDLK_CAPSLOCK;
+    keymap[KEY_a] = SDLK_a;
+    keymap[KEY_s] = SDLK_s;
+    keymap[KEY_d] = SDLK_d;
+    keymap[KEY_f] = SDLK_f;
+    keymap[KEY_g] = SDLK_g;
+    keymap[KEY_h] = SDLK_h;
+    keymap[KEY_j] = SDLK_j;
+    keymap[KEY_k] = SDLK_k;
+    keymap[KEY_l] = SDLK_l;
+    keymap[KEY_SEMICOLON] = SDLK_SEMICOLON;
+    keymap[KEY_QUOTE] = SDLK_QUOTE;
+    keymap[KEY_RETURN] = SDLK_RETURN;
+    keymap[KEY_KP4] = SDLK_KP4;
+    keymap[KEY_KP5] = SDLK_KP5;
+    keymap[KEY_KP6] = SDLK_KP6;
+    keymap[KEY_KP_PLUS] = SDLK_KP_PLUS;
+    keymap[KEY_LSHIFT] = SDLK_LSHIFT;
+    keymap[KEY_RSHIFT] = SDLK_RSHIFT;
+    keymap[KEY_z] = SDLK_z;
+    keymap[KEY_x] = SDLK_x;
+    keymap[KEY_c] = SDLK_c;
+    keymap[KEY_v] = SDLK_v;
+    keymap[KEY_b] = SDLK_b;
+    keymap[KEY_n] = SDLK_n;
+    keymap[KEY_m] = SDLK_m;
+    keymap[KEY_COMMA] = SDLK_COMMA;
+    keymap[KEY_PERIOD] = SDLK_PERIOD;
+    keymap[KEY_SLASH] = SDLK_SLASH;
+    keymap[KEY_UP] = SDLK_UP;
+    keymap[KEY_KP1] = SDLK_KP1;
+    keymap[KEY_KP2] = SDLK_KP2;
+    keymap[KEY_KP3] = SDLK_KP3;
+    keymap[KEY_KP_ENTER] = SDLK_KP_ENTER;
+    keymap[KEY_LCTRL] = SDLK_LCTRL;
+    keymap[KEY_LALT] = SDLK_LALT;
+    keymap[KEY_LMETA] = SDLK_LMETA;
+    keymap[KEY_RCTRL] = SDLK_RCTRL;
+    keymap[KEY_RALT] = SDLK_RALT;
+    keymap[KEY_RMETA] = SDLK_RMETA;
+    keymap[KEY_SPACE] = SDLK_SPACE;
+    keymap[KEY_LEFT] = SDLK_LEFT;
+    keymap[KEY_DOWN] = SDLK_DOWN;
+    keymap[KEY_RIGHT] = SDLK_RIGHT;
+    keymap[KEY_KP0] = SDLK_KP0;
+    keymap[KEY_KP_PERIOD] = SDLK_KP_PERIOD;
+    keymap[KEY_IBOOK_ENTER] = SDLK_KP_ENTER;
+    keymap[KEY_IBOOK_RIGHT] = SDLK_RIGHT;
+    keymap[KEY_IBOOK_DOWN] = SDLK_DOWN;
+    keymap[KEY_IBOOK_UP]      = SDLK_UP;
+    keymap[KEY_IBOOK_LEFT] = SDLK_LEFT;
+
+    /* 
+        Up there we setup a static scancode->keysym map. However, it will not
+        work very well on international keyboard. Hence we now query Mac OS X
+        for its own keymap to adjust our own mapping table. However, this is
+        basically only useful for ascii char keys. This is also the reason
+        why we keep the static table, too.
+     */
+
+    /* Get a pointer to the systems cached KCHR */
+    KCHRPtr = (void *)GetScriptManagerVariable(smKCHRCache);
+    if (KCHRPtr) {
+        /* Loop over all 127 possible scan codes */
+        for (i = 0; i < 0x7F; i++) {
+            /* We pretend a clean start to begin with (i.e. no dead keys active */
+            state = 0;
+
+            /* Now translate the key code to a key value */
+            value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
+
+            /* If the state become 0, it was a dead key. We need to translate again,
+                passing in the new state, to get the actual key value */
+            if (state != 0)
+                value = KeyTranslate(KCHRPtr, i, &state) & 0xff;
+
+            /* Now we should have a latin1 value, which are SDL keysyms */
+            if (value >= 32 && value <= 255) {
+                keymap[i] = value;
+            }
+        }
+    }
+
+    /* 
+        The keypad codes are re-setup here, because the loop above cannot
+        distinguish between a key on the keypad and a regular key. We maybe
+        could get around this problem in another fashion: NSEvent's flags
+        include a "NSNumericPadKeyMask" bit; we could check that and modify
+        the symbol we return on the fly. However, this flag seems to exhibit
+        some weird behaviour related to the num lock key
+    */
+    keymap[KEY_KP0] = SDLK_KP0;
+    keymap[KEY_KP1] = SDLK_KP1;
+    keymap[KEY_KP2] = SDLK_KP2;
+    keymap[KEY_KP3] = SDLK_KP3;
+    keymap[KEY_KP4] = SDLK_KP4;
+    keymap[KEY_KP5] = SDLK_KP5;
+    keymap[KEY_KP6] = SDLK_KP6;
+    keymap[KEY_KP7] = SDLK_KP7;
+    keymap[KEY_KP8] = SDLK_KP8;
+    keymap[KEY_KP9] = SDLK_KP9;
+    keymap[KEY_KP_MINUS] = SDLK_KP_MINUS;
+    keymap[KEY_KP_PLUS] = SDLK_KP_PLUS;
+    keymap[KEY_KP_PERIOD] = SDLK_KP_PERIOD;
+    keymap[KEY_KP_EQUALS] = SDLK_KP_EQUALS;
+    keymap[KEY_KP_DIVIDE] = SDLK_KP_DIVIDE;
+    keymap[KEY_KP_MULTIPLY] = SDLK_KP_MULTIPLY;
+    keymap[KEY_KP_ENTER] = SDLK_KP_ENTER;
+}
+
+/* This is the original behavior, before support was added for 
+ * differentiating between left and right versions of the keys.
+ */
+static void
+DoUnsidedModifiers(int keyboard, unsigned short scancode,
+                   unsigned int oldMods, unsigned int newMods)
+{
+    const int mapping[] = { SDLK_CAPSLOCK, SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA };
+    unsigned int i, bit;
+
+    /* Iterate through the bits, testing each against the current modifiers */
+    for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
+        unsigned int oldMask, newMask;
+
+        oldMask = oldMods & bit;
+        newMask = newMods & bit;
+
+        if (oldMask && oldMask != newMask) {        /* modifier up event */
+            /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
+            if (bit == NSAlphaShiftKeyMask) {
+                SDL_SendKeyboardKey(keyboard, SDL_PRESSED, (Uint8)scancode, mapping[i]);
+            }
+            SDL_SendKeyboardKey(keyboard, SDL_RELEASED, (Uint8)scancode, mapping[i]);
+        } else if (newMask && oldMask != newMask) { /* modifier down event */
+            SDL_SendKeyboardKey(keyboard, SDL_PRESSED, (Uint8)scancode, mapping[i]);
+            /* If this was Caps Lock, we need some additional voodoo to make SDL happy */
+            if (bit == NSAlphaShiftKeyMask) {
+                SDL_SendKeyboardKey(keyboard, SDL_RELEASED, (Uint8)scancode, mapping[i]);
+            }
+        }
+    }
+}
+
+/* This is a helper function for HandleModifierSide. This 
+ * function reverts back to behavior before the distinction between
+ * sides was made.
+ */
+static void
+HandleNonDeviceModifier(int keyboard, unsigned short scancode,
+                        unsigned int device_independent_mask,
+                        unsigned int oldMods,
+                        unsigned int newMods,
+                        SDLKey key_sym)
+{
+    unsigned int oldMask, newMask;
+    
+    /* Isolate just the bits we care about in the depedent bits so we can 
+     * figure out what changed
+     */ 
+    oldMask = oldMods & device_independent_mask;
+    newMask = newMods & device_independent_mask;
+    
+    if (oldMask && oldMask != newMask) {
+        SDL_SendKeyboardKey(keyboard, SDL_RELEASED, (Uint8)scancode, key_sym);
+    } else if (newMask && oldMask != newMask) {
+        SDL_SendKeyboardKey(keyboard, SDL_PRESSED, (Uint8)scancode, key_sym);
+    }
+}
+
+/* This is a helper function for HandleModifierSide. 
+ * This function sets the actual SDL_PrivateKeyboard event.
+ */
+static void
+HandleModifierOneSide(int keyboard, unsigned short scancode,
+                      unsigned int oldMods, unsigned int newMods,
+                      SDLKey key_sym, 
+                      unsigned int sided_device_dependent_mask)
+{
+    unsigned int old_dep_mask, new_dep_mask;
+
+    /* Isolate just the bits we care about in the depedent bits so we can 
+     * figure out what changed
+     */ 
+    old_dep_mask = oldMods & sided_device_dependent_mask;
+    new_dep_mask = newMods & sided_device_dependent_mask;
+
+    /* We now know that this side bit flipped. But we don't know if
+     * it went pressed to released or released to pressed, so we must 
+     * find out which it is.
+     */
+    if (new_dep_mask && old_dep_mask != new_dep_mask) {
+        SDL_SendKeyboardKey(keyboard, SDL_PRESSED, (Uint8)scancode, key_sym);
+    } else {
+        SDL_SendKeyboardKey(keyboard, SDL_RELEASED, (Uint8)scancode, key_sym);
+    }
+}
+
+/* This is a helper function for DoSidedModifiers.
+ * This function will figure out if the modifier key is the left or right side, 
+ * e.g. left-shift vs right-shift. 
+ */
+static void
+HandleModifierSide(int keyboard, unsigned short scancode,
+                   int device_independent_mask, 
+                   unsigned int oldMods, unsigned int newMods, 
+                   SDLKey left_key_sym, 
+                   SDLKey right_key_sym,
+                   unsigned int left_device_dependent_mask, 
+                   unsigned int right_device_dependent_mask)
+{
+    unsigned int device_dependent_mask = (left_device_dependent_mask |
+                                         right_device_dependent_mask);
+    unsigned int diff_mod;
+    
+    /* On the basis that the device independent mask is set, but there are 
+     * no device dependent flags set, we'll assume that we can't detect this 
+     * keyboard and revert to the unsided behavior.
+     */
+    if ((device_dependent_mask & newMods) == 0) {
+        /* Revert to the old behavior */
+        HandleNonDeviceModifier(keyboard, scancode, device_independent_mask, oldMods, newMods, left_key_sym);
+        return;
+    }
+
+    /* XOR the previous state against the new state to see if there's a change */
+    diff_mod = (device_dependent_mask & oldMods) ^
+               (device_dependent_mask & newMods);
+    if (diff_mod) {
+        /* A change in state was found. Isolate the left and right bits 
+         * to handle them separately just in case the values can simulataneously
+         * change or if the bits don't both exist.
+         */
+        if (left_device_dependent_mask & diff_mod) {
+            HandleModifierOneSide(keyboard, scancode, oldMods, newMods, left_key_sym, left_device_dependent_mask);
+        }
+        if (right_device_dependent_mask & diff_mod) {
+            HandleModifierOneSide(keyboard, scancode, oldMods, newMods, right_key_sym, right_device_dependent_mask);
+        }
+    }
+}
+   
+/* This is a helper function for DoSidedModifiers.
+ * This function will release a key press in the case that 
+ * it is clear that the modifier has been released (i.e. one side 
+ * can't still be down).
+ */
+static void
+ReleaseModifierSide(int keyboard, unsigned short scancode,
+                    unsigned int device_independent_mask, 
+                    unsigned int oldMods, unsigned int newMods,
+                    SDLKey left_key_sym, 
+                    SDLKey right_key_sym,
+                    unsigned int left_device_dependent_mask, 
+                    unsigned int right_device_dependent_mask)
+{
+    unsigned int device_dependent_mask = (left_device_dependent_mask |
+                                          right_device_dependent_mask);
+
+    /* On the basis that the device independent mask is set, but there are 
+     * no device dependent flags set, we'll assume that we can't detect this 
+     * keyboard and revert to the unsided behavior.
+     */
+    if ((device_dependent_mask & oldMods) == 0) {
+        /* In this case, we can't detect the keyboard, so use the left side 
+         * to represent both, and release it. 
+         */
+        SDL_SendKeyboardKey(keyboard, SDL_RELEASED, (Uint8)scancode, left_key_sym);
+        return;
+    }
+
+    /* 
+     * This could have been done in an if-else case because at this point,
+     * we know that all keys have been released when calling this function. 
+     * But I'm being paranoid so I want to handle each separately,
+     * so I hope this doesn't cause other problems.
+     */
+    if ( left_device_dependent_mask & oldMods ) {
+        SDL_SendKeyboardKey(keyboard, SDL_RELEASED, (Uint8)scancode, left_key_sym);
+    }
+    if ( right_device_dependent_mask & oldMods ) {
+        SDL_SendKeyboardKey(keyboard, SDL_RELEASED, (Uint8)scancode, right_key_sym);
+    }
+}
+
+/* This is a helper function for DoSidedModifiers.
+ * This function handles the CapsLock case.
+ */
+static void
+HandleCapsLock(int keyboard, unsigned short scancode,
+               unsigned int oldMods, unsigned int newMods)
+{
+    unsigned int oldMask, newMask;
+    
+    oldMask = oldMods & NSAlphaShiftKeyMask;
+    newMask = newMods & NSAlphaShiftKeyMask;
+
+    if (oldMask != newMask) {
+        SDL_SendKeyboardKey(keyboard, SDL_PRESSED, (Uint8)scancode, SDLK_CAPSLOCK);
+        SDL_SendKeyboardKey(keyboard, SDL_RELEASED, (Uint8)scancode, SDLK_CAPSLOCK);
+    }
+}
+
+/* This function will handle the modifier keys and also determine the 
+ * correct side of the key.
+ */
+static void
+DoSidedModifiers(int keyboard, unsigned short scancode,
+                 unsigned int oldMods, unsigned int newMods)
+{
+	/* Set up arrays for the key syms for the left and right side. */
+    const SDLKey left_mapping[]  = { SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA };
+    const SDLKey right_mapping[] = { SDLK_RSHIFT, SDLK_RCTRL, SDLK_RALT, SDLK_RMETA };
+	/* Set up arrays for the device dependent masks with indices that 
+     * correspond to the _mapping arrays 
+     */
+    const unsigned int left_device_mapping[]  = { NX_DEVICELSHIFTKEYMASK, NX_DEVICELCTLKEYMASK, NX_DEVICELALTKEYMASK, NX_DEVICELCMDKEYMASK };
+    const unsigned int right_device_mapping[] = { NX_DEVICERSHIFTKEYMASK, NX_DEVICERCTLKEYMASK, NX_DEVICERALTKEYMASK, NX_DEVICERCMDKEYMASK };
+
+    unsigned int i, bit;
+
+    /* Handle CAPSLOCK separately because it doesn't have a left/right side */
+    HandleCapsLock(keyboard, scancode, oldMods, newMods);
+
+    /* Iterate through the bits, testing each against the old modifiers */
+    for (i = 0, bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
+        unsigned int oldMask, newMask;
+		
+        oldMask = oldMods & bit;
+        newMask = newMods & bit;
+		
+        /* If the bit is set, we must always examine it because the left
+         * and right side keys may alternate or both may be pressed.
+         */
+        if (newMask) {
+            HandleModifierSide(keyboard, scancode, bit, oldMods, newMods,
+                               left_mapping[i], right_mapping[i],
+                               left_device_mapping[i], right_device_mapping[i]);
+        }
+        /* If the state changed from pressed to unpressed, we must examine
+            * the device dependent bits to release the correct keys.
+            */
+        else if (oldMask && oldMask != newMask) {
+            ReleaseModifierSide(keyboard, scancode, bit, oldMods, newMods,
+                              left_mapping[i], right_mapping[i],
+                              left_device_mapping[i], right_device_mapping[i]);
+        }
+    }
+}
+
+static void
+HandleModifiers(_THIS, unsigned short scancode, unsigned int modifierFlags)
+{
+    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+
+    if (modifierFlags == data->modifierFlags) {
+    	return;
+    }
+
+    /* 
+     * Starting with Panther (10.3.0), the ability to distinguish between 
+     * left side and right side modifiers is available.
+     */
+    if (data->osversion >= 0x1030) {
+        DoSidedModifiers(data->keyboard, scancode, data->modifierFlags, modifierFlags);
+    } else {
+        DoUnsidedModifiers(data->keyboard, scancode, data->modifierFlags, modifierFlags);
+    }
+    data->modifierFlags = modifierFlags;
+}
+
 void
 Cocoa_InitKeyboard(_THIS)
 {
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
     SDL_Keyboard keyboard;
 
+    InitKeymap(data->keymap);
+
     SDL_zero(keyboard);
     data->keyboard = SDL_AddKeyboard(&keyboard, -1);
 }
 
 void
+Cocoa_HandleKeyEvent(_THIS, NSEvent *event)
+{
+    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+    unsigned short scancode = [event keyCode];
+    const char *text;
+
+    if (scancode >= 256) {
+        /* Hmm, does this ever happen?  If so, need to extend the keymap... */
+        return;
+    }
+
+    switch ([event type]) {
+    case NSKeyDown:
+        if ([event isARepeat]) {
+            break;
+        }
+printf("NSKeyDown: %x, %x\n", scancode, [event modifierFlags]);
+        SDL_SendKeyboardKey(data->keyboard, SDL_PRESSED, (Uint8)scancode,
+                            data->keymap[scancode]);
+        text = [[event characters] UTF8String];
+        if(text && *text) {
+            SDL_SendKeyboardText(data->keyboard, text);
+        }
+        break;
+    case NSKeyUp:
+printf("NSKeyUp: %x, %x\n", scancode, [event modifierFlags]);
+        SDL_SendKeyboardKey(data->keyboard, SDL_RELEASED, (Uint8)scancode,
+                            data->keymap[scancode]);
+        break;
+    case NSFlagsChanged:
+printf("NSFlagsChanged: %x, %x\n", scancode, [event modifierFlags]);
+        HandleModifiers(_this, scancode, [event modifierFlags]);
+        break;
+    }
+}
+
+void
 Cocoa_QuitKeyboard(_THIS)
 {
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/cocoa/SDL_cocoakeys.h	Sun Jul 30 05:18:33 2006 +0000
@@ -0,0 +1,141 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2006 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/* These are the Macintosh key scancode constants -- from Inside Macintosh */
+
+#define KEY_ESCAPE		0x35
+#define KEY_F1			0x7A
+#define KEY_F2			0x78
+#define KEY_F3			0x63
+#define KEY_F4			0x76
+#define KEY_F5			0x60
+#define KEY_F6			0x61
+#define KEY_F7			0x62
+#define KEY_F8			0x64
+#define KEY_F9			0x65
+#define KEY_F10			0x6D
+#define KEY_F11			0x67
+#define KEY_F12			0x6F
+#define KEY_PRINT		0x69
+#define KEY_SCROLLOCK    0x6B
+#define KEY_PAUSE		0x71
+#define KEY_POWER		0x7F
+#define KEY_BACKQUOTE	0x32
+#define KEY_1			0x12
+#define KEY_2			0x13
+#define KEY_3			0x14
+#define KEY_4			0x15
+#define KEY_5			0x17
+#define KEY_6			0x16
+#define KEY_7			0x1A
+#define KEY_8			0x1C
+#define KEY_9			0x19
+#define KEY_0			0x1D
+#define KEY_MINUS		0x1B
+#define KEY_EQUALS		0x18
+#define KEY_BACKSPACE	0x33
+#define KEY_INSERT		0x72
+#define KEY_HOME			0x73
+#define KEY_PAGEUP		0x74
+#define KEY_NUMLOCK		0x47
+#define KEY_KP_EQUALS	0x51
+#define KEY_KP_DIVIDE	0x4B
+#define KEY_KP_MULTIPLY	0x43
+#define KEY_TAB			0x30
+#define KEY_q			0x0C
+#define KEY_w			0x0D
+#define KEY_e			0x0E
+#define KEY_r			0x0F
+#define KEY_t			0x11
+#define KEY_y			0x10
+#define KEY_u			0x20
+#define KEY_i			0x22
+#define KEY_o			0x1F
+#define KEY_p			0x23
+#define KEY_LEFTBRACKET	0x21
+#define KEY_RIGHTBRACKET	0x1E
+#define KEY_BACKSLASH	0x2A
+#define KEY_DELETE		0x75
+#define KEY_END			0x77
+#define KEY_PAGEDOWN		0x79
+#define KEY_KP7			0x59
+#define KEY_KP8			0x5B
+#define KEY_KP9			0x5C
+#define KEY_KP_MINUS		0x4E
+#define KEY_CAPSLOCK		0x39
+#define KEY_a			0x00
+#define KEY_s			0x01
+#define KEY_d			0x02
+#define KEY_f			0x03
+#define KEY_g			0x05
+#define KEY_h			0x04
+#define KEY_j			0x26
+#define KEY_k			0x28
+#define KEY_l			0x25
+#define KEY_SEMICOLON	0x29
+#define KEY_QUOTE		0x27
+#define KEY_RETURN		0x24
+#define KEY_KP4			0x56
+#define KEY_KP5			0x57
+#define KEY_KP6			0x58
+#define KEY_KP_PLUS		0x45
+#define KEY_LSHIFT		0x38
+#define KEY_z			0x06
+#define KEY_x			0x07
+#define KEY_c			0x08
+#define KEY_v			0x09
+#define KEY_b			0x0B
+#define KEY_n			0x2D
+#define KEY_m			0x2E
+#define KEY_COMMA		0x2B
+#define KEY_PERIOD		0x2F
+#define KEY_SLASH		0x2C
+#if 1                           /* Panther now defines right side keys */
+#define KEY_RSHIFT		0x3C
+#endif
+#define KEY_UP			0x7E
+#define KEY_KP1			0x53
+#define KEY_KP2			0x54
+#define KEY_KP3			0x55
+#define KEY_KP_ENTER		0x4C
+#define KEY_LCTRL		0x3B
+#define KEY_LALT			0x3A
+#define KEY_LMETA		0x37
+#define KEY_SPACE		0x31
+#if 1                           /* Panther now defines right side keys */
+#define KEY_RMETA		0x36
+#define KEY_RALT			0x3D
+#define KEY_RCTRL		0x3E
+#endif
+#define KEY_LEFT			0x7B
+#define KEY_DOWN			0x7D
+#define KEY_RIGHT		0x7C
+#define KEY_KP0			0x52
+#define KEY_KP_PERIOD	0x41
+
+/* Wierd, these keys are on my iBook under Mac OS X */
+#define KEY_IBOOK_ENTER		0x34
+#define KEY_IBOOK_LEFT		0x3B
+#define KEY_IBOOK_RIGHT		0x3C
+#define KEY_IBOOK_DOWN		0x3D
+#define KEY_IBOOK_UP			0x3E
--- a/src/video/cocoa/SDL_cocoavideo.h	Sat Jul 29 23:00:15 2006 +0000
+++ b/src/video/cocoa/SDL_cocoavideo.h	Sun Jul 30 05:18:33 2006 +0000
@@ -27,6 +27,7 @@
 #include <ApplicationServices/ApplicationServices.h>
 #include <Cocoa/Cocoa.h>
 
+#include "SDL_keysym.h"
 #include "../SDL_sysvideo.h"
 
 #include "SDL_cocoaevents.h"
@@ -40,6 +41,9 @@
 
 typedef struct SDL_VideoData
 {
+    SInt32 osversion;
+    SDLKey keymap[256];
+    unsigned int modifierFlags;
     int mouse;
     int keyboard;
 } SDL_VideoData;
--- a/src/video/cocoa/SDL_cocoavideo.m	Sat Jul 29 23:00:15 2006 +0000
+++ b/src/video/cocoa/SDL_cocoavideo.m	Sun Jul 30 05:18:33 2006 +0000
@@ -66,6 +66,9 @@
     }
     device->driverdata = data;
 
+    /* Find out what version of Mac OS X we're running */
+    Gestalt(gestaltSystemVersion, &data->osversion);
+
     /* Set the function pointers */
     device->VideoInit = Cocoa_VideoInit;
     device->VideoQuit = Cocoa_VideoQuit;
--- a/src/video/cocoa/SDL_cocoawindow.m	Sat Jul 29 23:00:15 2006 +0000
+++ b/src/video/cocoa/SDL_cocoawindow.m	Sun Jul 30 05:18:33 2006 +0000
@@ -148,49 +148,67 @@
 - (void)mouseDown:(NSEvent *)theEvent
 {
     int index;
+    int button;
 
     index = _data->videodata->mouse;
-    SDL_SendMouseButton(index, SDL_PRESSED, SDL_BUTTON_LEFT);
+    switch ([theEvent buttonNumber]) {
+    case 0:
+        button = SDL_BUTTON_LEFT;
+        break;
+    case 1:
+        button = SDL_BUTTON_RIGHT;
+        break;
+    case 2:
+        button = SDL_BUTTON_MIDDLE;
+        break;
+    default:
+        button = [theEvent buttonNumber];
+        break;
+    }
+    SDL_SendMouseButton(index, SDL_PRESSED, button);
 }
 
 - (void)rightMouseDown:(NSEvent *)theEvent
 {
-    int index;
-
-    index = _data->videodata->mouse;
-    SDL_SendMouseButton(index, SDL_PRESSED, SDL_BUTTON_RIGHT);
+    [self mouseDown:theEvent];
 }
 
 - (void)otherMouseDown:(NSEvent *)theEvent
 {
-    int index;
-
-    index = _data->videodata->mouse;
-    SDL_SendMouseButton(index, SDL_PRESSED, SDL_BUTTON_MIDDLE);
+    [self mouseDown:theEvent];
 }
 
 - (void)mouseUp:(NSEvent *)theEvent
 {
     int index;
+    int button;
 
     index = _data->videodata->mouse;
-    SDL_SendMouseButton(index, SDL_RELEASED, SDL_BUTTON_LEFT);
+    switch ([theEvent buttonNumber]) {
+    case 0:
+        button = SDL_BUTTON_LEFT;
+        break;
+    case 1:
+        button = SDL_BUTTON_RIGHT;
+        break;
+    case 2:
+        button = SDL_BUTTON_MIDDLE;
+        break;
+    default:
+        button = [theEvent buttonNumber];
+        break;
+    }
+    SDL_SendMouseButton(index, SDL_RELEASED, button);
 }
 
 - (void)rightMouseUp:(NSEvent *)theEvent
 {
-    int index;
-
-    index = _data->videodata->mouse;
-    SDL_SendMouseButton(index, SDL_RELEASED, SDL_BUTTON_RIGHT);
+    [self mouseUp:theEvent];
 }
 
 - (void)otherMouseUp:(NSEvent *)theEvent
 {
-    int index;
-
-    index = _data->videodata->mouse;
-    SDL_SendMouseButton(index, SDL_RELEASED, SDL_BUTTON_MIDDLE);
+    [self mouseUp:theEvent];
 }
 
 - (void)mouseMoved:(NSEvent *)theEvent
@@ -238,11 +256,21 @@
 
 - (void)keyDown:(NSEvent *)theEvent
 {
+    int index;
+
+    index = _data->videodata->keyboard;
 fprintf(stderr, "keyDown\n");
+    const char *text = [[theEvent characters] UTF8String];
+    if(text && *text) {
+        SDL_SendKeyboardText(index, text);
+    }
 }
 
 - (void)keyUp:(NSEvent *)theEvent
 {
+    int index;
+
+    index = _data->videodata->keyboard;
 fprintf(stderr, "keyUp\n");
 }