summaryrefslogtreecommitdiff
path: root/src/SFML/Window/OSX/SFOpenGLView.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/SFML/Window/OSX/SFOpenGLView.mm')
-rw-r--r--src/SFML/Window/OSX/SFOpenGLView.mm695
1 files changed, 695 insertions, 0 deletions
diff --git a/src/SFML/Window/OSX/SFOpenGLView.mm b/src/SFML/Window/OSX/SFOpenGLView.mm
new file mode 100644
index 0000000..b787e48
--- /dev/null
+++ b/src/SFML/Window/OSX/SFOpenGLView.mm
@@ -0,0 +1,695 @@
+////////////////////////////////////////////////////////////
+//
+// SFML - Simple and Fast Multimedia Library
+// Copyright (C) 2007-2012 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/Window/OSX/HIDInputManager.hpp> // For localizedKeys and nonLocalizedKeys
+#include <SFML/System/Err.hpp>
+
+#import <SFML/Window/OSX/SFOpenGLView.h>
+#import <SFML/Window/OSX/SFSilentResponder.h>
+#import <SFML/Window/OSX/SFKeyboardModifiersHelper.h>
+
+
+////////////////////////////////////////////////////////////
+/// Returns true if `event` represents a representable character.
+///
+/// The event is assumed to be a key down event.
+/// False is returned if the event is either escape or a non text unicode.
+///
+////////////////////////////////////////////////////////////
+BOOL isValidTextUnicode(NSEvent* event);
+
+
+////////////////////////////////////////////////////////////
+/// SFOpenGLView class : Privates Methods Declaration
+///
+////////////////////////////////////////////////////////////
+@interface SFOpenGLView ()
+
+////////////////////////////////////////////////////////////
+/// Handle view resized event.
+///
+////////////////////////////////////////////////////////////
+-(void)frameDidChange:(NSNotification *)notification;
+
+////////////////////////////////////////////////////////////
+/// Establish if the mouse is inside or outside the OpenGL view.
+///
+////////////////////////////////////////////////////////////
+-(BOOL)isMouseInside;
+
+////////////////////////////////////////////////////////////
+/// Update the mouse state (in or out) and fire an event
+/// if its state has changed.
+///
+////////////////////////////////////////////////////////////
+-(void)updateMouseState;
+
+////////////////////////////////////////////////////////////
+/// Convert the NSEvent mouse button type to SFML type.
+///
+/// Returns ButtonCount if the button is unknown
+///
+////////////////////////////////////////////////////////////
+-(sf::Mouse::Button)mouseButtonFromEvent:(NSEvent *)event;
+
+////////////////////////////////////////////////////////////
+/// Convert a key down/up NSEvent into an SFML key event.
+/// Based on localizedKeys and nonLocalizedKeys function.
+///
+/// Return sf::Keyboard::Unknown as Code if the key is unknown.
+///
+////////////////////////////////////////////////////////////
++(sf::Event::KeyEvent)convertNSKeyEventToSFMLEvent:(NSEvent *)anEvent;
+
+@end
+
+@implementation SFOpenGLView
+
+#pragma mark
+#pragma mark SFOpenGLView's methods
+
+////////////////////////////////////////////////////////
+-(id)initWithFrame:(NSRect)frameRect
+{
+ if ((self = [super initWithFrame:frameRect])) {
+ [self setRequesterTo:0];
+ [self enableKeyRepeat];
+ m_realSize = NSZeroSize;
+
+ // Register for mouse move event
+ m_mouseIsIn = [self isMouseInside];
+ NSUInteger opts = (NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingEnabledDuringMouseDrag);
+ m_trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
+ options:opts
+ owner:self
+ userInfo:nil];
+ [self addTrackingArea:m_trackingArea];
+
+ // Register for resize event
+ NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(frameDidChange:)
+ name:NSViewFrameDidChangeNotification
+ object:self];
+
+ // Create a hidden text view for parsing key down event properly
+ m_silentResponder = [[SFSilentResponder alloc] init];
+ m_hiddenTextView = [[NSTextView alloc] initWithFrame:NSZeroRect];
+ [m_hiddenTextView setNextResponder:m_silentResponder];
+ }
+
+ return self;
+}
+
+
+////////////////////////////////////////////////////////
+-(void)enterFullscreen
+{
+ // Remove the tracking area first,
+ // just to be sure we don't add it twice!
+ [self removeTrackingArea:m_trackingArea];
+ [self addTrackingArea:m_trackingArea];
+
+ // Fire an mouse entered event if needed
+ if (!m_mouseIsIn && m_requester != 0) {
+ m_requester->mouseMovedIn();
+ }
+
+ // Update status
+ m_mouseIsIn = YES;
+}
+
+
+////////////////////////////////////////////////////////
+-(void)exitFullscreen
+{
+ [self removeTrackingArea:m_trackingArea];
+
+ // Fire an mouse left event if needed
+ if (m_mouseIsIn && m_requester != 0) {
+ m_requester->mouseMovedOut();
+ }
+
+ // Update status
+ m_mouseIsIn = NO;
+}
+
+
+////////////////////////////////////////////////////////
+-(void)setRequesterTo:(sf::priv::WindowImplCocoa *)requester
+{
+ m_requester = requester;
+}
+
+
+////////////////////////////////////////////////////////
+-(void)setRealSize:(NSSize)newSize
+{
+ m_realSize = newSize;
+}
+
+
+////////////////////////////////////////////////////////
+-(NSPoint)computeGlobalPositionOfRelativePoint:(NSPoint)point
+{
+ // Recompute the mouse pos if required.
+ if (!NSEqualSizes(m_realSize, NSZeroSize)) {
+ point.x = point.x / m_realSize.width * [self frame].size.width;
+ point.y = point.y / m_realSize.height * [self frame].size.height;
+ }
+
+ // Note : -[NSWindow convertBaseToScreen:] is deprecated on 10.7
+ // but the recommended -[NSWindow convertRectToScreen] is not
+ // available until 10.7.
+ //
+ // So we stick with the old one for now.
+
+
+ // Flip SFML coordinates to match window coordinates
+ point.y = [self frame].size.height - point.y;
+
+ // Get the position of (x, y) in the coordinate system of the window.
+ point = [self convertPoint:point toView:self];
+ point = [self convertPoint:point toView:nil]; // nil means window
+
+ // Convert it to screen coordinates
+ point = [[self window] convertBaseToScreen:point];
+
+ // Flip screen coodinates to match CGDisplayMoveCursorToPoint referential.
+ float const screenHeight = [[[self window] screen] frame].size.height;
+ point.y = screenHeight - point.y;
+
+ return point;
+}
+
+
+////////////////////////////////////////////////////////
+-(void)enableKeyRepeat
+{
+ m_useKeyRepeat = YES;
+}
+
+
+////////////////////////////////////////////////////////
+-(void)disableKeyRepeat
+{
+ m_useKeyRepeat = NO;
+}
+
+
+////////////////////////////////////////////////////////
+-(void)frameDidChange:(NSNotification *)notification
+{
+ // Update mouse internal state.
+ [self updateMouseState];
+
+ // Update the OGL view to fit the new size.
+ [self update];
+
+ // Send an event
+ if (m_requester == 0) return;
+
+ // The new size
+ NSSize newSize = [self frame].size;
+ m_requester->windowResized(newSize.width, newSize.height);
+}
+
+
+////////////////////////////////////////////////////////
+-(BOOL)isMouseInside
+{
+ NSPoint relativeToWindow = [[self window] mouseLocationOutsideOfEventStream];
+ NSPoint relativeToView = [self convertPoint:relativeToWindow fromView:nil];
+
+ if (NSPointInRect(relativeToView, [self frame])) {
+ return YES;
+ }
+
+ return NO;
+}
+
+
+////////////////////////////////////////////////////////
+-(void)updateMouseState
+{
+ BOOL mouseWasIn = m_mouseIsIn;
+ m_mouseIsIn = [self isMouseInside];
+
+ // Send event if needed.
+ if (mouseWasIn && !m_mouseIsIn) {
+ [self mouseExited:nil];
+ } else if (!mouseWasIn && m_mouseIsIn) {
+ [self mouseEntered:nil];
+ }
+}
+
+
+#pragma mark
+#pragma mark Subclassing methods
+
+
+////////////////////////////////////////////////////////
+-(void)dealloc
+{
+ // Unregister
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [self removeTrackingArea:m_trackingArea];
+
+ // Release attributes
+ [m_hiddenTextView release];
+ [m_silentResponder release];
+ [m_trackingArea release];
+
+ [self setRequesterTo:0];
+
+ [super dealloc];
+}
+
+
+////////////////////////////////////////////////////////
+-(BOOL)acceptsFirstResponder
+{
+ // Accepts key event.
+ return YES;
+}
+
+
+////////////////////////////////////////////////////////
+-(BOOL)canBecomeKeyView
+{
+ // Accepts key event.
+ return YES;
+}
+
+
+#pragma mark
+#pragma mark Mouse-event methods
+
+
+////////////////////////////////////////////////////////
+-(void)mouseDown:(NSEvent *)theEvent
+{
+ // Forward to...
+ [self otherMouseDown:theEvent];
+
+ // Transmit to non-SFML responder
+ [[self nextResponder] mouseDown:theEvent];
+}
+
+
+////////////////////////////////////////////////////////
+-(void)mouseUp:(NSEvent *)theEvent
+{
+ // Forward to...
+ [self otherMouseUp:theEvent];
+
+ // Transmit to non-SFML responder
+ [[self nextResponder] mouseUp:theEvent];
+}
+
+
+////////////////////////////////////////////////////////
+-(void)mouseMoved:(NSEvent *)theEvent
+{
+ // Forward to...
+ [self otherMouseDragged:theEvent];
+
+ // Transmit to non-SFML responder
+ [[self nextResponder] mouseMoved:theEvent];
+}
+
+
+////////////////////////////////////////////////////////
+-(void)scrollWheel:(NSEvent *)theEvent
+{
+ if (m_requester != 0) {
+ NSPoint loc = [self cursorPositionFromEvent:theEvent];
+
+ m_requester->mouseWheelScrolledAt([theEvent deltaY], loc.x, loc.y);
+ }
+
+ // Transmit to non-SFML responder
+ [[self nextResponder] scrollWheel:theEvent];
+}
+
+
+////////////////////////////////////////////////////////
+-(void)mouseEntered:(NSEvent *)theEvent
+{
+ // There are two cases when we need to fire an event:
+ // a) the event is nil, meaning that the method was
+ // called from our code (e.g. updateMouseState)
+ // b) the mouse was outside the view.
+ BOOL shouldFire = (theEvent == nil || m_mouseIsIn == NO);
+
+ // Update status
+ m_mouseIsIn = YES;
+
+ if (m_requester == 0) return;
+
+ // Fire (or not) an event
+ if (shouldFire) {
+ m_requester->mouseMovedIn();
+ }
+}
+
+
+////////////////////////////////////////////////////////
+-(void)mouseExited:(NSEvent *)theEvent
+{
+ // Similarly to mouseEntered:
+ BOOL shouldFire = (theEvent == nil || m_mouseIsIn == YES);
+
+ // Update status
+ m_mouseIsIn = NO;
+
+ if (m_requester == 0) return;
+
+ // Fire (or not) an event
+ if (shouldFire) {
+ m_requester->mouseMovedOut();
+ }
+}
+
+
+////////////////////////////////////////////////////////
+-(void)rightMouseDown:(NSEvent *)theEvent
+{
+ // Forward to...
+ [self otherMouseDown:theEvent];
+
+ // Transmit to non-SFML responder
+ [[self nextResponder] rightMouseDown:theEvent];
+}
+
+
+////////////////////////////////////////////////////////
+-(void)rightMouseUp:(NSEvent *)theEvent
+{
+ // Forward to...
+ [self otherMouseUp:theEvent];
+
+ // Transmit to non-SFML responder
+ [[self nextResponder] rightMouseUp:theEvent];
+}
+
+
+////////////////////////////////////////////////////////
+-(void)otherMouseDown:(NSEvent *)theEvent
+{
+ sf::Mouse::Button button = [self mouseButtonFromEvent:theEvent];
+
+ if (m_requester != 0) {
+ NSPoint loc = [self cursorPositionFromEvent:theEvent];
+
+ if (button != sf::Mouse::ButtonCount) {
+ m_requester->mouseDownAt(button, loc.x, loc.y);
+ }
+ }
+
+ // If the event is not forwarded by mouseDown or rightMouseDown...
+ if (button != sf::Mouse::Left && button != sf::Mouse::Right) {
+ // ... transmit to non-SFML responder
+ [[self nextResponder] otherMouseDown:theEvent];
+ }
+}
+
+
+////////////////////////////////////////////////////////
+-(void)otherMouseUp:(NSEvent *)theEvent
+{
+ sf::Mouse::Button button = [self mouseButtonFromEvent:theEvent];
+
+ if (m_requester != 0) {
+ NSPoint loc = [self cursorPositionFromEvent:theEvent];
+
+ if (button != sf::Mouse::ButtonCount) {
+ m_requester->mouseUpAt(button, loc.x, loc.y);
+ }
+ }
+
+ // If the event is not forwarded by mouseUp or rightMouseUp...
+ if (button != sf::Mouse::Left && button != sf::Mouse::Right) {
+ // ... transmit to non-SFML responder
+ [[self nextResponder] otherMouseUp:theEvent];
+ }
+}
+
+
+////////////////////////////////////////////////////////
+-(void)rightMouseDragged:(NSEvent *)theEvent
+{
+ // Forward to...
+ [self otherMouseDragged:theEvent];
+
+ // Transmit to non-SFML responder
+ [[self nextResponder] rightMouseDragged:theEvent];
+}
+
+
+////////////////////////////////////////////////////////
+-(void)mouseDragged:(NSEvent *)theEvent
+{
+ // Forward to...
+ [self otherMouseDragged:theEvent];
+
+ // Transmit to non-SFML responder
+ [[self nextResponder] mouseDragged:theEvent];
+}
+
+
+////////////////////////////////////////////////////////
+-(void)otherMouseDragged:(NSEvent *)theEvent
+{
+ if (m_requester != 0) {
+ NSPoint loc = [self cursorPositionFromEvent:theEvent];
+
+ // Make sure the point is inside the view.
+ // (mouseEntered: and mouseExited: are not immediately called
+ // when the mouse is dragged. That would be too easy!)
+ [self updateMouseState];
+ if (m_mouseIsIn) {
+ m_requester->mouseMovedAt(loc.x, loc.y);
+ }
+ }
+
+ // If the event is not forwarded by mouseDragged or rightMouseDragged...
+ sf::Mouse::Button button = [self mouseButtonFromEvent:theEvent];
+ if (button != sf::Mouse::Left && button != sf::Mouse::Right) {
+ // ... transmit to non-SFML responder
+ [[self nextResponder] otherMouseUp:theEvent];
+ }
+}
+
+
+////////////////////////////////////////////////////////
+-(NSPoint)cursorPositionFromEvent:(NSEvent *)eventOrNil
+{
+ NSPoint loc;
+ // If no event given then get current mouse pos.
+ if (eventOrNil == nil) {
+ NSPoint rawPos = [[self window] mouseLocationOutsideOfEventStream];
+ loc = [self convertPoint:rawPos fromView:nil];
+ } else {
+ loc = [self convertPoint:[eventOrNil locationInWindow] fromView:nil];
+ }
+
+ // Don't forget to change to SFML coord system.
+ float h = [self frame].size.height;
+ loc.y = h - loc.y;
+
+ // Recompute the mouse pos if required.
+ if (!NSEqualSizes(m_realSize, NSZeroSize)) {
+ loc.x = loc.x * m_realSize.width / [self frame].size.width;
+ loc.y = loc.y * m_realSize.height / [self frame].size.height;
+ }
+
+ return loc;
+}
+
+
+////////////////////////////////////////////////////////
+-(sf::Mouse::Button)mouseButtonFromEvent:(NSEvent *)event
+{
+ switch ([event buttonNumber]) {
+ case 0: return sf::Mouse::Left;
+ case 1: return sf::Mouse::Right;
+ case 2: return sf::Mouse::Middle;
+ case 3: return sf::Mouse::XButton1;
+ case 4: return sf::Mouse::XButton2;
+ default: return sf::Mouse::ButtonCount; // Never happens! (hopefully)
+ }
+}
+
+
+#pragma mark
+#pragma mark Key-event methods
+
+
+////////////////////////////////////////////////////////
+-(void)keyDown:(NSEvent *)theEvent
+{
+ // Transmit to non-SFML responder
+ [[self nextResponder] keyDown:theEvent];
+
+ if (m_requester == 0) return;
+
+ // Handle key down event
+ if (m_useKeyRepeat || ![theEvent isARepeat]) {
+ sf::Event::KeyEvent key = [SFOpenGLView convertNSKeyEventToSFMLEvent:theEvent];
+
+ if (key.code != sf::Keyboard::Unknown) { // The key is recognized.
+ m_requester->keyDown(key);
+ }
+ }
+
+
+ // Handle text entred event:
+ // Ignore event if we don't want repeated keystrokes
+ if (m_useKeyRepeat || ![theEvent isARepeat]) {
+ // Ignore escape key and other non text keycode (See NSEvent.h)
+ // because they produce a sound alert.
+ if (isValidTextUnicode(theEvent)) {
+ // Send the event to the hidden text view for processing
+ [m_hiddenTextView interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
+ }
+
+ // Carefully handle backspace and delete..
+ // Note: the event is intentionally sent to the hidden view
+ // even if we do something more specific below. This way
+ // key combination are correctly interpreted.
+
+ unsigned short keycode = [theEvent keyCode];
+
+ // Backspace
+ if (keycode == 0x33) {
+ // Send the correct unicode value (i.e. 8) instead of 127 (which is 'delete')
+ m_requester->textEntered(8);
+ }
+
+ // Delete
+ else if (keycode == 0x75 || keycode == NSDeleteFunctionKey) {
+ // Instead of the value 63272 we send 127.
+ m_requester->textEntered(127);
+ }
+
+ // Otherwise, let's see what our hidden field has computed
+ else {
+ NSString* string = [m_hiddenTextView string];
+
+ // Send each character to SFML event requester
+ for (NSUInteger index = 0; index < [string length]; ++index) {
+ m_requester->textEntered([string characterAtIndex:index]);
+ }
+
+ // Empty our hidden cache
+ [m_hiddenTextView setString:@""];
+ }
+ }
+}
+
+
+////////////////////////////////////////////////////////
+-(void)keyUp:(NSEvent *)theEvent
+{
+ // Transmit to non-SFML responder
+ [[self nextResponder] keyUp:theEvent];
+
+ if (m_requester == 0) return;
+
+ sf::Event::KeyEvent key = [SFOpenGLView convertNSKeyEventToSFMLEvent:theEvent];
+
+ if (key.code != sf::Keyboard::Unknown) { // The key is recognized.
+ m_requester->keyUp(key);
+ }
+}
+
+
+////////////////////////////////////////////////////////
+-(void)flagsChanged:(NSEvent *)theEvent
+{
+ // Transmit to non-SFML responder
+ [[self nextResponder] flagsChanged:theEvent];
+
+ if (m_requester == 0) return;
+
+ NSUInteger modifiers = [theEvent modifierFlags];
+ handleModifiersChanged(modifiers, *m_requester);
+}
+
+
+////////////////////////////////////////////////////////
++(sf::Event::KeyEvent)convertNSKeyEventToSFMLEvent:(NSEvent *)anEvent
+{
+ // Key code
+ sf::Keyboard::Key key = sf::Keyboard::Unknown;
+
+ // First we look if the key down is from a list of characters
+ // that depend on keyboard localization.
+ NSString* string = [anEvent charactersIgnoringModifiers];
+ if ([string length] > 0) {
+ key = sf::priv::HIDInputManager::localizedKeys([string characterAtIndex:0]);
+ }
+
+ // If the key is not a localized one, we try to find a corresponding code
+ // through virtual key code.
+ if (key == sf::Keyboard::Unknown) {
+ key = sf::priv::HIDInputManager::nonLocalizedKeys([anEvent keyCode]);
+ }
+
+//#ifdef SFML_DEBUG // Don't bother the final customers with annoying messages.
+// if (key.code == sf::Keyboard::Unknown) { // The key is unknown.
+// sf::err() << "This is an unknow key. Virtual key code is 0x"
+// << std::hex
+// << [anEvent keyCode]
+// << "."
+// << std::endl;
+// }
+//#endif
+
+ return keyEventWithModifiers([anEvent modifierFlags], key);
+}
+
+@end
+
+
+#pragma mark - C-like functions
+
+BOOL isValidTextUnicode(NSEvent* event)
+{
+ if ([event keyCode] == 0x35) { // Escape
+ return false;
+ } else if ([[event characters] length] > 0) {
+ unichar code = [[event characters] characterAtIndex:0];
+ return code < 0xF700 || code > 0xF8FF;
+ } else {
+ return true;
+ }
+}
+