SFML/src/SFML/Window/OSX/WindowImplCocoa.mm
2014-09-23 14:12:11 +02:00

519 lines
14 KiB
Plaintext

////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2014 Marco Antognini (antognini.marco@gmail.com),
// 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
////////////////////////////////////////////////////////////
#include <SFML/Window/OSX/WindowImplCocoa.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/String.hpp>
#import <SFML/Window/OSX/AutoreleasePoolWrapper.h>
#import <SFML/Window/OSX/cpp_objc_conversion.h>
#import <SFML/Window/OSX/SFApplication.h>
#import <SFML/Window/OSX/SFApplicationDelegate.h>
#import <SFML/Window/OSX/SFKeyboardModifiersHelper.h>
#import <SFML/Window/OSX/SFViewController.h>
#import <SFML/Window/OSX/SFWindowController.h>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Get the scale factor of the main screen
///
////////////////////////////////////////////////////////////
CGFloat getDefaultScaleFactor()
{
return [[NSScreen mainScreen] backingScaleFactor];
}
////////////////////////////////////////////////////////////
/// \brief Scale SFML coordinates to backing coordinates
///
/// Use -[NSScreen backingScaleFactor] to find out if the user
/// has a retina display or not.
///
/// \param in SFML coordinates to be converted
/// \param delegate a object implementing WindowImplDelegateProtocol, or nil for default scale
///
////////////////////////////////////////////////////////////
template <class T>
void scaleIn(T& in, id<WindowImplDelegateProtocol> delegate)
{
in /= delegate ? [delegate displayScaleFactor] : getDefaultScaleFactor();
}
template <class T>
void scaleInWidthHeight(T& in, id<WindowImplDelegateProtocol> delegate)
{
scaleIn(in.width, delegate);
scaleIn(in.height, delegate);
}
template <class T>
void scaleInXY(T& in, id<WindowImplDelegateProtocol> delegate)
{
scaleIn(in.x, delegate);
scaleIn(in.y, delegate);
}
////////////////////////////////////////////////////////////
/// \brief Scale backing coordinates to SFML coordinates
///
/// Use -[NSScreen backingScaleFactor] to find out if the user
/// has a retina display or not.
///
/// \param out backing coordinates to be converted
/// \param delegate a object implementing WindowImplDelegateProtocol, or nil for default scale
///
////////////////////////////////////////////////////////////
template <class T>
void scaleOut(T& out, id<WindowImplDelegateProtocol> delegate)
{
out *= delegate ? [delegate displayScaleFactor] : getDefaultScaleFactor();
}
template <class T>
void scaleOutWidthHeight(T& out, id<WindowImplDelegateProtocol> delegate)
{
scaleOut(out.width, delegate);
scaleOut(out.height, delegate);
}
template <class T>
void scaleOutXY(T& out, id<WindowImplDelegateProtocol> delegate)
{
scaleOut(out.x, delegate);
scaleOut(out.y, delegate);
}
#pragma mark
#pragma mark WindowImplCocoa's ctor/dtor
////////////////////////////////////////////////////////////
WindowImplCocoa::WindowImplCocoa(WindowHandle handle) :
m_showCursor(true)
{
// Ask for a pool.
retainPool();
// Treat the handle as it real type
id nsHandle = (id)handle;
if ([nsHandle isKindOfClass:[NSWindow class]])
{
// We have a window.
m_delegate = [[SFWindowController alloc] initWithWindow:nsHandle];
}
else if ([nsHandle isKindOfClass:[NSView class]])
{
// We have a view.
m_delegate = [[SFViewController alloc] initWithView:nsHandle];
}
else
{
sf::err() << "Cannot import this Window Handle because it is neither "
<< "a <NSWindow*> nor <NSView*> object "
<< "(or any of their subclasses). You gave a <"
<< [[nsHandle className] UTF8String]
<< "> object."
<< std::endl;
return;
}
[m_delegate setRequesterTo:this];
// Finally, set up keyboard helper
initialiseKeyboardHelper();
}
////////////////////////////////////////////////////////////
WindowImplCocoa::WindowImplCocoa(VideoMode mode,
const String& title,
unsigned long style,
const ContextSettings& /*settings*/) :
m_showCursor(true)
{
// Transform the app process.
setUpProcess();
// Ask for a pool.
retainPool();
// Use backing size
scaleInWidthHeight(mode, nil);
m_delegate = [[SFWindowController alloc] initWithMode:mode andStyle:style];
[m_delegate changeTitle:sfStringToNSString(title)];
[m_delegate setRequesterTo:this];
// Finally, set up keyboard helper
initialiseKeyboardHelper();
}
////////////////////////////////////////////////////////////
WindowImplCocoa::~WindowImplCocoa()
{
[m_delegate closeWindow];
[m_delegate release];
// Put the next window in front, if any.
NSArray* windows = [NSApp orderedWindows];
if ([windows count] > 0)
[[windows objectAtIndex:0] makeKeyAndOrderFront:nil];
drainCurrentPool(); // Make sure everything was freed
// This solve some issue when sf::Window::Create is called for the
// second time (nothing was render until the function was called again)
releasePool();
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::applyContext(NSOpenGLContextRef context) const
{
[m_delegate applyContext:context];
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::setUpProcess(void)
{
static bool isTheProcessSetAsApplication = false;
if (!isTheProcessSetAsApplication)
{
// Do it only once !
isTheProcessSetAsApplication = true;
// Make sure NSApp is properly initialized
[SFApplication sharedApplication];
// Set the process as a normal application so it can get focus
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp activateIgnoringOtherApps:YES];
// Register an application delegate if there is none
if (![[SFApplication sharedApplication] delegate])
[[NSApplication sharedApplication] setDelegate:[[SFApplicationDelegate alloc] init]];
// Create menus for the application (before finishing launching!)
[SFApplication setUpMenuBar];
// Tell the application to stop bouncing in the Dock.
[[SFApplication sharedApplication] finishLaunching];
// NOTE : This last call won't harm anything even if SFML window was
// created with an external handle.
}
}
#pragma mark
#pragma mark WindowImplCocoa's window-event methods
////////////////////////////////////////////////////////////
void WindowImplCocoa::windowClosed(void)
{
Event event;
event.type = Event::Closed;
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::windowResized(unsigned int width, unsigned int height)
{
Event event;
event.type = Event::Resized;
event.size.width = width;
event.size.height = height;
scaleOutWidthHeight(event.size, m_delegate);
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::windowLostFocus(void)
{
if (!m_showCursor)
[m_delegate showMouseCursor]; // Make sure the cursor is visible
Event event;
event.type = Event::LostFocus;
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::windowGainedFocus(void)
{
if (!m_showCursor)
[m_delegate hideMouseCursor]; // Restore user's setting
Event event;
event.type = Event::GainedFocus;
pushEvent(event);
}
#pragma mark
#pragma mark WindowImplCocoa's mouse-event methods
////////////////////////////////////////////////////////////
void WindowImplCocoa::mouseDownAt(Mouse::Button button, int x, int y)
{
Event event;
event.type = Event::MouseButtonPressed;
event.mouseButton.button = button;
event.mouseButton.x = x;
event.mouseButton.y = y;
scaleOutXY(event.mouseButton, m_delegate);
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::mouseUpAt(Mouse::Button button, int x, int y)
{
Event event;
event.type = Event::MouseButtonReleased;
event.mouseButton.button = button;
event.mouseButton.x = x;
event.mouseButton.y = y;
scaleOutXY(event.mouseButton, m_delegate);
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::mouseMovedAt(int x, int y)
{
Event event;
event.type = Event::MouseMoved;
event.mouseMove.x = x;
event.mouseMove.y = y;
scaleOutXY(event.mouseMove, m_delegate);
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::mouseWheelScrolledAt(float delta, int x, int y)
{
Event event;
event.type = Event::MouseWheelMoved;
event.mouseWheel.delta = delta;
event.mouseWheel.x = x;
event.mouseWheel.y = y;
scaleOutXY(event.mouseWheel, m_delegate);
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::mouseMovedIn(void)
{
if (!m_showCursor)
[m_delegate hideMouseCursor]; // Restore user's setting
Event event;
event.type = Event::MouseEntered;
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::mouseMovedOut(void)
{
if (!m_showCursor)
[m_delegate showMouseCursor]; // Make sure the cursor is visible
Event event;
event.type = Event::MouseLeft;
pushEvent(event);
}
#pragma mark
#pragma mark WindowImplCocoa's key-event methods
////////////////////////////////////////////////////////////
void WindowImplCocoa::keyDown(Event::KeyEvent key)
{
Event event;
event.type = Event::KeyPressed;
event.key = key;
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::keyUp(Event::KeyEvent key)
{
Event event;
event.type = Event::KeyReleased;
event.key = key;
pushEvent(event);
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::textEntered(unichar charcode)
{
Event event;
event.type = Event::TextEntered;
event.text.unicode = charcode;
pushEvent(event);
}
#pragma mark
#pragma mark WindowImplCocoa's event-related methods
////////////////////////////////////////////////////////////
void WindowImplCocoa::processEvents()
{
[m_delegate processEvent];
drainCurrentPool(); // Reduce memory footprint
}
#pragma mark
#pragma mark WindowImplCocoa's private methods
////////////////////////////////////////////////////////////
WindowHandle WindowImplCocoa::getSystemHandle() const
{
return [m_delegate getSystemHandle];
}
////////////////////////////////////////////////////////////
Vector2i WindowImplCocoa::getPosition() const
{
NSPoint pos = [m_delegate position];
sf::Vector2i ret(pos.x, pos.y);
scaleOutXY(ret, m_delegate);
return ret;
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::setPosition(const Vector2i& position)
{
sf::Vector2i backingPosition = position;
scaleInXY(backingPosition, m_delegate);
[m_delegate setWindowPositionToX:backingPosition.x Y:backingPosition.y];
}
////////////////////////////////////////////////////////////
Vector2u WindowImplCocoa::getSize() const
{
NSSize size = [m_delegate size];
Vector2u ret(size.width, size.height);
scaleOutXY(ret, m_delegate);
return ret;
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::setSize(const Vector2u& size)
{
sf::Vector2u backingSize = size;
scaleInXY(backingSize, m_delegate);
[m_delegate resizeTo:backingSize.x by:backingSize.y];
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::setTitle(const String& title)
{
[m_delegate changeTitle:sfStringToNSString(title)];
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::setIcon(unsigned int width, unsigned int height, const Uint8* pixels)
{
[m_delegate setIconTo:width by:height with:pixels];
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::setVisible(bool visible)
{
if (visible)
[m_delegate showWindow];
else
[m_delegate hideWindow];
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::setMouseCursorVisible(bool visible)
{
m_showCursor = visible;
if (m_showCursor)
[m_delegate showMouseCursor];
else
[m_delegate hideMouseCursor];
}
////////////////////////////////////////////////////////////
void WindowImplCocoa::setKeyRepeatEnabled(bool enabled)
{
if (enabled)
[m_delegate enableKeyRepeat];
else
[m_delegate disableKeyRepeat];
}
} // namespace priv
} // namespace sf