From 85d87922f4f1315230f55a12c09dd46bfba04f6d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 20 Aug 2004 22:35:23 +0000 Subject: [PATCH] Date: Thu, 17 Jun 2004 11:38:51 -0700 (PDT) From: Eric Wing Subject: New OS X patch (was Re: [SDL] Bug with inverted mouse coordinates in I have a new patch for OS X I would like to submit. First, it appears no further action has been taken on my fix from Apple on the OpenGL windowed mode mouse inversion problem. The fix would reunify the code, and no longer require case checking for which version of the OS you are running. This is probably a good fix because the behavior with the old code could change again with future versions of the OS, so those fixes are included in this new patch. But in addition, when I was at Apple, I asked them about the ability to distinguish between the modifier keys on the left and right sides of the keyboard (e.g. Left Shift, Right Shift, Left/Right Alt, L/R Cmd, L/R Ctrl). They told me that starting with Panther, the OS began supporting this feature. This has always been a source of annoyance for me when bringing a program that comes from Windows or Linux to OS X when the keybindings happened to need distinguishable left-side and right-side keys. So the rest of the patch I am submitting contains new code to support this feature on Panther (and presumably later versions of the OS). So after removing the OS version checks for the mouse inversion problem, I reused the OS version checks to activate the Left/Right detection of modifier keys. If you are running Panther (or above), the new code will attempt to distinguish between sides. For the older OS's, the code path reverts to the original code. I've tested with Panther on a G4 Cube, G5 dual processor, and Powerbook Rev C. The Cube and G5 keyboards demonstrated the ability to distinguish between sides. The Powerbook seems to only have left-side keys, but the patch was still able to handle it by producing the same results as before the patch. I also wanted to test a non-Apple keyboard. Unfortunately, I don't have any PC USB keyboards. However, I was able to borrow a Sun Microsystems USB keyboard, so I tried that out on the G5, and I got the correct behavior for left and right sides. I'm expecting that if it worked with a Sun keyboard, most other keyboards should work with no problems. --- src/video/quartz/SDL_QuartzEvents.m | 309 +++++++++++++++++++++++++++- src/video/quartz/SDL_QuartzKeys.h | 12 +- src/video/quartz/SDL_QuartzVideo.m | 2 +- src/video/quartz/SDL_QuartzWM.m | 22 +- 4 files changed, 318 insertions(+), 27 deletions(-) diff --git a/src/video/quartz/SDL_QuartzEvents.m b/src/video/quartz/SDL_QuartzEvents.m index 617ae9d9f..e75038e92 100644 --- a/src/video/quartz/SDL_QuartzEvents.m +++ b/src/video/quartz/SDL_QuartzEvents.m @@ -27,6 +27,42 @@ #include // For wake from sleep detection #include "SDL_QuartzKeys.h" +/* + * In Panther, this header defines device dependent masks for + * right side keys. These definitions only exist in Panther, but + * the header seems to exist at least in Jaguar and probably earlier + * versions of the OS, so this should't break anything. + */ +#include +/* + * These are not defined before Panther. To keep the code compiling + * on systems without these, I will define if they don't exist. + */ +#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 + void QZ_InitOSKeymap (_THIS) { const void *KCHRPtr; UInt32 state; @@ -115,6 +151,7 @@ void QZ_InitOSKeymap (_THIS) { keymap[QZ_KP6] = SDLK_KP6; keymap[QZ_KP_PLUS] = SDLK_KP_PLUS; keymap[QZ_LSHIFT] = SDLK_LSHIFT; + keymap[QZ_RSHIFT] = SDLK_RSHIFT; keymap[QZ_z] = SDLK_z; keymap[QZ_x] = SDLK_x; keymap[QZ_c] = SDLK_c; @@ -133,6 +170,9 @@ void QZ_InitOSKeymap (_THIS) { keymap[QZ_LCTRL] = SDLK_LCTRL; keymap[QZ_LALT] = SDLK_LALT; keymap[QZ_LMETA] = SDLK_LMETA; + keymap[QZ_RCTRL] = SDLK_RCTRL; + keymap[QZ_RALT] = SDLK_RALT; + keymap[QZ_RMETA] = SDLK_RMETA; keymap[QZ_SPACE] = SDLK_SPACE; keymap[QZ_LEFT] = SDLK_LEFT; keymap[QZ_DOWN] = SDLK_DOWN; @@ -259,7 +299,10 @@ static void QZ_DoKey (_THIS, int state, NSEvent *event) { [ NSApp sendEvent:event ]; } -static void QZ_DoModifiers (_THIS, unsigned int newMods) { +/* This is the original behavior, before support was added for + * differentiating between left and right versions of the keys. + */ +static void QZ_DoUnsidedModifiers (_THIS, unsigned int newMods) { const int mapping[] = { SDLK_CAPSLOCK, SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA }; @@ -267,9 +310,6 @@ static void QZ_DoModifiers (_THIS, unsigned int newMods) { int bit; SDL_keysym key; - if (current_mods == newMods) - return; - key.scancode = 0; key.sym = SDLK_UNKNOWN; key.unicode = 0; @@ -302,7 +342,268 @@ static void QZ_DoModifiers (_THIS, unsigned int newMods) { SDL_PrivateKeyboard (SDL_RELEASED, &key); } } +} + +/* This is a helper function for QZ_HandleModifierSide. This + * function reverts back to behavior before the distinction between + * sides was made. + */ +static void QZ_HandleNonDeviceModifier ( _THIS, unsigned int device_independent_mask, unsigned int newMods, unsigned int key_sym) { + unsigned int currentMask, newMask; + SDL_keysym key; + + key.scancode = 0; + key.sym = key_sym; + key.unicode = 0; + key.mod = KMOD_NONE; + + /* Isolate just the bits we care about in the depedent bits so we can + * figure out what changed + */ + currentMask = current_mods & device_independent_mask; + newMask = newMods & device_independent_mask; + + if ( currentMask && + currentMask != newMask ) { /* modifier up event */ + SDL_PrivateKeyboard (SDL_RELEASED, &key); + } + else if ( newMask && + currentMask != newMask ) { /* modifier down event */ + SDL_PrivateKeyboard (SDL_PRESSED, &key); + } +} + +/* This is a helper function for QZ_HandleModifierSide. + * This function sets the actual SDL_PrivateKeyboard event. + */ +static void QZ_HandleModifierOneSide ( _THIS, unsigned int newMods, + unsigned int key_sym, + unsigned int sided_device_dependent_mask ) { + + SDL_keysym key; + unsigned int current_dep_mask, new_dep_mask; + + key.scancode = 0; + key.sym = key_sym; + key.unicode = 0; + key.mod = KMOD_NONE; + + /* Isolate just the bits we care about in the depedent bits so we can + * figure out what changed + */ + current_dep_mask = current_mods & 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 && + current_dep_mask != new_dep_mask ) { + /* Modifier down event */ + SDL_PrivateKeyboard (SDL_PRESSED, &key); + } + else /* Modifier up event */ { + SDL_PrivateKeyboard (SDL_RELEASED, &key); + } +} +/* This is a helper function for QZ_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 QZ_HandleModifierSide ( _THIS, int device_independent_mask, + unsigned int newMods, + unsigned int left_key_sym, + unsigned int right_key_sym, + unsigned int left_device_dependent_mask, + unsigned int right_device_dependent_mask ) { + unsigned int device_dependent_mask = 0; + unsigned int diff_mod = 0; + + 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 & newMods) == 0 ) { + /* Revert to the old behavior */ + QZ_HandleNonDeviceModifier ( this, device_independent_mask, 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 & current_mods) + ^ (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 ) { + QZ_HandleModifierOneSide ( this, newMods, left_key_sym, left_device_dependent_mask ); + } + if ( right_device_dependent_mask & diff_mod ) { + QZ_HandleModifierOneSide ( this, newMods, right_key_sym, right_device_dependent_mask ); + } + } +} + +/* This is a helper function for QZ_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 QZ_ReleaseModifierSide ( _THIS, + unsigned int device_independent_mask, + unsigned int newMods, + unsigned int left_key_sym, + unsigned int right_key_sym, + unsigned int left_device_dependent_mask, + unsigned int right_device_dependent_mask ) { + unsigned int device_dependent_mask = 0; + SDL_keysym key; + + key.scancode = 0; + key.sym = SDLK_UNKNOWN; + key.unicode = 0; + key.mod = KMOD_NONE; + + 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 & current_mods) == 0 ) { + /* In this case, we can't detect the keyboard, so use the left side + * to represent both, and release it. + */ + key.sym = left_key_sym; + SDL_PrivateKeyboard (SDL_RELEASED, &key); + + 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 & current_mods ) { + key.sym = left_key_sym; + SDL_PrivateKeyboard (SDL_RELEASED, &key); + } + if ( right_device_dependent_mask & current_mods ) { + key.sym = right_key_sym; + SDL_PrivateKeyboard (SDL_RELEASED, &key); + } +} + +/* This is a helper function for QZ_DoSidedModifiers. + * This function handles the CapsLock case. + */ +static void QZ_HandleCapsLock (_THIS, unsigned int newMods) { + unsigned int currentMask, newMask; + SDL_keysym key; + + key.scancode = 0; + key.sym = SDLK_CAPSLOCK; + key.unicode = 0; + key.mod = KMOD_NONE; + + currentMask = current_mods & NSAlphaShiftKeyMask; + newMask = newMods & NSAlphaShiftKeyMask; + + if ( currentMask && + currentMask != newMask ) { /* modifier up event */ + /* If this was Caps Lock, we need some additional voodoo to make SDL happy */ + SDL_PrivateKeyboard (SDL_PRESSED, &key); + SDL_PrivateKeyboard (SDL_RELEASED, &key); + } + else if ( newMask && + currentMask != newMask ) { /* modifier down event */ + /* If this was Caps Lock, we need some additional voodoo to make SDL happy */ + SDL_PrivateKeyboard (SDL_PRESSED, &key); + SDL_PrivateKeyboard (SDL_RELEASED, &key); + } +} + +/* This function will handle the modifier keys and also determine the + * correct side of the key. + */ +static void QZ_DoSidedModifiers (_THIS, unsigned int newMods) { + /* Set up arrays for the key syms for the left and right side. */ + const unsigned int left_mapping[] = { SDLK_LSHIFT, SDLK_LCTRL, SDLK_LALT, SDLK_LMETA }; + const unsigned int 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; + unsigned int bit; + + /* Handle CAPSLOCK separately because it doesn't have a left/right side */ + QZ_HandleCapsLock ( this, newMods ); + + /* Iterate through the bits, testing each against the current modifiers */ + for (i = 0, bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) { + + unsigned int currentMask, newMask; + + currentMask = current_mods & 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 ) { + QZ_HandleModifierSide ( this, bit, 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 ( currentMask && + currentMask != newMask ) { /* modifier up event */ + QZ_ReleaseModifierSide ( this, bit, newMods, + left_mapping[i], + right_mapping[i], + left_device_mapping[i], + right_device_mapping[i] ); + } + } +} + +/* This function is called to handle the modifiers. + * It will try to distinguish between the left side and right side + * of the keyboard for those modifiers that qualify if the + * operating system version supports it. Otherwise, the code + * will not try to make the distinction. + */ +static void QZ_DoModifiers (_THIS, unsigned int newMods) { + + if (current_mods == newMods) + return; + + /* + * Starting with Panther (10.3.0), the ability to distinguish between + * left side and right side modifiers is available. + */ + if( system_version >= 0x1030 ) { + QZ_DoSidedModifiers (this, newMods); + } + else { + QZ_DoUnsidedModifiers (this, newMods); + } + current_mods = newMods; } diff --git a/src/video/quartz/SDL_QuartzKeys.h b/src/video/quartz/SDL_QuartzKeys.h index 5929d4da4..c007c994d 100644 --- a/src/video/quartz/SDL_QuartzKeys.h +++ b/src/video/quartz/SDL_QuartzKeys.h @@ -109,8 +109,8 @@ #define QZ_COMMA 0x2B #define QZ_PERIOD 0x2F #define QZ_SLASH 0x2C -#if 0 /* These are the same as the left versions - use left by default */ -#define QZ_RSHIFT 0x38 +#if 1 /* Panther now defines right side keys */ +#define QZ_RSHIFT 0x3C #endif #define QZ_UP 0x7E #define QZ_KP1 0x53 @@ -121,10 +121,10 @@ #define QZ_LALT 0x3A #define QZ_LMETA 0x37 #define QZ_SPACE 0x31 -#if 0 /* These are the same as the left versions - use left by default */ -#define QZ_RMETA 0x37 -#define QZ_RALT 0x3A -#define QZ_RCTRL 0x3B +#if 1 /* Panther now defines right side keys */ +#define QZ_RMETA 0x36 +#define QZ_RALT 0x3D +#define QZ_RCTRL 0x3E #endif #define QZ_LEFT 0x7B #define QZ_DOWN 0x7D diff --git a/src/video/quartz/SDL_QuartzVideo.m b/src/video/quartz/SDL_QuartzVideo.m index 61952eded..aec717796 100644 --- a/src/video/quartz/SDL_QuartzVideo.m +++ b/src/video/quartz/SDL_QuartzVideo.m @@ -207,7 +207,7 @@ static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format) { if ( Gestalt(gestaltSystemVersion, &system_version) != noErr ) system_version = 0; - + /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */ QZ_RegisterForSleepNotifications (this); diff --git a/src/video/quartz/SDL_QuartzWM.m b/src/video/quartz/SDL_QuartzWM.m index 3dccd6f02..df2d0d788 100644 --- a/src/video/quartz/SDL_QuartzWM.m +++ b/src/video/quartz/SDL_QuartzWM.m @@ -138,12 +138,8 @@ void QZ_PrivateSDLToCocoa (_THIS, NSPoint *p) { p->y = CGDisplayPixelsHigh (display_id) - p->y; } else { - - NSPoint newPoint; - - newPoint = [ window_view convertPoint:*p toView:[ qz_window contentView ] ]; - - *p = newPoint; + + *p = [ window_view convertPoint:*p toView: nil ]; } } @@ -155,17 +151,11 @@ void QZ_PrivateCocoaToSDL (_THIS, NSPoint *p) { p->y = CGDisplayPixelsHigh (display_id) - p->y; } else { - - NSPoint newPoint; - - newPoint = [ window_view convertPoint:*p fromView:[ qz_window contentView ] ]; - - *p = newPoint; - /* If OSX version is 10.3.0 or later, we need a workaround in OpenGL mode */ - if( system_version >= 0x1030 && (SDL_VideoSurface->flags & (SDL_OPENGL | SDL_OPENGLBLIT)) ) { - p->y = [window_view frame].size.height - p->y - 1; - } + *p = [ window_view convertPoint:*p fromView: nil ]; + + /* The coordinates need to be inverted */ + p->y = [window_view frame].size.height - p->y - 1; } }