From a855d6202536ff28e5aae4f22a0f31d8f5b325d0 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 21 Jan 2017 15:18:31 -0500 Subject: Port of Duke Nukem 3D This ports Fabien Sanglard's Chocolate Duke to run on a version of SDL for Rockbox. Change-Id: I8f2c4c78af19de10c1633ed7bb7a997b43256dd9 --- apps/plugins/sdl/src/video/quartz/CGS.h | 84 + .../sdl/src/video/quartz/SDL_QuartzEvents.m | 1063 ++++++++++++ apps/plugins/sdl/src/video/quartz/SDL_QuartzGL.m | 292 ++++ apps/plugins/sdl/src/video/quartz/SDL_QuartzKeys.h | 146 ++ .../plugins/sdl/src/video/quartz/SDL_QuartzVideo.h | 229 +++ .../plugins/sdl/src/video/quartz/SDL_QuartzVideo.m | 1689 ++++++++++++++++++++ apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.h | 27 + apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.m | 444 +++++ .../sdl/src/video/quartz/SDL_QuartzWindow.h | 51 + .../sdl/src/video/quartz/SDL_QuartzWindow.m | 231 +++ 10 files changed, 4256 insertions(+) create mode 100644 apps/plugins/sdl/src/video/quartz/CGS.h create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzEvents.m create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzGL.m create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzKeys.h create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.h create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.m create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.h create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.m create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzWindow.h create mode 100644 apps/plugins/sdl/src/video/quartz/SDL_QuartzWindow.m (limited to 'apps/plugins/sdl/src/video/quartz') diff --git a/apps/plugins/sdl/src/video/quartz/CGS.h b/apps/plugins/sdl/src/video/quartz/CGS.h new file mode 100644 index 0000000000..abe47f7320 --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/CGS.h @@ -0,0 +1,84 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* + Obscuring code: maximum number of windows above ours (inclusive) + + Note: this doesn't work too well in practice and should be + phased out when we add OpenGL 2D acceleration. It was never + enabled in the first place, so this shouldn't be a problem ;-) +*/ +#define kMaxWindows 256 + +/* Some of the Core Graphics Server API for obscuring code */ +#define kCGSWindowLevelTop 2147483632 +#define kCGSWindowLevelDockIconDrag 500 +#define kCGSWindowLevelDockMenu 101 +#define kCGSWindowLevelMenuIgnore 21 +#define kCGSWindowLevelMenu 20 +#define kCGSWindowLevelDockLabel 12 +#define kCGSWindowLevelDockIcon 11 +#define kCGSWindowLevelDock 10 +#define kCGSWindowLevelUtility 3 +#define kCGSWindowLevelNormal 0 + +/* + For completeness; We never use these window levels, they are always below us + #define kCGSWindowLevelMBarShadow -20 + #define kCGSWindowLevelDesktopPicture -2147483647 + #define kCGSWindowLevelDesktop -2147483648 +*/ + +typedef CGError CGSError; +typedef long CGSWindowCount; +typedef void * CGSConnectionID; +typedef int CGSWindowID; +typedef CGSWindowID* CGSWindowIDList; +typedef CGWindowLevel CGSWindowLevel; +typedef NSRect CGSRect; + +extern CGSConnectionID _CGSDefaultConnection (); + +extern CGSError CGSGetOnScreenWindowList (CGSConnectionID cid, + CGSConnectionID owner, + CGSWindowCount listCapacity, + CGSWindowIDList list, + CGSWindowCount *listCount); + +extern CGSError CGSGetScreenRectForWindow (CGSConnectionID cid, + CGSWindowID wid, + CGSRect *rect); + +extern CGWindowLevel CGSGetWindowLevel (CGSConnectionID cid, + CGSWindowID wid, + CGSWindowLevel *level); + +extern CGSError CGSDisplayHWFill (CGDirectDisplayID id, unsigned int x, unsigned int y, + unsigned int w, unsigned int h, unsigned int color); + +extern CGSError CGSDisplayCanHWFill (CGDirectDisplayID id); + +extern CGSError CGSGetMouseEnabledFlags (CGSConnectionID cid, CGSWindowID wid, int *flags); + +int CGSDisplayHWSync (CGDirectDisplayID id); + diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzEvents.m b/apps/plugins/sdl/src/video/quartz/SDL_QuartzEvents.m new file mode 100644 index 0000000000..773eb010be --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzEvents.m @@ -0,0 +1,1063 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_QuartzVideo.h" +#include "SDL_QuartzWM.h" + +#include /* For wake from sleep detection */ +#include /* For wake from sleep detection */ +#include "SDL_QuartzKeys.h" + +/* + * On Leopard, this is missing from the 64-bit headers + */ +#if defined(__LP64__) && !defined(__POWER__) +/* + * Workaround for a bug in the 10.5 SDK: By accident, OSService.h does + * not include Power.h at all when compiling in 64bit mode. This has + * been fixed in 10.6, but for 10.5, we manually define UsrActivity + * to ensure compilation works. + */ +#define UsrActivity 1 +#endif + +/* + * 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) { + BOOL saw_layout = NO; + UInt32 state; + UInt32 value; + Uint16 i; + int world = SDLK_WORLD_0; + + for ( i=0; ikeysym map. However, it will not + work very well on international keyboard. Hence we now query MacOS + 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. + */ + +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1050) + if (TISCopyCurrentKeyboardLayoutInputSource != NULL) { + TISInputSourceRef src = TISCopyCurrentKeyboardLayoutInputSource(); + if (src != NULL) { + CFDataRef data = (CFDataRef) + TISGetInputSourceProperty(src, + kTISPropertyUnicodeKeyLayoutData); + if (data != NULL) { + const UCKeyboardLayout *layout = (const UCKeyboardLayout *) + CFDataGetBytePtr(data); + if (layout != NULL) { + const UInt32 kbdtype = LMGetKbdType(); + saw_layout = YES; + + /* Loop over all 127 possible scan codes */ + for (i = 0; i < 0x7F; i++) { + UniChar buf[16]; + UniCharCount count = 0; + + /* We pretend a clean start to begin with (i.e. no dead keys active */ + state = 0; + + if (UCKeyTranslate(layout, i, kUCKeyActionDown, 0, kbdtype, + 0, &state, 16, &count, buf) != noErr) { + continue; + } + + /* 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) { + if (UCKeyTranslate(layout, i, kUCKeyActionDown, 0, kbdtype, + 0, &state, 16, &count, buf) != noErr) { + continue; + } + } + + if (count != 1) { + continue; /* no multi-char. Use SDL 1.3 instead. :) */ + } + + value = (UInt32) buf[0]; + if (value >= 128) { + /* Some non-ASCII char, map it to SDLK_WORLD_* */ + if (world < 0xFF) { + keymap[i] = world++; + } + } else if (value >= 32) { /* non-control ASCII char */ + keymap[i] = value; + } + } + } + } + CFRelease(src); + } + } +#endif + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1050) + if (!saw_layout) { + /* Get a pointer to the systems cached KCHR */ + const void *KCHRPtr = (const 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 an ascii value, or 0. Try to figure out to which SDL symbol it maps */ + if (value >= 128) { /* Some non-ASCII char, map it to SDLK_WORLD_* */ + if (world < 0xFF) { + keymap[i] = world++; + } + } else if (value >= 32) { /* non-control ASCII char */ + keymap[i] = value; + } + } + } + } +#endif + + /* + 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[QZ_KP0] = SDLK_KP0; + keymap[QZ_KP1] = SDLK_KP1; + keymap[QZ_KP2] = SDLK_KP2; + keymap[QZ_KP3] = SDLK_KP3; + keymap[QZ_KP4] = SDLK_KP4; + keymap[QZ_KP5] = SDLK_KP5; + keymap[QZ_KP6] = SDLK_KP6; + keymap[QZ_KP7] = SDLK_KP7; + keymap[QZ_KP8] = SDLK_KP8; + keymap[QZ_KP9] = SDLK_KP9; + keymap[QZ_KP_MINUS] = SDLK_KP_MINUS; + keymap[QZ_KP_PLUS] = SDLK_KP_PLUS; + keymap[QZ_KP_PERIOD] = SDLK_KP_PERIOD; + keymap[QZ_KP_EQUALS] = SDLK_KP_EQUALS; + keymap[QZ_KP_DIVIDE] = SDLK_KP_DIVIDE; + keymap[QZ_KP_MULTIPLY] = SDLK_KP_MULTIPLY; + keymap[QZ_KP_ENTER] = SDLK_KP_ENTER; +} + +static void QZ_DoKey (_THIS, int state, NSEvent *event) { + + NSString *chars = NULL; + unsigned int i, numChars; + SDL_keysym key; + + /* + A key event can contain multiple characters, + or no characters at all. In most cases, it + will contain a single character. If it contains + 0 characters, we'll use 0 as the unicode. If it + contains multiple characters, we'll use 0 as + the scancode/keysym. + */ + if (SDL_TranslateUNICODE && state == SDL_PRESSED) { + [field_edit interpretKeyEvents:[NSArray arrayWithObject:event]]; + chars = [ event characters ]; + numChars = [ chars length ]; + if (numChars > 0) + [field_edit setString:@""]; + } else { + numChars = 0; + } + + if (numChars == 0) { + + key.scancode = [ event keyCode ]; + key.sym = keymap [ key.scancode ]; + key.unicode = 0; + key.mod = KMOD_NONE; + + SDL_PrivateKeyboard (state, &key); + } + else if (numChars >= 1) { + + key.scancode = [ event keyCode ]; + key.sym = keymap [ key.scancode ]; + key.unicode = [ chars characterAtIndex:0 ]; + key.mod = KMOD_NONE; + + SDL_PrivateKeyboard (state, &key); + + for (i = 1; i < numChars; i++) { + + key.scancode = 0; + key.sym = 0; + key.unicode = [ chars characterAtIndex:i]; + key.mod = KMOD_NONE; + + SDL_PrivateKeyboard (state, &key); + } + } + + if (SDL_getenv ("SDL_ENABLEAPPEVENTS")) + [ NSApp sendEvent:event ]; +} + +/* 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 }; + + int i; + int bit; + SDL_keysym key; + + key.scancode = 0; + key.sym = SDLK_UNKNOWN; + key.unicode = 0; + key.mod = KMOD_NONE; + + /* Iterate through the bits, testing each against the current modifiers */ + for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) { + + unsigned int currentMask, newMask; + + currentMask = current_mods & bit; + newMask = newMods & bit; + + if ( currentMask && + currentMask != newMask ) { /* modifier up event */ + + key.sym = mapping[i]; + /* If this was Caps Lock, we need some additional voodoo to make SDL happy */ + if (bit == NSAlphaShiftKeyMask) + SDL_PrivateKeyboard (SDL_PRESSED, &key); + SDL_PrivateKeyboard (SDL_RELEASED, &key); + } + else if ( newMask && + currentMask != newMask ) { /* modifier down event */ + + key.sym = mapping[i]; + SDL_PrivateKeyboard (SDL_PRESSED, &key); + /* If this was Caps Lock, we need some additional voodoo to make SDL happy */ + if (bit == NSAlphaShiftKeyMask) + 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; +} + +static void QZ_GetMouseLocation (_THIS, NSPoint *p) { + *p = [ NSEvent mouseLocation ]; /* global coordinates */ + if (qz_window) + QZ_PrivateGlobalToLocal (this, p); + QZ_PrivateCocoaToSDL (this, p); +} + +void QZ_DoActivate (_THIS) { + + SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS | (QZ_IsMouseInWindow (this) ? SDL_APPMOUSEFOCUS : 0)); + + QZ_UpdateCursor(this); + + /* Regrab input, only if it was previously grabbed */ + if ( current_grab_mode == SDL_GRAB_ON ) { + + /* Restore cursor location if input was grabbed */ + QZ_PrivateWarpCursor (this, cursor_loc.x, cursor_loc.y); + QZ_ChangeGrabState (this, QZ_ENABLE_GRAB); + } + else { + /* Update SDL's mouse location */ + NSPoint p; + QZ_GetMouseLocation (this, &p); + SDL_PrivateMouseMotion (0, 0, p.x, p.y); + } + + QZ_UpdateCursor(this); +} + +void QZ_DoDeactivate (_THIS) { + + SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS | SDL_APPMOUSEFOCUS); + + /* Get the current cursor location, for restore on activate */ + QZ_GetMouseLocation (this, &cursor_loc); + + /* Reassociate mouse and cursor */ + CGAssociateMouseAndMouseCursorPosition (1); + + QZ_UpdateCursor(this); +} + +void QZ_SleepNotificationHandler (void * refcon, + io_service_t service, + natural_t messageType, + void * messageArgument ) +{ + SDL_VideoDevice *this = (SDL_VideoDevice*)refcon; + + switch(messageType) + { + case kIOMessageSystemWillSleep: + IOAllowPowerChange(power_connection, (long) messageArgument); + break; + case kIOMessageCanSystemSleep: + IOAllowPowerChange(power_connection, (long) messageArgument); + break; + case kIOMessageSystemHasPoweredOn: + /* awake */ + SDL_PrivateExpose(); + break; + } +} + +void QZ_RegisterForSleepNotifications (_THIS) +{ + CFRunLoopSourceRef rls; + IONotificationPortRef thePortRef; + io_object_t notifier; + + power_connection = IORegisterForSystemPower (this, &thePortRef, QZ_SleepNotificationHandler, ¬ifier); + + if (power_connection == 0) + NSLog(@"SDL: QZ_SleepNotificationHandler() IORegisterForSystemPower failed."); + + rls = IONotificationPortGetRunLoopSource (thePortRef); + CFRunLoopAddSource (CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease (rls); +} + + +/* Try to map Quartz mouse buttons to SDL's lingo... */ +static int QZ_OtherMouseButtonToSDL(int button) +{ + switch (button) + { + case 0: + return(SDL_BUTTON_LEFT); /* 1 */ + case 1: + return(SDL_BUTTON_RIGHT); /* 3 */ + case 2: + return(SDL_BUTTON_MIDDLE); /* 2 */ + } + + /* >= 3: skip 4 & 5, since those are the SDL mousewheel buttons. */ + return(button + 3); +} + + +void QZ_PumpEvents (_THIS) +{ + int32_t dx, dy; + + NSDate *distantPast; + NSEvent *event; + NSRect winRect; + NSAutoreleasePool *pool; + + if (!SDL_VideoSurface) + return; /* don't do anything if there's no screen surface. */ + + /* Update activity every five seconds to prevent screensaver. --ryan. */ + if (!allow_screensaver) { + static Uint32 screensaverTicks; + Uint32 nowTicks = SDL_GetTicks(); + if ((nowTicks - screensaverTicks) > 5000) + { + UpdateSystemActivity(UsrActivity); + screensaverTicks = nowTicks; + } + } + + pool = [ [ NSAutoreleasePool alloc ] init ]; + distantPast = [ NSDate distantPast ]; + + winRect = NSMakeRect (0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h); + + /* while grabbed, accumulate all mouse moved events into one SDL mouse event */ + dx = 0; + dy = 0; + + do { + + /* Poll for an event. This will not block */ + event = [ NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:distantPast + inMode: NSDefaultRunLoopMode dequeue:YES ]; + if (event != nil) { + + int button; + unsigned int type; + BOOL isInGameWin; + + #define DO_MOUSE_DOWN(button) do { \ + if ( SDL_GetAppState() & SDL_APPMOUSEFOCUS ) { \ + SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); \ + expect_mouse_up |= 1<= winRect.size.width ) + p.x = winRect.size.width-1; + + if ( p.y >= winRect.size.height ) + p.y = winRect.size.height-1; + + QZ_PrivateWarpCursor (this, p.x, p.y); + } + else + if ( !isInGameWin && (SDL_GetAppState() & SDL_APPMOUSEFOCUS) ) { + + SDL_PrivateAppActive (0, SDL_APPMOUSEFOCUS); + + if (grab_state == QZ_INVISIBLE_GRAB) + /*The cursor has left the window even though it is + disassociated from the mouse (and therefore + shouldn't move): this can happen with Wacom + tablets, and it effectively breaks the grab, since + mouse down events now go to background + applications. The only possibility to avoid this + seems to be talking to the tablet driver + (AppleEvents) to constrain its mapped area to the + window, which may not be worth the effort. For + now, handle the condition more gracefully than + before by reassociating cursor and mouse until the + cursor enters the window again, making it obvious + to the user that the grab is broken.*/ + CGAssociateMouseAndMouseCursorPosition (1); + + QZ_UpdateCursor(this); + } + else + if ( isInGameWin && (SDL_GetAppState() & (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS)) == SDL_APPINPUTFOCUS ) { + + SDL_PrivateAppActive (1, SDL_APPMOUSEFOCUS); + + QZ_UpdateCursor(this); + + if (grab_state == QZ_INVISIBLE_GRAB) { /*see comment above*/ + QZ_PrivateWarpCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2); + CGAssociateMouseAndMouseCursorPosition (0); + } + } + break; + case NSScrollWheel: + if ( isInGameWin ) { + float dy, dx; + Uint8 button; + dy = [ event deltaY ]; + dx = [ event deltaX ]; + if ( dy > 0.0 ) /* Scroll up */ + button = SDL_BUTTON_WHEELUP; + else if ( dy < 0.0 ) /* Scroll down */ + button = SDL_BUTTON_WHEELDOWN; + else + break; /* Horizontal scroll */ + /* For now, wheel is sent as a quick down+up */ + SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); + SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0); + } + break; + case NSKeyUp: + QZ_DoKey (this, SDL_RELEASED, event); + break; + case NSKeyDown: + QZ_DoKey (this, SDL_PRESSED, event); + break; + case NSFlagsChanged: + break; + case NSAppKitDefined: + [ NSApp sendEvent:event ]; + if ([ event subtype ] == NSApplicationActivatedEventType && (mode_flags & SDL_FULLSCREEN)) { + /* the default handling of this event seems to reset any cursor set by [NSCursor set] (used by SDL_SetCursor() in fullscreen mode) to the default system arrow cursor */ + SDL_Cursor *sdlc = SDL_GetCursor(); + if (sdlc != NULL && sdlc->wm_cursor != NULL) { + [ sdlc->wm_cursor->nscursor set ]; + } + } + break; + /* case NSApplicationDefined: break; */ + /* case NSPeriodic: break; */ + /* case NSCursorUpdate: break; */ + default: + [ NSApp sendEvent:event ]; + } + } + } while (event != nil); + + /* handle accumulated mouse moved events */ + if (dx != 0 || dy != 0) + SDL_PrivateMouseMotion (0, 1, dx, dy); + + [ pool release ]; +} + +void QZ_UpdateMouse (_THIS) +{ + NSPoint p; + QZ_GetMouseLocation (this, &p); + SDL_PrivateAppActive (QZ_IsMouseInWindow (this), SDL_APPMOUSEFOCUS); + SDL_PrivateMouseMotion (0, 0, p.x, p.y); +} diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzGL.m b/apps/plugins/sdl/src/video/quartz/SDL_QuartzGL.m new file mode 100644 index 0000000000..d38dceb56b --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzGL.m @@ -0,0 +1,292 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_QuartzVideo.h" + +/* + * GL_ARB_Multisample is supposed to be available in 10.1, according to Apple: + * + * http://developer.apple.com/graphicsimaging/opengl/extensions.html#GL_ARB_multisample + * + * ...but it isn't in the system headers, according to Sam: + * + * http://lists.libsdl.org/pipermail/sdl-libsdl.org/2003-December/039794.html + * + * These are normally enums and not #defines in the system headers. + * + * --ryan. + */ +#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1020) +#define NSOpenGLPFASampleBuffers ((NSOpenGLPixelFormatAttribute) 55) +#define NSOpenGLPFASamples ((NSOpenGLPixelFormatAttribute) 56) +#endif + +#ifdef __powerpc__ /* we lost this in 10.6, which has no PPC support. */ +@implementation NSOpenGLContext (CGLContextAccess) +- (CGLContextObj) cglContext; +{ + return _contextAuxiliary; +} +@end +CGLContextObj QZ_GetCGLContextObj(NSOpenGLContext *nsctx) +{ + return [nsctx cglContext]; +} +#else +CGLContextObj QZ_GetCGLContextObj(NSOpenGLContext *nsctx) +{ + return (CGLContextObj) [nsctx CGLContextObj]; +} +#endif + + +/* OpenGL helper functions (used internally) */ + +int QZ_SetupOpenGL (_THIS, int bpp, Uint32 flags) { + + NSOpenGLPixelFormatAttribute attr[32]; + NSOpenGLPixelFormat *fmt; + int i = 0; + int colorBits = bpp; + + /* if a GL library hasn't been loaded at this point, load the default. */ + if (!this->gl_config.driver_loaded) { + if (QZ_GL_LoadLibrary(this, NULL) == -1) + return 0; + } + + if ( flags & SDL_FULLSCREEN ) { + + attr[i++] = NSOpenGLPFAFullScreen; + } + /* In windowed mode, the OpenGL pixel depth must match device pixel depth */ + else if ( colorBits != device_bpp ) { + + colorBits = device_bpp; + } + + attr[i++] = NSOpenGLPFAColorSize; + attr[i++] = colorBits; + + attr[i++] = NSOpenGLPFADepthSize; + attr[i++] = this->gl_config.depth_size; + + if ( this->gl_config.double_buffer ) { + attr[i++] = NSOpenGLPFADoubleBuffer; + } + + if ( this->gl_config.stereo ) { + attr[i++] = NSOpenGLPFAStereo; + } + + if ( this->gl_config.stencil_size != 0 ) { + attr[i++] = NSOpenGLPFAStencilSize; + attr[i++] = this->gl_config.stencil_size; + } + + if ( (this->gl_config.accum_red_size + + this->gl_config.accum_green_size + + this->gl_config.accum_blue_size + + this->gl_config.accum_alpha_size) > 0 ) { + attr[i++] = NSOpenGLPFAAccumSize; + attr[i++] = this->gl_config.accum_red_size + this->gl_config.accum_green_size + this->gl_config.accum_blue_size + this->gl_config.accum_alpha_size; + } + + if ( this->gl_config.multisamplebuffers != 0 ) { + attr[i++] = NSOpenGLPFASampleBuffers; + attr[i++] = this->gl_config.multisamplebuffers; + } + + if ( this->gl_config.multisamplesamples != 0 ) { + attr[i++] = NSOpenGLPFASamples; + attr[i++] = this->gl_config.multisamplesamples; + attr[i++] = NSOpenGLPFANoRecovery; + } + + if ( this->gl_config.accelerated > 0 ) { + attr[i++] = NSOpenGLPFAAccelerated; + } + + attr[i++] = NSOpenGLPFAScreenMask; + attr[i++] = CGDisplayIDToOpenGLDisplayMask (display_id); + attr[i] = 0; + + fmt = [ [ NSOpenGLPixelFormat alloc ] initWithAttributes:attr ]; + if (fmt == nil) { + SDL_SetError ("Failed creating OpenGL pixel format"); + return 0; + } + + gl_context = [ [ NSOpenGLContext alloc ] initWithFormat:fmt + shareContext:nil]; + + [ fmt release ]; + + if (gl_context == nil) { + SDL_SetError ("Failed creating OpenGL context"); + return 0; + } + + /* Synchronize QZ_GL_SwapBuffers() to vertical retrace. + * (Apple's documentation is not completely clear about what this setting + * exactly does, IMHO - for a detailed explanation see + * http://lists.apple.com/archives/mac-opengl/2006/Jan/msg00080.html ) + */ + if ( this->gl_config.swap_control >= 0 ) { + GLint value; + value = this->gl_config.swap_control; + [ gl_context setValues: &value forParameter: NSOpenGLCPSwapInterval ]; + } + + /* + * Wisdom from Apple engineer in reference to UT2003's OpenGL performance: + * "You are blowing a couple of the internal OpenGL function caches. This + * appears to be happening in the VAO case. You can tell OpenGL to up + * the cache size by issuing the following calls right after you create + * the OpenGL context. The default cache size is 16." --ryan. + */ + + #ifndef GLI_ARRAY_FUNC_CACHE_MAX + #define GLI_ARRAY_FUNC_CACHE_MAX 284 + #endif + + #ifndef GLI_SUBMIT_FUNC_CACHE_MAX + #define GLI_SUBMIT_FUNC_CACHE_MAX 280 + #endif + + { + GLint cache_max = 64; + CGLContextObj ctx = QZ_GetCGLContextObj(gl_context); + CGLSetParameter (ctx, GLI_SUBMIT_FUNC_CACHE_MAX, &cache_max); + CGLSetParameter (ctx, GLI_ARRAY_FUNC_CACHE_MAX, &cache_max); + } + + /* End Wisdom from Apple Engineer section. --ryan. */ + + return 1; +} + +void QZ_TearDownOpenGL (_THIS) { + + [ NSOpenGLContext clearCurrentContext ]; + [ gl_context clearDrawable ]; + [ gl_context release ]; +} + + +/* SDL OpenGL functions */ +static const char *DEFAULT_OPENGL_LIB_NAME = + "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"; + +int QZ_GL_LoadLibrary (_THIS, const char *location) { + if ( gl_context != NULL ) { + SDL_SetError("OpenGL context already created"); + return -1; + } + + if (opengl_library != NULL) + SDL_UnloadObject(opengl_library); + + if (location == NULL) + location = DEFAULT_OPENGL_LIB_NAME; + + opengl_library = SDL_LoadObject(location); + if (opengl_library != NULL) { + this->gl_config.driver_loaded = 1; + return 0; + } + + this->gl_config.driver_loaded = 0; + return -1; +} + +void* QZ_GL_GetProcAddress (_THIS, const char *proc) { + return SDL_LoadFunction(opengl_library, proc); +} + +int QZ_GL_GetAttribute (_THIS, SDL_GLattr attrib, int* value) { + + GLenum attr = 0; + + QZ_GL_MakeCurrent (this); + + switch (attrib) { + case SDL_GL_RED_SIZE: attr = GL_RED_BITS; break; + case SDL_GL_BLUE_SIZE: attr = GL_BLUE_BITS; break; + case SDL_GL_GREEN_SIZE: attr = GL_GREEN_BITS; break; + case SDL_GL_ALPHA_SIZE: attr = GL_ALPHA_BITS; break; + case SDL_GL_DOUBLEBUFFER: attr = GL_DOUBLEBUFFER; break; + case SDL_GL_DEPTH_SIZE: attr = GL_DEPTH_BITS; break; + case SDL_GL_STENCIL_SIZE: attr = GL_STENCIL_BITS; break; + case SDL_GL_ACCUM_RED_SIZE: attr = GL_ACCUM_RED_BITS; break; + case SDL_GL_ACCUM_GREEN_SIZE: attr = GL_ACCUM_GREEN_BITS; break; + case SDL_GL_ACCUM_BLUE_SIZE: attr = GL_ACCUM_BLUE_BITS; break; + case SDL_GL_ACCUM_ALPHA_SIZE: attr = GL_ACCUM_ALPHA_BITS; break; + case SDL_GL_STEREO: attr = GL_STEREO; break; + case SDL_GL_MULTISAMPLEBUFFERS: attr = GL_SAMPLE_BUFFERS_ARB; break; + case SDL_GL_MULTISAMPLESAMPLES: attr = GL_SAMPLES_ARB; break; + case SDL_GL_BUFFER_SIZE: + { + GLint bits = 0; + GLint component; + + /* there doesn't seem to be a single flag in OpenGL for this! */ + glGetIntegerv (GL_RED_BITS, &component); bits += component; + glGetIntegerv (GL_GREEN_BITS,&component); bits += component; + glGetIntegerv (GL_BLUE_BITS, &component); bits += component; + glGetIntegerv (GL_ALPHA_BITS, &component); bits += component; + + *value = bits; + return 0; + } + case SDL_GL_ACCELERATED_VISUAL: + { + GLint val; + /* FIXME: How do we get this information here? + [fmt getValues: &val forAttribute: NSOpenGLPFAAccelerated attr forVirtualScreen: 0]; + */ + val = (this->gl_config.accelerated != 0);; + *value = val; + return 0; + } + case SDL_GL_SWAP_CONTROL: + { + GLint val; + [ gl_context getValues: &val forParameter: NSOpenGLCPSwapInterval ]; + *value = val; + return 0; + } + } + + glGetIntegerv (attr, (GLint *)value); + return 0; +} + +int QZ_GL_MakeCurrent (_THIS) { + [ gl_context makeCurrentContext ]; + return 0; +} + +void QZ_GL_SwapBuffers (_THIS) { + [ gl_context flushBuffer ]; +} diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzKeys.h b/apps/plugins/sdl/src/video/quartz/SDL_QuartzKeys.h new file mode 100644 index 0000000000..82b78595e3 --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzKeys.h @@ -0,0 +1,146 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* These are the Macintosh key scancode constants -- from Inside Macintosh */ + +#define QZ_ESCAPE 0x35 +#define QZ_F1 0x7A +#define QZ_F2 0x78 +#define QZ_F3 0x63 +#define QZ_F4 0x76 +#define QZ_F5 0x60 +#define QZ_F6 0x61 +#define QZ_F7 0x62 +#define QZ_F8 0x64 +#define QZ_F9 0x65 +#define QZ_F10 0x6D +#define QZ_F11 0x67 +#define QZ_F12 0x6F +#define QZ_F13 0x69 +#define QZ_F14 0x6B +#define QZ_F15 0x71 +/* +#define QZ_PRINT 0x69 +#define QZ_SCROLLOCK 0x6B +#define QZ_PAUSE 0x71 +*/ +#define QZ_POWER 0x7F +#define QZ_BACKQUOTE 0x32 +#define QZ_1 0x12 +#define QZ_2 0x13 +#define QZ_3 0x14 +#define QZ_4 0x15 +#define QZ_5 0x17 +#define QZ_6 0x16 +#define QZ_7 0x1A +#define QZ_8 0x1C +#define QZ_9 0x19 +#define QZ_0 0x1D +#define QZ_MINUS 0x1B +#define QZ_EQUALS 0x18 +#define QZ_BACKSPACE 0x33 +#define QZ_INSERT 0x72 +#define QZ_HOME 0x73 +#define QZ_PAGEUP 0x74 +#define QZ_NUMLOCK 0x47 +#define QZ_KP_EQUALS 0x51 +#define QZ_KP_DIVIDE 0x4B +#define QZ_KP_MULTIPLY 0x43 +#define QZ_TAB 0x30 +#define QZ_q 0x0C +#define QZ_w 0x0D +#define QZ_e 0x0E +#define QZ_r 0x0F +#define QZ_t 0x11 +#define QZ_y 0x10 +#define QZ_u 0x20 +#define QZ_i 0x22 +#define QZ_o 0x1F +#define QZ_p 0x23 +#define QZ_LEFTBRACKET 0x21 +#define QZ_RIGHTBRACKET 0x1E +#define QZ_BACKSLASH 0x2A +#define QZ_DELETE 0x75 +#define QZ_END 0x77 +#define QZ_PAGEDOWN 0x79 +#define QZ_KP7 0x59 +#define QZ_KP8 0x5B +#define QZ_KP9 0x5C +#define QZ_KP_MINUS 0x4E +#define QZ_CAPSLOCK 0x39 +#define QZ_a 0x00 +#define QZ_s 0x01 +#define QZ_d 0x02 +#define QZ_f 0x03 +#define QZ_g 0x05 +#define QZ_h 0x04 +#define QZ_j 0x26 +#define QZ_k 0x28 +#define QZ_l 0x25 +#define QZ_SEMICOLON 0x29 +#define QZ_QUOTE 0x27 +#define QZ_RETURN 0x24 +#define QZ_KP4 0x56 +#define QZ_KP5 0x57 +#define QZ_KP6 0x58 +#define QZ_KP_PLUS 0x45 +#define QZ_LSHIFT 0x38 +#define QZ_z 0x06 +#define QZ_x 0x07 +#define QZ_c 0x08 +#define QZ_v 0x09 +#define QZ_b 0x0B +#define QZ_n 0x2D +#define QZ_m 0x2E +#define QZ_COMMA 0x2B +#define QZ_PERIOD 0x2F +#define QZ_SLASH 0x2C +#if 1 /* Panther now defines right side keys */ +#define QZ_RSHIFT 0x3C +#endif +#define QZ_UP 0x7E +#define QZ_KP1 0x53 +#define QZ_KP2 0x54 +#define QZ_KP3 0x55 +#define QZ_KP_ENTER 0x4C +#define QZ_LCTRL 0x3B +#define QZ_LALT 0x3A +#define QZ_LMETA 0x37 +#define QZ_SPACE 0x31 +#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 +#define QZ_RIGHT 0x7C +#define QZ_KP0 0x52 +#define QZ_KP_PERIOD 0x41 + +/* Wierd, these keys are on my iBook under Mac OS X */ +#define QZ_IBOOK_ENTER 0x34 +#define QZ_IBOOK_LEFT 0x3B +#define QZ_IBOOK_RIGHT 0x3C +#define QZ_IBOOK_DOWN 0x3D +#define QZ_IBOOK_UP 0x3E diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.h b/apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.h new file mode 100644 index 0000000000..7506e0cd10 --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.h @@ -0,0 +1,229 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* + @file SDL_QuartzVideo.h + @author Darrell Walisser, Max Horn, et al. + + @abstract SDL video driver for Mac OS X. + + @discussion + + TODO + - Hardware Cursor support with NSCursor instead of Carbon + - Keyboard repeat/mouse speed adjust (if needed) + - Multiple monitor support (currently only main display) + - Accelerated blitting support + - Fix white OpenGL window on minimize (fixed) (update: broken again on 10.2) + - Find out what events should be sent/ignored if window is minimized + - Find a way to deal with external resolution/depth switch while app is running + - Check accuracy of QZ_SetGamma() + Problems: + - OGL not working in full screen with software renderer + - SetColors sets palette correctly but clears framebuffer + - Crash in CG after several mode switches (I think this has been fixed) + - Retained windows don't draw their title bar quite right (OS Bug) (not using retained windows) + - Cursor in 8 bit modes is screwy (might just be Radeon PCI bug) (update: not just Radeon) + - Warping cursor delays mouse events for a fraction of a second, + there is a hack around this that helps a bit +*/ + +/* Needs to be first, so QuickTime.h doesn't include glext.h (10.4) */ +#include "SDL_opengl.h" + +#include +#include +#include /* For CGL functions and types */ +#include /* For powersave handling */ +#include + +#include "SDL_thread.h" +#include "SDL_video.h" +#include "SDL_error.h" +#include "SDL_timer.h" +#include "SDL_loadso.h" +#include "SDL_syswm.h" +#include "../SDL_sysvideo.h" +#include "../SDL_pixels_c.h" +#include "../../events/SDL_events_c.h" + + +#ifdef __powerpc__ +/* + This is a workaround to directly access NSOpenGLContext's CGL context + We need this to check for errors NSOpenGLContext doesn't support + Please note this is only used on PowerPC (Intel Macs are guaranteed to + have a better API for this, since it showed up in Mac OS X 10.3). +*/ +@interface NSOpenGLContext (CGLContextAccess) +- (CGLContextObj) cglContext; +@end +#endif + +/* use this to get the CGLContext; it handles Cocoa interface changes. */ +CGLContextObj QZ_GetCGLContextObj(NSOpenGLContext *nsctx); + + +/* Main driver structure to store required state information */ +typedef struct SDL_PrivateVideoData { + BOOL use_new_mode_apis; /* 1 == >= 10.6 APIs available */ + BOOL allow_screensaver; /* 0 == disable screensaver */ + CGDirectDisplayID display; /* 0 == main display (only support single display) */ + const void *mode; /* current mode of the display */ + const void *save_mode; /* original mode of the display */ + CGDirectPaletteRef palette; /* palette of an 8-bit display */ + NSOpenGLContext *gl_context; /* OpenGL rendering context */ + NSGraphicsContext *nsgfx_context; /* Cocoa graphics context */ + Uint32 width, height, bpp; /* frequently used data about the display */ + Uint32 flags; /* flags for current mode, for teardown purposes */ + Uint32 video_set; /* boolean; indicates if video was set correctly */ + Uint32 warp_flag; /* boolean; notify to event loop that a warp just occured */ + Uint32 warp_ticks; /* timestamp when the warp occured */ + NSWindow *window; /* Cocoa window to implement the SDL window */ + NSView *view; /* the window's view; draw 2D and OpenGL into this view */ + CGContextRef cg_context; /* CoreGraphics rendering context */ + SDL_Surface *resize_icon; /* icon for the resize badge, we have to draw it by hand */ + SDL_GrabMode current_grab_mode; /* default value is SDL_GRAB_OFF */ + SDL_Rect **client_mode_list; /* resolution list to pass back to client */ + SDLKey keymap[256]; /* Mac OS X to SDL key mapping */ + Uint32 current_mods; /* current keyboard modifiers, to track modifier state */ + NSText *field_edit; /* a field editor for keyboard composition processing */ + Uint32 last_virtual_button;/* last virtual mouse button pressed */ + io_connect_t power_connection; /* used with IOKit to detect wake from sleep */ + Uint8 expect_mouse_up; /* used to determine when to send mouse up events */ + Uint8 grab_state; /* used to manage grab behavior */ + NSPoint cursor_loc; /* saved cursor coords, for activate/deactivate when grabbed */ + BOOL cursor_should_be_visible; /* tells if cursor is supposed to be visible (SDL_ShowCursor) */ + BOOL cursor_visible; /* tells if cursor is *actually* visible or not */ + Uint8* sw_buffers[2]; /* pointers to the two software buffers for double-buffer emulation */ + SDL_Thread *thread; /* thread for async updates to the screen */ + SDL_sem *sem1, *sem2; /* synchronization for async screen updates */ + Uint8 *current_buffer; /* the buffer being copied to the screen */ + BOOL quit_thread; /* used to quit the async blitting thread */ + SInt32 system_version; /* used to dis-/enable workarounds depending on the system version */ + + void *opengl_library; /* dynamically loaded OpenGL library. */ +} SDL_PrivateVideoData; + +#define _THIS SDL_VideoDevice *this +#define display_id (this->hidden->display) +#define mode (this->hidden->mode) +#define save_mode (this->hidden->save_mode) +#define use_new_mode_apis (this->hidden->use_new_mode_apis) +#define allow_screensaver (this->hidden->allow_screensaver) +#define palette (this->hidden->palette) +#define gl_context (this->hidden->gl_context) +#define nsgfx_context (this->hidden->nsgfx_context) +#define device_width (this->hidden->width) +#define device_height (this->hidden->height) +#define device_bpp (this->hidden->bpp) +#define mode_flags (this->hidden->flags) +#define qz_window (this->hidden->window) +#define window_view (this->hidden->view) +#define cg_context (this->hidden->cg_context) +#define video_set (this->hidden->video_set) +#define warp_ticks (this->hidden->warp_ticks) +#define warp_flag (this->hidden->warp_flag) +#define resize_icon (this->hidden->resize_icon) +#define current_grab_mode (this->hidden->current_grab_mode) +#define client_mode_list (this->hidden->client_mode_list) +#define keymap (this->hidden->keymap) +#define current_mods (this->hidden->current_mods) +#define field_edit (this->hidden->field_edit) +#define last_virtual_button (this->hidden->last_virtual_button) +#define power_connection (this->hidden->power_connection) +#define expect_mouse_up (this->hidden->expect_mouse_up) +#define grab_state (this->hidden->grab_state) +#define cursor_loc (this->hidden->cursor_loc) +#define cursor_should_be_visible (this->hidden->cursor_should_be_visible) +#define cursor_visible (this->hidden->cursor_visible) +#define sw_buffers (this->hidden->sw_buffers) +#define sw_contexts (this->hidden->sw_contexts) +#define thread (this->hidden->thread) +#define sem1 (this->hidden->sem1) +#define sem2 (this->hidden->sem2) +#define current_buffer (this->hidden->current_buffer) +#define quit_thread (this->hidden->quit_thread) +#define system_version (this->hidden->system_version) +#define opengl_library (this->hidden->opengl_library) + +/* grab states - the input is in one of these states */ +enum { + QZ_UNGRABBED = 0, + QZ_VISIBLE_GRAB, + QZ_INVISIBLE_GRAB +}; + +/* grab actions - these can change the grabbed state */ +enum { + QZ_ENABLE_GRAB = 0, + QZ_DISABLE_GRAB, + QZ_HIDECURSOR, + QZ_SHOWCURSOR +}; + +/* Gamma Functions */ +int QZ_SetGamma (_THIS, float red, float green, float blue); +int QZ_GetGamma (_THIS, float *red, float *green, float *blue); +int QZ_SetGammaRamp (_THIS, Uint16 *ramp); +int QZ_GetGammaRamp (_THIS, Uint16 *ramp); + +/* OpenGL functions */ +int QZ_SetupOpenGL (_THIS, int bpp, Uint32 flags); +void QZ_TearDownOpenGL (_THIS); +void* QZ_GL_GetProcAddress (_THIS, const char *proc); +int QZ_GL_GetAttribute (_THIS, SDL_GLattr attrib, int* value); +int QZ_GL_MakeCurrent (_THIS); +void QZ_GL_SwapBuffers (_THIS); +int QZ_GL_LoadLibrary (_THIS, const char *location); + +/* Cursor and Mouse functions */ +void QZ_FreeWMCursor (_THIS, WMcursor *cursor); +WMcursor* QZ_CreateWMCursor (_THIS, Uint8 *data, Uint8 *mask, + int w, int h, int hot_x, int hot_y); +int QZ_ShowWMCursor (_THIS, WMcursor *cursor); +void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y); +void QZ_MoveWMCursor (_THIS, int x, int y); +void QZ_CheckMouseMode (_THIS); +void QZ_UpdateMouse (_THIS); + +/* Event functions */ +void QZ_InitOSKeymap (_THIS); +void QZ_PumpEvents (_THIS); + +/* Window Manager functions */ +void QZ_SetCaption (_THIS, const char *title, const char *icon); +void QZ_SetIcon (_THIS, SDL_Surface *icon, Uint8 *mask); +int QZ_IconifyWindow (_THIS); +SDL_GrabMode QZ_GrabInput (_THIS, SDL_GrabMode grab_mode); +/*int QZ_GetWMInfo (_THIS, SDL_SysWMinfo *info);*/ + +/* Private functions (used internally) */ +void QZ_PrivateWarpCursor (_THIS, int x, int y); +void QZ_ChangeGrabState (_THIS, int action); +void QZ_RegisterForSleepNotifications (_THIS); +void QZ_PrivateGlobalToLocal (_THIS, NSPoint *p); +void QZ_PrivateCocoaToSDL (_THIS, NSPoint *p); +BOOL QZ_IsMouseInWindow (_THIS); +void QZ_DoActivate (_THIS); +void QZ_DoDeactivate (_THIS); diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.m b/apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.m new file mode 100644 index 0000000000..fa04e9d107 --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzVideo.m @@ -0,0 +1,1689 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_QuartzVideo.h" +#include "SDL_QuartzWindow.h" + +/* These APIs aren't just deprecated; they're gone from the headers in the + 10.7 SDK. If we're using a >= 10.7 SDK, but targeting < 10.7, then we + force these function declarations. */ +#if ((MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && (MAC_OS_X_VERSION_MAX_ALLOWED >= 1070)) +CG_EXTERN void *CGDisplayBaseAddress(CGDirectDisplayID display) + CG_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_6, + __IPHONE_NA, __IPHONE_NA); +CG_EXTERN size_t CGDisplayBytesPerRow(CGDirectDisplayID display) + CG_AVAILABLE_BUT_DEPRECATED(__MAC_10_0, __MAC_10_6, + __IPHONE_NA, __IPHONE_NA); +#endif + + +static inline BOOL IS_LION_OR_LATER(_THIS) +{ + return (system_version >= 0x1070); +} + +static inline BOOL IS_SNOW_LEOPARD_OR_LATER(_THIS) +{ + return (system_version >= 0x1060); +} + +#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) && !defined(__LP64__) /* Fixed in Snow Leopard */ +/* + Add methods to get at private members of NSScreen. + Since there is a bug in Apple's screen switching code + that does not update this variable when switching + to fullscreen, we'll set it manually (but only for the + main screen). +*/ +@interface NSScreen (NSScreenAccess) +- (void) setFrame:(NSRect)frame; +@end + +@implementation NSScreen (NSScreenAccess) +- (void) setFrame:(NSRect)frame; +{ + _frame = frame; +} +@end +static inline void QZ_SetFrame(_THIS, NSScreen *nsscreen, NSRect frame) +{ + if (!IS_SNOW_LEOPARD_OR_LATER(this)) { + [nsscreen setFrame:frame]; + } +} +#else +static inline void QZ_SetFrame(_THIS, NSScreen *nsscreen, NSRect frame) +{ +} +#endif + +@interface SDLTranslatorResponder : NSTextView +{ +} +- (void) doCommandBySelector:(SEL)myselector; +@end + +@implementation SDLTranslatorResponder +- (void) doCommandBySelector:(SEL) myselector {} +@end + +/* absent in 10.3.9. */ +CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef); + +/* Bootstrap functions */ +static int QZ_Available (); +static SDL_VideoDevice* QZ_CreateDevice (int device_index); +static void QZ_DeleteDevice (SDL_VideoDevice *device); + +/* Initialization, Query, Setup, and Redrawing functions */ +static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format); + +static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, + Uint32 flags); +static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop, BOOL save_gl); + +static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, + int width, int height, int bpp, + Uint32 flags); +static int QZ_ToggleFullScreen (_THIS, int on); +static int QZ_SetColors (_THIS, int first_color, + int num_colors, SDL_Color *colors); + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) +static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface); +static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface); +static int QZ_ThreadFlip (_THIS); +static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface); +static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects); +static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects); +#endif + +static void QZ_UpdateRects (_THIS, int num_rects, SDL_Rect *rects); +static void QZ_VideoQuit (_THIS); + +static int QZ_LockHWSurface(_THIS, SDL_Surface *surface); +static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface); +static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface); +static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface); + +/* Bootstrap binding, enables entry point into the driver */ +VideoBootStrap QZ_bootstrap = { + "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice +}; + +/* Disable compiler warnings we can't avoid. */ +#if (defined(__GNUC__) && (__GNUC__ >= 4)) +# if (MAC_OS_X_VERSION_MAX_ALLOWED <= 1070) +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# endif +#endif + +static void QZ_ReleaseDisplayMode(_THIS, const void *moderef) +{ + /* we only own these references in the 10.6+ API. */ +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) + if (use_new_mode_apis) { + CGDisplayModeRelease((CGDisplayModeRef) moderef); + } +#endif +} + +static void QZ_ReleaseDisplayModeList(_THIS, CFArrayRef mode_list) +{ + /* we only own these references in the 10.6+ API. */ +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) + if (use_new_mode_apis) { + CFRelease(mode_list); + } +#endif +} + + +/* Bootstrap functions */ +static int QZ_Available () +{ + return 1; +} + +static SDL_VideoDevice* QZ_CreateDevice (int device_index) +{ +#pragma unused (device_index) + + SDL_VideoDevice *device; + SDL_PrivateVideoData *hidden; + + device = (SDL_VideoDevice*) SDL_malloc (sizeof (*device) ); + hidden = (SDL_PrivateVideoData*) SDL_malloc (sizeof (*hidden) ); + + if (device == NULL || hidden == NULL) + SDL_OutOfMemory (); + + SDL_memset (device, 0, sizeof (*device) ); + SDL_memset (hidden, 0, sizeof (*hidden) ); + + device->hidden = hidden; + + device->VideoInit = QZ_VideoInit; + device->ListModes = QZ_ListModes; + device->SetVideoMode = QZ_SetVideoMode; + device->ToggleFullScreen = QZ_ToggleFullScreen; + device->UpdateMouse = QZ_UpdateMouse; + device->SetColors = QZ_SetColors; + /* device->UpdateRects = QZ_UpdateRects; this is determined by SetVideoMode() */ + device->VideoQuit = QZ_VideoQuit; + + device->LockHWSurface = QZ_LockHWSurface; + device->UnlockHWSurface = QZ_UnlockHWSurface; + device->AllocHWSurface = QZ_AllocHWSurface; + device->FreeHWSurface = QZ_FreeHWSurface; + + device->SetGamma = QZ_SetGamma; + device->GetGamma = QZ_GetGamma; + device->SetGammaRamp = QZ_SetGammaRamp; + device->GetGammaRamp = QZ_GetGammaRamp; + + device->GL_GetProcAddress = QZ_GL_GetProcAddress; + device->GL_GetAttribute = QZ_GL_GetAttribute; + device->GL_MakeCurrent = QZ_GL_MakeCurrent; + device->GL_SwapBuffers = QZ_GL_SwapBuffers; + device->GL_LoadLibrary = QZ_GL_LoadLibrary; + + device->FreeWMCursor = QZ_FreeWMCursor; + device->CreateWMCursor = QZ_CreateWMCursor; + device->ShowWMCursor = QZ_ShowWMCursor; + device->WarpWMCursor = QZ_WarpWMCursor; + device->MoveWMCursor = QZ_MoveWMCursor; + device->CheckMouseMode = QZ_CheckMouseMode; + device->InitOSKeymap = QZ_InitOSKeymap; + device->PumpEvents = QZ_PumpEvents; + + device->SetCaption = QZ_SetCaption; + device->SetIcon = QZ_SetIcon; + device->IconifyWindow = QZ_IconifyWindow; + /*device->GetWMInfo = QZ_GetWMInfo;*/ + device->GrabInput = QZ_GrabInput; + + /* + * This is a big hassle, needing QuickDraw and QuickTime on older + * systems, and god knows what on 10.6, so we immediately fail here, + * which causes SDL to make an RGB surface and manage the YUV overlay + * in software. Sorry. Use SDL 1.3 if you want YUV rendering in a pixel + * shader. :) + */ + /*device->CreateYUVOverlay = QZ_CreateYUVOverlay;*/ + + device->free = QZ_DeleteDevice; + + return device; +} + +static void QZ_DeleteDevice (SDL_VideoDevice *device) +{ + _THIS = device; + QZ_ReleaseDisplayMode(this, save_mode); + QZ_ReleaseDisplayMode(this, mode); + SDL_free (device->hidden); + SDL_free (device); +} + +static void QZ_GetModeInfo(_THIS, const void *_mode, Uint32 *w, Uint32 *h, Uint32 *bpp) +{ + *w = *h = *bpp = 0; + if (_mode == NULL) { + return; + } + +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) + if (use_new_mode_apis) { + CGDisplayModeRef vidmode = (CGDisplayModeRef) _mode; + CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode); + + *w = (Uint32) CGDisplayModeGetWidth(vidmode); + *h = (Uint32) CGDisplayModeGetHeight(vidmode); + + /* we only care about the 32-bit modes... */ + if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + *bpp = 32; + } + + CFRelease(fmt); + } +#endif + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060) + if (!use_new_mode_apis) { + CFDictionaryRef vidmode = (CFDictionaryRef) _mode; + CFNumberGetValue ( + CFDictionaryGetValue (vidmode, kCGDisplayBitsPerPixel), + kCFNumberSInt32Type, bpp); + + CFNumberGetValue ( + CFDictionaryGetValue (vidmode, kCGDisplayWidth), + kCFNumberSInt32Type, w); + + CFNumberGetValue ( + CFDictionaryGetValue (vidmode, kCGDisplayHeight), + kCFNumberSInt32Type, h); + } +#endif + + /* we only care about the 32-bit modes... */ + if (*bpp != 32) { + *bpp = 0; + } +} + +static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format) +{ + NSRect r = NSMakeRect(0.0, 0.0, 0.0, 0.0); + const char *env = NULL; + + if ( Gestalt(gestaltSystemVersion, &system_version) != noErr ) + system_version = 0; + + use_new_mode_apis = NO; + +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) + use_new_mode_apis = IS_SNOW_LEOPARD_OR_LATER(this); +#endif + + /* Initialize the video settings; this data persists between mode switches */ + display_id = kCGDirectMainDisplay; + +#if 0 /* The mouse event code needs to take this into account... */ + env = getenv("SDL_VIDEO_FULLSCREEN_DISPLAY"); + if ( env ) { + int monitor = SDL_atoi(env); + CGDirectDisplayID activeDspys [3]; + CGDisplayCount dspyCnt; + CGGetActiveDisplayList (3, activeDspys, &dspyCnt); + if ( monitor >= 0 && monitor < dspyCnt ) { + display_id = activeDspys[monitor]; + } + } +#endif + +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) + if (use_new_mode_apis) { + save_mode = CGDisplayCopyDisplayMode(display_id); + } +#endif + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060) + if (!use_new_mode_apis) { + save_mode = CGDisplayCurrentMode(display_id); + } +#endif + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) + if (!IS_LION_OR_LATER(this)) { + palette = CGPaletteCreateDefaultColorPalette(); + } +#endif + + if (save_mode == NULL) { + SDL_SetError("Couldn't figure out current display mode."); + return -1; + } + + /* Allow environment override of screensaver disable. */ + env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER"); + if ( env ) { + allow_screensaver = SDL_atoi(env); + } else { +#ifdef SDL_VIDEO_DISABLE_SCREENSAVER + allow_screensaver = 0; +#else + allow_screensaver = 1; +#endif + } + + /* Gather some information that is useful to know about the display */ + QZ_GetModeInfo(this, save_mode, &device_width, &device_height, &device_bpp); + if (device_bpp == 0) { + QZ_ReleaseDisplayMode(this, save_mode); + save_mode = NULL; + SDL_SetError("Unsupported display mode"); + return -1; + } + + /* Determine the current screen size */ + this->info.current_w = device_width; + this->info.current_h = device_height; + + /* Determine the default screen depth */ + video_format->BitsPerPixel = device_bpp; + + /* Set misc globals */ + current_grab_mode = SDL_GRAB_OFF; + cursor_should_be_visible = YES; + cursor_visible = YES; + current_mods = 0; + field_edit = [[SDLTranslatorResponder alloc] initWithFrame:r]; + + /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */ + QZ_RegisterForSleepNotifications (this); + + /* Fill in some window manager capabilities */ + this->info.wm_available = 1; + + return 0; +} + +static SDL_Rect** QZ_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags) +{ + CFArrayRef mode_list = NULL; /* list of available fullscreen modes */ + CFIndex num_modes; + CFIndex i; + + int list_size = 0; + + /* Any windowed mode is acceptable */ + if ( (flags & SDL_FULLSCREEN) == 0 ) + return (SDL_Rect**)-1; + + /* Free memory from previous call, if any */ + if ( client_mode_list != NULL ) { + int i; + + for (i = 0; client_mode_list[i] != NULL; i++) + SDL_free (client_mode_list[i]); + + SDL_free (client_mode_list); + client_mode_list = NULL; + } + +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) + if (use_new_mode_apis) { + mode_list = CGDisplayCopyAllDisplayModes(display_id, NULL); + } +#endif + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060) + if (!use_new_mode_apis) { + mode_list = CGDisplayAvailableModes(display_id); + } +#endif + + num_modes = CFArrayGetCount (mode_list); + + /* Build list of modes with the requested bpp */ + for (i = 0; i < num_modes; i++) { + Uint32 width, height, bpp; + const void *onemode = CFArrayGetValueAtIndex(mode_list, i); + + QZ_GetModeInfo(this, onemode, &width, &height, &bpp); + + if (bpp && (bpp == format->BitsPerPixel)) { + int hasMode = SDL_FALSE; + int i; + + /* Check if mode is already in the list */ + for (i = 0; i < list_size; i++) { + if (client_mode_list[i]->w == width && + client_mode_list[i]->h == height) { + hasMode = SDL_TRUE; + break; + } + } + + /* Grow the list and add mode to the list */ + if ( ! hasMode ) { + SDL_Rect *rect; + + list_size++; + + if (client_mode_list == NULL) + client_mode_list = (SDL_Rect**) + SDL_malloc (sizeof(*client_mode_list) * (list_size+1) ); + else { + /* !!! FIXME: this leaks memory if SDL_realloc() fails! */ + client_mode_list = (SDL_Rect**) + SDL_realloc (client_mode_list, sizeof(*client_mode_list) * (list_size+1)); + } + + rect = (SDL_Rect*) SDL_malloc (sizeof(**client_mode_list)); + + if (client_mode_list == NULL || rect == NULL) { + QZ_ReleaseDisplayModeList(this, mode_list); + SDL_OutOfMemory (); + return NULL; + } + + rect->x = rect->y = 0; + rect->w = width; + rect->h = height; + + client_mode_list[list_size-1] = rect; + client_mode_list[list_size] = NULL; + } + } + } + + QZ_ReleaseDisplayModeList(this, mode_list); + + /* Sort list largest to smallest (by area) */ + { + int i, j; + for (i = 0; i < list_size; i++) { + for (j = 0; j < list_size-1; j++) { + + int area1, area2; + area1 = client_mode_list[j]->w * client_mode_list[j]->h; + area2 = client_mode_list[j+1]->w * client_mode_list[j+1]->h; + + if (area1 < area2) { + SDL_Rect *tmp = client_mode_list[j]; + client_mode_list[j] = client_mode_list[j+1]; + client_mode_list[j+1] = tmp; + } + } + } + } + + return client_mode_list; +} + +static SDL_bool QZ_WindowPosition(_THIS, int *x, int *y) +{ + const char *window = getenv("SDL_VIDEO_WINDOW_POS"); + if ( window ) { + if ( sscanf(window, "%d,%d", x, y) == 2 ) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + +static CGError QZ_SetDisplayMode(_THIS, const void *vidmode) +{ +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) + if (use_new_mode_apis) { + return CGDisplaySetDisplayMode(display_id, (CGDisplayModeRef) vidmode, NULL); + } +#endif + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060) + if (!use_new_mode_apis) { + return CGDisplaySwitchToMode(display_id, (CFDictionaryRef) vidmode); + } +#endif + + return kCGErrorFailure; +} + +static inline CGError QZ_RestoreDisplayMode(_THIS) +{ + return QZ_SetDisplayMode(this, save_mode); +} + +static void QZ_UnsetVideoMode (_THIS, BOOL to_desktop, BOOL save_gl) +{ + /* Reset values that may change between switches */ + this->info.blit_fill = 0; + this->FillHWRect = NULL; + this->UpdateRects = NULL; + this->LockHWSurface = NULL; + this->UnlockHWSurface = NULL; + + if (cg_context) { + CGContextFlush (cg_context); + CGContextRelease (cg_context); + cg_context = nil; + } + + /* Release fullscreen resources */ + if ( mode_flags & SDL_FULLSCREEN ) { + + NSRect screen_rect; + + /* Release double buffer stuff */ +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) + if ( !IS_LION_OR_LATER(this) && (mode_flags & SDL_DOUBLEBUF) ) { + quit_thread = YES; + SDL_SemPost (sem1); + SDL_WaitThread (thread, NULL); + SDL_DestroySemaphore (sem1); + SDL_DestroySemaphore (sem2); + SDL_free (sw_buffers[0]); + } +#endif + + /* If we still have a valid window, close it. */ + if ( qz_window ) { + NSCAssert([ qz_window delegate ] == nil, @"full screen window shouldn't have a delegate"); /* if that should ever change, we'd have to release it here */ + [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */ + qz_window = nil; + window_view = nil; + } + /* + Release the OpenGL context + Do this first to avoid trash on the display before fade + */ + if ( mode_flags & SDL_OPENGL ) { + if (!save_gl) { + QZ_TearDownOpenGL (this); + } + + #ifdef __powerpc__ /* we only use this for pre-10.3 compatibility. */ + CGLSetFullScreen (NULL); + #endif + } + if (to_desktop) { + /* !!! FIXME: keep an eye on this. + * This API is officially unavailable for 64-bit binaries. + * It happens to work, as of 10.7, but we're going to see if + * we can just simply do without it on newer OSes... + */ + #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__) + if ( !IS_LION_OR_LATER(this) ) { + ShowMenuBar (); + } + #endif + + /* Restore original screen resolution/bpp */ + QZ_RestoreDisplayMode (this); + CGReleaseAllDisplays (); + /* + Reset the main screen's rectangle + See comment in QZ_SetVideoFullscreen for why we do this + */ + screen_rect = NSMakeRect(0,0,device_width,device_height); + QZ_SetFrame(this, [ NSScreen mainScreen ], screen_rect); + } + } + /* Release window mode resources */ + else { + id delegate = [ qz_window delegate ]; + [ qz_window close ]; /* includes release because [qz_window isReleasedWhenClosed] */ + if (delegate != nil) [ delegate release ]; + qz_window = nil; + window_view = nil; + + /* Release the OpenGL context */ + if ( mode_flags & SDL_OPENGL ) { + if (!save_gl) { + QZ_TearDownOpenGL (this); + } + } + } + + /* Signal successful teardown */ + video_set = SDL_FALSE; +} + +static const void *QZ_BestMode(_THIS, const int bpp, const int w, const int h) +{ + const void *best = NULL; + + if (bpp == 0) { + return NULL; + } + +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060) + if (use_new_mode_apis) { + /* apparently, we have to roll our own now. :/ */ + CFArrayRef mode_list = CGDisplayCopyAllDisplayModes(display_id, NULL); + if (mode_list != NULL) { + const CFIndex num_modes = CFArrayGetCount(mode_list); + CFIndex i; + for (i = 0; i < num_modes; i++) { + const void *vidmode = CFArrayGetValueAtIndex(mode_list, i); + Uint32 thisw, thish, thisbpp; + QZ_GetModeInfo(this, vidmode, &thisw, &thish, &thisbpp); + + /* We only care about exact matches, apparently. */ + if ((thisbpp == bpp) && (thisw == w) && (thish == h)) { + best = vidmode; + break; /* got it! */ + } + } + CGDisplayModeRetain((CGDisplayModeRef) best); /* NULL is ok */ + CFRelease(mode_list); + } + } +#endif + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1060) + if (!use_new_mode_apis) { + boolean_t exact = 0; + best = CGDisplayBestModeForParameters(display_id, bpp, w, h, &exact); + if (!exact) { + best = NULL; + } + } +#endif + + return best; +} + +static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int width, + int height, int bpp, Uint32 flags, + const BOOL save_gl) +{ + const BOOL isLion = IS_LION_OR_LATER(this); + NSRect screen_rect; + CGError error; + NSRect contentRect; + CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; + + current->flags = SDL_FULLSCREEN; + current->w = width; + current->h = height; + + contentRect = NSMakeRect (0, 0, width, height); + + /* Fade to black to hide resolution-switching flicker (and garbage + that is displayed by a destroyed OpenGL context, if applicable) */ + if ( CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess ) { + CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); + } + + /* Destroy any previous mode */ + if (video_set == SDL_TRUE) + QZ_UnsetVideoMode (this, FALSE, save_gl); + + /* Sorry, QuickDraw was ripped out. */ + if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) { + SDL_SetError ("Embedded QuickDraw windows are no longer supported"); + goto ERR_NO_MATCH; + } + + QZ_ReleaseDisplayMode(this, mode); /* NULL is okay. */ + + /* See if requested mode exists */ + mode = QZ_BestMode(this, bpp, width, height); + + /* Require an exact match to the requested mode */ + if ( mode == NULL ) { + SDL_SetError ("Failed to find display resolution: %dx%dx%d", width, height, bpp); + goto ERR_NO_MATCH; + } + + /* Put up the blanking window (a window above all other windows) */ + if (getenv ("SDL_SINGLEDISPLAY")) + error = CGDisplayCapture (display_id); + else + error = CGCaptureAllDisplays (); + + if ( CGDisplayNoErr != error ) { + SDL_SetError ("Failed capturing display"); + goto ERR_NO_CAPTURE; + } + + /* Do the physical switch */ + if ( CGDisplayNoErr != QZ_SetDisplayMode(this, mode) ) { + SDL_SetError ("Failed switching display resolution"); + goto ERR_NO_SWITCH; + } + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) + if ( !isLion ) { + current->pixels = (Uint32*) CGDisplayBaseAddress (display_id); + current->pitch = CGDisplayBytesPerRow (display_id); + + current->flags |= SDL_HWSURFACE; + current->flags |= SDL_PREALLOC; + /* current->hwdata = (void *) CGDisplayGetDrawingContext (display_id); */ + + this->UpdateRects = QZ_DirectUpdate; + this->LockHWSurface = QZ_LockHWSurface; + this->UnlockHWSurface = QZ_UnlockHWSurface; + + /* Setup double-buffer emulation */ + if ( flags & SDL_DOUBLEBUF ) { + + /* + Setup a software backing store for reasonable results when + double buffering is requested (since a single-buffered hardware + surface looks hideous). + + The actual screen blit occurs in a separate thread to allow + other blitting while waiting on the VBL (and hence results in higher framerates). + */ + this->LockHWSurface = NULL; + this->UnlockHWSurface = NULL; + this->UpdateRects = NULL; + + current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF); + this->UpdateRects = QZ_DoubleBufferUpdate; + this->LockHWSurface = QZ_LockDoubleBuffer; + this->UnlockHWSurface = QZ_UnlockDoubleBuffer; + this->FlipHWSurface = QZ_FlipDoubleBuffer; + + current->pixels = SDL_malloc (current->pitch * current->h * 2); + if (current->pixels == NULL) { + SDL_OutOfMemory (); + goto ERR_DOUBLEBUF; + } + + sw_buffers[0] = current->pixels; + sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h; + + quit_thread = NO; + sem1 = SDL_CreateSemaphore (0); + sem2 = SDL_CreateSemaphore (1); + thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this); + } + + if ( CGDisplayCanSetPalette (display_id) ) + current->flags |= SDL_HWPALETTE; + } +#endif + + /* Check if we should recreate the window */ + if (qz_window == nil) { + /* Manually create a window, avoids having a nib file resource */ + qz_window = [ [ SDL_QuartzWindow alloc ] + initWithContentRect:contentRect + styleMask:(isLion ? NSBorderlessWindowMask : 0) + backing:NSBackingStoreBuffered + defer:NO ]; + + if (qz_window != nil) { + [ qz_window setAcceptsMouseMovedEvents:YES ]; + [ qz_window setViewsNeedDisplay:NO ]; + if (isLion) { + [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ]; + } + } + } + /* We already have a window, just change its size */ + else { + [ qz_window setContentSize:contentRect.size ]; + current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags; + [ window_view setFrameSize:contentRect.size ]; + } + + /* Setup OpenGL for a fullscreen context */ + if (flags & SDL_OPENGL) { + + if ( ! save_gl ) { + if ( ! QZ_SetupOpenGL (this, bpp, flags) ) { + goto ERR_NO_GL; + } + } + + /* Initialize the NSView and add it to our window. The presence of a valid window and + view allow the cursor to be changed whilst in fullscreen.*/ + window_view = [ [ NSView alloc ] initWithFrame:contentRect ]; + + if ( isLion ) { + [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; + } + + [ [ qz_window contentView ] addSubview:window_view ]; + + /* Apparently Lion checks some version flag set by the linker + and changes API behavior. Annoying. */ + if ( isLion ) { + [ qz_window setLevel:CGShieldingWindowLevel() ]; + [ gl_context setView: window_view ]; + //[ gl_context setFullScreen ]; + [ gl_context update ]; + } + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) + if ( !isLion ) { + CGLError err; + CGLContextObj ctx; + + [ qz_window setLevel:NSNormalWindowLevel ]; + ctx = QZ_GetCGLContextObj (gl_context); + err = CGLSetFullScreen (ctx); + + if (err) { + SDL_SetError ("Error setting OpenGL fullscreen: %s", CGLErrorString(err)); + goto ERR_NO_GL; + } + } +#endif + + [ window_view release ]; + [ gl_context makeCurrentContext]; + + glClear (GL_COLOR_BUFFER_BIT); + + [ gl_context flushBuffer ]; + + current->flags |= SDL_OPENGL; + } else if (isLion) { /* For 2D, we build a CGBitmapContext */ + CGColorSpaceRef cgColorspace; + + /* Only recreate the view if it doesn't already exist */ + if (window_view == nil) { + window_view = [ [ NSView alloc ] initWithFrame:contentRect ]; + [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; + [ [ qz_window contentView ] addSubview:window_view ]; + [ window_view release ]; + } + + cgColorspace = CGColorSpaceCreateDeviceRGB(); + current->pitch = 4 * current->w; + current->pixels = SDL_malloc (current->h * current->pitch); + + cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h, + 8, current->pitch, cgColorspace, + kCGImageAlphaNoneSkipFirst); + CGColorSpaceRelease (cgColorspace); + + current->flags |= SDL_SWSURFACE; + current->flags |= SDL_ASYNCBLIT; + current->hwdata = (void *) cg_context; + + /* Force this window to draw above _everything_. */ + [ qz_window setLevel:CGShieldingWindowLevel() ]; + + this->UpdateRects = QZ_UpdateRects; + this->LockHWSurface = QZ_LockHWSurface; + this->UnlockHWSurface = QZ_UnlockHWSurface; + } + + if (isLion) { + [ qz_window setHasShadow:NO]; + [ qz_window setOpaque:YES]; + [ qz_window makeKeyAndOrderFront:nil ]; + } + + /* !!! FIXME: keep an eye on this. + * This API is officially unavailable for 64-bit binaries. + * It happens to work, as of 10.7, but we're going to see if + * we can just simply do without it on newer OSes... + */ + #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__) + if ( !isLion ) { + /* If we don't hide menu bar, it will get events and interrupt the program */ + HideMenuBar (); + } + #endif + + /* Fade in again (asynchronously) */ + if ( fade_token != kCGDisplayFadeReservationInvalidToken ) { + CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation(fade_token); + } + + /* + There is a bug in Cocoa where NSScreen doesn't synchronize + with CGDirectDisplay, so the main screen's frame is wrong. + As a result, coordinate translation produces incorrect results. + We can hack around this bug by setting the screen rect + ourselves. This hack should be removed if/when the bug is fixed. + */ + screen_rect = NSMakeRect(0,0,width,height); + QZ_SetFrame(this, [ NSScreen mainScreen ], screen_rect); + + /* Save the flags to ensure correct tear-down */ + mode_flags = current->flags; + + /* Set app state, hide cursor if necessary, ... */ + QZ_DoActivate(this); + + return current; + + /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ +ERR_NO_GL: goto ERR_DOUBLEBUF; /* this goto is to stop a compiler warning on newer SDKs. */ +ERR_DOUBLEBUF: QZ_RestoreDisplayMode(this); +ERR_NO_SWITCH: CGReleaseAllDisplays (); +ERR_NO_CAPTURE: +ERR_NO_MATCH: if ( fade_token != kCGDisplayFadeReservationInvalidToken ) { + CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation (fade_token); + } + return NULL; +} + +static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width, + int height, int *bpp, Uint32 flags, + const BOOL save_gl) +{ + unsigned int style; + NSRect contentRect; + int center_window = 1; + int origin_x, origin_y; + CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; + + current->flags = 0; + current->w = width; + current->h = height; + + contentRect = NSMakeRect (0, 0, width, height); + + /* + Check if we should completely destroy the previous mode + - If it is fullscreen + - If it has different noframe or resizable attribute + - If it is OpenGL (since gl attributes could be different) + - If new mode is OpenGL, but previous mode wasn't + */ + if (video_set == SDL_TRUE) { + if (mode_flags & SDL_FULLSCREEN) { + /* Fade to black to hide resolution-switching flicker (and garbage + that is displayed by a destroyed OpenGL context, if applicable) */ + if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) { + CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); + } + QZ_UnsetVideoMode (this, TRUE, save_gl); + } + else if ( ((mode_flags ^ flags) & (SDL_NOFRAME|SDL_RESIZABLE)) || + (mode_flags & SDL_OPENGL) || + (flags & SDL_OPENGL) ) { + QZ_UnsetVideoMode (this, TRUE, save_gl); + } + } + + /* Sorry, QuickDraw was ripped out. */ + if (getenv("SDL_NSWindowPointer") || getenv("SDL_NSQuickDrawViewPointer")) { + SDL_SetError ("Embedded QuickDraw windows are no longer supported"); + if (fade_token != kCGDisplayFadeReservationInvalidToken) { + CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation (fade_token); + } + return NULL; + } + + /* Check if we should recreate the window */ + if (qz_window == nil) { + + /* Set the window style based on input flags */ + if ( flags & SDL_NOFRAME ) { + style = NSBorderlessWindowMask; + current->flags |= SDL_NOFRAME; + } else { + style = NSTitledWindowMask; + style |= (NSMiniaturizableWindowMask | NSClosableWindowMask); + if ( flags & SDL_RESIZABLE ) { + style |= NSResizableWindowMask; + current->flags |= SDL_RESIZABLE; + } + } + + /* Manually create a window, avoids having a nib file resource */ + qz_window = [ [ SDL_QuartzWindow alloc ] + initWithContentRect:contentRect + styleMask:style + backing:NSBackingStoreBuffered + defer:NO ]; + + if (qz_window == nil) { + SDL_SetError ("Could not create the Cocoa window"); + if (fade_token != kCGDisplayFadeReservationInvalidToken) { + CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation (fade_token); + } + return NULL; + } + + /*[ qz_window setReleasedWhenClosed:YES ];*/ /* no need to set this as it's the default for NSWindows */ + QZ_SetCaption(this, this->wm_title, this->wm_icon); + [ qz_window setAcceptsMouseMovedEvents:YES ]; + [ qz_window setViewsNeedDisplay:NO ]; + + if ( QZ_WindowPosition(this, &origin_x, &origin_y) ) { + /* have to flip the Y value (NSPoint is lower left corner origin) */ + [ qz_window setFrameTopLeftPoint:NSMakePoint((float) origin_x, (float) (this->info.current_h - origin_y))]; + center_window = 0; + } else if ( center_window ) { + [ qz_window center ]; + } + + [ qz_window setDelegate: + [ [ SDL_QuartzWindowDelegate alloc ] init ] ]; + [ qz_window setContentView: [ [ [ SDL_QuartzView alloc ] init ] autorelease ] ]; + } + /* We already have a window, just change its size */ + else { + [ qz_window setContentSize:contentRect.size ]; + current->flags |= (SDL_NOFRAME|SDL_RESIZABLE) & mode_flags; + [ window_view setFrameSize:contentRect.size ]; + } + + /* For OpenGL, we bind the context to a subview */ + if ( flags & SDL_OPENGL ) { + + if ( ! save_gl ) { + if ( ! QZ_SetupOpenGL (this, *bpp, flags) ) { + if (fade_token != kCGDisplayFadeReservationInvalidToken) { + CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation (fade_token); + } + return NULL; + } + } + + window_view = [ [ NSView alloc ] initWithFrame:contentRect ]; + [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; + [ [ qz_window contentView ] addSubview:window_view ]; + [ gl_context setView: window_view ]; + [ window_view release ]; + [ gl_context makeCurrentContext]; + [ qz_window makeKeyAndOrderFront:nil ]; + current->flags |= SDL_OPENGL; + } + /* For 2D, we build a CGBitmapContext */ + else { + CGColorSpaceRef cgColorspace; + + /* Only recreate the view if it doesn't already exist */ + if (window_view == nil) { + + window_view = [ [ NSView alloc ] initWithFrame:contentRect ]; + [ window_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; + [ [ qz_window contentView ] addSubview:window_view ]; + [ window_view release ]; + [ qz_window makeKeyAndOrderFront:nil ]; + } + + cgColorspace = CGColorSpaceCreateDeviceRGB(); + current->pitch = 4 * current->w; + current->pixels = SDL_malloc (current->h * current->pitch); + + cg_context = CGBitmapContextCreate (current->pixels, current->w, current->h, + 8, current->pitch, cgColorspace, + kCGImageAlphaNoneSkipFirst); + CGColorSpaceRelease (cgColorspace); + + current->flags |= SDL_SWSURFACE; + current->flags |= SDL_ASYNCBLIT; + current->hwdata = (void *) cg_context; + + this->UpdateRects = QZ_UpdateRects; + this->LockHWSurface = QZ_LockHWSurface; + this->UnlockHWSurface = QZ_UnlockHWSurface; + } + + /* Save flags to ensure correct teardown */ + mode_flags = current->flags; + + /* Fade in again (asynchronously) if we came from a fullscreen mode and faded to black */ + if (fade_token != kCGDisplayFadeReservationInvalidToken) { + CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation (fade_token); + } + + return current; +} + + +static SDL_Surface* QZ_SetVideoModeInternal (_THIS, SDL_Surface *current, + int width, int height, int bpp, + Uint32 flags, BOOL save_gl) +{ + const BOOL isLion = IS_LION_OR_LATER(this); + + current->flags = 0; + current->pixels = NULL; + + /* Setup full screen video */ + if ( flags & SDL_FULLSCREEN ) { + if ( isLion ) { + bpp = 32; + } + current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags, save_gl ); + if (current == NULL) + return NULL; + } + /* Setup windowed video */ + else { + /* Force bpp to 32 */ + bpp = 32; + current = QZ_SetVideoWindowed (this, current, width, height, &bpp, flags, save_gl ); + if (current == NULL) + return NULL; + } + + if (qz_window != nil) { + nsgfx_context = [NSGraphicsContext graphicsContextWithWindow:qz_window]; + [NSGraphicsContext setCurrentContext:nsgfx_context]; + } + + /* Setup the new pixel format */ + { + int amask = 0, + rmask = 0, + gmask = 0, + bmask = 0; + + switch (bpp) { + case 16: /* (1)-5-5-5 RGB */ + amask = 0; + rmask = 0x7C00; + gmask = 0x03E0; + bmask = 0x001F; + break; + case 24: + SDL_SetError ("24bpp is not available"); + return NULL; + case 32: /* (8)-8-8-8 ARGB */ + amask = 0x00000000; + if ( (!isLion) && (flags & SDL_FULLSCREEN) ) { + rmask = 0x00FF0000; + gmask = 0x0000FF00; + bmask = 0x000000FF; + } else { +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + rmask = 0x0000FF00; + gmask = 0x00FF0000; + bmask = 0xFF000000; +#else + rmask = 0x00FF0000; + gmask = 0x0000FF00; + bmask = 0x000000FF; +#endif + break; + } + } + + if ( ! SDL_ReallocFormat (current, bpp, + rmask, gmask, bmask, amask ) ) { + SDL_SetError ("Couldn't reallocate pixel format"); + return NULL; + } + } + + /* Signal successful completion (used internally) */ + video_set = SDL_TRUE; + + return current; +} + +static SDL_Surface* QZ_SetVideoMode(_THIS, SDL_Surface *current, + int width, int height, int bpp, + Uint32 flags) +{ + /* Don't throw away the GL context if we can just resize the current one. */ +#if 0 /* !!! FIXME: half-finished side project. Reenable this if you ever debug the corner cases. */ + const BOOL save_gl = ( (video_set == SDL_TRUE) && ((flags & SDL_OPENGL) == (current->flags & SDL_OPENGL)) && (bpp == current->format->BitsPerPixel) ); +#else + const BOOL save_gl = NO; +#endif + + NSOpenGLContext *glctx = gl_context; + SDL_Surface* retval = NULL; + + if (save_gl) { + [glctx retain]; /* just so we don't lose this when killing old views, etc */ + } + + retval = QZ_SetVideoModeInternal (this, current, width, height, bpp, flags, save_gl); + + if (save_gl) { + [glctx release]; /* something else should own this now, or we legitimately release it. */ + } + + return retval; +} + + +static int QZ_ToggleFullScreen (_THIS, int on) +{ + return 0; +} + +static int QZ_SetColors (_THIS, int first_color, int num_colors, + SDL_Color *colors) +{ +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) + /* we shouldn't have an 8-bit mode on Lion! */ + if (!IS_LION_OR_LATER(this)) { + CGTableCount index; + CGDeviceColor color; + + for (index = first_color; index < first_color+num_colors; index++) { + + /* Clamp colors between 0.0 and 1.0 */ + color.red = colors->r / 255.0; + color.blue = colors->b / 255.0; + color.green = colors->g / 255.0; + + colors++; + + CGPaletteSetColorAtIndex (palette, color, index); + } + + return ( CGDisplayNoErr == CGDisplaySetPalette (display_id, palette) ); + } +#endif + + return 0; +} + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) +static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface) +{ + return 1; +} + +static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface) +{ +} + +/* The VBL delay is based on code by Ian R Ollmann's RezLib */ +static AbsoluteTime QZ_SecondsToAbsolute ( double seconds ) +{ + union + { + UInt64 i; + Nanoseconds ns; + } temp; + + temp.i = seconds * 1000000000.0; + + return NanosecondsToAbsolute ( temp.ns ); +} + +static int QZ_ThreadFlip (_THIS) +{ + Uint8 *src, *dst; + int skip, len, h; + + /* + Give this thread the highest scheduling priority possible, + in the hopes that it will immediately run after the VBL delay + */ + { + pthread_t current_thread; + int policy; + struct sched_param param; + + current_thread = pthread_self (); + pthread_getschedparam (current_thread, &policy, ¶m); + policy = SCHED_RR; + param.sched_priority = sched_get_priority_max (policy); + pthread_setschedparam (current_thread, policy, ¶m); + } + + while (1) { + + SDL_SemWait (sem1); + if (quit_thread) + return 0; + + /* + * We have to add SDL_VideoSurface->offset here, since we might be a + * smaller surface in the center of the framebuffer (you asked for + * a fullscreen resolution smaller than the hardware could supply + * so SDL is centering it in a bigger resolution)... + */ + dst = ((Uint8 *)((size_t)CGDisplayBaseAddress (display_id))) + SDL_VideoSurface->offset; + src = current_buffer + SDL_VideoSurface->offset; + len = SDL_VideoSurface->w * SDL_VideoSurface->format->BytesPerPixel; + h = SDL_VideoSurface->h; + skip = SDL_VideoSurface->pitch; + + /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */ + { + + /* The VBL delay is based on Ian Ollmann's RezLib */ + double refreshRate; + double linesPerSecond; + double target; + double position; + double adjustment; + AbsoluteTime nextTime; + CFNumberRef refreshRateCFNumber; + + refreshRateCFNumber = CFDictionaryGetValue (mode, kCGDisplayRefreshRate); + if ( NULL == refreshRateCFNumber ) { + SDL_SetError ("Mode has no refresh rate"); + goto ERROR; + } + + if ( 0 == CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) ) { + SDL_SetError ("Error getting refresh rate"); + goto ERROR; + } + + if ( 0 == refreshRate ) { + + SDL_SetError ("Display has no refresh rate, using 60hz"); + + /* ok, for LCD's we'll emulate a 60hz refresh, which may or may not look right */ + refreshRate = 60.0; + } + + linesPerSecond = refreshRate * h; + target = h; + + /* Figure out the first delay so we start off about right */ + position = CGDisplayBeamPosition (display_id); + if (position > target) + position = 0; + + adjustment = (target - position) / linesPerSecond; + + nextTime = AddAbsoluteToAbsolute (UpTime (), QZ_SecondsToAbsolute (adjustment)); + + MPDelayUntil (&nextTime); + } + + + /* On error, skip VBL delay */ + ERROR: + + /* TODO: use CGContextDrawImage here too! Create two CGContextRefs the same way we + create two buffers, replace current_buffer with current_context and set it + appropriately in QZ_FlipDoubleBuffer. */ + while ( h-- ) { + + SDL_memcpy (dst, src, len); + src += skip; + dst += skip; + } + + /* signal flip completion */ + SDL_SemPost (sem2); + } + + return 0; +} + +static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface) +{ + /* wait for previous flip to complete */ + SDL_SemWait (sem2); + + current_buffer = surface->pixels; + + if (surface->pixels == sw_buffers[0]) + surface->pixels = sw_buffers[1]; + else + surface->pixels = sw_buffers[0]; + + /* signal worker thread to do the flip */ + SDL_SemPost (sem1); + + return 0; +} + +static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects) +{ + /* perform a flip if someone calls updaterects on a doublebuferred surface */ + this->FlipHWSurface (this, SDL_VideoSurface); +} + +static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects) +{ +#pragma unused(this,num_rects,rects) +} +#endif + +/* Resize icon, BMP format */ +static const unsigned char QZ_ResizeIcon[] = { + 0x42,0x4d,0x31,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00, + 0x00,0x00,0x0d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00, + 0x00,0x00,0xfb,0x01,0x00,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff, + 0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda, + 0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8, + 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xda,0xda,0xda,0x87, + 0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8, + 0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xd5,0xd5,0x87,0x87,0x87,0xe8,0xe8,0xe8, + 0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda, + 0xda,0xda,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda, + 0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7, + 0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8, + 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8, + 0xe8,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xd9,0xd9,0xd9,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xdc, + 0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb, + 0xdb,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,0xdb,0x87,0x87,0x87,0xe8, + 0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdc, + 0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b +}; + +static void QZ_DrawResizeIcon (_THIS) +{ + /* Check if we should draw the resize icon */ + if (SDL_VideoSurface->flags & SDL_RESIZABLE) { + + SDL_Rect icon_rect; + + /* Create the icon image */ + if (resize_icon == NULL) { + + SDL_RWops *rw; + SDL_Surface *tmp; + + rw = SDL_RWFromConstMem (QZ_ResizeIcon, sizeof(QZ_ResizeIcon)); + tmp = SDL_LoadBMP_RW (rw, SDL_TRUE); + + resize_icon = SDL_ConvertSurface (tmp, SDL_VideoSurface->format, SDL_SRCCOLORKEY); + SDL_SetColorKey (resize_icon, SDL_SRCCOLORKEY, 0xFFFFFF); + + SDL_FreeSurface (tmp); + } + + icon_rect.x = SDL_VideoSurface->w - 13; + icon_rect.y = SDL_VideoSurface->h - 13; + icon_rect.w = 13; + icon_rect.h = 13; + + SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect); + } +} + +static void QZ_UpdateRects (_THIS, int numRects, SDL_Rect *rects) +{ + if (SDL_VideoSurface->flags & SDL_OPENGLBLIT) { + QZ_GL_SwapBuffers (this); + } + else if ( [ qz_window isMiniaturized ] ) { + + /* Do nothing if miniaturized */ + } + + else { + NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; + if (ctx != nsgfx_context) { /* uhoh, you might be rendering from another thread... */ + [NSGraphicsContext setCurrentContext:nsgfx_context]; + ctx = nsgfx_context; + } + CGContextRef cgc = (CGContextRef) [ctx graphicsPort]; + QZ_DrawResizeIcon (this); + CGContextFlush (cg_context); + CGImageRef image = CGBitmapContextCreateImage (cg_context); + CGRect rectangle = CGRectMake (0,0,[window_view frame].size.width,[window_view frame].size.height); + + CGContextDrawImage (cgc, rectangle, image); + CGImageRelease(image); + CGContextFlush (cgc); + } +} + +static void QZ_VideoQuit (_THIS) +{ + CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; + + /* Restore gamma settings */ + CGDisplayRestoreColorSyncSettings (); + + /* Ensure the cursor will be visible and working when we quit */ + CGDisplayShowCursor (display_id); + CGAssociateMouseAndMouseCursorPosition (1); + + if (mode_flags & SDL_FULLSCREEN) { + /* Fade to black to hide resolution-switching flicker (and garbage + that is displayed by a destroyed OpenGL context, if applicable) */ + if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess) { + CGDisplayFade (fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); + } + QZ_UnsetVideoMode (this, TRUE, FALSE); + if (fade_token != kCGDisplayFadeReservationInvalidToken) { + CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation (fade_token); + } + } + else + QZ_UnsetVideoMode (this, TRUE, FALSE); + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) + if (!IS_LION_OR_LATER(this)) { + CGPaletteRelease(palette); + } +#endif + + if (opengl_library) { + SDL_UnloadObject(opengl_library); + opengl_library = NULL; + } + this->gl_config.driver_loaded = 0; + + if (field_edit) { + [field_edit release]; + field_edit = NULL; + } +} + +static int QZ_LockHWSurface(_THIS, SDL_Surface *surface) +{ + return 1; +} + +static void QZ_UnlockHWSurface(_THIS, SDL_Surface *surface) +{ +} + +static int QZ_AllocHWSurface(_THIS, SDL_Surface *surface) +{ + return(-1); /* unallowed (no HWSURFACE support here). */ +} + +static void QZ_FreeHWSurface (_THIS, SDL_Surface *surface) +{ +} + +/* Gamma functions */ +int QZ_SetGamma (_THIS, float red, float green, float blue) +{ + const CGGammaValue min = 0.0, max = 1.0; + + if (red == 0.0) + red = FLT_MAX; + else + red = 1.0 / red; + + if (green == 0.0) + green = FLT_MAX; + else + green = 1.0 / green; + + if (blue == 0.0) + blue = FLT_MAX; + else + blue = 1.0 / blue; + + if ( CGDisplayNoErr == CGSetDisplayTransferByFormula + (display_id, min, max, red, min, max, green, min, max, blue) ) { + + return 0; + } + else { + + return -1; + } +} + +int QZ_GetGamma (_THIS, float *red, float *green, float *blue) +{ + CGGammaValue dummy; + if ( CGDisplayNoErr == CGGetDisplayTransferByFormula + (display_id, &dummy, &dummy, red, + &dummy, &dummy, green, &dummy, &dummy, blue) ) + + return 0; + else + return -1; +} + +int QZ_SetGammaRamp (_THIS, Uint16 *ramp) +{ + const uint32_t tableSize = 255; + CGGammaValue redTable[tableSize]; + CGGammaValue greenTable[tableSize]; + CGGammaValue blueTable[tableSize]; + + int i; + + /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */ + for (i = 0; i < 256; i++) + redTable[i % 256] = ramp[i] / 65535.0; + + for (i=256; i < 512; i++) + greenTable[i % 256] = ramp[i] / 65535.0; + + for (i=512; i < 768; i++) + blueTable[i % 256] = ramp[i] / 65535.0; + + if ( CGDisplayNoErr == CGSetDisplayTransferByTable + (display_id, tableSize, redTable, greenTable, blueTable) ) + return 0; + else + return -1; +} + +int QZ_GetGammaRamp (_THIS, Uint16 *ramp) +{ + const uint32_t tableSize = 255; + CGGammaValue redTable[tableSize]; + CGGammaValue greenTable[tableSize]; + CGGammaValue blueTable[tableSize]; + uint32_t actual; + int i; + + if ( CGDisplayNoErr != CGGetDisplayTransferByTable + (display_id, tableSize, redTable, greenTable, blueTable, &actual) || + actual != tableSize) + + return -1; + + /* Pack tables into one array, with values from 0 to 65535 */ + for (i = 0; i < 256; i++) + ramp[i] = redTable[i % 256] * 65535.0; + + for (i=256; i < 512; i++) + ramp[i] = greenTable[i % 256] * 65535.0; + + for (i=512; i < 768; i++) + ramp[i] = blueTable[i % 256] * 65535.0; + + return 0; +} + diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.h b/apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.h new file mode 100644 index 0000000000..0b0767e8e2 --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.h @@ -0,0 +1,27 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +struct WMcursor { + NSCursor *nscursor; +}; + +void QZ_UpdateCursor(_THIS); diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.m b/apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.m new file mode 100644 index 0000000000..d526424d4c --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzWM.m @@ -0,0 +1,444 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_QuartzVideo.h" +#include "SDL_QuartzWM.h" + + +void QZ_FreeWMCursor (_THIS, WMcursor *cursor) { + + if ( cursor != NULL ) { + [ cursor->nscursor release ]; + free (cursor); + } +} + +WMcursor* QZ_CreateWMCursor (_THIS, Uint8 *data, Uint8 *mask, + int w, int h, int hot_x, int hot_y) { + WMcursor *cursor; + NSBitmapImageRep *imgrep; + NSImage *img; + unsigned char *planes[5]; + int i; + NSAutoreleasePool *pool; + + pool = [ [ NSAutoreleasePool alloc ] init ]; + + /* Allocate the cursor memory */ + cursor = (WMcursor *)SDL_malloc(sizeof(WMcursor)); + if (cursor == NULL) goto outOfMemory; + + /* create the image representation and get the pointers to its storage */ + imgrep = [ [ [ NSBitmapImageRep alloc ] initWithBitmapDataPlanes: NULL pixelsWide: w pixelsHigh: h bitsPerSample: 1 samplesPerPixel: 2 hasAlpha: YES isPlanar: YES colorSpaceName: NSDeviceWhiteColorSpace bytesPerRow: (w+7)/8 bitsPerPixel: 0 ] autorelease ]; + if (imgrep == nil) goto outOfMemory; + [ imgrep getBitmapDataPlanes: planes ]; + + /* copy data and mask, extending the mask to all black pixels because the inversion effect doesn't work with Cocoa's alpha-blended cursors */ + for (i = 0; i < (w+7)/8*h; i++) { + planes[0][i] = data[i] ^ 0xFF; + planes[1][i] = mask[i] | data[i]; + } + + /* create image and cursor */ + img = [ [ [ NSImage alloc ] initWithSize: NSMakeSize(w, h) ] autorelease ]; + if (img == nil) goto outOfMemory; + [ img addRepresentation: imgrep ]; + if (system_version < 0x1030) { /* on 10.2, cursors must be 16*16 */ + if (w > 16 || h > 16) { /* too big: scale it down */ + [ img setScalesWhenResized: YES ]; + hot_x = hot_x*16/w; + hot_y = hot_y*16/h; + } + else { /* too small (or just right): extend it (from the bottom left corner, so hot_y must be adjusted) */ + hot_y += 16 - h; + } + [ img setSize: NSMakeSize(16, 16) ]; + } + cursor->nscursor = [ [ NSCursor alloc ] initWithImage: img hotSpot: NSMakePoint(hot_x, hot_y) ]; + if (cursor->nscursor == nil) goto outOfMemory; + + [ pool release ]; + return(cursor); + +outOfMemory: + [ pool release ]; + if (cursor != NULL) SDL_free(cursor); + SDL_OutOfMemory(); + return(NULL); +} + +void QZ_UpdateCursor (_THIS) { + BOOL state; + + if (cursor_should_be_visible || !(SDL_GetAppState() & SDL_APPMOUSEFOCUS)) { + state = YES; + } else { + state = NO; + } + if (state != cursor_visible) { + if (state) { + [ NSCursor unhide ]; + } else { + [ NSCursor hide ]; + } + cursor_visible = state; + } +} + +BOOL QZ_IsMouseInWindow (_THIS) { + if (qz_window == nil || (mode_flags & SDL_FULLSCREEN)) return YES; /*fullscreen*/ + else { + NSPoint p = [ qz_window mouseLocationOutsideOfEventStream ]; + p.y -= 1.0f; /* Apparently y goes from 1 to h, not from 0 to h-1 (i.e. the "location of the mouse" seems to be defined as "the location of the top left corner of the mouse pointer's hot pixel" */ + return NSPointInRect(p, [ window_view frame ]); + } +} + +int QZ_ShowWMCursor (_THIS, WMcursor *cursor) { + + if ( cursor == NULL) { + if ( cursor_should_be_visible ) { + cursor_should_be_visible = NO; + QZ_ChangeGrabState (this, QZ_HIDECURSOR); + } + QZ_UpdateCursor(this); + } + else { + if ( qz_window != nil && !(mode_flags & SDL_FULLSCREEN) ) { + [ qz_window invalidateCursorRectsForView: [ qz_window contentView ] ]; + } + if ( ! cursor_should_be_visible ) { + cursor_should_be_visible = YES; + QZ_ChangeGrabState (this, QZ_SHOWCURSOR); + } + [ cursor->nscursor performSelectorOnMainThread:@selector(set) withObject:nil waitUntilDone:NO ]; + QZ_UpdateCursor(this); + } + + return 1; +} + +/* + Coordinate conversion functions, for convenience + Cocoa sets the origin at the lower left corner of the window/screen + SDL, CoreGraphics/WindowServer, and QuickDraw use the origin at the upper left corner + The routines were written so they could be called before SetVideoMode() has finished; + this might have limited usefulness at the moment, but the extra cost is trivial. +*/ + +/* Convert Cocoa screen coordinate to Cocoa window coordinate */ +void QZ_PrivateGlobalToLocal (_THIS, NSPoint *p) { + + if ( ! CGDisplayIsCaptured (display_id) ) + *p = [ qz_window convertScreenToBase:*p ]; +} + + +/* Convert Cocoa window coordinate to Cocoa screen coordinate */ +void QZ_PrivateLocalToGlobal (_THIS, NSPoint *p) { + + if ( ! CGDisplayIsCaptured (display_id) ) + *p = [ qz_window convertBaseToScreen:*p ]; +} + +/* Convert SDL coordinate to Cocoa coordinate */ +void QZ_PrivateSDLToCocoa (_THIS, NSPoint *p) { + + if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */ + + p->y = CGDisplayPixelsHigh (display_id) - p->y; + } + else { + + *p = [ window_view convertPoint:*p toView: nil ]; + p->y = [window_view frame].size.height - p->y; + } +} + +/* Convert Cocoa coordinate to SDL coordinate */ +void QZ_PrivateCocoaToSDL (_THIS, NSPoint *p) { + + if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */ + + p->y = CGDisplayPixelsHigh (display_id) - p->y; + } + else { + + *p = [ window_view convertPoint:*p fromView: nil ]; + p->y = [window_view frame].size.height - p->y; + } +} + +/* Convert SDL coordinate to window server (CoreGraphics) coordinate */ +CGPoint QZ_PrivateSDLToCG (_THIS, NSPoint *p) { + + CGPoint cgp; + + if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */ + + int height; + + QZ_PrivateSDLToCocoa (this, p); + QZ_PrivateLocalToGlobal (this, p); + + height = CGDisplayPixelsHigh (display_id); + p->y = height - p->y; + } + + cgp.x = p->x; + cgp.y = p->y; + + return cgp; +} + +#if 0 /* Dead code */ +/* Convert window server (CoreGraphics) coordinate to SDL coordinate */ +void QZ_PrivateCGToSDL (_THIS, NSPoint *p) { + + if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */ + + int height; + + /* Convert CG Global to Cocoa Global */ + height = CGDisplayPixelsHigh (display_id); + p->y = height - p->y; + + QZ_PrivateGlobalToLocal (this, p); + QZ_PrivateCocoaToSDL (this, p); + } +} +#endif /* Dead code */ + +void QZ_PrivateWarpCursor (_THIS, int x, int y) { + NSPoint p; + CGPoint cgp; + + p = NSMakePoint (x, y); + cgp = QZ_PrivateSDLToCG (this, &p); + + /* this is the magic call that fixes cursor "freezing" after warp */ + CGAssociateMouseAndMouseCursorPosition (0); + CGWarpMouseCursorPosition (cgp); + if (grab_state != QZ_INVISIBLE_GRAB) { /* can't leave it disassociated? */ + CGAssociateMouseAndMouseCursorPosition (1); + } + SDL_PrivateAppActive (QZ_IsMouseInWindow (this), SDL_APPMOUSEFOCUS); +} + +void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y) { + + /* Only allow warping when in foreground */ + if ( ! [ NSApp isActive ] ) + return; + + /* Do the actual warp */ + if (grab_state != QZ_INVISIBLE_GRAB) QZ_PrivateWarpCursor (this, x, y); + + /* Generate the mouse moved event */ + SDL_PrivateMouseMotion (0, 0, x, y); +} + +void QZ_MoveWMCursor (_THIS, int x, int y) { } +void QZ_CheckMouseMode (_THIS) { } + +void QZ_SetCaption (_THIS, const char *title, const char *icon) { + + if ( qz_window != nil ) { + NSString *string; + if ( title != NULL ) { + string = [ [ NSString alloc ] initWithUTF8String:title ]; + [ qz_window setTitle:string ]; + [ string release ]; + } + if ( icon != NULL ) { + string = [ [ NSString alloc ] initWithUTF8String:icon ]; + [ qz_window setMiniwindowTitle:string ]; + [ string release ]; + } + } +} + +void QZ_SetIcon (_THIS, SDL_Surface *icon, Uint8 *mask) +{ + NSBitmapImageRep *imgrep; + NSImage *img; + SDL_Surface *mergedSurface; + NSAutoreleasePool *pool; + Uint8 *pixels; + SDL_bool iconSrcAlpha; + Uint8 iconAlphaValue; + int i, j, maskPitch, index; + + pool = [ [ NSAutoreleasePool alloc ] init ]; + + imgrep = [ [ [ NSBitmapImageRep alloc ] initWithBitmapDataPlanes: NULL pixelsWide: icon->w pixelsHigh: icon->h bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES isPlanar: NO colorSpaceName: NSDeviceRGBColorSpace bytesPerRow: 4*icon->w bitsPerPixel: 32 ] autorelease ]; + if (imgrep == nil) goto freePool; + pixels = [ imgrep bitmapData ]; + SDL_memset(pixels, 0, 4*icon->w*icon->h); /* make the background, which will survive in colorkeyed areas, completely transparent */ + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define BYTEORDER_DEPENDENT_RGBA_MASKS 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF +#else +#define BYTEORDER_DEPENDENT_RGBA_MASKS 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 +#endif + mergedSurface = SDL_CreateRGBSurfaceFrom(pixels, icon->w, icon->h, 32, 4*icon->w, BYTEORDER_DEPENDENT_RGBA_MASKS); + if (mergedSurface == NULL) goto freePool; + + /* blit, with temporarily cleared SRCALPHA flag because we want to copy, not alpha-blend */ + iconSrcAlpha = ((icon->flags & SDL_SRCALPHA) != 0); + iconAlphaValue = icon->format->alpha; + SDL_SetAlpha(icon, 0, 255); + SDL_BlitSurface(icon, NULL, mergedSurface, NULL); + if (iconSrcAlpha) SDL_SetAlpha(icon, SDL_SRCALPHA, iconAlphaValue); + + SDL_FreeSurface(mergedSurface); + + /* apply mask, source alpha, and premultiply color values by alpha */ + maskPitch = (icon->w+7)/8; + for (i = 0; i < icon->h; i++) { + for (j = 0; j < icon->w; j++) { + index = i*4*icon->w + j*4; + if (!(mask[i*maskPitch + j/8] & (128 >> j%8))) { + pixels[index + 3] = 0; + } + else { + if (iconSrcAlpha) { + if (icon->format->Amask == 0) pixels[index + 3] = icon->format->alpha; + } + else { + pixels[index + 3] = 255; + } + } + if (pixels[index + 3] < 255) { + pixels[index + 0] = (Uint16)pixels[index + 0]*pixels[index + 3]/255; + pixels[index + 1] = (Uint16)pixels[index + 1]*pixels[index + 3]/255; + pixels[index + 2] = (Uint16)pixels[index + 2]*pixels[index + 3]/255; + } + } + } + + img = [ [ [ NSImage alloc ] initWithSize: NSMakeSize(icon->w, icon->h) ] autorelease ]; + if (img == nil) goto freePool; + [ img addRepresentation: imgrep ]; + [ NSApp setApplicationIconImage:img ]; + +freePool: + [ pool release ]; +} + +int QZ_IconifyWindow (_THIS) { + + if ( ! [ qz_window isMiniaturized ] ) { + [ qz_window miniaturize:nil ]; + if ( ! [ qz_window isMiniaturized ] ) { + SDL_SetError ("window iconification failed"); + return 0; + } + return 1; + } + else { + SDL_SetError ("window already iconified"); + return 0; + } +} + +/* +int QZ_GetWMInfo (_THIS, SDL_SysWMinfo *info) { + info->nsWindowPtr = qz_window; + return 0; +}*/ + +void QZ_ChangeGrabState (_THIS, int action) { + + /* + Figure out what the next state should be based on the action. + Ignore actions that can't change the current state. + */ + if ( grab_state == QZ_UNGRABBED ) { + if ( action == QZ_ENABLE_GRAB ) { + if ( cursor_should_be_visible ) + grab_state = QZ_VISIBLE_GRAB; + else + grab_state = QZ_INVISIBLE_GRAB; + } + } + else if ( grab_state == QZ_VISIBLE_GRAB ) { + if ( action == QZ_DISABLE_GRAB ) + grab_state = QZ_UNGRABBED; + else if ( action == QZ_HIDECURSOR ) + grab_state = QZ_INVISIBLE_GRAB; + } + else { + assert( grab_state == QZ_INVISIBLE_GRAB ); + + if ( action == QZ_DISABLE_GRAB ) + grab_state = QZ_UNGRABBED; + else if ( action == QZ_SHOWCURSOR ) + grab_state = QZ_VISIBLE_GRAB; + } + + /* now apply the new state */ + if (grab_state == QZ_UNGRABBED) { + + CGAssociateMouseAndMouseCursorPosition (1); + } + else if (grab_state == QZ_VISIBLE_GRAB) { + + CGAssociateMouseAndMouseCursorPosition (1); + } + else { + assert( grab_state == QZ_INVISIBLE_GRAB ); + + QZ_PrivateWarpCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2); + CGAssociateMouseAndMouseCursorPosition (0); + } +} + +SDL_GrabMode QZ_GrabInput (_THIS, SDL_GrabMode grab_mode) { + + int doGrab = grab_mode & SDL_GRAB_ON; + /*int fullscreen = grab_mode & SDL_GRAB_FULLSCREEN;*/ + + if ( this->screen == NULL ) { + SDL_SetError ("QZ_GrabInput: screen is NULL"); + return SDL_GRAB_OFF; + } + + if ( ! video_set ) { + /*SDL_SetError ("QZ_GrabInput: video is not set, grab will take effect on mode switch"); */ + current_grab_mode = grab_mode; + return grab_mode; /* Will be set later on mode switch */ + } + + if ( grab_mode != SDL_GRAB_QUERY ) { + if ( doGrab ) + QZ_ChangeGrabState (this, QZ_ENABLE_GRAB); + else + QZ_ChangeGrabState (this, QZ_DISABLE_GRAB); + + current_grab_mode = doGrab ? SDL_GRAB_ON : SDL_GRAB_OFF; + QZ_UpdateCursor(this); + } + + return current_grab_mode; +} diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzWindow.h b/apps/plugins/sdl/src/video/quartz/SDL_QuartzWindow.h new file mode 100644 index 0000000000..d19375b747 --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzWindow.h @@ -0,0 +1,51 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1050) +typedef unsigned int NSUInteger; +#endif + +/* Subclass of NSWindow to fix genie effect and support resize events */ +@interface SDL_QuartzWindow : NSWindow +{ + BOOL watchForMouseUp; +} + +- (void)miniaturize:(id)sender; +- (void)display; +- (void)setFrame:(NSRect)frameRect display:(BOOL)flag; +- (void)appDidHide:(NSNotification*)note; +- (void)appWillUnhide:(NSNotification*)note; +- (void)appDidUnhide:(NSNotification*)note; +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag; +@end + +/* Delegate for our NSWindow to send SDLQuit() on close */ +@interface SDL_QuartzWindowDelegate : NSObject +- (BOOL)windowShouldClose:(id)sender; +@end + +/* Subclass of NSView to set cursor rectangle */ +@interface SDL_QuartzView : NSView +- (void)resetCursorRects; +@end diff --git a/apps/plugins/sdl/src/video/quartz/SDL_QuartzWindow.m b/apps/plugins/sdl/src/video/quartz/SDL_QuartzWindow.m new file mode 100644 index 0000000000..375833fb7b --- /dev/null +++ b/apps/plugins/sdl/src/video/quartz/SDL_QuartzWindow.m @@ -0,0 +1,231 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_QuartzVideo.h" +#include "SDL_QuartzWM.h" +#include "SDL_QuartzWindow.h" + +/* + This function makes the *SDL region* of the window 100% opaque. + The genie effect uses the alpha component. Otherwise, + it doesn't seem to matter what value it has. +*/ +static void QZ_SetPortAlphaOpaque () { + + SDL_Surface *surface = current_video->screen; + int bpp; + + bpp = surface->format->BitsPerPixel; + + if (bpp == 32) { + + Uint32 *pixels = (Uint32*) surface->pixels; + Uint32 rowPixels = surface->pitch / 4; + Uint32 i, j; + + for (i = 0; i < surface->h; i++) + for (j = 0; j < surface->w; j++) { + + pixels[ (i * rowPixels) + j ] |= 0xFF000000; + } + } +} + +@implementation SDL_QuartzWindow + +/* we override these methods to fix the miniaturize animation/dock icon bug */ +- (void)miniaturize:(id)sender +{ + if (SDL_VideoSurface->flags & SDL_OPENGL) { + + /* + Future: Grab framebuffer and put into NSImage + [ qz_window setMiniwindowImage:image ]; + */ + } + else { + + /* make the alpha channel opaque so anim won't have holes in it */ + QZ_SetPortAlphaOpaque (); + } + + /* window is hidden now */ + SDL_PrivateAppActive (0, SDL_APPACTIVE); + + [ super miniaturize:sender ]; +} + +- (void)display +{ + /* + This method fires just before the window deminaturizes from the Dock. + + We'll save the current visible surface, let the window manager redraw any + UI elements, and restore the SDL surface. This way, no expose event + is required, and the deminiaturize works perfectly. + */ + SDL_VideoDevice *this = (SDL_VideoDevice*)current_video; + + /* make sure pixels are fully opaque */ + if (! ( SDL_VideoSurface->flags & SDL_OPENGL ) ) + QZ_SetPortAlphaOpaque (); + + /* save current visible SDL surface */ + [ self cacheImageInRect:[ window_view frame ] ]; + + /* let the window manager redraw controls, border, etc */ + [ super display ]; + + /* restore visible SDL surface */ + [ self restoreCachedImage ]; + + /* window is visible again */ + SDL_PrivateAppActive (1, SDL_APPACTIVE); +} + +- (void)setFrame:(NSRect)frameRect display:(BOOL)flag +{ + + /* + If the video surface is NULL, this originated from QZ_SetVideoMode, + so don't send the resize event. + */ + SDL_VideoDevice *this = (SDL_VideoDevice*)current_video; + + if (this && SDL_VideoSurface == NULL) { + + [ super setFrame:frameRect display:flag ]; + } + else if (this && qz_window) { + + NSRect newViewFrame; + + [ super setFrame:frameRect display:flag ]; + + newViewFrame = [ window_view frame ]; + + SDL_PrivateResize (newViewFrame.size.width, newViewFrame.size.height); + } +} + +/* QZ_DoActivate() calls a low-level CoreGraphics routine to adjust + the cursor position, if input is being grabbed. If app activation is + triggered by a mouse click in the title bar, then the window manager + gets confused and thinks we're dragging the window. The solution + below postpones the activate event to avoid this scenario. */ +- (void)becomeKeyWindow +{ + NSEvent *event = [self currentEvent]; + if ([event type] == NSLeftMouseDown && [event window] == self) + watchForMouseUp = YES; + else + [super becomeKeyWindow]; +} + +- (void)sendEvent:(NSEvent *)event +{ + [super sendEvent:event]; + if (watchForMouseUp && [event type] == NSLeftMouseUp) + { + watchForMouseUp = NO; + [super becomeKeyWindow]; + } +} + +- (void)appDidHide:(NSNotification*)note +{ + SDL_PrivateAppActive (0, SDL_APPACTIVE); +} + +- (void)appWillUnhide:(NSNotification*)note +{ + SDL_VideoDevice *this = (SDL_VideoDevice*)current_video; + + if ( this ) { + + /* make sure pixels are fully opaque */ + if (! ( SDL_VideoSurface->flags & SDL_OPENGL ) ) + QZ_SetPortAlphaOpaque (); + + /* save current visible SDL surface */ + [ self cacheImageInRect:[ window_view frame ] ]; + } +} + +- (void)appDidUnhide:(NSNotification*)note +{ + /* restore cached image, since it may not be current, post expose event too */ + [ self restoreCachedImage ]; + + /*SDL_PrivateExpose ();*/ + + SDL_PrivateAppActive (1, SDL_APPACTIVE); +} + +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag +{ + /* Make our window subclass receive these application notifications */ + [ [ NSNotificationCenter defaultCenter ] addObserver:self + selector:@selector(appDidHide:) name:NSApplicationDidHideNotification object:NSApp ]; + + [ [ NSNotificationCenter defaultCenter ] addObserver:self + selector:@selector(appDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp ]; + + [ [ NSNotificationCenter defaultCenter ] addObserver:self + selector:@selector(appWillUnhide:) name:NSApplicationWillUnhideNotification object:NSApp ]; + + return [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ]; +} + +@end + +@implementation SDL_QuartzWindowDelegate +- (BOOL)windowShouldClose:(id)sender +{ + SDL_PrivateQuit(); + return NO; +} + +- (void)windowDidBecomeKey:(NSNotification *)aNotification +{ + QZ_DoActivate (current_video); +} + +- (void)windowDidResignKey:(NSNotification *)aNotification +{ + QZ_DoDeactivate (current_video); +} + +@end + +@implementation SDL_QuartzView + +- (void)resetCursorRects +{ + SDL_Cursor *sdlc = SDL_GetCursor(); + if (sdlc != NULL && sdlc->wm_cursor != NULL) { + [self addCursorRect: [self visibleRect] cursor: sdlc->wm_cursor->nscursor]; + } +} + +@end -- cgit v1.2.3