diff options
Diffstat (limited to 'src/SFML/Window/Cocoa/WindowImplCocoa.mm')
-rw-r--r-- | src/SFML/Window/Cocoa/WindowImplCocoa.mm | 1456 |
1 files changed, 1456 insertions, 0 deletions
diff --git a/src/SFML/Window/Cocoa/WindowImplCocoa.mm b/src/SFML/Window/Cocoa/WindowImplCocoa.mm new file mode 100644 index 0000000..460c765 --- /dev/null +++ b/src/SFML/Window/Cocoa/WindowImplCocoa.mm @@ -0,0 +1,1456 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2008 Lucas Soltic (elmerod@gmail.com) and Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#import <SFML/Window/Cocoa/WindowImplCocoa.hpp> +#import <SFML/Window/Cocoa/WindowController.h> +#import <SFML/Window/Cocoa/AppController.h> +#import <SFML/Window/WindowStyle.hpp> +#import <SFML/System.hpp> +#import <OpenGL/OpenGL.h> +#import <OpenGL/gl.h> +#import <Cocoa/Cocoa.h> +#import <iostream> + + +#pragma mark Notes + +// ENABLE_ANTIALIASING macro : +// +// I use this to prevent the use of antialiasing +// as OpenGL context sharing does not allow only one of the +// shared OpenGL context to be shared. That means +// antialiasing could not be used without re-compiling +// the whole sfml-window library. +// +// I've no way to fix this for now. + +// ENABLE_WINDOWIMPORT macro : +// +// I use this to disable the import of Cocoa windows +// as it does not work fine for now and is not +// a high priority. + + + + + +namespace sf +{ +namespace priv +{ + +// Do something only once (useful in loops) +#define ONCE(make) \ +{ static int __done = 0;\ +if (!__done) {\ +make;\ +__done = 1;\ +} } + +// Has the event been used or not ? If not, it must be sent to NSApp +enum { + UsedEvent, + UnusedEvent +}; + + +//////////////////////////////////////////////////////////// +/// Structure containing all the members I can't directly put in the class definition +/// because I would have to hide them in a #ifdef __OBJC__ block and the object +/// allocator would allocate space for it as it would be called from a C++ code +/// that wouldn't see these members +//////////////////////////////////////////////////////////// +struct objc_members { + WindowController *controller; + SFWindow *window; + NSOpenGLContext *context; + NSOpenGLView *view; +}; + +//////////////////////////////////////////////////////////// +/// Pointer to the shared OpenGL context +//////////////////////////////////////////////////////////// +static NSOpenGLContext *sharedContext = nil; + + +//////////////////////////////////////////////////////////// +/// Private function declarations +//////////////////////////////////////////////////////////// +static SFWindow * MakeWindow(WindowSettings& params, unsigned long style, VideoMode& mode, NSString *title); +static NSOpenGLContext *MakeOpenGLContext(WindowSettings& params); +static NSOpenGLView * MakeOpenGLView(SFWindow *window, NSOpenGLContext *context, WindowSettings& params); +static void ConfigureWindow(SFWindow *window, NSOpenGLView *view, WindowController *controller); +static Key::Code KeyForVirtualCode(unsigned short vCode); +static Key::Code KeyForUnicode(unsigned short uniCode); +static bool IsTextEvent(NSEvent *event); +static bool MouseInside(SFWindow *window, NSView *view); +static NSPoint MouseLocation(SFWindow *window); + + +//////////////////////////////////////////////////////////// +/// Default constructor +/// (creates a dummy window to provide a valid OpenGL context) +//////////////////////////////////////////////////////////// +WindowImplCocoa::WindowImplCocoa() : +members(NULL), +useKeyRepeat(false), +mouseIn(false), +wheelStatus(0.0f), +fullscreen(false), +fullscreenMode(0, 0, 0), +desktopMode(0, 0, 0) +{ + Initialize(); + + // We just want to have a valid support for an OpenGL context + + // So we create the OpenGL context + WindowSettings params(0, 0, 0); + members->context = MakeOpenGLContext(params); + + if (members->context != nil) { + // Increase the reference counter for the shared OpenGL context + sharedContext = [members->context retain]; + + // Then we make it the current active OpenGL context + SetActive(); + } else { + std::cerr << "*** SFML: Unable to make the main shared OpenGL context" << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +/// Create the window implementation from an existing control +//////////////////////////////////////////////////////////// +WindowImplCocoa::WindowImplCocoa(WindowHandle Handle, WindowSettings& params) : +members(NULL), +useKeyRepeat(false), +mouseIn(false), +wheelStatus(0.0f), +fullscreen(false), +fullscreenMode(0, 0, 0), +desktopMode(0, 0, 0) +{ + Initialize(); + +#if ENABLE_WINDOWIMPORT + // Register ourselves for event handling + [[AppController sharedController] registerWindow:this]; + + // Make a WindowController to handle notifications + members->controller = [[WindowController controllerWithWindow:this] retain]; + + // Use existing window + members->window = [static_cast <SFWindow *> (Handle) retain]; + + if (members->window != nil) { + // We make the OpenGL context, associate it to the OpenGL view + // and add the view to our window + members->context = MakeOpenGLContext(params); + + if (members->context != nil) { + members->view = MakeOpenGLView(members->window, members->context, params); + + if (members->view != nil) { + // initial mouse state + mouseIn = MouseInside(members->window, members->view); + + // Initialize myWidth and myHeight members from base class with the window size + myWidth = (unsigned) [members->window frame].size.width; + myHeight = (unsigned) [members->window frame].size.height; + } else { + //error(__FILE__, __LINE__, "failed to make the OpenGL view for the public window"); + } + } else { + //error(__FILE__, __LINE__, "failed to make the OpenGL context for the public window"); + } + } else { + //error(__FILE__, __LINE__, "invalid imported window"); + } +#else + std::cerr << "*** SFML: making a sf::Window from a Cocoa one is not available in this version of the SFML" << std::endl; +#endif +} + + +//////////////////////////////////////////////////////////// +/// Create the window implementation +//////////////////////////////////////////////////////////// +WindowImplCocoa::WindowImplCocoa(VideoMode Mode, const std::string& Title, unsigned long WindowStyle, WindowSettings& params) : +members(NULL), +useKeyRepeat(false), +mouseIn(false), +wheelStatus(0.0f), +fullscreen(WindowStyle & Style::Fullscreen), +fullscreenMode(0, 0, 0), +desktopMode(0, 0, 0) +{ + Initialize(); + + // Make a WindowController to handle notifications + members->controller = [[WindowController controllerWithWindow:this] retain]; + + // Create a new window with given size, title and style + // First we define some objects used for our window + NSString *title = massert([NSString stringWithUTF8String:Title.c_str()]); + + // We create the window + members->window = MakeWindow(params, WindowStyle, Mode, title); + + + if (members->window != nil) { + members->context = MakeOpenGLContext(params); + + if (members->context != nil) { + // We make the OpenGL context, associate it to the OpenGL view + // and add the view to our window + members->view = MakeOpenGLView(members->window, members->context, params); + + if (members->view != nil) { + // Set observers and some window settings + ConfigureWindow(members->window, members->view, members->controller); + + // initial mouse state + mouseIn = MouseInside(members->window, members->view); + + // We set the myWidth and myHeight members to the correct values + myWidth = Mode.Width; + myHeight = Mode.Height; + + if (WindowStyle & Style::Fullscreen) { + fullscreenMode = Mode; + + // Using this because full screen window was not always + // in front of the other application windows when unhiding app + [members->window setLevel:NSFloatingWindowLevel]; + } + } else { + std::cerr << "*** SFML: failed to make the OpenGL view for the public window" << std::endl; + [members->context release], members->context = nil; + [sharedContext release]; + [members->window release], members->window = nil; + } + } else { + std::cerr << "*** SFML: failed to make the OpenGL context for the public window" << std::endl; + [members->window release], members->window = nil; + } + } else { + std::cerr << "*** SFML: failed to make the public window" << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +/// Destructor +//////////////////////////////////////////////////////////// +WindowImplCocoa::~WindowImplCocoa() +{ + // Destroy the OpenGL context, the window and every resource allocated by this class + Show(false); + + if (members) { + if (members->window) + [[NSNotificationCenter defaultCenter] removeObserver:members->window]; + if (members->view) + [[NSNotificationCenter defaultCenter] removeObserver:members->view]; + [members->controller release]; + } + + [sharedContext release]; + if (members) { + [members->context release]; + [members->view release]; + [members->window release]; + } + + [[AppController sharedController] unregisterWindow:this]; + free (members); +} + + +//////////////////////////////////////////////////////////// +/// Check if there's an active context on the current thread +//////////////////////////////////////////////////////////// +bool WindowImplCocoa::IsContextActive() +{ + return ([NSOpenGLContext currentContext] != NULL); +} + + +//////////////////////////////////////////////////////////// +/// Handle a Cocoa NSEvent +//////////////////////////////////////////////////////////// +void WindowImplCocoa::HandleEvent(void *eventRef) +{ + if (eventRef == nil) { + std::cerr << "*** SFML: cannot handle a NULL event. Returning." << std::endl; + return; + } + + NSEvent *event = static_cast <NSEvent *> (eventRef); + int eventStatus = UnusedEvent; + + switch ([event type]) { + case NSKeyDown: + eventStatus = HandleKeyDown(eventRef); + break; + + case NSKeyUp: + eventStatus = HandleKeyUp(eventRef); + break; + + case NSFlagsChanged: + eventStatus = HandleModifierKey(eventRef); + break; + + case NSScrollWheel: + eventStatus = HandleMouseWheel(eventRef); + break; + + case NSLeftMouseDown: + case NSRightMouseDown: + eventStatus = HandleMouseDown(eventRef); + break; + + case NSLeftMouseUp: + case NSRightMouseUp: + eventStatus = HandleMouseUp(eventRef); + break; + + case NSMouseMoved: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + eventStatus = HandleMouseMove(eventRef); + break; + + default: + break; + } + + if (eventStatus == UnusedEvent) { + [NSApp sendEvent:event]; + } +} + + +//////////////////////////////////////////////////////////// +/// Handle event sent by the default NSNotificationCenter +//////////////////////////////////////////////////////////// +void WindowImplCocoa::HandleNotifiedEvent(Event& event) +{ + // Set myWidth and myHeight to correct value if + // window size changed + switch (event.Type) { + case Event::Resized: + myWidth = event.Size.Width; + myHeight = event.Size.Height; + break; + + default: + break; + } + + // And send the event + SendEvent(event); +} + + +//////////////////////////////////////////////////////////// +/// Handle a key down event (NSEvent) +//////////////////////////////////////////////////////////// +int WindowImplCocoa::HandleKeyDown(void *eventRef) +{ + NSEvent *event = static_cast <NSEvent *> (eventRef); + + Event sfEvent; + unichar chr = 0, rawchr = 0; + unsigned long length = [[event characters] length]; + unsigned mods = [event modifierFlags]; + + if (length) { + chr = [[event characters] characterAtIndex:0]; + + // Note : I got a crash (out of bounds exception) while typing so now I test... + if ([[event charactersIgnoringModifiers] length]) + rawchr = [[event charactersIgnoringModifiers] characterAtIndex:0]; + + } + + if (mods & NSCommandKeyMask) { + // Application commands + [NSApp sendEvent:event]; + } + + // User events + + if (!useKeyRepeat && [event isARepeat]) { + return UsedEvent; + } + +#if 1 + // Is it also a text event ? + if (IsTextEvent(event)) { + // tampon d'accueil des caracteres UTF-16 recuperes a partir de l'evenement clavier + unichar *utf16Characters = new unichar[length]; + + // recuperation des caracteres + [[event characters] getCharacters:utf16Characters]; + + // tampon d'accueil des caracteres convertis en UTF-32 + Uint32 utf32Characters[2]; + + // conversion des caracteres + const Uint32 *addr = Unicode::UTF16ToUTF32(utf16Characters, + utf16Characters + length, + utf32Characters); + + // si il y a eu des caracteres convertis ? + if (addr > utf32Characters) { + sfEvent.Type = Event::TextEntered; + sfEvent.Text.Unicode = utf32Characters[0]; + + SendEvent(sfEvent); + } + + delete[] utf16Characters; + } +#else + // Is it also a text event ? + if (IsTextEvent(event)) { + static NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:1]; + + sfEvent.Type = Event::TextEntered; + sfEvent.Text.Unicode = chr; + + NSText *field = [members->window fieldEditor:YES forObject:nil]; + [arr addObject:event]; + [field interpretKeyEvents:arr]; + + if ([[field string] length]) { + unichar unichr = [[field string] characterAtIndex:0]; + sfEvent.Text.Unicode = unichr; + SendEvent(sfEvent); + + unichar str[2] = {unichr, 0}; + NSLog(@"Char::%@", [NSString stringWithCharacters:str length:2]); + + [field setString:@""]; + [arr removeAllObjects]; + } + + + } +#endif + + // Anyway it's also a KeyPressed event + sfEvent.Type = Event::KeyPressed; + + // Get the keys + if (Key::Code(0) == (sfEvent.Key.Code = KeyForUnicode(rawchr))) { + sfEvent.Key.Code = KeyForVirtualCode([event keyCode]); + } + + // Get the modifiers + sfEvent.Key.Alt = mods & NSAlternateKeyMask; + sfEvent.Key.Control = mods & NSControlKeyMask; + sfEvent.Key.Shift = mods & NSShiftKeyMask; + + // Send the event + SendEvent(sfEvent); + + return UsedEvent; +} + + +//////////////////////////////////////////////////////////// +/// Handle a key up event (NSEvent) +//////////////////////////////////////////////////////////// +int WindowImplCocoa::HandleKeyUp(void *eventRef) +{ + NSEvent *event = static_cast <NSEvent *> (eventRef); + + Event sfEvent; + unsigned mods = [event modifierFlags]; + unichar chr = 0, rawchr = 0; + + if ([[event characters] length]) { + chr = [[event characters] characterAtIndex:0]; + + if ([[event charactersIgnoringModifiers] length]) + rawchr = [[event charactersIgnoringModifiers] characterAtIndex:0]; + + } + + if (mods & NSCommandKeyMask) { + [NSApp sendEvent:event]; + } + + sfEvent.Type = Event::KeyReleased; + + // Get the code + if (Key::Code(0) == (sfEvent.Key.Code = KeyForUnicode(rawchr))) { + sfEvent.Key.Code = KeyForVirtualCode([event keyCode]); + } + + // Get the modifiers + sfEvent.Key.Alt = mods & NSAlternateKeyMask; + sfEvent.Key.Control = mods & NSControlKeyMask; + sfEvent.Key.Shift = mods & NSShiftKeyMask; + + SendEvent(sfEvent); + + return UsedEvent; +} + + +//////////////////////////////////////////////////////////// +/// Handle a key modifier event [Command, Option, Control, Shift] +//////////////////////////////////////////////////////////// +int WindowImplCocoa::HandleModifierKey(void *eventRef) +{ + NSEvent *event = static_cast <NSEvent *> (eventRef); + Event sfEvent; + unsigned mods = [event modifierFlags]; + + sfEvent.Type = Event::KeyPressed; + sfEvent.Key.Code = KeyForVirtualCode([event keyCode]); + + sfEvent.Key.Alt = mods & NSAlternateKeyMask; + sfEvent.Key.Control = mods & NSControlKeyMask; + sfEvent.Key.Shift = mods & NSShiftKeyMask; + + if (!(mods & NSAlternateKeyMask) && + (sfEvent.Key.Code == Key::LAlt || sfEvent.Key.Code == Key::RAlt)) { + sfEvent.Type = Event::KeyReleased; + } + + if (!(mods & NSControlKeyMask) && + (sfEvent.Key.Code == Key::LControl || sfEvent.Key.Code == Key::RControl)) { + sfEvent.Type = Event::KeyReleased; + } + + if (!(mods & NSShiftKeyMask) && + (sfEvent.Key.Code == Key::LShift || sfEvent.Key.Code == Key::RShift)) { + sfEvent.Type = Event::KeyReleased; + } + + if (!(mods & NSCommandKeyMask) && + (sfEvent.Key.Code == Key::LSystem || sfEvent.Key.Code == Key::RSystem)) { + sfEvent.Type = Event::KeyReleased; + } + + SendEvent(sfEvent); + + return UnusedEvent; +} + + +//////////////////////////////////////////////////////////// +/// Handle a mouse down event (NSEvent) +//////////////////////////////////////////////////////////// +int WindowImplCocoa::HandleMouseDown(void *eventRef) +{ + NSEvent *event = static_cast <NSEvent *> (eventRef); + Event sfEvent; + NSPoint loc = {0, 0}; + unsigned mods = [event modifierFlags]; + + switch ([event type]) { + case NSLeftMouseDown: + sfEvent.Type = Event::MouseButtonPressed; + + if (mods & NSControlKeyMask) { + sfEvent.MouseButton.Button = Mouse::Right; + } else { + sfEvent.MouseButton.Button = Mouse::Left; + } + + // Get mouse position + loc = MouseLocation(members->window); + + sfEvent.MouseButton.X = (int) loc.x; + sfEvent.MouseButton.Y = (int) loc.y; + + SendEvent(sfEvent); + break; + + case NSRightMouseDown: + sfEvent.Type = Event::MouseButtonPressed; + sfEvent.MouseButton.Button = Mouse::Right; + + // Get mouse position + loc = MouseLocation(members->window); + + sfEvent.MouseButton.X = (int) loc.x; + sfEvent.MouseButton.Y = (int) loc.y; + + SendEvent(sfEvent); + break; + + default: + break; + } + + return UnusedEvent; + +} + + +//////////////////////////////////////////////////////////// +/// Handle a mouse up event (NSEvent) +//////////////////////////////////////////////////////////// +int WindowImplCocoa::HandleMouseUp(void *eventRef) +{ + NSEvent *event = static_cast <NSEvent *> (eventRef); + Event sfEvent; + NSPoint loc = {0, 0}; + unsigned mods = [event modifierFlags]; + + switch ([event type]) { + case NSLeftMouseUp: + sfEvent.Type = Event::MouseButtonReleased; + + if (mods & NSControlKeyMask) { + sfEvent.MouseButton.Button = Mouse::Right; + } else { + sfEvent.MouseButton.Button = Mouse::Left; + } + + // Get mouse position + loc = MouseLocation(members->window); + + sfEvent.MouseButton.X = (int) loc.x; + sfEvent.MouseButton.Y = (int) loc.y; + + SendEvent(sfEvent); + break; + + case NSRightMouseUp: + sfEvent.Type = Event::MouseButtonReleased; + sfEvent.MouseButton.Button = Mouse::Right; + + // Get mouse position + loc = MouseLocation(members->window); + + sfEvent.MouseButton.X = (int) loc.x; + sfEvent.MouseButton.Y = (int) loc.y; + + SendEvent(sfEvent); + break; + + default: + break; + } + + return UnusedEvent; +} + + +//////////////////////////////////////////////////////////// +/// Handle a mouse move event (NSEvent) +//////////////////////////////////////////////////////////// +int WindowImplCocoa::HandleMouseMove(void *eventRef) +{ + Event sfEvent; + NSPoint loc = {0, 0}; + + loc = MouseLocation(members->window); + sfEvent.Type = Event::MouseMoved; + + sfEvent.MouseMove.X = (int) loc.x; + sfEvent.MouseMove.Y = (int) loc.y; + + SendEvent(sfEvent); + + // MouseEntered and MouseLeft events + if (MouseInside(members->window, members->view) && !mouseIn) { + sfEvent.Type = Event::MouseEntered; + mouseIn = true; + SendEvent(sfEvent); + } else if (!MouseInside(members->window, members->view) && mouseIn) { + sfEvent.Type = Event::MouseLeft; + mouseIn = false; + SendEvent(sfEvent); + } + + return UnusedEvent; +} + + +//////////////////////////////////////////////////////////// +/// Handle a mouse wheel event (NSEvent) +//////////////////////////////////////////////////////////// +int WindowImplCocoa::HandleMouseWheel(void *eventRef) +{ + NSEvent *event = static_cast <NSEvent *> (eventRef); + + wheelStatus += [event deltaY]; + + if (fabs(wheelStatus) > 1.0f) { + Event sfEvent; + sfEvent.Type = Event::MouseWheelMoved; + sfEvent.MouseWheel.Delta = (int) wheelStatus; + SendEvent(sfEvent); + + wheelStatus -= (int) wheelStatus; + } + + return UnusedEvent; +} + + +//////////////////////////////////////////////////////////// +/// Return a pointer to the SFWindow object +//////////////////////////////////////////////////////////// +void *WindowImplCocoa::CocoaWindow(void) +{ + return static_cast <void *> (members->window); +} + +//////////////////////////////////////////////////////////// +/// Return whether the window is in full screen mode +//////////////////////////////////////////////////////////// +bool WindowImplCocoa::IsFullscreen(void) +{ + return fullscreen; +} + +//////////////////////////////////////////////////////////// +/// /see sfWindowImpl::Display +//////////////////////////////////////////////////////////// +void WindowImplCocoa::Display() +{ + [members->context flushBuffer]; +} + + +//////////////////////////////////////////////////////////// +/// /see sfWindowImpl::ProcessEvents +//////////////////////////////////////////////////////////// +void WindowImplCocoa::ProcessEvents() +{ + if (![NSApp isRunning]) + return; + + [[AppController sharedController] processEvents]; +} + + +//////////////////////////////////////////////////////////// +/// /see sfWindowImpl::MakeActive +//////////////////////////////////////////////////////////// +void WindowImplCocoa::SetActive(bool Active) const +{ + if (Active) { + if ([NSOpenGLContext currentContext] != members->context) + [members->context makeCurrentContext]; + } else { + if ([NSOpenGLContext currentContext] == members->context) + [NSOpenGLContext clearCurrentContext]; + } +} + + +//////////////////////////////////////////////////////////// +/// /see sfWindowImpl::UseVerticalSync +//////////////////////////////////////////////////////////// +void WindowImplCocoa::UseVerticalSync(bool Enabled) +{ + GLint enable = (Enabled) ? 1 : 0; + [members->context setValues:&enable forParameter:NSOpenGLCPSwapInterval]; +} + + +//////////////////////////////////////////////////////////// +/// /see sfWindowImpl::ShowMouseCursor +//////////////////////////////////////////////////////////// +void WindowImplCocoa::ShowMouseCursor(bool flag) +{ + if (flag) { + [NSCursor unhide]; + } else { + [NSCursor hide]; + } +} + + +//////////////////////////////////////////////////////////// +/// /see sfWindowImpl::SetCursorPosition +//////////////////////////////////////////////////////////// +void WindowImplCocoa::SetCursorPosition(unsigned int Left, unsigned int Top) +{ + NSPoint pos = NSMakePoint ((float) Left, (float) Top); + + if (members->window) { + // Flip for SFML window coordinate system + pos.y = [members->window frame].size.height - pos.y; + + // Adjust for view reference instead of window + pos.y -= [members->window frame].size.height - [members->view frame].size.height; + + // Convert to screen coordinates + NSPoint absolute = [members->window convertBaseToScreen:pos]; + + // Flip screen coodinates + absolute.y = [[NSScreen mainScreen] frame].size.height - absolute.y; + + // Move cursor + CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, CGPointMake(absolute.x, absolute.y)); + } else { + std::cerr << "*** SFML: uninitialized 'members->window' member (objc_members) in " + << this << " (sf::priv::WindowImplCocoa)" << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +/// /see sfWindowImpl::SetPosition +//////////////////////////////////////////////////////////// +void WindowImplCocoa::SetPosition(int Left, int Top) +{ + if (members->window) { + if (!fullscreen) { + // Change the window position + Top = (int) [[members->window screen] frame].size.height - Top; + [members->window setFrameTopLeftPoint:NSMakePoint(Left, Top)]; + } + } else { + std::cerr << "*** SFML: uninitialized 'members->window' member (objc_members) in " + << this << " (sf::priv::WindowImplCocoa)"; + } +} + + +//////////////////////////////////////////////////////////// +/// /see WindowImpl::SetSize +/// +//////////////////////////////////////////////////////////// +void WindowImplCocoa::SetSize(unsigned int Width, unsigned int Height) +{ + if (members->window) { + if (!fullscreen) { + [members->window setFrame:NSMakeRect([members->window frame].origin.x, + [members->window frame].origin.y, + (float) Width, (float) Height) + display:YES]; + } + } else { + std::cerr << "*** SFML: uninitialized 'members->window' member (objc_members) in " + << this << " (sf::priv::WindowImplCocoa)" << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +/// /see sfWindowImpl::Show +//////////////////////////////////////////////////////////// +void WindowImplCocoa::Show(bool State) +{ + if (State && ![members->window isVisible]) { + // Wanna open the closed window + + // Register ourselves for event handling + [[AppController sharedController] registerWindow:this]; + + if (fullscreen) { + desktopMode = VideoMode::GetDesktopMode(); + + CFDictionaryRef displayMode = CGDisplayBestModeForParameters (kCGDirectMainDisplay, + fullscreenMode.BitsPerPixel, + fullscreenMode.Width, + fullscreenMode.Height, + NULL); + + CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; + + // Fade to a black screen + [SharedAppController doFadeOperation:FillScreen time:0.5f sync:true token:&token]; + [NSMenu setMenuBarVisible:NO]; + + // Switch to the wished display mode + CGDisplaySwitchToMode(kCGDirectMainDisplay, displayMode); + + // Open the window + [members->window makeKeyAndOrderFront:nil]; + [members->window center]; + + // Fade to normal screen + [SharedAppController doFadeOperation:CleanScreen time:0.2f sync:false token:&token]; + + } else { + // Show the window + // Note: using these two lines instead of -[NSWindow makeKeyAndOrderFront:] + // in order to prevent the standard window buttons from not displaying + // the "mouse over" icons + [members->window orderFront:nil]; + [members->window makeKeyWindow]; + + } + } else if (!State && [members->window isVisible]) { + // Wanna close the opened window + + if (fullscreen) { + CFDictionaryRef displayMode = CGDisplayBestModeForParameters (kCGDirectMainDisplay, + desktopMode.BitsPerPixel, + desktopMode.Width, + desktopMode.Height, + NULL); + + CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; + + // Fade to black screen + [SharedAppController doFadeOperation:FillScreen time:0.2f sync:true token:&token]; + + // Switch to the wished display mode + CGDisplaySwitchToMode(kCGDirectMainDisplay, displayMode); + + // Close the window + [members->window close]; + [NSMenu setMenuBarVisible:YES]; + + // Fade to normal screen + [SharedAppController doFadeOperation:CleanScreen time:0.5f sync:false token:&token]; + + // Do not sync but sleep so that the Dock and the Finder desktop go back to normal + // state before the end of the fade operation + sf::Sleep(0.5f); + } else { + // Close the window + [members->window close]; + + } + + // Unregister ourselves from the event handler + [[AppController sharedController] unregisterWindow:this]; + } +} + + +//////////////////////////////////////////////////////////// +/// /see sfWindowImpl::EnableKeyRepeat +//////////////////////////////////////////////////////////// +void WindowImplCocoa::EnableKeyRepeat(bool Enabled) +{ + useKeyRepeat = Enabled; +} + + +//////////////////////////////////////////////////////////// +/// see WindowImpl::SetIcon +//////////////////////////////////////////////////////////// +void WindowImplCocoa::SetIcon(unsigned int Width, unsigned int Height, const Uint8* Pixels) +{ + // Nothing to do +} + + +//////////////////////////////////////////////////////////// +/// Make some allocations and initializations +//////////////////////////////////////////////////////////// +void WindowImplCocoa::Initialize(void) +{ + // Allocate mem for the private objc members + members = new objc_members; + bzero(members, sizeof(*members)); + + // Needed to always have an autorelease pool as soon as application is launched + ONCE([SharedAppController resetPool]); + + // Register application if needed and launch it + ONCE([SharedAppController runApplication]); +} + + +//////////////////////////////////////////////////////////// +/// Make the window +//////////////////////////////////////////////////////////// +static SFWindow *MakeWindow(WindowSettings& params, unsigned long style, VideoMode& mode, NSString *title) +{ + SFWindow *window = nil; + + NSRect frame = NSMakeRect (0.0f, 0.0f, (float) mode.Width, (float) mode.Height); + unsigned int mask = 0; + + // We grab options from WindowStyle and add them to our window mask + if (style & Style::None || style & Style::Fullscreen) { + mask |= NSBorderlessWindowMask; + + if (style & style & Style::Fullscreen) { + // Check display mode and put new values in 'mode' if needed + boolean_t exact = true; + CFDictionaryRef properties = CGDisplayBestModeForParameters(kCGDirectMainDisplay, mode.BitsPerPixel, + mode.Width, mode.Height, &exact); + + if (!properties) { + std::cerr << "*** SFML: unable to get a display mode with the given parameters" << std::endl; + return nil; + } + + if (exact == false) { + CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(properties, kCGDisplayWidth), + kCFNumberIntType, &mode.Width); + + CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(properties, kCGDisplayHeight), + kCFNumberIntType, &mode.Height); + + CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(properties, kCGDisplayBitsPerPixel), + kCFNumberIntType, &mode.BitsPerPixel); + } + } + + } else { + if (style & Style::Titlebar) { + mask |= NSTitledWindowMask; + mask |= NSMiniaturizableWindowMask; + } + + if (style & Style::Resize) { + mask |= NSTitledWindowMask; + mask |= NSMiniaturizableWindowMask; + mask |= NSResizableWindowMask; + } + + if (style & Style::Close) { + mask |= NSTitledWindowMask; + mask |= NSClosableWindowMask; + mask |= NSMiniaturizableWindowMask; + } + } + + // Now we make the window with the values we got + // Note: defer flag set to NO to be able to use OpenGL in our window + window =[massert([SFWindow alloc]) initWithContentRect:frame + styleMask:mask + backing:NSBackingStoreBuffered + defer:NO]; + + if (window != nil) { + // We set title and window position + [window setTitle:title]; + [window center]; + } + + return window; +} + + +//////////////////////////////////////////////////////////// +/// Make the OpenGL pixel format from the given attributes +//////////////////////////////////////////////////////////// +static NSOpenGLContext *MakeOpenGLContext(WindowSettings& params) +{ + NSOpenGLPixelFormat *pixFormat = nil; + NSOpenGLContext *context = nil; + unsigned idx = 0; +#if ENABLE_ANTIALIASING + unsigned samplesIdx = 0; +#endif + + // Attributes list + NSOpenGLPixelFormatAttribute attribs[15] = {(NSOpenGLPixelFormatAttribute) 0}; + + // Accelerated, double buffered + attribs[idx++] = NSOpenGLPFAClosestPolicy; + attribs[idx++] = NSOpenGLPFADoubleBuffer; + attribs[idx++] = NSOpenGLPFAAccelerated; + + // windowed context + attribs[idx++] = NSOpenGLPFAWindow; + + // Color size ; usually 32 bits per pixel + attribs[idx++] = NSOpenGLPFAColorSize; + attribs[idx++] = (NSOpenGLPixelFormatAttribute) VideoMode::GetDesktopMode().BitsPerPixel; + + // Z-buffer size + attribs[idx++] = NSOpenGLPFADepthSize; + attribs[idx++] = (NSOpenGLPixelFormatAttribute) params.DepthBits; + + // Stencil bits (I don't really know what's that...) + attribs[idx++] = NSOpenGLPFAStencilSize; + attribs[idx++] = (NSOpenGLPixelFormatAttribute) params.StencilBits; + +#if ENABLE_ANTIALIASING + // Antialiasing settings + if (params.AntialiasingLevel) { + samplesIdx = idx; + + attribs[idx++] = NSOpenGLPFASamples; + attribs[idx++] = (NSOpenGLPixelFormatAttribute) params.AntialiasingLevel; + + attribs[idx++] = NSOpenGLPFASampleBuffers; + attribs[idx++] = (NSOpenGLPixelFormatAttribute) GL_TRUE; + } +#endif + + pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; + +#if ENABLE_ANTIALIASING + // If pixel format creation fails and antialiasing level is + // greater than 2, we set it to 2. + if (pixFormat == nil && params.AntialiasingLevel > 2) { + std::cerr << "Failed to find a pixel format supporting " << params.AntialiasingLevel << " antialiasing levels ; trying with 2 levels" << std::endl; + params.AntialiasingLevel = attribs[samplesIdx + 1] = (NSOpenGLPixelFormatAttribute) 2; + + pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; + } + + // If pixel format creation fails and antialiasing is enabled, + // we disable it. + if (pixFormat == nil && params.AntialiasingLevel > 0) { + std::cerr << "Failed to find a pixel format supporting antialiasing ; antialiasing will be disabled" << std::endl; + attribs[samplesIdx] = (NSOpenGLPixelFormatAttribute) nil; + + pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; + } +#endif + + if (pixFormat) { + context = [[NSOpenGLContext alloc] initWithFormat:pixFormat + shareContext:[sharedContext retain]]; + + // Get the effective properties from our OpenGL context + GLint tmpDepthSize = 0, tmpStencilBits = 0, tmpAntialiasingLevel = 0; + + if (context) { + [pixFormat getValues:&tmpDepthSize + forAttribute:NSOpenGLPFADepthSize + forVirtualScreen:[context currentVirtualScreen]]; + + [pixFormat getValues:&tmpStencilBits + forAttribute:NSOpenGLPFAStencilSize + forVirtualScreen:[context currentVirtualScreen]]; + + [pixFormat getValues:&tmpAntialiasingLevel + forAttribute:NSOpenGLPFASamples + forVirtualScreen:[context currentVirtualScreen]]; + } + + + params.DepthBits = (unsigned) tmpDepthSize; + params.StencilBits = (unsigned) tmpStencilBits; + params.AntialiasingLevel = (unsigned) tmpAntialiasingLevel; + + [pixFormat release]; + } + + return context; +} + + +static NSOpenGLView * MakeOpenGLView(SFWindow *window, NSOpenGLContext *context, WindowSettings& params) +{ + assert(window != nil); + assert(context != nil); + + NSOpenGLView *view = nil; + + + // We make the NSOpenGLView + view = [[NSOpenGLView alloc] initWithFrame:[[window contentView] bounds] + pixelFormat:nil]; + + if (view) { + // We add the NSOpenGLView to the window + [[window contentView] addSubview:view]; + + [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [view clearGLContext]; + [view setOpenGLContext:context]; + [context setView:view]; + } + + return view; +} + + +static void ConfigureWindow(SFWindow *window, NSOpenGLView *view, WindowController *controller) +{ + assert(window != nil); + assert(view != nil); + assert(controller != nil); + + // We need to update the OpenGL view when it changes + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:controller + selector:@selector(viewFrameDidChange:) + name:NSViewFrameDidChangeNotification + object:view]; + + // We want to know when our window got the focus + [nc addObserver:controller + selector:@selector(windowDidBecomeMain:) + name:NSWindowDidBecomeMainNotification + object:window]; + + // We want to know when our window lost the focus + [nc addObserver:controller + selector:@selector(windowDidResignMain:) + name:NSWindowDidResignMainNotification + object:window]; + + // We want to know when the user closes the window + [nc addObserver:controller + selector:@selector(windowWillClose:) + name:NSWindowWillCloseNotification + object:window]; + + // I want to re-center the window if it's a full screen one and moved by Spaces + [nc addObserver:controller + selector:@selector(windowDidMove:) + name:NSWindowDidMoveNotification + object:window]; + + + // Needed not to make application crash when releasing the window in our destructor + // (I prefer to take control of everything :P) + [window setReleasedWhenClosed:NO]; + [window setAcceptsMouseMovedEvents:YES]; +} + + +//////////////////////////////////////////////////////////// +/// Return the SFML key corresponding to a key code +//////////////////////////////////////////////////////////// +static Key::Code KeyForVirtualCode(unsigned short vCode) +{ + static struct { + unsigned short code; + Key::Code sfKey; + } virtualTable[] = + { + {0x35, Key::Escape}, + {0x31, Key::Space}, + {0x24, Key::Return}, // main Return key + {0x4C, Key::Return}, // pav Return key + {0x33, Key::Back}, + {0x30, Key::Tab}, + {0x74, Key::PageUp}, + {0x79, Key::PageDown}, + {0x77, Key::End}, + {0x73, Key::Home}, + {0x72, Key::Insert}, + {0x75, Key::Delete}, + {0x45, Key::Add}, + {0x4E, Key::Subtract}, + {0x43, Key::Multiply}, + {0x4B, Key::Divide}, + + {0x7A, Key::F1}, {0x78, Key::F2}, {0x63, Key::F3}, + {0x76, Key::F4}, {0x60, Key::F5}, {0x61, Key::F6}, + {0x62, Key::F7}, {0x64, Key::F8}, {0x65, Key::F9}, + {0x6D, Key::F10}, {0x67, Key::F11}, {0x6F, Key::F12}, + {0x69, Key::F13}, {0x6B, Key::F14}, {0x71, Key::F15}, + + {0x7B, Key::Left}, + {0x7C, Key::Right}, + {0x7E, Key::Up}, + {0x7D, Key::Down}, + + {0x52, Key::Numpad0}, {0x53, Key::Numpad1}, {0x54, Key::Numpad2}, + {0x55, Key::Numpad3}, {0x56, Key::Numpad4}, {0x57, Key::Numpad5}, + {0x58, Key::Numpad6}, {0x59, Key::Numpad7}, {0x5B, Key::Numpad8}, + {0x5C, Key::Numpad9}, + + {0x1D, Key::Num0}, {0x12, Key::Num1}, {0x13, Key::Num2}, + {0x14, Key::Num3}, {0x15, Key::Num4}, {0x17, Key::Num5}, + {0x16, Key::Num6}, {0x1A, Key::Num7}, {0x1C, Key::Num8}, + {0x19, Key::Num9}, + + {0x3B, Key::LControl}, //< Left Ctrl + {0x3A, Key::LAlt}, //< Left Option/Alt + {0x37, Key::LSystem}, //< Left Command + {0x38, Key::LShift}, //< Left Shift + {0x3E, Key::RControl}, //< Right Ctrl + {0x3D, Key::RAlt}, //< Right Option/Alt + {0x36, Key::RSystem}, //< Right Command + {0x3C, Key::RShift}, //< Right Shift + + {0x39, Key::Code(0)} //< Caps Lock + }; + + Key::Code result = Key::Code(0); + + for (unsigned i = 0;virtualTable[i].code;i++) { + if (virtualTable[i].code == vCode) { + result = virtualTable[i].sfKey; + break; + } + } + + return result; +} + + +//////////////////////////////////////////////////////////// +/// Return the SFML key corresponding to a unicode code +//////////////////////////////////////////////////////////// +static Key::Code KeyForUnicode(unsigned short uniCode) +{ + static struct { + unsigned short character; + Key::Code sfKey; + } unicodeTable[] = + { + {'!', Key::Code(0)}, //< No Key for this code + {'"', Key::Code(0)}, //< No Key for this code + {'#', Key::Code(0)}, //< No Key for this code + {'$', Key::Code(0)}, //< No Key for this code + {'%', Key::Code(0)}, //< No Key for this code + {'&', Key::Code(0)}, //< No Key for this code + {'\'', Key::Quote}, + {'(', Key::Code(0)}, //< No Key for this code + {')', Key::Code(0)}, //< No Key for this code + {'*', Key::Multiply}, + {'+', Key::Add}, + {',', Key::Comma}, + {'-', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'.', Key::Period}, + {'/', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'0', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'1', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'2', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'3', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'4', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'5', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'6', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'7', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'8', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {'9', Key::Code(0)}, //< Handled by KeyForVirtualCode() + {':', Key::Code(0)}, //< No Key for this code + {';', Key::SemiColon}, + {'<', Key::Code(0)}, //< No Key for this code + {'=', Key::Equal}, + {'>', Key::Code(0)}, //< No Key for this code + {'?', Key::Code(0)}, //< No Key for this code + {'@', Key::Code(0)}, //< No Key for this code + {'A', Key::A}, {'B', Key::B}, {'C', Key::C}, + {'D', Key::D}, {'E', Key::E}, {'F', Key::F}, + {'G', Key::G}, {'H', Key::H}, {'I', Key::I}, + {'J', Key::J}, {'K', Key::K}, {'L', Key::L}, + {'M', Key::M}, {'N', Key::N}, {'O', Key::O}, + {'P', Key::P}, {'Q', Key::Q}, {'R', Key::R}, + {'S', Key::S}, {'T', Key::T}, {'U', Key::U}, + {'V', Key::V}, {'W', Key::W}, {'X', Key::X}, + {'Y', Key::Y}, {'Z', Key::Z}, + {'[', Key::LBracket}, + {'\\', Key::BackSlash}, + {']', Key::RBracket}, + {'^', Key::Code(0)}, //< No Key for this code + {'_', Key::Code(0)}, //< No Key for this code + {'`', Key::Code(0)}, //< No Key for this code + {'a', Key::A}, {'b', Key::B}, {'c', Key::C}, + {'d', Key::D}, {'e', Key::E}, {'f', Key::F}, + {'g', Key::G}, {'h', Key::H}, {'i', Key::I}, + {'j', Key::J}, {'k', Key::K}, {'l', Key::L}, + {'m', Key::M}, {'n', Key::N}, {'o', Key::O}, + {'p', Key::P}, {'q', Key::Q}, {'r', Key::R}, + {'s', Key::S}, {'t', Key::T}, {'u', Key::U}, + {'v', Key::V}, {'w', Key::W}, {'x', Key::X}, + {'y', Key::Y}, {'z', Key::Z}, + {'{', Key::Code(0)}, //< No Key for this code + {'|', Key::Code(0)}, //< No Key for this code + {'}', Key::Code(0)}, //< No Key for this code + {'~', Key::Tilde}, + {0, Key::Code(0)} + }; + + Key::Code result = Key::Code(0); + + for (unsigned i = 0;unicodeTable[i].character;i++) { + if (unicodeTable[i].character == uniCode) { + result = unicodeTable[i].sfKey; + break; + } + } + + return result; +} + + +//////////////////////////////////////////////////////////// +/// Return whether 'ev' must be considered as a TextEntered event +//////////////////////////////////////////////////////////// +static bool IsTextEvent(NSEvent *event) +{ + bool res = false; + + if (event && [event type] == NSKeyDown && [[event characters] length]) { + unichar code = [[event characters] characterAtIndex:0]; + + // Codes from 0xF700 to 0xF8FF are non text keys (see NSEvent.h) + if (code < 0xF700 || code > 0xF8FF) + res = true; + } + + return res; +} + + +//////////////////////////////////////////////////////////// +/// Return whether the mouse is on our OpenGL view +//////////////////////////////////////////////////////////// +static bool MouseInside(SFWindow *window, NSView *view) +{ + bool res = false; + + if (window && view && [window isVisible]) { + NSPoint relativeToWindow = [window mouseLocationOutsideOfEventStream]; + NSPoint relativeToView = [view convertPoint:relativeToWindow fromView:nil]; + + if (NSPointInRect (relativeToView, [view bounds])) + res = true; + } + + return res; +} + + +//////////////////////////////////////////////////////////// +/// Return the mouse location in the SFML coordinates according to 'window' +//////////////////////////////////////////////////////////// +static NSPoint MouseLocation(SFWindow *window) +{ + NSPoint location = [NSEvent mouseLocation]; + NSPoint relativeLocation = {0, 0}; + + if (window) { + [window convertScreenToBase:location]; + relativeLocation.y = [[window contentView] frame].size.height - relativeLocation.y; + } else { + std::cerr << "*** SFML: tried to get mouse location from no window" << std::endl; + } + + return relativeLocation; +} + +} // namespace priv + +} // namespace sf + |