/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2011 by Wenzel Jakob and others. Mitsuba is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. Mitsuba 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include using namespace mitsuba; #define DEFINE_KEY(xsym, sym) m_keymap[xsym] = Device::sym @implementation CustomView - (id) initWithFrame: (NSRect) frame { self = [super initWithFrame: frame]; if (self) { m_modifiers = 0; m_firstMouseMotion = true; m_focused = false; m_mouseInWindow = false; m_ignoreNextMouseEvent = false; /* Cocoa <-> Mitsuba keyboard mappings */ for (uint i=0; i<0xFF; i++) m_keymap[i] = Device::ENoSpecial; DEFINE_KEY(QZ_BACKSPACE, EKeyBackspace); DEFINE_KEY(QZ_TAB, EKeyTab); DEFINE_KEY(QZ_RETURN, EKeyReturn); DEFINE_KEY(QZ_PAUSE, EKeyPause); DEFINE_KEY(QZ_ESCAPE, EKeyEscape); DEFINE_KEY(QZ_DELETE, EKeyDelete); DEFINE_KEY(QZ_KP0, EKeyKeyPad0); DEFINE_KEY(QZ_KP1, EKeyKeyPad1); DEFINE_KEY(QZ_KP2, EKeyKeyPad2); DEFINE_KEY(QZ_KP3, EKeyKeyPad3); DEFINE_KEY(QZ_KP4, EKeyKeyPad4); DEFINE_KEY(QZ_KP5, EKeyKeyPad5); DEFINE_KEY(QZ_KP6, EKeyKeyPad6); DEFINE_KEY(QZ_KP7, EKeyKeyPad7); DEFINE_KEY(QZ_KP8, EKeyKeyPad8); DEFINE_KEY(QZ_KP9, EKeyKeyPad9); DEFINE_KEY(QZ_KP_PERIOD, EKeyKeyPadPeriod); DEFINE_KEY(QZ_KP_DIVIDE, EKeyKeyPadDivide); DEFINE_KEY(QZ_KP_MULTIPLY, EKeyKeyPadMultiply); DEFINE_KEY(QZ_KP_MINUS, EKeyKeyPadMinus); DEFINE_KEY(QZ_KP_PLUS, EKeyKeyPadPlus); DEFINE_KEY(QZ_KP_ENTER, EKeyKeyPadEnter); DEFINE_KEY(QZ_KP_EQUALS, EKeyKeyPadEquals); DEFINE_KEY(QZ_UP, EKeyUp); DEFINE_KEY(QZ_DOWN, EKeyDown); DEFINE_KEY(QZ_RIGHT, EKeyRight); DEFINE_KEY(QZ_LEFT, EKeyLeft); DEFINE_KEY(QZ_INSERT, EKeyInsert); DEFINE_KEY(QZ_HOME, EKeyHome); DEFINE_KEY(QZ_END, EKeyEnd); DEFINE_KEY(QZ_PAGEUP, EKeyPageUp); DEFINE_KEY(QZ_PAGEDOWN, EKeyPageDown); DEFINE_KEY(QZ_F1, EKeyF1); DEFINE_KEY(QZ_F2, EKeyF2); DEFINE_KEY(QZ_F3, EKeyF3); DEFINE_KEY(QZ_F4, EKeyF4); DEFINE_KEY(QZ_F5, EKeyF5); DEFINE_KEY(QZ_F6, EKeyF6); DEFINE_KEY(QZ_F7, EKeyF7); DEFINE_KEY(QZ_F8, EKeyF8); DEFINE_KEY(QZ_F9, EKeyF9); DEFINE_KEY(QZ_F10, EKeyF10); DEFINE_KEY(QZ_F11, EKeyF11); DEFINE_KEY(QZ_F12, EKeyF12); DEFINE_KEY(QZ_NUMLOCK, EKeyNumLock); DEFINE_KEY(QZ_CAPSLOCK, EKeyCapsLock); DEFINE_KEY(QZ_SCROLLOCK, EKeyScrollLock); DEFINE_KEY(QZ_LSHIFT, EKeyLShift); DEFINE_KEY(QZ_RSHIFT, EKeyRShift); DEFINE_KEY(QZ_LMETA, EKeyLMeta); DEFINE_KEY(QZ_RMETA, EKeyRMeta); DEFINE_KEY(QZ_LALT, EKeyLAlt); DEFINE_KEY(QZ_RALT, EKeyRAlt); DEFINE_KEY(QZ_LCTRL, EKeyLControl); DEFINE_KEY(QZ_RCTRL, EKeyRControl); } return self; } - (void) setDevice: (NSGLDevice *) device { m_device = device; } - (void) ignoreNextMouseEvent { m_ignoreNextMouseEvent = true; } - (void) ignoreFirstMouseMotion { m_firstMouseMotion = false; } - (BOOL) focused { return m_focused; } - (BOOL) acceptsFirstResponder { return YES; } - (uint) extractModifiers: (uint) modifiers { uint result = 0; if (modifiers & NSAlphaShiftKeyMask) result |= 0x10; if (modifiers & NSShiftKeyMask) result |= Device::EShiftModifier; if (modifiers & NSControlKeyMask) result |= Device::EControlModifier; if (modifiers & NSAlternateKeyMask) result |= Device::EAltModifier; if (modifiers & NSCommandKeyMask) result |= Device::EMetaModifier; return result; } - (void) windowDidBecomeKey: (NSNotification *) notification { bool cursorInWindow = m_device->isMouseInWindow(); m_focused = true; m_device->pushEvent(DeviceEvent(Device::EGainFocusEvent)); m_buttonMask = 0; if (!m_device->getCursor() && cursorInWindow) { m_mouseInWindow = true; [NSCursor hide]; } } - (void) windowDidResignKey: (NSNotification *) notification { m_focused = false; m_device->pushEvent(DeviceEvent(Device::ELoseFocusEvent)); m_buttonMask = 0; if (!m_device->getCursor()) { [NSCursor unhide]; } } - (void) handleEvent: (NSEvent *) event { DeviceEvent deviceEvent(Device::ENoEvent); uint type = [event type]; switch (type) { case NSFlagsChanged: { const uint list1[] = { Device::EShiftModifier, Device::EControlModifier, Device::EAltModifier, Device::EMetaModifier, 0x10}; const uint list2[] = { Device::EKeyLShift, Device::EKeyLControl, Device::EKeyLAlt, Device::EKeyLMeta, Device::EKeyCapsLock}; uint newModifiers = [self extractModifiers: [event modifierFlags]]; uint difference = m_modifiers ^ newModifiers; m_modifiers = newModifiers; for (uint i=0; i<5; i++) { if ((difference & list1[i]) != 0) { deviceEvent.setType((m_modifiers & list1[i]) == 0 ? Device::EKeyUpEvent : Device::EKeyDownEvent); deviceEvent.getKeyboardInterpreted()[0] = '\0'; deviceEvent.setKeyboardSpecial(list2[i]); deviceEvent.setKeyboardKey('\0'); deviceEvent.setKeyboardModifiers(m_modifiers); m_device->pushEvent(deviceEvent); } } } return; case NSLeftMouseUp: case NSRightMouseUp: case NSOtherMouseUp: deviceEvent.setType(Device::EMouseButtonUpEvent); case NSLeftMouseDown: case NSRightMouseDown: case NSOtherMouseDown: { NSPoint location = [((NSWindow *) m_device->getWindow()) mouseLocationOutsideOfEventStream]; if (deviceEvent.getType() == Device::ENoEvent) deviceEvent.setType(Device::EMouseButtonDownEvent); deviceEvent.setMousePosition(Point2i((int) location.x, m_device->getSize().y - (int) location.y)); deviceEvent.setMouseRelative(Vector2i(0, 0)); uint buttonNumber = [event buttonNumber]; uint buttonMask = 0; if (buttonNumber == 0) buttonMask = Device::ELeftButton; else if (buttonNumber == 1) buttonMask = Device::ERightButton; else if (buttonNumber == 2) buttonMask = Device::EMiddleButton; else return; if (deviceEvent.getType() == Device::EMouseButtonDownEvent) m_buttonMask |= buttonMask; else m_buttonMask &= ~buttonMask; deviceEvent.setMouseButton(buttonMask); } break; case NSScrollWheel: { NSPoint location = [((NSWindow *) m_device->getWindow()) mouseLocationOutsideOfEventStream]; float deltaX = [event deltaX], deltaY = [event deltaY]; if (deltaX > 0 || deltaY > 0) deviceEvent.setMouseButton(Device::EWheelUpButton); else deviceEvent.setMouseButton(Device::EWheelDownButton); deviceEvent.setMousePosition(Point2i((int) location.x, m_device->getSize().y - (int) location.y)); deviceEvent.setMouseRelative(Vector2i(0, 0)); deviceEvent.setType(Device::EMouseButtonDownEvent); m_device->pushEvent(deviceEvent); deviceEvent.setType(Device::EMouseButtonUpEvent); m_device->pushEvent(deviceEvent); return; } break; case NSLeftMouseDragged: case NSRightMouseDragged: case NSOtherMouseDragged: case NSMouseMoved: { if (m_ignoreNextMouseEvent) { m_ignoreNextMouseEvent = false; return; } NSPoint location = [((NSWindow *) m_device->getWindow()) mouseLocationOutsideOfEventStream]; Point2i absolute((int) location.x, m_device->getSize().y - (int) location.y); bool cursorInWindow = m_device->isMouseInWindow(); if (m_buttonMask == 0) deviceEvent.setType(Device::EMouseMotionEvent); else deviceEvent.setType(Device::EMouseDragEvent); deviceEvent.setMousePosition(absolute); deviceEvent.setMouseButton(m_buttonMask); if (m_firstMouseMotion) { deviceEvent.setMouseRelative(Vector2i(absolute.x, absolute.y)); m_firstMouseMotion = false; } else { deviceEvent.setMouseRelative(Vector2i((int) [event deltaX], (int) [event deltaY])); } if (!cursorInWindow && m_focused && !m_device->getCursor()) { if (m_mouseInWindow) { m_mouseInWindow = false; [NSCursor unhide]; } } else if (cursorInWindow && m_focused && !m_device->getCursor()) { if (!m_mouseInWindow) { m_mouseInWindow = true; [NSCursor hide]; } } if (cursorInWindow) m_mouseInWindow = true; if (absolute.x > m_device->getSize().x || absolute.x < 0 || absolute.y > m_device->getSize().y || absolute.y< 0) return; } break; case NSKeyUp: deviceEvent.setType(Device::EKeyUpEvent); case NSKeyDown: { if (deviceEvent.getType() == Device::ENoEvent) deviceEvent.setType(Device::EKeyDownEvent); if ([event isARepeat]) return; NSString *characters = [event characters]; uint count = [characters length]; if (count == 0 || count == 1) { unsigned char scanCode = [event keyCode]; uint special = m_keymap[scanCode]; strncpy(deviceEvent.getKeyboardInterpreted(), [characters UTF8String], 15); if (special != 0) { deviceEvent.setKeyboardKey('\0'); deviceEvent.setKeyboardSpecial(m_keymap[scanCode]); deviceEvent.setKeyboardModifiers([self extractModifiers: [event modifierFlags]]); } else { if (count > 0) deviceEvent.setKeyboardKey([[event charactersIgnoringModifiers] UTF8String][0]); else deviceEvent.setKeyboardKey('\0'); deviceEvent.setKeyboardSpecial(Device::ENoSpecial); deviceEvent.setKeyboardModifiers(0); } } } } if (deviceEvent.getType() != Device::ENoEvent) m_device->pushEvent(deviceEvent); } - (void) mouseDown: (NSEvent *) event { [self handleEvent: event]; } - (void) mouseUp: (NSEvent *) event { [self handleEvent: event]; } - (void) mouseMoved: (NSEvent *) event { [self handleEvent: event]; } - (void) mouseDragged: (NSEvent *) event { [self handleEvent: event]; } - (void) rightMouseDown: (NSEvent *) event { [self handleEvent: event]; } - (void) rightMouseUp: (NSEvent *) event { [self handleEvent: event]; } - (void) rightMouseMoved: (NSEvent *) event { [self handleEvent: event]; } - (void) rightMouseDragged: (NSEvent *) event { [self handleEvent: event]; } - (void) otherMouseDown: (NSEvent *) event { [self handleEvent: event]; } - (void) otherMouseUp: (NSEvent *) event { [self handleEvent: event]; } - (void) otherMouseMoved: (NSEvent *) event { [self handleEvent: event]; } - (void) otherMouseDragged: (NSEvent *) event { [self handleEvent: event]; } - (void) scrollWheel: (NSEvent *) event { [self handleEvent: event]; } - (void) keyDown: (NSEvent *) event { [self handleEvent: event]; } - (void) keyUp: (NSEvent *) event { [self handleEvent: event]; } - (void) flagsChanged: (NSEvent *) event { [self handleEvent: event]; } - (BOOL) windowShouldClose: (id) sender { m_device->pushEvent(DeviceEvent(Device::EQuitEvent)); return NO; } @end MTS_NAMESPACE_BEGIN NSGLDevice::NSGLDevice(NSGLSession *session) : Device(session), m_visible(false), m_cursor(true) { m_title = "Mitsuba [nsgl]"; } NSGLDevice::~NSGLDevice() { Log(EDebug, "Destroying NSGL device"); if (m_initialized) shutdown(); } void NSGLDevice::init(Device *other) { Device::init(other); __mts_init_cocoa(); NSOpenGLPixelFormatAttribute attribs[32]; uint i=0; Device::init(); m_currentContext = NULL; Log(EDebug, "Initializing NSGL device"); NSRect contentRect = NSMakeRect(m_position.x, m_position.y, m_size.x, m_size.y); /* Protect the event queue */ m_mutex = new Mutex(); /* Create the device window */ m_window = [[NSWindow alloc] initWithContentRect: contentRect styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask backing: NSBackingStoreBuffered defer: NO]; if (m_window == nil) Log(EError, "Could not create window"); if (m_center) [m_window center]; /* Create a sub-view as drawing destination and in order to catch events */ m_view = [[CustomView alloc] initWithFrame: contentRect]; if (m_view == nil) Log(EError, "Could not create view"); [m_view setDevice: this]; [[m_window contentView] addSubview: m_view]; [m_window setDelegate: m_view]; [m_window setAcceptsMouseMovedEvents: YES]; /* Pixel format setup */ AssertEx(m_redBits == m_blueBits || m_redBits == m_greenBits, "NSGL does not support individual color depths"); attribs[i++] = NSOpenGLPFAColorSize; attribs[i++] = (NSOpenGLPixelFormatAttribute) m_redBits; attribs[i++] = NSOpenGLPFAAlphaSize; attribs[i++] = (NSOpenGLPixelFormatAttribute) m_alphaBits; attribs[i++] = NSOpenGLPFADepthSize; attribs[i++] = (NSOpenGLPixelFormatAttribute) m_depthBits; attribs[i++] = NSOpenGLPFAStencilSize; attribs[i++] = (NSOpenGLPixelFormatAttribute) m_stencilBits; if (m_doubleBuffer) { attribs[i++] = NSOpenGLPFADoubleBuffer; } if (m_fsaa > 1) { attribs[i++] = NSOpenGLPFASampleBuffers; attribs[i++] = (NSOpenGLPixelFormatAttribute) 1; attribs[i++] = NSOpenGLPFASamples; attribs[i++] = (NSOpenGLPixelFormatAttribute) m_fsaa; } attribs[i++] = NSOpenGLPFANoRecovery; // Never switch renderers attribs[i++] = NSOpenGLPFAWindow; attribs[i++] = (NSOpenGLPixelFormatAttribute) 0; attribs[i++] = (NSOpenGLPixelFormatAttribute) 0; m_fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs]; if (m_fmt == nil) Log(EError, "Could not create OpenGL pixel format!"); m_initialized = true; setTitle(m_title); } void NSGLDevice::shutdown() { Device::shutdown(); Log(EDebug, "Shutting down NSGL device"); setVisible(false); [m_fmt release]; [m_view release]; [m_window release]; m_initialized = false; } void NSGLDevice::setTitle(const std::string &title) { std::string finalTitle; Assert(m_initialized); if (m_showFPS && m_fps != 0) { finalTitle = formatString("%s - %i FPS", title.c_str(), m_fps); } else { finalTitle = title; } NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSString *string = [NSString stringWithUTF8String: finalTitle.c_str()]; [m_window setTitle: string]; Device::setTitle(title); [pool release]; } void NSGLDevice::setPosition(const Point2i &position) { Assert(m_initialized); NSPoint point = NSMakePoint(position.x, CGDisplayPixelsHigh(kCGDirectMainDisplay) - position.y); [m_window setFrameTopLeftPoint: point]; Device::setPosition(position); } void NSGLDevice::setVisible(bool visible) { Assert(m_initialized); if (visible && !m_visible) { [m_window makeKeyAndOrderFront: nil]; m_visible = true; } else if (!visible && m_visible) { [m_window orderOut: nil]; m_visible = false; } } void NSGLDevice::warpMouse(const Point2i &position) { Assert(m_initialized); NSRect frame = [m_window frame]; CGPoint point; point.x = position.x + frame.origin.x; point.y = position.y + frame.origin.y; /* Avoids cursor freezing */ [m_view ignoreNextMouseEvent]; CGSetLocalEventsSuppressionInterval(0.0f); CGWarpMouseCursorPosition(point); } void NSGLDevice::setGrab(bool grab) { Assert(m_initialized); if (grab) { warpMouse(Point2i(getSize().x / 2, getSize().y/2)); CGAssociateMouseAndMouseCursorPosition(false); } else { CGAssociateMouseAndMouseCursorPosition(true); } [m_view ignoreFirstMouseMotion]; showCursor(!grab); } void NSGLDevice::showCursor(bool enabled) { Assert(m_initialized); if (!m_cursor && enabled) { [NSCursor unhide]; m_cursor = true; } else if (m_cursor && !enabled) { m_cursor = false; if (isMouseInWindow() && [m_view focused]) { [NSCursor hide]; } } } void NSGLDevice::flip() { Assert(m_initialized); Device::flip(); if (m_doubleBuffer) { Assert(m_currentContext != NULL); [m_currentContext flushBuffer]; } } void NSGLDevice::pushEvent(const DeviceEvent &event) { m_mutex->lock(); m_deviceEvents.push_back(event); m_mutex->unlock(); } void NSGLDevice::processEvents() { Assert(m_initialized); m_mutex->lock(); for (std::vector::iterator it = m_deviceEvents.begin(); it!=m_deviceEvents.end(); ++it) fireDeviceEvent(*it); m_deviceEvents.clear(); m_mutex->unlock(); } bool NSGLDevice::isMouseInWindow() { return m_fullscreen || NSPointInRect([m_window mouseLocationOutsideOfEventStream], [m_view frame]); } void NSGLDevice::makeCurrent(Renderer *renderer) { Assert(m_initialized); m_currentContext = static_cast(static_cast(renderer)->getContext()); [m_currentContext setView: m_view]; [m_currentContext makeCurrentContext]; } MTS_IMPLEMENT_CLASS(NSGLDevice, false, Device) MTS_NAMESPACE_END