diff options
author | kbr@google.com <kbr@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-24 20:40:56 +0000 |
---|---|---|
committer | kbr@google.com <kbr@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-24 20:40:56 +0000 |
commit | d135fb2e48bc5a8804c7b7ef69bffe6163f7d633 (patch) | |
tree | 3cb6defc8d0b6653fb1b9ea13d17496bad7e33a8 /o3d/plugin | |
parent | 760b2822b77c2e85983b3a860e6c527b6adb4edb (diff) | |
download | chromium_src-d135fb2e48bc5a8804c7b7ef69bffe6163f7d633.zip chromium_src-d135fb2e48bc5a8804c7b7ef69bffe6163f7d633.tar.gz chromium_src-d135fb2e48bc5a8804c7b7ef69bffe6163f7d633.tar.bz2 |
Implemented full-screen support for Core Graphics and Core Animation
rendering models using a top-level NSWindow and rendering to an
NSOpenGLContext which shares resources with the CGLContextObj used to
render into the plugin region. Synthesizes and dispatches NPCocoaEvents
for the mouse, keyboard and focus change events received by the window.
Exposed primitives needed to reset state in the OpenGL context and to
switch the Renderer's notion of the context. Fixed bug in offscreen
rendering support previously added in support of Core Graphics drawing
model.
Tested with full-screen sample and modified render-mode sample on Mac OS
X 10.6 in Safari 4 and on Mac OS X 10.5 with a version of Chromium with
the O3D blacklist entry removed.
BUG=http://code.google.com/p/o3d/issues/detail?id=221
TEST=none
Review URL: http://codereview.chromium.org/2095023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@48076 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/plugin')
-rw-r--r-- | o3d/plugin/cross/o3d_glue.cc | 9 | ||||
-rw-r--r-- | o3d/plugin/cross/o3d_glue.h | 36 | ||||
-rw-r--r-- | o3d/plugin/mac/fullscreen_window_mac.h | 2 | ||||
-rw-r--r-- | o3d/plugin/mac/fullscreen_window_mac.mm | 325 | ||||
-rw-r--r-- | o3d/plugin/mac/main_mac.mm | 245 | ||||
-rw-r--r-- | o3d/plugin/mac/o3d_layer.mm | 10 | ||||
-rw-r--r-- | o3d/plugin/mac/plugin_mac.h | 2 | ||||
-rw-r--r-- | o3d/plugin/mac/plugin_mac.mm | 133 |
8 files changed, 615 insertions, 147 deletions
diff --git a/o3d/plugin/cross/o3d_glue.cc b/o3d/plugin/cross/o3d_glue.cc index 35c0a14..6de01fd 100644 --- a/o3d/plugin/cross/o3d_glue.cc +++ b/o3d/plugin/cross/o3d_glue.cc @@ -138,6 +138,10 @@ PluginObject::PluginObject(NPP npp) mac_cgl_pbuffer_(0), last_mac_event_time_(0), gl_layer_(NULL), + mac_fullscreen_window_(NULL), + mac_fullscreen_nsopenglcontext_(NULL), + mac_fullscreen_nsopenglpixelformat_(NULL), + was_offscreen_(false), #endif // OS_MACOSX #ifdef OS_LINUX display_(NULL), @@ -224,6 +228,7 @@ void PluginObject::TearDown() { ClearPluginProperty(hWnd_); #elif defined(OS_MACOSX) o3d::ReleaseSafariBrowserWindow(mac_cocoa_window_); + CleanupFullscreenOpenGLContext(); #elif defined(OS_LINUX) SetDisplay(NULL); #endif // OS_WIN @@ -1143,6 +1148,10 @@ bool PluginObject::AllocateOffscreenRenderSurfaces(int width, int height) { } void PluginObject::DeallocateOffscreenRenderSurfaces() { + if (client_) { + client_->SetOffscreenRenderingSurfaces(RenderSurface::Ref(), + RenderDepthStencilSurface::Ref()); + } offscreen_render_surface_.Reset(); offscreen_depth_render_surface_.Reset(); offscreen_readback_bitmap_.Reset(); diff --git a/o3d/plugin/cross/o3d_glue.h b/o3d/plugin/cross/o3d_glue.h index 109b7c7..6f94a0b 100644 --- a/o3d/plugin/cross/o3d_glue.h +++ b/o3d/plugin/cross/o3d_glue.h @@ -240,13 +240,11 @@ class PluginObject: public NPObject { void SetDisplay(Display *display); #elif defined(OS_MACOSX) FullscreenWindowMac* GetFullscreenMacWindow() { - return mac_fullscreen_window_.get(); + return mac_fullscreen_window_; } - // Assumes ownership of the passed pointer. Passing NULL deletes the - // last non-NULL value set. void SetFullscreenMacWindow(FullscreenWindowMac* window) { - mac_fullscreen_window_.reset(window); + mac_fullscreen_window_ = window; } WindowRef GetMacWindow() { @@ -265,6 +263,17 @@ class PluginObject: public NPObject { return mac_agl_context_; } + CGLContextObj GetMacCGLContext() { + return mac_cgl_context_; + } + + void SetMacCGLContext(CGLContextObj obj); + + CGLContextObj GetFullscreenShareContext(); + CGLPixelFormatObj GetFullscreenCGLPixelFormatObj(); + void* GetFullscreenNSOpenGLContext(); + void CleanupFullscreenOpenGLContext(); + NPDrawingModel drawing_model_; NPEventModel event_model_; WindowRef mac_window_; // may be NULL in the Chrome case @@ -288,7 +297,24 @@ class PluginObject: public NPObject { // Fullscreen related stuff. // NULL if not in fullscreen mode. - scoped_ptr<FullscreenWindowMac> mac_fullscreen_window_; + // Must not be a scoped_ptr due to reentrancy during termination. + FullscreenWindowMac* mac_fullscreen_window_; + // When rendering using CGL, we need to use an NSOpenGLContext to + // implement full-screen support. In order to share textures and + // other resources between the core CGL context and the full-screen + // one, we need to allocate the NSOpenGLContext first, because with + // pre-10.6 APIs it isn't possible to make an NSOpenGLContext share + // resources with a preexisting CGLContextObj. + void* mac_fullscreen_nsopenglcontext_; + // On 10.5 (Core Graphics drawing model) it appears that we need to + // forcibly reuse the CGLContextObj from the NSOpenGLPixelFormat + // that we use to create the NSOpenGLContext for full-screen, or + // the share context is reported invalid. + void* mac_fullscreen_nsopenglpixelformat_; + // Indication when we transition to full-screen mode of whether we + // were using off-screen rendering (Core Graphics drawing model, in + // particular). + bool was_offscreen_; #endif // OS_MACOSX #ifdef OS_LINUX diff --git a/o3d/plugin/mac/fullscreen_window_mac.h b/o3d/plugin/mac/fullscreen_window_mac.h index 693150d..61cd840 100644 --- a/o3d/plugin/mac/fullscreen_window_mac.h +++ b/o3d/plugin/mac/fullscreen_window_mac.h @@ -64,6 +64,8 @@ class FullscreenWindowMac { virtual CGRect GetWindowBounds() const = 0; virtual bool IsActive() const = 0; + virtual void PrepareToRender() const = 0; + virtual void FinishRendering() const = 0; protected: FullscreenWindowMac() {} diff --git a/o3d/plugin/mac/fullscreen_window_mac.mm b/o3d/plugin/mac/fullscreen_window_mac.mm index 6824894..bbf99eb 100644 --- a/o3d/plugin/mac/fullscreen_window_mac.mm +++ b/o3d/plugin/mac/fullscreen_window_mac.mm @@ -31,11 +31,16 @@ #import "fullscreen_window_mac.h" +#include <AppKit/NSEvent.h> +#include <Cocoa/Cocoa.h> #include <QuickTime/QuickTime.h> #import "graphics_utils_mac.h" #import "overlay_window_mac.h" #import "plugin_mac.h" +#import "base/cocoa_protocols_mac.h" +#import "base/logging.h" +#import "base/scoped_nsobject.h" #import "core/cross/display_mode.h" #import "plugin/cross/o3d_glue.h" #import "plugin/mac/graphics_utils_mac.h" @@ -258,6 +263,8 @@ class CarbonFullscreenWindowMac : public FullscreenWindowMac { virtual bool Shutdown(const GLint* last_buffer_rect); virtual CGRect GetWindowBounds() const; virtual bool IsActive() const; + virtual void PrepareToRender() const; + virtual void FinishRendering() const; private: PluginObject* obj_; @@ -342,6 +349,305 @@ bool CarbonFullscreenWindowMac::IsActive() const { return fullscreen_window_ == ActiveNonFloatingWindow(); } +void CarbonFullscreenWindowMac::PrepareToRender() const { + // Nothing needs to be done here; the AGL context used to render + // into the plugin region is reused for rendering into the + // full-screen window. +} + +void CarbonFullscreenWindowMac::FinishRendering() const { +} + +//---------------------------------------------------------------------- +// Cocoa full-screen implementation. +// + +class CocoaFullscreenWindowMac; + +} // namespace o3d + +using o3d::CocoaFullscreenWindowMac; + +@interface O3DFullscreenView : NSView { +} + +- (void)rightMouseDown:(NSEvent*)theEvent; + +@end + +@interface O3DFullscreenWindow : NSWindow<NSWindowDelegate> { + CocoaFullscreenWindowMac* owner_; +} + +- (id)initWithOwner:(CocoaFullscreenWindowMac*)owner; + +@end + +namespace o3d { + +class CocoaFullscreenWindowMac : public FullscreenWindowMac { + public: + CocoaFullscreenWindowMac(PluginObject* obj); + virtual ~CocoaFullscreenWindowMac(); + + virtual bool Initialize(int target_width, int target_height); + virtual bool Shutdown(const GLint* last_buffer_rect); + virtual CGRect GetWindowBounds() const; + virtual bool IsActive() const; + virtual void PrepareToRender() const; + virtual void FinishRendering() const; + + void DispatchKeyEvent(NPCocoaEventType kind, + NSEvent* event); + void DispatchMouseEvent(NPCocoaEventType kind, + NSEvent* event); + void DispatchFocusLostEvent(); + + private: + PluginObject* obj_; + // The CGL context with which the plugin was previously rendering. + CGLContextObj saved_cgl_context_; + // This must not be a scoped_nsobject. AppKit must have + // responsibility for releasing this object to prevent it from being + // deleted in the middle of a focus transfer. + O3DFullscreenWindow* window_; + scoped_nsobject<O3DFullscreenView> view_; + NSOpenGLContext* context_; + // State bit indicating whether we are still in the process of going + // full-screen. + mutable bool going_fullscreen_; + + DISALLOW_COPY_AND_ASSIGN(CocoaFullscreenWindowMac); +}; + +} // namespace o3d + +@implementation O3DFullscreenView +- (void)rightMouseDown:(NSEvent*)event { + // Needed to forward right mouse button events. + NSResponder* next = [self nextResponder]; + if (next != nil) { + [next rightMouseDown:event]; + } +} +@end + +@implementation O3DFullscreenWindow +- (id) initWithOwner:(CocoaFullscreenWindowMac*)owner { + // The screen containing the menu bar is always index 0 in the + // screens array. We could also consider taking the screen with the + // focus fullscreen; this is the result of [NSScreen mainScreen]. + NSScreen* mainScreen = [[NSScreen screens] objectAtIndex:0]; + + self = [super initWithContentRect:[mainScreen frame] + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO + screen:mainScreen]; + if (self) { + owner_ = owner; + // We need to set ourselves as the delegate in order to receive + // focus changed notifications. + [self setDelegate:self]; + } + return self; +} + +- (BOOL)canBecomeKeyWindow { + // Needed to receive keyboard events. + return YES; +} + +- (void)keyDown:(NSEvent*)event { + owner_->DispatchKeyEvent(NPCocoaEventKeyDown, event); +} + +- (void)keyUp:(NSEvent*)event { + owner_->DispatchKeyEvent(NPCocoaEventKeyUp, event); +} + +- (void)mouseDown:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseDown, event); +} + +- (void)mouseDragged:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseDragged, event); +} + +- (void)mouseEntered:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseEntered, event); +} + +- (void)mouseExited:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseExited, event); +} + +- (void)mouseMoved:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseMoved, event); +} + +- (void)mouseUp:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseUp, event); +} + +- (void)rightMouseDown:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseDown, event); +} + +- (void)rightMouseDragged:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseDragged, event); +} + +- (void)rightMouseUp:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseUp, event); +} + +- (void)otherMouseDown:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseDown, event); +} + +- (void)otherMouseDragged:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseDragged, event); +} + +- (void)otherMouseUp:(NSEvent*)event { + owner_->DispatchMouseEvent(NPCocoaEventMouseUp, event); +} + +- (void)windowDidResignKey:(NSNotification*)notification { + owner_->DispatchFocusLostEvent(); +} + +- (void)windowDidResignMain:(NSNotification*)notification { + owner_->DispatchFocusLostEvent(); +} + +@end + +namespace o3d { + +CocoaFullscreenWindowMac::CocoaFullscreenWindowMac(PluginObject* obj) + : obj_(obj), + saved_cgl_context_(NULL), + window_(nil), + going_fullscreen_(false) { +} + +CocoaFullscreenWindowMac::~CocoaFullscreenWindowMac() { +} + +bool CocoaFullscreenWindowMac::Initialize(int target_width, + int target_height) { + window_ = [[O3DFullscreenWindow alloc] initWithOwner: this]; + if (window_ == nil) { + return false; + } + [window_ setLevel:NSMainMenuWindowLevel+1]; + [window_ setOpaque:YES]; + view_.reset([[O3DFullscreenView alloc] init]); + [window_ setContentView:view_]; + + // Fetch the NSOpenGLContext which was created earlier and which + // shares resources with the already-created CGLContextObj. + context_ = (NSOpenGLContext*) obj_->GetFullscreenNSOpenGLContext(); + [context_ setView:view_]; + // We need to make the plugin think we are rendering directly with + // the CGLContextObj underneath the NSOpenGLContext, because the + // renderer uses CGLSetCurrentContext directly when preparing to + // render. This should be a no-op because we already make the + // wrapper NSOpenGLContext current in PrepareToRender. + saved_cgl_context_ = obj_->GetMacCGLContext(); + obj_->SetMacCGLContext((CGLContextObj) [context_ CGLContextObj]); + [window_ makeKeyAndOrderFront:window_]; + FullscreenWindowMac::Initialize(target_width, target_height); + going_fullscreen_ = true; + return true; +} + +bool CocoaFullscreenWindowMac::Shutdown(const GLint* last_buffer_rect) { + if (saved_cgl_context_) { + obj_->SetMacCGLContext(saved_cgl_context_); + saved_cgl_context_ = NULL; + } + [NSOpenGLContext clearCurrentContext]; + if (context_) { + [context_ clearDrawable]; + context_ = nil; + } + if (window_) { + [window_ close]; + window_ = nil; + } + view_.reset(); + return true; +} + +CGRect CocoaFullscreenWindowMac::GetWindowBounds() const { + NSRect frame = [window_ frame]; + return CGRectMake(frame.origin.x, frame.origin.y, + frame.size.width, frame.size.height); +} + +bool CocoaFullscreenWindowMac::IsActive() const { + if (going_fullscreen_) + return true; + return [window_ isKeyWindow] && [window_ isMainWindow]; +} + +void CocoaFullscreenWindowMac::PrepareToRender() const { + [context_ update]; + [context_ makeCurrentContext]; + going_fullscreen_ = false; +} + +void CocoaFullscreenWindowMac::FinishRendering() const { + [context_ flushBuffer]; +} + +void CocoaFullscreenWindowMac::DispatchKeyEvent(NPCocoaEventType kind, + NSEvent* event) { + NPCocoaEvent cocoa_event; + memset(&cocoa_event, 0, sizeof(cocoa_event)); + // We need to dispatch events through HandleCocoaEvent in order for + // the escape key to disable full-screen, for example. + cocoa_event.type = kind; + cocoa_event.data.key.modifierFlags = [event modifierFlags]; + cocoa_event.data.key.characters = (NPNSString*) [event characters]; + cocoa_event.data.key.charactersIgnoringModifiers = + (NPNSString*) [event charactersIgnoringModifiers]; + cocoa_event.data.key.isARepeat = [event isARepeat]; + cocoa_event.data.key.keyCode = [event keyCode]; + HandleCocoaEvent(obj_->npp(), &cocoa_event); +} + +void CocoaFullscreenWindowMac::DispatchMouseEvent(NPCocoaEventType kind, + NSEvent* event) { + NPCocoaEvent cocoa_event; + memset(&cocoa_event, 0, sizeof(cocoa_event)); + cocoa_event.type = kind; + cocoa_event.data.mouse.modifierFlags = [event modifierFlags]; + NSPoint location = [event locationInWindow]; + cocoa_event.data.mouse.pluginX = location.x; + // We must transform the Y origin to the upper left. + NSRect frame = [window_ frame]; + cocoa_event.data.mouse.pluginY = frame.size.height - location.y; + cocoa_event.data.mouse.buttonNumber = [event buttonNumber]; + cocoa_event.data.mouse.clickCount = [event clickCount]; + cocoa_event.data.mouse.deltaX = [event deltaX]; + cocoa_event.data.mouse.deltaY = [event deltaY]; + cocoa_event.data.mouse.deltaZ = [event deltaZ]; + HandleCocoaEvent(obj_->npp(), &cocoa_event); +} + +void CocoaFullscreenWindowMac::DispatchFocusLostEvent() { + NPCocoaEvent cocoa_event; + memset(&cocoa_event, 0, sizeof(cocoa_event)); + cocoa_event.type = NPCocoaEventFocusChanged; + // cocoa_event.data.focus.hasFocus is already false. + // We aren't testing this in HandleCocoaEvent anyway. + HandleCocoaEvent(obj_->npp(), &cocoa_event); +} + //---------------------------------------------------------------------- // FullscreenWindowMac implementation. // @@ -350,12 +656,19 @@ FullscreenWindowMac* FullscreenWindowMac::Create( glue::_o3d::PluginObject* obj, int target_width, int target_height) { - FullscreenWindowMac* window = new CarbonFullscreenWindowMac(obj); - if (!window->Initialize(target_width, target_height)) { + FullscreenWindowMac* window = NULL; + if (obj->GetMacAGLContext()) { + DLOG(INFO) << "Using Carbon full-screen code."; + window = new CarbonFullscreenWindowMac(obj); + } else if (obj->GetMacCGLContext()) { + DLOG(INFO) << "Using Cocoa full-screen code."; + window = new CocoaFullscreenWindowMac(obj); + } + if (window && !window->Initialize(target_width, target_height)) { delete window; - return NULL; + window = NULL; } - + return window; } @@ -365,7 +678,7 @@ FullscreenWindowMac::~FullscreenWindowMac() { bool FullscreenWindowMac::Initialize(int target_width, int target_height) { #ifdef O3D_PLUGIN_ENABLE_FULLSCREEN_MSG overlay_window_.reset(new OverlayWindowMac()); -#endif +#endif return true; } @@ -374,7 +687,7 @@ void FullscreenWindowMac::IdleCallback() { if (overlay_window_.get()) { overlay_window_->IdleCallback(); } -#endif +#endif } bool FullscreenWindowMac::Shutdown(const GLint* last_buffer_rect) { diff --git a/o3d/plugin/mac/main_mac.mm b/o3d/plugin/mac/main_mac.mm index 86ae0de..3f38d65 100644 --- a/o3d/plugin/mac/main_mac.mm +++ b/o3d/plugin/mac/main_mac.mm @@ -463,108 +463,6 @@ EventModifiers CocoaToEventRecordModifiers(NSUInteger inMods) { return outMods; } -// Handle an NPCocoaEvent style event. The Cocoa event interface is -// a recent addition to the NAPI spec. -// See https://wiki.mozilla.org/Mac:NPAPI_Event_Models for further details. -// The principle advantages are that we can get scrollwheel messages, -// mouse-moved messages, and can tell which mouse button was pressed. -// This API will also be required for a carbon-free 64 bit version for 10.6. -bool HandleCocoaEvent(NPP instance, NPCocoaEvent* the_event) { - PluginObject* obj = static_cast<PluginObject*>(instance->pdata); - FullscreenWindowMac* fullscreen_window = obj->GetFullscreenMacWindow(); - bool handled = false; - - if (g_logger) g_logger->UpdateLogging(); - - obj->MacEventReceived(); - switch (the_event->type) { - case NPCocoaEventDrawRect: - // We need to call the render callback from here if we are rendering - // off-screen because it doesn't get called anywhere else. - if (obj->drawing_model_ == NPDrawingModelCoreAnimation) { - O3DLayer* layer = ObjO3DLayer(obj); - if (layer) { - [layer setNeedsDisplay]; - } - } else { - DrawPlugin(obj, - obj->IsOffscreenRenderingEnabled(), - the_event->data.draw.context); - } - handled = true; - break; - case NPCocoaEventMouseDown: - case NPCocoaEventMouseUp: - case NPCocoaEventMouseMoved: - case NPCocoaEventMouseDragged: - case NPCocoaEventMouseEntered: - case NPCocoaEventMouseExited: - case NPCocoaEventScrollWheel: - HandleCocoaMouseEvent(obj, the_event); - break; - case NPCocoaEventKeyDown: - // Hard-coded trapdoor to get out of fullscreen mode if user hits escape. - if (fullscreen_window) { - NSString *chars = - (NSString*) the_event->data.key.charactersIgnoringModifiers; - - if (chars == NULL || [chars length] == 0) { - break; - } - - if ([chars characterAtIndex:0] == '\e') { - obj->CancelFullscreenDisplay(); - break; - } - } // otherwise fall through - case NPCocoaEventKeyUp: { - EventKind eventKind = (the_event->type == NPCocoaEventKeyUp) ? keyUp : - (the_event->data.key.isARepeat) ? autoKey : keyDown; - - EventModifiers modifiers = - CocoaToEventRecordModifiers(the_event->data.key.modifierFlags); - - NSString *chars = - (NSString*) the_event->data.key.charactersIgnoringModifiers; - - if (chars == NULL || [chars length] == 0) { - break; - } - - DispatchKeyboardEvent(obj, - eventKind, - [chars characterAtIndex:0], - the_event->data.key.keyCode, - modifiers); - break; - } - case NPCocoaEventFlagsChanged: - case NPCocoaEventFocusChanged: - case NPCocoaEventWindowFocusChanged: - // Safari tab switching recovery code. - if (obj->mac_surface_hidden_) { - obj->mac_surface_hidden_ = false; - NPN_ForceRedraw(instance); // invalidate to cause a redraw - } - - // Auto-recovery for any situation where another window comes in front - // of the fullscreen window and we need to exit fullscreen mode. - // This can happen because another browser window has come forward, or - // because another app has been called to the front. - // TODO: We'll have problems with this when dealing with e.g. - // Japanese text input IME windows. - if (fullscreen_window && !fullscreen_window->IsActive()) { - obj->CancelFullscreenDisplay(); - } - - break; - case NPCocoaEventTextInput: - break; - } - - return handled; -} - // List of message types from mozilla's nsplugindefs.h, which is more // complete than the list in NPAPI.h. enum nsPluginEventType { @@ -934,6 +832,108 @@ bool HandleMacEvent(EventRecord* the_event, NPP instance) { return handled; } +// Handle an NPCocoaEvent style event. The Cocoa event interface is +// a recent addition to the NAPI spec. +// See https://wiki.mozilla.org/Mac:NPAPI_Event_Models for further details. +// The principle advantages are that we can get scrollwheel messages, +// mouse-moved messages, and can tell which mouse button was pressed. +// This API will also be required for a carbon-free 64 bit version for 10.6. +bool HandleCocoaEvent(NPP instance, NPCocoaEvent* the_event) { + PluginObject* obj = static_cast<PluginObject*>(instance->pdata); + FullscreenWindowMac* fullscreen_window = obj->GetFullscreenMacWindow(); + bool handled = false; + + if (g_logger) g_logger->UpdateLogging(); + + obj->MacEventReceived(); + switch (the_event->type) { + case NPCocoaEventDrawRect: + // We need to call the render callback from here if we are rendering + // off-screen because it doesn't get called anywhere else. + if (obj->drawing_model_ == NPDrawingModelCoreAnimation) { + O3DLayer* layer = ObjO3DLayer(obj); + if (layer) { + [layer setNeedsDisplay]; + } + } else { + DrawPlugin(obj, + obj->IsOffscreenRenderingEnabled(), + the_event->data.draw.context); + } + handled = true; + break; + case NPCocoaEventMouseDown: + case NPCocoaEventMouseUp: + case NPCocoaEventMouseMoved: + case NPCocoaEventMouseDragged: + case NPCocoaEventMouseEntered: + case NPCocoaEventMouseExited: + case NPCocoaEventScrollWheel: + HandleCocoaMouseEvent(obj, the_event); + break; + case NPCocoaEventKeyDown: + // Hard-coded trapdoor to get out of fullscreen mode if user hits escape. + if (fullscreen_window) { + NSString *chars = + (NSString*) the_event->data.key.charactersIgnoringModifiers; + + if (chars == NULL || [chars length] == 0) { + break; + } + + if ([chars characterAtIndex:0] == '\e') { + obj->CancelFullscreenDisplay(); + break; + } + } // otherwise fall through + case NPCocoaEventKeyUp: { + EventKind eventKind = (the_event->type == NPCocoaEventKeyUp) ? keyUp : + (the_event->data.key.isARepeat) ? autoKey : keyDown; + + EventModifiers modifiers = + CocoaToEventRecordModifiers(the_event->data.key.modifierFlags); + + NSString *chars = + (NSString*) the_event->data.key.charactersIgnoringModifiers; + + if (chars == NULL || [chars length] == 0) { + break; + } + + DispatchKeyboardEvent(obj, + eventKind, + [chars characterAtIndex:0], + the_event->data.key.keyCode, + modifiers); + break; + } + case NPCocoaEventFlagsChanged: + case NPCocoaEventFocusChanged: + case NPCocoaEventWindowFocusChanged: + // Safari tab switching recovery code. + if (obj->mac_surface_hidden_) { + obj->mac_surface_hidden_ = false; + NPN_ForceRedraw(instance); // invalidate to cause a redraw + } + + // Auto-recovery for any situation where another window comes in front + // of the fullscreen window and we need to exit fullscreen mode. + // This can happen because another browser window has come forward, or + // because another app has been called to the front. + // TODO: We'll have problems with this when dealing with e.g. + // Japanese text input IME windows. + if (fullscreen_window && !fullscreen_window->IsActive()) { + obj->CancelFullscreenDisplay(); + } + + break; + case NPCocoaEventTextInput: + break; + } + + return handled; +} + NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) { HANDLE_CRASHES; @@ -983,6 +983,8 @@ NPError NPP_Destroy(NPP instance, NPSavedData** save) { o3d::gRenderTimer.RemoveInstance(instance); #endif + // TODO(maf) / TODO(kbr): are we leaking AGL / CGL contexts? + obj->TearDown(); NPN_ReleaseObject(obj); instance->pdata = NULL; @@ -1071,41 +1073,28 @@ NPError NPP_SetWindow(NPP instance, NPWindow* window) { // CGL context rendering to a 1x1 pbuffer. Later we use the O3D // RenderSurface APIs to set up the framebuffer object which is used // for rendering. - static const CGLPixelFormatAttribute attribs[] = { - (CGLPixelFormatAttribute) kCGLPFAPBuffer, - (CGLPixelFormatAttribute) 0 - }; - CGLPixelFormatObj pixelFormat; - GLint numPixelFormats; - if (CGLChoosePixelFormat(attribs, - &pixelFormat, - &numPixelFormats) != kCGLNoError) { - DLOG(ERROR) << "Error choosing pixel format."; - return NPERR_GENERIC_ERROR; - } - if (!pixelFormat) { - DLOG(ERROR) << "Unable to find pbuffer compatible pixel format."; - return NPERR_GENERIC_ERROR; - } + CGLContextObj share_context = obj->GetFullscreenShareContext(); + CGLPixelFormatObj pixel_format = obj->GetFullscreenCGLPixelFormatObj(); + DCHECK(share_context); + CGLError result; CGLContextObj context; - CGLError res = CGLCreateContext(pixelFormat, 0, &context); - CGLDestroyPixelFormat(pixelFormat); - if (res != kCGLNoError) { - DLOG(ERROR) << "Error creating context."; + result = CGLCreateContext(pixel_format, share_context, &context); + if (result != kCGLNoError) { + DLOG(ERROR) << "Error " << result << " creating context."; return NPERR_GENERIC_ERROR; } CGLPBufferObj pbuffer; - if (CGLCreatePBuffer(1, 1, - GL_TEXTURE_2D, GL_RGBA, - 0, &pbuffer) != kCGLNoError) { + if ((result = CGLCreatePBuffer(1, 1, + GL_TEXTURE_2D, GL_RGBA, + 0, &pbuffer)) != kCGLNoError) { CGLDestroyContext(context); - DLOG(ERROR) << "Error creating pbuffer."; + DLOG(ERROR) << "Error " << result << " creating pbuffer."; return NPERR_GENERIC_ERROR; } - if (CGLSetPBuffer(context, pbuffer, 0, 0, 0) != kCGLNoError) { + if ((result = CGLSetPBuffer(context, pbuffer, 0, 0, 0)) != kCGLNoError) { CGLDestroyContext(context); CGLDestroyPBuffer(pbuffer); - DLOG(ERROR) << "Error attaching pbuffer to context."; + DLOG(ERROR) << "Error " << result << " attaching pbuffer to context."; return NPERR_GENERIC_ERROR; } // Must make the context current for renderer creation to succeed diff --git a/o3d/plugin/mac/o3d_layer.mm b/o3d/plugin/mac/o3d_layer.mm index 9f43048..194cc5e 100644 --- a/o3d/plugin/mac/o3d_layer.mm +++ b/o3d/plugin/mac/o3d_layer.mm @@ -115,10 +115,14 @@ using o3d::DisplayWindowMac; * renderers from pixel format 'pixelFormat'. The default implementation * allocates a new context with a null share context. */ - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { - glContext_ = [super copyCGLContextForPixelFormat:pixelFormat]; - obj_->mac_cgl_context_ = glContext_; + CGLContextObj share_context = obj_->GetFullscreenShareContext(); + DCHECK(share_context); + if (CGLCreateContext(pixelFormat, share_context, &glContext_) != + kCGLNoError) { + glContext_ = [super copyCGLContextForPixelFormat:pixelFormat]; + } + obj_->SetMacCGLContext(glContext_); created_context_ = true; - return glContext_; } diff --git a/o3d/plugin/mac/plugin_mac.h b/o3d/plugin/mac/plugin_mac.h index ebf9c3a..8d003ee 100644 --- a/o3d/plugin/mac/plugin_mac.h +++ b/o3d/plugin/mac/plugin_mac.h @@ -98,6 +98,8 @@ char* CreatePosixFilePathFromHFSFilePath(const char* hfsPath); bool HandleMacEvent(EventRecord* the_event, NPP instance); +bool HandleCocoaEvent(NPP instance, NPCocoaEvent* the_event); + o3d::Event::Button MacOSMouseButtonNumberToO3DButton(int inButton); bool GetBrowserVersionInfo(int *returned_major, diff --git a/o3d/plugin/mac/plugin_mac.mm b/o3d/plugin/mac/plugin_mac.mm index 057d43d..d65ccec 100644 --- a/o3d/plugin/mac/plugin_mac.mm +++ b/o3d/plugin/mac/plugin_mac.mm @@ -39,6 +39,7 @@ #include "plugin/cross/o3d_glue.h" #include "plugin/cross/main.h" #include "core/mac/display_window_mac.h" +#include "core/cross/gl/renderer_gl.h" #include "plugin/mac/graphics_utils_mac.h" #import "plugin/mac/o3d_layer.h" @@ -238,19 +239,24 @@ void RenderTimer::TimerCallback(CFRunLoopTimerRef timer, void* info) { NPP instance = instances_[i]; PluginObject* obj = static_cast<PluginObject*>(instance->pdata); - if (obj->drawing_model_ == NPDrawingModelCoreAnimation) { + bool in_fullscreen = obj->GetFullscreenMacWindow(); + + if (obj->drawing_model_ == NPDrawingModelCoreAnimation && + !in_fullscreen) { O3DLayer* o3dLayer = static_cast<O3DLayer*>(obj->gl_layer_); if (o3dLayer) { obj->client()->Tick(); [o3dLayer setNeedsDisplay]; } - return; + continue; } ManageSafariTabSwitching(obj); obj->client()->Tick(); - bool in_fullscreen = obj->GetFullscreenMacWindow(); + // It's possible that event processing may have torn down the + // full-screen window in the call above. + in_fullscreen = obj->GetFullscreenMacWindow(); if (in_fullscreen) { obj->GetFullscreenMacWindow()->IdleCallback(); @@ -286,7 +292,13 @@ void RenderTimer::TimerCallback(CFRunLoopTimerRef timer, void* info) { rect.right = obj->width(); NPN_InvalidateRect(instance, &rect); } else { + if (in_fullscreen) { + obj->GetFullscreenMacWindow()->PrepareToRender(); + } obj->client()->RenderClient(true); + if (in_fullscreen) { + obj->GetFullscreenMacWindow()->FinishRendering(); + } } } } @@ -528,6 +540,11 @@ bool PluginObject::RequestFullscreenDisplay() { } } + was_offscreen_ = IsOffscreenRenderingEnabled(); + if (was_offscreen_) { + DisableOffscreenRendering(); + } + FullscreenWindowMac* fullscreen_window = FullscreenWindowMac::Create(this, target_width, target_height); SetFullscreenMacWindow(fullscreen_window); @@ -543,16 +560,26 @@ bool PluginObject::RequestFullscreenDisplay() { } void PluginObject::CancelFullscreenDisplay() { + FullscreenWindowMac* window = GetFullscreenMacWindow(); + // if not in fullscreen mode, do nothing - if (!GetFullscreenMacWindow()) + if (!window) return; - GetFullscreenMacWindow()->Shutdown(last_buffer_rect_); + // The focus change during closing of the fullscreen window may + // cause the plugin to become reentrant. Store the full-screen + // window on the stack to prevent attempting to shut down twice. SetFullscreenMacWindow(NULL); + window->Shutdown(last_buffer_rect_); + delete window; renderer_->Resize(prev_width_, prev_height_); fullscreen_ = false; client()->SendResizeEvent(prev_width_, prev_height_, false); + if (was_offscreen_) { + EnableOffscreenRendering(); + } + // Somehow the browser window does not automatically activate again // when we close the fullscreen window, so explicitly reactivate it. if (mac_cocoa_window_) { @@ -563,5 +590,101 @@ void PluginObject::CancelFullscreenDisplay() { } } +#define PFA(number) static_cast<NSOpenGLPixelFormatAttribute>(number) + +#define O3D_NSO_COLOR_AND_DEPTH_SETTINGS NSOpenGLPFAClosestPolicy, \ + NSOpenGLPFAColorSize, PFA(24), \ + NSOpenGLPFAAlphaSize, PFA(8), \ + NSOpenGLPFADepthSize, PFA(24), \ + NSOpenGLPFADoubleBuffer, +// The Core Animation code path on 10.6 core dumps if the +// NSOpenGLPFAPixelBuffer and NSOpenGLPFAWindow attributes are +// specified. It seems risky for the 10.5 code path if they aren't, +// since this actual CGLPixelFormatObj is used to create the pbuffer's +// context, but no ill effects have been seen there so leaving them +// out for now. +#define O3D_NSO_PBUFFER_SETTINGS +#define O3D_NSO_STENCIL_SETTINGS NSOpenGLPFAStencilSize, PFA(8), +#define O3D_NSO_HARDWARE_RENDERER \ + NSOpenGLPFAAccelerated, NSOpenGLPFANoRecovery, +#define O3D_NSO_MULTISAMPLE \ + NSOpenGLPFAMultisample, NSOpenGLPFASamples, PFA(4), +#define O3D_NSO_END PFA(0) + +CGLContextObj PluginObject::GetFullscreenShareContext() { + if (mac_fullscreen_nsopenglcontext_ == NULL) { + static const NSOpenGLPixelFormatAttribute attributes[] = { + O3D_NSO_COLOR_AND_DEPTH_SETTINGS + O3D_NSO_PBUFFER_SETTINGS + O3D_NSO_STENCIL_SETTINGS + O3D_NSO_HARDWARE_RENDERER + O3D_NSO_MULTISAMPLE + O3D_NSO_END + }; + NSOpenGLPixelFormat* pixel_format = + [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + if (!pixel_format) { + // Try a less capable set. + static const NSOpenGLPixelFormatAttribute low_end_attributes[] = { + O3D_NSO_COLOR_AND_DEPTH_SETTINGS + O3D_NSO_PBUFFER_SETTINGS + O3D_NSO_STENCIL_SETTINGS + O3D_NSO_HARDWARE_RENDERER + O3D_NSO_END + }; + pixel_format = + [[NSOpenGLPixelFormat alloc] initWithAttributes:low_end_attributes]; + } + if (pixel_format) { + mac_fullscreen_nsopenglpixelformat_ = pixel_format; + + NSOpenGLContext* context = + [[NSOpenGLContext alloc] initWithFormat:pixel_format + shareContext:nil]; + mac_fullscreen_nsopenglcontext_ = context; + } else { + DLOG(ERROR) << "Error choosing NSOpenGLPixelFormat."; + } + } + + NSOpenGLContext* context = (NSOpenGLContext*) mac_fullscreen_nsopenglcontext_; + return (CGLContextObj) [context CGLContextObj]; +} + +void* PluginObject::GetFullscreenNSOpenGLContext() { + return mac_fullscreen_nsopenglcontext_; +} + +CGLPixelFormatObj PluginObject::GetFullscreenCGLPixelFormatObj() { + NSOpenGLPixelFormat* pixel_format = + (NSOpenGLPixelFormat*) mac_fullscreen_nsopenglpixelformat_; + if (pixel_format == nil) { + return NULL; + } + return (CGLPixelFormatObj) [pixel_format CGLPixelFormatObj]; +} + +void PluginObject::CleanupFullscreenOpenGLContext() { + NSOpenGLContext* context = + (NSOpenGLContext*) mac_fullscreen_nsopenglcontext_; + mac_fullscreen_nsopenglcontext_ = NULL; + if (context) { + [context release]; + } + NSOpenGLPixelFormat* format = + (NSOpenGLPixelFormat*) mac_fullscreen_nsopenglpixelformat_; + mac_fullscreen_nsopenglpixelformat_ = NULL; + if (format) { + [format release]; + } +} + +void PluginObject::SetMacCGLContext(CGLContextObj context) { + mac_cgl_context_ = context; + if (renderer_) { + ((o3d::RendererGL*) renderer_)->set_mac_cgl_context(context); + } +} + } // namespace glue } // namespace o3d |