diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.cpp b/code/nel/src/3d/driver/opengl/driver_opengl.cpp index 2b486c2fd..bde12782f 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl.cpp @@ -1594,8 +1594,7 @@ bool CDriverGL::setMode(const GfxMode& mode) _FullScreen = !mode.Windowed; #elif defined(NL_OS_MAC) && defined(NL_MAC_NATIVE) -# warning "OpenGL Driver: Missing Mac Implementation" - nlwarning("OpenGL Driver: Missing Mac Implementation"); + NL3D::MAC::setMode(mode); #elif defined(NL_OS_UNIX) #ifdef XF86VIDMODE diff --git a/code/nel/src/3d/driver/opengl/mac/cocoa_adapter.h b/code/nel/src/3d/driver/opengl/mac/cocoa_adapter.h index bc8ccf48e..478f4b024 100644 --- a/code/nel/src/3d/driver/opengl/mac/cocoa_adapter.h +++ b/code/nel/src/3d/driver/opengl/mac/cocoa_adapter.h @@ -55,6 +55,9 @@ bool init(uint windowIcon = 0, emptyProc exitFunc = 0); /// mac specific stuff while calling CDriverGL::setDisplay() bool setDisplay(nlWindow wnd, const GfxMode& mode, bool show, bool resizeable); +/// mac specific stuff while calling CDriverGL::setMode() +bool setMode(const GfxMode& mode); + /// mac specific stuff while calling CDriverGL::getWindowSize() void getWindowSize(uint32 &width, uint32 &height); diff --git a/code/nel/src/3d/driver/opengl/mac/cocoa_adapter.mm b/code/nel/src/3d/driver/opengl/mac/cocoa_adapter.mm index a4eb72077..6270e5b11 100644 --- a/code/nel/src/3d/driver/opengl/mac/cocoa_adapter.mm +++ b/code/nel/src/3d/driver/opengl/mac/cocoa_adapter.mm @@ -25,7 +25,7 @@ #include "cocoa_opengl_view.h" #include "cocoa_window.h" -// virtual key codes are only defined here. we still do not need to link carbon +// Virtual key codes are only defined here. We still do not need to link carbon. // see: http://lists.apple.com/archives/Cocoa-dev/2009/May/msg01180.html #include @@ -33,120 +33,301 @@ namespace NL3D { namespace MAC { -static NSApplication* g_app = 0; -static NSAutoreleasePool* g_pool = 0; -static CocoaWindow* g_window = 0; -static CocoaOpenGLView* g_glview = 0; -static NSOpenGLContext* g_glctx = 0; +static NSApplication* g_app = nil; +static NSAutoreleasePool* g_pool = nil; +static CocoaWindow* g_window = nil; +static CocoaOpenGLView* g_glview = nil; +static NSOpenGLContext* g_glctx = nil; + +#define UGLY_BACKBUFFER_SIZE_WORKAROUND + +#ifdef UGLY_BACKBUFFER_SIZE_WORKAROUND +static int g_bufferSize[2]; +#endif void ctor() { - nldebug("mac cpp bridge called"); - // create a pool, cocoa code would leak memory otherwise g_pool = [[NSAutoreleasePool alloc] init]; - + // init the application object g_app = [NSApplication sharedApplication]; } void dtor() { - nldebug("mac cpp bridge called"); - + /* + TODO there might be some more stuff to release ;) + */ + // release the pool [g_pool release]; } bool init(uint windowIcon, emptyProc exitFunc) { - nldebug("mac cpp bridge called with %u %u", windowIcon, exitFunc); + /* + TODO nothing to do here? split other stuff to match api cleanly. + */ return true; } bool setDisplay(nlWindow wnd, const GfxMode& mode, bool show, bool resizeable) { - nldebug("mac cpp bridge called with %u %u %u %u", wnd, &mode, show, resizeable); - - // create a window - /* TODO: NSBackingStoreBuffered ??? */ - g_window = [[CocoaWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1024, 768) + // create a cocoa window with the size provided by the mode parameter + g_window = [[CocoaWindow alloc] + initWithContentRect:NSMakeRect(0, 0, mode.Width, mode.Height) styleMask:NSTitledWindowMask | NSResizableWindowMask | - NSClosableWindowMask | NSMiniaturizableWindowMask + NSClosableWindowMask | NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:NO]; + if(!g_window) + nlerror("cannot create window"); + + /* + TODO use mode.Depth + TODO NSOpenGLPFAOffScreen + */ // setup opengl settings NSOpenGLPixelFormatAttribute att[] = { - NSOpenGLPFAWindow, NSOpenGLPFADoubleBuffer, - NSOpenGLPFAColorSize, 24, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFADepthSize, 24, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFAStencilSize, 8, NSOpenGLPFANoRecovery, NSOpenGLPFAAccelerated, + NSOpenGLPFABackingStore, 0 }; // put the settings into a format object NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes:att]; + + if(!format) + nlerror("cannot create NSOpenGLPixelFormat"); // create a opengl view with the created format g_glview = [[CocoaOpenGLView alloc] - initWithFrame:NSMakeRect(0, 0, 1024, 768) pixelFormat: format]; + initWithFrame:NSMakeRect(0, 0, 0, 0) pixelFormat: format]; - // create a opengl context for the view - g_glctx = [g_glview openGLContext]; + if(!g_glview) + nlerror("cannot create view"); - // setup some stuff in the window + // put the view into the window [g_window setContentView:g_glview]; - [g_window makeKeyAndOrderFront:nil]; + + // set the window to non transparent + [g_window setOpaque:YES]; + + // enable mouse move events, NeL wants them [g_window setAcceptsMouseMovedEvents:YES]; + // create a opengl context for the view + g_glctx = [g_glview openGLContext]; + + if(!g_glctx) + nlerror("cannot create context"); + // make the views opengl context the currrent one [g_glctx makeCurrentContext]; + // put the window to the front and make it the key window + [g_window makeKeyAndOrderFront:nil]; + // tell the application that we are running now [g_app finishLaunching]; // free the pixel format object [format release]; + + // further mode setting, like switching to fullscreen and resolution setup + setMode(mode); + + return true; +} + +bool setMode(const GfxMode& mode) +{ + // for fullscreen mode, adjust the back buffer size to the desired resolution + if(!mode.Windowed) + { + // set the back buffer manually to match the desired rendering resolution + GLint dim[2] = { mode.Width, mode.Height }; + CGLError error = CGLSetParameter((CGLContextObj)[g_glctx CGLContextObj], + kCGLCPSurfaceBackingSize, dim); + + if(error != kCGLNoError) + nlerror("cannot set kCGLCPSurfaceBackingSize parameter (%s)", + CGLErrorString(error)); + } + + // leave fullscreen mode, enter windowed mode + if(mode.Windowed && [g_glview isInFullScreenMode]) + { + // pull the view back from fullscreen restoring windows options + [g_glview exitFullScreenModeWithOptions:nil]; + + // disable manual setting of back buffer size, cocoa handles this + // automatically as soon as the window gets resized + CGLError error = CGLDisable((CGLContextObj)[g_glctx CGLContextObj], + kCGLCESurfaceBackingSize); + + if(error != kCGLNoError) + nlerror("cannot disable kCGLCESurfaceBackingSize (%s)", + CGLErrorString(error)); + } + + // enter fullscreen, leave windowed mode + else if(!mode.Windowed && ![g_glview isInFullScreenMode]) + { + // enable manual back buffer size for mode setting in fullscreen + CGLError error = CGLEnable((CGLContextObj)[g_glctx CGLContextObj], + kCGLCESurfaceBackingSize); + + if(error != kCGLNoError) + nlerror("cannot enable kCGLCESurfaceBackingSize (%s)", + CGLErrorString(error)); + + // put the view in fullscreen mode, hiding the dock but enabling the menubar + // to pop up if the mouse hits the top screen border. + // NOTE: withOptions:nil disables + application switching! + /* + TODO check if simply using NSView enterFullScreenMode is a good idea. + the context can be set to full screen as well? performance differences? + */ + [g_glview enterFullScreenMode:[NSScreen mainScreen] withOptions: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: + NSApplicationPresentationHideDock | + NSApplicationPresentationAutoHideMenuBar], + NSFullScreenModeApplicationPresentationOptions, nil]]; + } + + +#ifdef UGLY_BACKBUFFER_SIZE_WORKAROUND + g_bufferSize[0] = mode.Width; + g_bufferSize[1] = mode.Height; +#endif return true; } void getWindowSize(uint32 &width, uint32 &height) { - NSRect rect = [g_glview bounds]; - width = rect.size.width; - height = rect.size.height; + if(!g_glctx) + return; + + // A cocoa fullscreen view stays at the native resolution of the display. + // When changing the rendering resolution, the size of the back buffer gets + // changed, but the view still stays at full resolution. So the scaling of + // the image from the rendered resolution to the view's resolution is done + // by cocoa automatically while flushing buffers. + // That's why, in fullscreen mode, return the resolution from the back buffer, + // not the one from the window. + + // check if manual back buffer sizing is enabled (thats only in fullscreen) + +#ifdef UGLY_BACKBUFFER_SIZE_WORKAROUND + if([g_glview isInFullScreenMode]) + { + width = g_bufferSize[0]; + height = g_bufferSize[1]; + } + else + { + NSRect rect = [g_glview frame]; + width = rect.size.width; + height = rect.size.height; + } +#else + + /* + TODO does not work atm, "invalid enumeration" + */ + GLint surfaceBackingSizeSet = 0; + CGLError error = CGLIsEnabled((CGLContextObj)[g_glctx CGLContextObj], + kCGLCESurfaceBackingSize, &surfaceBackingSizeSet); + + if(error != kCGLNoError) + nlerror("cannot check kCGLCESurfaceBackingSize state (%s)", + CGLErrorString(error)); + + // if in fullscreen mode + if(surfaceBackingSizeSet) + { + /* + TODO does not work atm, "invalid enumeration" + */ + // get the back buffer size + GLint dim[2]; + CGLError error = CGLGetParameter((CGLContextObj)[g_glctx CGLContextObj], + kCGLCPSurfaceBackingSize, dim); + + if(error != kCGLNoError) + nlerror("cannot get kCGLCPSurfaceBackingSize value (%s)", + CGLErrorString(error)); + + // put size into ref params + width = dim[0]; + height = dim[1]; + } + + // if in windowed mode + else + { + // return the views size + NSRect rect = [g_glview frame]; + + // put size into ref params + width = rect.size.width; + height = rect.size.height; + } +#endif } void getWindowPos(uint32 &x, uint32 &y) { + // get the size of the screen NSRect screenRect = [[g_window screen] frame]; + + // get the size of the window NSRect windowRect = [g_window frame]; + + // simply return x x = windowRect.origin.x; + + // map y from cocoa to NeL coordinates before returning y = screenRect.size.height - windowRect.size.height - windowRect.origin.y; } void setWindowPos(uint32 x, uint32 y) { + // get the size of the screen NSRect screenRect = [[g_window screen] frame]; + + // get the size of the window NSRect windowRect = [g_window frame]; + + // convert y from NeL coordinates to cocoa coordinates y = screenRect.size.height - y; + + // tell cocoa to move the window [g_window setFrameTopLeftPoint:NSMakePoint(x, y)]; } void setWindowTitle(const ucstring &title) { + // well... set the title of the window [g_window setTitle:[NSString stringWithUTF8String:title.toUtf8().c_str()]]; } void swapBuffers() { + // make cocoa draw buffer contents to the view [g_glctx flushBuffer]; } @@ -321,7 +502,7 @@ bool isTextKeyEvent(NSEvent* event) TODO check why iswprint(character) does not solve it. it always returns false, even for π, é, ... */ - // > 127 but not printable + // characters > 127 but not printable if( nelKey == NLMISC::KeyF1 || nelKey == NLMISC::KeyF2 || nelKey == NLMISC::KeyF3 || nelKey == NLMISC::KeyF4 || nelKey == NLMISC::KeyF5 || nelKey == NLMISC::KeyF6 || @@ -354,7 +535,7 @@ void submitEvents(NLMISC::CEventServer& server, g_pool = [[NSAutoreleasePool alloc] init]; // we break if there was no event to handle - /* TODO maximum? */ + /* TODO maximum number of events processed in one update? */ while(true) { // get the next event to handle @@ -368,53 +549,74 @@ void submitEvents(NLMISC::CEventServer& server, // NSLog(@"%@", event); - uint32 width, height; - /* TODO cache? */ - getWindowSize(width, height); + // get the views size + NSRect rect = [g_glview frame]; - // get the mouse position in nel style (relative) - float mouseX = event.locationInWindow.x / (float)width; - float mouseY = event.locationInWindow.y / (float)height; + // convert the mouse position to NeL style (relative) + float mouseX = event.locationInWindow.x / (float)rect.size.width; + float mouseY = event.locationInWindow.y / (float)rect.size.height; switch(event.type) { case NSLeftMouseDown: + /* + TODO modifiers with mouse events + */ server.postEvent(new NLMISC::CEventMouseDown( mouseX, mouseY, NLMISC::leftButton /* modifiers */, eventEmitter)); break; case NSLeftMouseUp: + /* + TODO modifiers with mouse events + */ server.postEvent(new NLMISC::CEventMouseUp( mouseX, mouseY, NLMISC::leftButton /* modifiers */, eventEmitter)); break; case NSRightMouseDown: + /* + TODO modifiers with mouse events + */ server.postEvent(new NLMISC::CEventMouseDown( mouseX, mouseY, NLMISC::rightButton /* modifiers */, eventEmitter)); break; case NSRightMouseUp: + /* + TODO modifiers with mouse events + */ server.postEvent(new NLMISC::CEventMouseUp( mouseX, mouseY, NLMISC::rightButton /* modifiers */, eventEmitter)); break; case NSMouseMoved: + /* + TODO modifiers with mouse events + */ server.postEvent(new NLMISC::CEventMouseMove( mouseX, mouseY, (NLMISC::TMouseButton)0 /* modifiers */, eventEmitter)); break; case NSLeftMouseDragged: + /* + TODO modifiers with mouse events + */ server.postEvent(new NLMISC::CEventMouseMove( mouseX, mouseY, NLMISC::leftButton /* modifiers */, eventEmitter)); break; case NSRightMouseDragged:break; + /* + TODO modifiers with mouse events + */ server.postEvent(new NLMISC::CEventMouseMove( mouseX, mouseY, NLMISC::rightButton /* modifiers */, eventEmitter)); case NSMouseEntered:break; case NSMouseExited:break; case NSKeyDown: - // push the key press event to the new event server + // push the key press event to the event server server.postEvent(new NLMISC::CEventKeyDown( virtualKeycodeToNelKey([event keyCode]), modifierFlagsToNelKeyButton([event modifierFlags]), [event isARepeat] == NO, eventEmitter)); + // if this was a text event if(isTextKeyEvent(event)) { ucstring ucstr; @@ -422,7 +624,7 @@ void submitEvents(NLMISC::CEventServer& server, // get the string associated with the key press event ucstr.fromUtf8([[event characters] UTF8String]); - // push to event server + // push the text event to event server as well server.postEvent(new NLMISC::CEventChar( ucstr[0], NLMISC::noKeyButton, @@ -430,6 +632,7 @@ void submitEvents(NLMISC::CEventServer& server, } break; case NSKeyUp: + // push the key release event to the event server server.postEvent(new NLMISC::CEventKeyUp( virtualKeycodeToNelKey([event keyCode]), modifierFlagsToNelKeyButton([event modifierFlags]), diff --git a/code/nel/src/3d/driver/opengl/mac/cocoa_opengl_view.h b/code/nel/src/3d/driver/opengl/mac/cocoa_opengl_view.h index 6557128e1..1ccc7e9e2 100644 --- a/code/nel/src/3d/driver/opengl/mac/cocoa_opengl_view.h +++ b/code/nel/src/3d/driver/opengl/mac/cocoa_opengl_view.h @@ -23,7 +23,7 @@ */ @interface CocoaOpenGLView : NSOpenGLView { - NSMutableAttributedString* backingStore; + NSMutableAttributedString* characterStorage; NSRange markedRange; } diff --git a/code/nel/src/3d/driver/opengl/mac/cocoa_opengl_view.m b/code/nel/src/3d/driver/opengl/mac/cocoa_opengl_view.m index 0c816a4d4..eb23fd65a 100644 --- a/code/nel/src/3d/driver/opengl/mac/cocoa_opengl_view.m +++ b/code/nel/src/3d/driver/opengl/mac/cocoa_opengl_view.m @@ -24,7 +24,7 @@ { if(self = [super initWithFrame:frame]) { - backingStore = [[NSMutableAttributedString alloc] initWithString:@""]; + characterStorage = [[NSMutableAttributedString alloc] initWithString:@""]; return self; } return nil; @@ -32,7 +32,7 @@ - (void)dealloc { - [backingStore release]; + [characterStorage release]; [super dealloc]; } -(BOOL)acceptsFirstResponder @@ -77,13 +77,13 @@ if([aString length] == 0) { - [backingStore deleteCharactersInRange:replacementRange]; + [characterStorage deleteCharactersInRange:replacementRange]; [self unmarkText]; } else { markedRange = NSMakeRange(replacementRange.location, [aString length]); - [backingStore replaceCharactersInRange:replacementRange withString:aString]; + [characterStorage replaceCharactersInRange:replacementRange withString:aString]; } } @@ -102,7 +102,7 @@ -(NSAttributedString*)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - return [backingStore attributedSubstringFromRange:aRange]; + return [characterStorage attributedSubstringFromRange:aRange]; } -(void)insertText:(id)aString @@ -111,7 +111,7 @@ if(replacementRange.location == NSNotFound) replacementRange = markedRange; - [backingStore replaceCharactersInRange:replacementRange withString:aString]; + [characterStorage replaceCharactersInRange:replacementRange withString:aString]; } -(NSUInteger)characterIndexForPoint:(NSPoint)aPoint