From 247a8a7174bf41f6156e70f77f11c629fef8e918 Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 9 Oct 2015 19:45:39 -0300 Subject: [PATCH] Make progress in Skia/OSX port It includes: - Use ARC instead of GC (compiling with -fobjc-arc flag) - Implement GLContextCGL::getStencilBits/getSampleCount functions - Modify OSXEventQueue to avoid creating a thread for app_main() - NativeDialogs class can be compiled in 10.4 (with GC) and 10.6 (with ARC) - Split she/osx/view.h into view.h and view.mm - get_local_mouse_pos() takes care of the window scale - Temporal she::clock_value() impl - Working SkiaWindow with Quartz and some progress with OpenGL --- src/she/CMakeLists.txt | 4 + src/she/gl/gl_context_cgl.h | 32 ++-- src/she/osx/app.h | 4 - src/she/osx/app.mm | 52 +----- src/she/osx/app_delegate.h | 5 +- src/she/osx/app_delegate.mm | 19 +-- src/she/osx/event_queue.mm | 29 +++- src/she/osx/native_dialogs.mm | 18 +- src/she/osx/view.h | 134 ++------------- src/she/osx/view.mm | 184 ++++++++++++++++++++ src/she/osx/window.h | 14 +- src/she/osx/window.mm | 39 ++++- src/she/osx/window_delegate.h | 15 +- src/she/skia/she.cpp | 7 +- src/she/skia/skia_system.h | 4 - src/she/skia/skia_window_osx.h | 2 + src/she/skia/skia_window_osx.mm | 287 +++++++++++++++++++++++++++----- 17 files changed, 574 insertions(+), 275 deletions(-) create mode 100644 src/she/osx/view.mm diff --git a/src/she/CMakeLists.txt b/src/she/CMakeLists.txt index fb45214ce..c02f32ae0 100644 --- a/src/she/CMakeLists.txt +++ b/src/she/CMakeLists.txt @@ -60,6 +60,9 @@ if(USE_SKIA_BACKEND) elseif(APPLE) add_definitions(-DSK_BUILD_FOR_MAC) add_definitions(-Wno-ignored-attributes -Wno-unused-result) + + # Use Automatic Reference Counting + add_definitions(-fobjc-arc) endif() if(CMAKE_BUILD_TYPE STREQUAL Debug) @@ -144,6 +147,7 @@ if(USE_SKIA_BACKEND) osx/app.mm osx/app_delegate.mm osx/event_queue.mm + osx/view.mm osx/window.mm skia/skia_window_osx.mm) endif() diff --git a/src/she/gl/gl_context_cgl.h b/src/she/gl/gl_context_cgl.h index 87ff095e9..24b80b6e8 100644 --- a/src/she/gl/gl_context_cgl.h +++ b/src/she/gl/gl_context_cgl.h @@ -19,8 +19,10 @@ class GLContextCGL : public GLContext { public: typedef void* NativeHandle; - GLContextCGL(void*) - : m_glctx(nullptr) { + GLContextCGL(NativeHandle window) + : m_glctx(nullptr) + , m_stencilBits(0) + , m_sampleCount(0) { } ~GLContextCGL() { @@ -29,21 +31,23 @@ public: bool createGLContext() override { CGLPixelFormatAttribute attributes[] = { -#if MAC_OS_X_VERSION_10_7 kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core, -#endif + kCGLPFAAccelerated, kCGLPFADoubleBuffer, (CGLPixelFormatAttribute)0 }; - CGLPixelFormatObj pixFormat; + CGLPixelFormatObj format; GLint npix; - CGLChoosePixelFormat(attributes, &pixFormat, &npix); - if (!pixFormat) + CGLChoosePixelFormat(attributes, &format, &npix); + if (!format) return false; - CGLCreateContext(pixFormat, nullptr, &m_glctx); - CGLReleasePixelFormat(pixFormat); + CGLDescribePixelFormat(format, 0, kCGLPFASamples, &m_sampleCount); + CGLDescribePixelFormat(format, 0, kCGLPFAStencilSize, &m_stencilBits); + + CGLCreateContext(format, nullptr, &m_glctx); + CGLReleasePixelFormat(format); if (!m_glctx) return false; @@ -59,15 +63,21 @@ public: } int getStencilBits() override { - return 0; + return m_stencilBits; } int getSampleCount() override { - return 0; + return m_sampleCount; + } + + CGLContextObj cglContext() { + return m_glctx; } private: CGLContextObj m_glctx; + int m_stencilBits; + int m_sampleCount; }; } // namespace she diff --git a/src/she/osx/app.h b/src/she/osx/app.h index d33e6156f..ff1ebcdcb 100644 --- a/src/she/osx/app.h +++ b/src/she/osx/app.h @@ -22,13 +22,9 @@ namespace she { ~OSXApp(); int run(int argc, char* argv[]); - void joinUserThread(); - - void stopUIEventLoop(); private: static OSXApp* g_instance; - base::thread* m_userThread; }; } // namespace she diff --git a/src/she/osx/app.mm b/src/she/osx/app.mm index c38c96810..a628bf8f6 100644 --- a/src/she/osx/app.mm +++ b/src/she/osx/app.mm @@ -24,7 +24,6 @@ namespace she { OSXApp* OSXApp::g_instance = nullptr; OSXApp::OSXApp() - : m_userThread(nullptr) { g_instance = this; } @@ -36,61 +35,14 @@ OSXApp::~OSXApp() int OSXApp::run(int argc, char* argv[]) { - NSAutoreleasePool* pool = [NSAutoreleasePool new]; - (void)pool; - NSApplication* app = [NSApplication sharedApplication]; - OSXAppDelegate* appDelegate = [OSXAppDelegate new]; - - // Create default main menu - NSMenu* mainMenu = [[NSMenu new] autorelease]; - { - NSMenu* appMenu = [[NSMenu new] autorelease]; - NSMenuItem* quitItem = [appMenu addItemWithTitle:@"Quit " PACKAGE - action:@selector(quit:) - keyEquivalent:@"q"]; - [quitItem setKeyEquivalentModifierMask:NSCommandKeyMask]; - [quitItem setTarget:appDelegate]; - - NSMenuItem* appMenuItem = [[NSMenuItem new] autorelease]; - [appMenuItem setSubmenu:appMenu]; - - [mainMenu setTitle:@PACKAGE]; - [mainMenu addItem:appMenuItem]; - } + id appDelegate = [OSXAppDelegate new]; [app setActivationPolicy:NSApplicationActivationPolicyRegular]; [app setDelegate:appDelegate]; - [app setMainMenu:mainMenu]; - - // The whole application runs in a background thread (non-main UI thread). - m_userThread = new base::thread([&]() { - // Ignore return value, as [NSApp run] doesn't return we cannot use it. - app_main(argc, argv); - }); - - [app run]; - - // In this case, the main NSRunLoop was stopped, so we have to terminate here. - //if (m_userThread) - // joinUserThread(); + app_main(argc, argv); return 0; } -void OSXApp::joinUserThread() -{ - // Join the user background thread to call all destructors and close everything properly. - m_userThread->join(); - delete m_userThread; - m_userThread = nullptr; -} - -void OSXApp::stopUIEventLoop() -{ - // Stop the main NSRunLoop and post a dummy event to wake it up. - [NSApp stop:nil]; - [NSApp postEvent:[NSEvent new] atStart:true]; -} - } // namespace she diff --git a/src/she/osx/app_delegate.h b/src/she/osx/app_delegate.h index 60b0655c0..0ba642087 100644 --- a/src/she/osx/app_delegate.h +++ b/src/she/osx/app_delegate.h @@ -13,10 +13,9 @@ #include @interface OSXAppDelegate : NSObject -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication; - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender; -- (void)applicationWillTerminate:(NSNotification*)aNotification; -- (void)quit:(id)sender; +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)app; +- (void)applicationWillTerminate:(NSNotification*)notification; @end #endif diff --git a/src/she/osx/app_delegate.mm b/src/she/osx/app_delegate.mm index e090891fd..3115f4a4a 100644 --- a/src/she/osx/app_delegate.mm +++ b/src/she/osx/app_delegate.mm @@ -21,28 +21,21 @@ @implementation OSXAppDelegate -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication -{ - return YES; -} - - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender { return NSTerminateNow; } -- (void)applicationWillTerminate:(NSNotification*)aNotification +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)app +{ + return YES; +} + +- (void)applicationWillTerminate:(NSNotification*)notification { she::Event ev; ev.setType(she::Event::CloseDisplay); she::instance()->eventQueue()->queueEvent(ev); - - she::OSXApp::instance()->joinUserThread(); -} - -- (void)quit:(id)sender -{ - [[NSApp mainWindow] performClose:self]; } @end diff --git a/src/she/osx/event_queue.mm b/src/she/osx/event_queue.mm index e020ef136..5fd45f0d3 100644 --- a/src/she/osx/event_queue.mm +++ b/src/she/osx/event_queue.mm @@ -16,10 +16,37 @@ namespace she { +static NSWindow* g_window = nil; + void OSXEventQueue::getEvent(Event& ev, bool canWait) { + ev.setType(Event::None); + +retry:; + NSApplication* app = [NSApplication sharedApplication]; + if (!app) + return; + + // Pump the whole queue of Cocoa events + NSEvent* event; + do { + event = [app nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) + [app sendEvent: event]; + } while (event); + if (!m_events.try_pop(ev)) { - ev.setType(Event::None); + if (canWait) { + // Wait until there is a Cocoa event in queue + [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:NO]; + goto retry; + } } } diff --git a/src/she/osx/native_dialogs.mm b/src/she/osx/native_dialogs.mm index 8d8f5387a..9921d1824 100644 --- a/src/she/osx/native_dialogs.mm +++ b/src/she/osx/native_dialogs.mm @@ -17,8 +17,8 @@ #include "base/path.h" -@interface OpenSaveHelper : NSObject -{ +@interface OpenSaveHelper : NSObject { +@private NSSavePanel* panel; she::Display* display; int result; @@ -56,13 +56,17 @@ she::NativeCursor oldCursor = display->nativeMouseCursor(); display->setNativeMouseCursor(she::kArrowCursor); +#ifndef __MAC_10_6 // runModalForTypes is deprecated in 10.6 if ([panel isKindOfClass:[NSOpenPanel class]]) { // As we're using OS X 10.4 framework, it looks like runModal // doesn't recognize the allowedFileTypes property. So we force it // using runModalForTypes: selector. - result = [panel runModalForTypes:[panel allowedFileTypes]]; + + result = [(NSOpenPanel*)panel runModalForTypes:[panel allowedFileTypes]]; } - else { + else +#endif + { result = [panel runModal]; } @@ -128,12 +132,12 @@ public: } else { panel = [NSOpenPanel openPanel]; - [panel setAllowsMultipleSelection:NO]; + [(NSOpenPanel*)panel setAllowsMultipleSelection:NO]; + [(NSOpenPanel*)panel setCanChooseDirectories:NO]; } [panel setTitle:[NSString stringWithUTF8String:m_title.c_str()]]; [panel setCanCreateDirectories:YES]; - [panel setCanChooseDirectories:NO]; std::string defPath = base::get_file_path(m_filename); std::string defName = base::get_file_name(m_filename); @@ -164,8 +168,10 @@ public: else retValue = false; +#if !__has_feature(objc_arc) [helper release]; [types release]; +#endif return retValue; } diff --git a/src/she/osx/view.h b/src/she/osx/view.h index 05d73f611..a156f569a 100644 --- a/src/she/osx/view.h +++ b/src/she/osx/view.h @@ -4,32 +4,23 @@ // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. -inline gfx::Point get_local_mouse_pos(NSView* view, NSEvent* event) -{ - NSPoint point = [view convertPoint:[event locationInWindow] - fromView:nil]; - // "she" layer coordinates expect (X,Y) origin at the top-left corner. - return gfx::Point(point.x, - view.bounds.size.height - point.y); -} +#ifndef SHE_OSX_VIEW_H_INCLUDED +#define SHE_OSX_VIEW_H_INCLUDED +#pragma once -inline she::Event::MouseButton get_mouse_buttons(NSEvent* event) -{ - switch ([event buttonNumber]) { - case 0: return she::Event::LeftButton; break; - case 1: return she::Event::RightButton; break; - case 2: return she::Event::MiddleButton; break; - // TODO add support for other buttons - } - return she::Event::MouseButton::NoneButton; -} +#include +#include +#include -@interface OSXView : NSView -{ +@interface OSXView : NSView { +@private NSTrackingArea* m_trackingArea; } - (id)initWithFrame:(NSRect)frameRect; -- (void)dealloc; +- (void)viewDidHide; +- (void)viewDidUnhide; +- (void)viewDidMoveToWindow; +- (void)drawRect:(NSRect)dirtyRect; - (void)mouseDown:(NSEvent*)event; - (void)mouseUp:(NSEvent*)event; - (void)mouseEntered:(NSEvent*)event; @@ -41,103 +32,4 @@ inline she::Event::MouseButton get_mouse_buttons(NSEvent* event) - (void)destroyMouseTrackingArea; @end -@implementation OSXView - -- (id)initWithFrame:(NSRect)frameRect -{ - self = [super initWithFrame:frameRect]; - if (self != nil) { - [self createMouseTrackingArea]; - } - return self; -} - -- (void)dealloc -{ - [self destroyMouseTrackingArea]; - [super dealloc]; -} - -- (void)mouseDown:(NSEvent*)event -{ - she::Event ev; - ev.setType(she::Event::MouseDown); - ev.setPosition(get_local_mouse_pos(self, event)); - ev.setButton(she::Event::LeftButton); - she::queue_event(ev); -} - -- (void)mouseUp:(NSEvent*)event -{ - she::Event ev; - ev.setType(she::Event::MouseUp); - ev.setPosition(get_local_mouse_pos(self, event)); - ev.setButton(she::Event::LeftButton); - she::queue_event(ev); -} - -- (void)mouseEntered:(NSEvent*)event -{ - she::Event ev; - ev.setType(she::Event::MouseEnter); - ev.setPosition(get_local_mouse_pos(self, event)); - she::queue_event(ev); -} - -- (void)mouseMoved:(NSEvent*)event -{ - she::Event ev; - ev.setType(she::Event::MouseMove); - ev.setPosition(get_local_mouse_pos(self, event)); - she::queue_event(ev); -} - -- (void)mouseExited:(NSEvent*)event -{ - she::Event ev; - ev.setType(she::Event::MouseLeave); - ev.setPosition(get_local_mouse_pos(self, event)); - she::queue_event(ev); -} - -- (void)mouseDragged:(NSEvent*)event -{ - she::Event ev; - ev.setType(she::Event::MouseMove); - ev.setPosition(get_local_mouse_pos(self, event)); - ev.setButton(get_mouse_buttons(event)); - she::queue_event(ev); -} - -- (void)setFrameSize:(NSSize)newSize -{ - [super setFrameSize:newSize]; - - // Re-create the mouse tracking area - [self destroyMouseTrackingArea]; - [self createMouseTrackingArea]; -} - -- (void)createMouseTrackingArea -{ - // Create a tracking area to receive mouseMoved events - m_trackingArea = - [[NSTrackingArea alloc] - initWithRect:self.bounds - options:(NSTrackingMouseEnteredAndExited | - NSTrackingMouseMoved | - NSTrackingActiveAlways | - NSTrackingEnabledDuringMouseDrag) - owner:self - userInfo:nil]; - [self addTrackingArea:m_trackingArea]; -} - -- (void)destroyMouseTrackingArea -{ - [self removeTrackingArea:m_trackingArea]; - [m_trackingArea release]; - m_trackingArea = nil; -} - -@end +#endif diff --git a/src/she/osx/view.mm b/src/she/osx/view.mm new file mode 100644 index 000000000..22d0f4b50 --- /dev/null +++ b/src/she/osx/view.mm @@ -0,0 +1,184 @@ +// SHE library +// Copyright (C) 2015 David Capello +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "she/osx/view.h" + +#include "gfx/point.h" +#include "she/event.h" +#include "she/event_queue.h" +#include "she/osx/window.h" + +namespace { + +inline gfx::Point get_local_mouse_pos(NSView* view, NSEvent* event) +{ + NSPoint point = [view convertPoint:[event locationInWindow] + fromView:nil]; + int scale = 1; + + if ([view window]) + scale = [(OSXWindow*)[view window] scale]; + + // "she" layer coordinates expect (X,Y) origin at the top-left corner. + return gfx::Point(point.x / scale, + (view.bounds.size.height - point.y) / scale); +} + +inline she::Event::MouseButton get_mouse_buttons(NSEvent* event) +{ + switch ([event buttonNumber]) { + case 0: return she::Event::LeftButton; break; + case 1: return she::Event::RightButton; break; + case 2: return she::Event::MiddleButton; break; + // TODO add support for other buttons + } + return she::Event::MouseButton::NoneButton; +} + +} // anonymous namespace + +@implementation OSXView + +- (id)initWithFrame:(NSRect)frameRect +{ + self = [super initWithFrame:frameRect]; + if (self != nil) { + [self createMouseTrackingArea]; + } + return self; +} + +- (void)viewDidHide +{ + [super viewDidHide]; + [self destroyMouseTrackingArea]; +} + +- (void)viewDidUnhide +{ + [super viewDidUnhide]; + [self createMouseTrackingArea]; +} + +- (void)viewDidMoveToWindow +{ + [super viewDidMoveToWindow]; + + if ([self window]) { + OSXWindowImpl* impl = [((OSXWindow*)[self window]) impl]; + if (impl) + impl->onWindowChanged(); + } +} + +- (void)drawRect:(NSRect)dirtyRect +{ + [super drawRect:dirtyRect]; + + if ([self window]) { + OSXWindowImpl* impl = [((OSXWindow*)[self window]) impl]; + if (impl) + impl->onDrawRect(gfx::Rect(dirtyRect.origin.x, + dirtyRect.origin.y, + dirtyRect.size.width, + dirtyRect.size.height)); + } +} + +- (void)mouseDown:(NSEvent*)event +{ + she::Event ev; + ev.setType(she::Event::MouseDown); + ev.setPosition(get_local_mouse_pos(self, event)); + ev.setButton(she::Event::LeftButton); + she::queue_event(ev); +} + +- (void)mouseUp:(NSEvent*)event +{ + she::Event ev; + ev.setType(she::Event::MouseUp); + ev.setPosition(get_local_mouse_pos(self, event)); + ev.setButton(she::Event::LeftButton); + she::queue_event(ev); +} + +- (void)mouseEntered:(NSEvent*)event +{ + she::Event ev; + ev.setType(she::Event::MouseEnter); + ev.setPosition(get_local_mouse_pos(self, event)); + she::queue_event(ev); +} + +- (void)mouseMoved:(NSEvent*)event +{ + she::Event ev; + ev.setType(she::Event::MouseMove); + ev.setPosition(get_local_mouse_pos(self, event)); + she::queue_event(ev); +} + +- (void)mouseExited:(NSEvent*)event +{ + she::Event ev; + ev.setType(she::Event::MouseLeave); + ev.setPosition(get_local_mouse_pos(self, event)); + she::queue_event(ev); +} + +- (void)mouseDragged:(NSEvent*)event +{ + she::Event ev; + ev.setType(she::Event::MouseMove); + ev.setPosition(get_local_mouse_pos(self, event)); + ev.setButton(get_mouse_buttons(event)); + she::queue_event(ev); +} + +- (void)setFrameSize:(NSSize)newSize +{ + [super setFrameSize:newSize]; + + // Re-create the mouse tracking area + [self destroyMouseTrackingArea]; + [self createMouseTrackingArea]; + + // Call OSXWindowImpl::onResize handler + if ([self window]) { + OSXWindowImpl* impl = [((OSXWindow*)[self window]) impl]; + if (impl) + impl->onResize(gfx::Size(newSize.width, + newSize.height)); + } +} + +- (void)createMouseTrackingArea +{ + // Create a tracking area to receive mouseMoved events + m_trackingArea = + [[NSTrackingArea alloc] + initWithRect:self.bounds + options:(NSTrackingMouseEnteredAndExited | + NSTrackingMouseMoved | + NSTrackingActiveAlways | + NSTrackingEnabledDuringMouseDrag) + owner:self + userInfo:nil]; + [self addTrackingArea:m_trackingArea]; +} + +- (void)destroyMouseTrackingArea +{ + [self removeTrackingArea:m_trackingArea]; + m_trackingArea = nil; +} + +@end diff --git a/src/she/osx/window.h b/src/she/osx/window.h index 4cc7513b1..8384c4037 100644 --- a/src/she/osx/window.h +++ b/src/she/osx/window.h @@ -14,22 +14,32 @@ #include +#include "gfx/rect.h" #include "gfx/size.h" class OSXWindowImpl { public: virtual ~OSXWindowImpl() { } virtual void onClose() = 0; + virtual void onResize(const gfx::Size& size) = 0; + virtual void onDrawRect(const gfx::Rect& rect) = 0; + virtual void onWindowChanged() = 0; }; -@interface OSXWindow : NSWindow -{ +@class OSXWindowDelegate; + +@interface OSXWindow : NSWindow { +@private OSXWindowImpl* m_impl; + OSXWindowDelegate* m_delegate; + int m_scale; gfx::Size m_clientSize; gfx::Size m_restoredSize; } - (OSXWindow*)initWithImpl:(OSXWindowImpl*)impl; - (OSXWindowImpl*)impl; +- (int)scale; +- (void)setScale:(int)scale; - (gfx::Size)clientSize; - (gfx::Size)restoredSize; @end diff --git a/src/she/osx/window.mm b/src/she/osx/window.mm index f7ddcdb91..a9eb59566 100644 --- a/src/she/osx/window.mm +++ b/src/she/osx/window.mm @@ -11,7 +11,7 @@ #include "she/osx/window.h" #include "she/event.h" -#include "she/event_queue.h" +#include "she/osx/event_queue.h" #include "she/osx/view.h" #include "she/osx/window_delegate.h" @@ -20,23 +20,28 @@ - (OSXWindow*)initWithImpl:(OSXWindowImpl*)impl { m_impl = impl; + m_scale = 1; NSRect rect = NSMakeRect(0, 0, 640, 480); m_clientSize.w = m_restoredSize.w = rect.size.width; m_clientSize.h = m_restoredSize.h = rect.size.height; + self = [self initWithContentRect:rect + styleMask:(NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask) + backing:NSBackingStoreBuffered + defer:NO]; + if (!self) + return nil; + + m_delegate = [[OSXWindowDelegate alloc] initWithWindowImpl:impl]; + OSXView* view = [[OSXView alloc] initWithFrame:rect]; [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - [super initWithContentRect:rect - styleMask:(NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask | NSResizableWindowMask) - backing:NSBackingStoreBuffered - defer:NO]; - - [self setDelegate:[[OSXWindowDelegate alloc] initWithWindow:self]]; + [self setDelegate:m_delegate]; [self setContentView:view]; [self center]; + return self; } @@ -45,6 +50,22 @@ return m_impl; } +- (int)scale +{ + return m_scale; +} + +- (void)setScale:(int)scale +{ + m_scale = scale; + + if (m_impl) { + NSRect bounds = [[self contentView] bounds]; + m_impl->onResize(gfx::Size(bounds.size.width, + bounds.size.height)); + } +} + - (gfx::Size)clientSize { return m_clientSize; diff --git a/src/she/osx/window_delegate.h b/src/she/osx/window_delegate.h index 4bc82f644..807fcf233 100644 --- a/src/she/osx/window_delegate.h +++ b/src/she/osx/window_delegate.h @@ -4,22 +4,19 @@ // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. -@interface OSXWindowDelegate : NSObject -{ +class OSXWindowImpl; + +@interface OSXWindowDelegate : NSObject { +@private OSXWindowImpl* m_impl; } -- (OSXWindowDelegate*)initWithWindow:(OSXWindow*)window; -- (BOOL)windowShouldClose:(id)sender; -- (void)windowWillClose:(NSNotification *)notification; -- (void)windowDidResize:(NSNotification*)notification; -- (void)windowDidMiniaturize:(NSNotification*)notification; @end @implementation OSXWindowDelegate -- (OSXWindowDelegate*)initWithWindow:(OSXWindow*)window +- (OSXWindowDelegate*)initWithWindowImpl:(OSXWindowImpl*)impl { - m_impl = [window impl]; + m_impl = impl; return self; } diff --git a/src/she/skia/she.cpp b/src/she/skia/she.cpp index 7cd1e1690..b7868000d 100644 --- a/src/she/skia/she.cpp +++ b/src/she/skia/she.cpp @@ -17,7 +17,8 @@ #include "she/skia/skia_system.h" #if __APPLE__ -#include "she/osx/app.h" + #include "she/osx/app.h" + #include #endif namespace she { @@ -55,8 +56,10 @@ int clock_value() // TODO #if _WIN32 return (int)GetTickCount(); +#elif defined(__APPLE__) + return TickCount(); #else - return 0; // clock_var; + return 0; #endif } diff --git a/src/she/skia/skia_system.h b/src/she/skia/skia_system.h index bbf576f3d..28ca648bd 100644 --- a/src/she/skia/skia_system.h +++ b/src/she/skia/skia_system.h @@ -20,7 +20,6 @@ #ifdef _WIN32 #include "she/win/event_queue.h" #elif __APPLE__ - #include "she/osx/app.h" #include "she/osx/event_queue.h" #else #error There is no EventQueue implementation for your platform @@ -38,9 +37,6 @@ public: } ~SkiaSystem() { -#if __APPLE__ - OSXApp::instance()->stopUIEventLoop(); -#endif } void dispose() override { diff --git a/src/she/skia/skia_window_osx.h b/src/she/skia/skia_window_osx.h index 5d4c1a48d..a9dc87a1a 100644 --- a/src/she/skia/skia_window_osx.h +++ b/src/she/skia/skia_window_osx.h @@ -41,6 +41,8 @@ public: void* handle(); private: + void destroyImpl(); + class Impl; Impl* m_impl; diff --git a/src/she/skia/skia_window_osx.mm b/src/she/skia/skia_window_osx.mm index be7e8f3b9..9e49e1c13 100644 --- a/src/she/skia/skia_window_osx.mm +++ b/src/she/skia/skia_window_osx.mm @@ -10,16 +10,22 @@ #include "she/skia/skia_window_osx.h" +#include "base/unique_ptr.h" +#include "gfx/size.h" #include "she/event.h" #include "she/event_queue.h" #include "she/osx/window.h" -#include "gfx/size.h" +#include "she/skia/skia_display.h" +#include "she/system.h" + +#include "mac/SkCGUtils.h" #if SK_SUPPORT_GPU #include "GrContext.h" #include "she/gl/gl_context_cgl.h" #include "she/skia/gl_context_skia.h" + #include "she/skia/skia_surface.h" #endif @@ -27,68 +33,266 @@ namespace she { class SkiaWindow::Impl : public OSXWindowImpl { public: - bool closing; - int scale; - OSXWindow* window; -#if SK_SUPPORT_GPU - GLContextSkia gl; -#endif + Impl(EventQueue* queue, SkiaDisplay* display) + : m_display(display) + , m_backend(Backend::NONE) + , m_nsGL(nil) { + m_closing = false; + m_window = [[OSXWindow alloc] initWithImpl:this]; + } - Impl() + ~Impl() { #if SK_SUPPORT_GPU - : gl(nullptr) + if (m_backend == Backend::GL) + detachGL(); #endif - { - closing = false; - scale = 1; - window = [[OSXWindow alloc] initWithImpl:this]; + } + + gfx::Size clientSize() const { + return [m_window clientSize]; + } + + gfx::Size restoredSize() const { + return [m_window restoredSize]; + } + + int scale() const { + return [m_window scale]; + } + + void setScale(int scale) { + [m_window setScale:scale]; + } + + void setVisible(bool visible) { + if (visible) { + // Make the first OSXWindow as the main one. + [m_window makeKeyAndOrderFront:nil]; + + // The main window can be changed only when the NSWindow + // is visible (i.e. when NSWindow::canBecomeMainWindow + // returns YES). + [m_window makeMainWindow]; + } + else { + [m_window close]; + } + } + + void setTitle(const std::string& title) { + [m_window setTitle:[NSString stringWithUTF8String:title.c_str()]]; + } + + void updateWindow(const gfx::Rect& bounds) { + [[m_window contentView] setNeedsDisplay:YES]; + } + + void* handle() { + return (__bridge void*)m_window; } // OSXWindowImpl impl void onClose() override { - closing = true; + m_closing = true; } + + void onResize(const gfx::Size& size) override { + bool gpu = she::instance()->gpuAcceleration(); + (void)gpu; + +#if SK_SUPPORT_GPU + if (gpu && attachGL()) { + m_backend = Backend::GL; + } + else +#endif + { +#if SK_SUPPORT_GPU + detachGL(); +#endif + m_backend = Backend::NONE; + } + +#if SK_SUPPORT_GPU + if (m_glCtx) + createRenderTarget(size); +#endif + + m_display->resize(size); + } + + void onDrawRect(const gfx::Rect& rect) override { +#if SK_SUPPORT_GPU + // Flush operations to the SkCanvas + { + SkiaSurface* surface = static_cast(m_display->getSurface()); + surface->flush(); + } +#endif + + switch (m_backend) { + + case Backend::NONE: + paintGC(rect); + break; + +#ifdef SK_SUPPORT_GPU + case Backend::GL: + if (m_nsGL) + [m_nsGL flushBuffer]; + break; +#endif + } + } + + void onWindowChanged() override { + if (m_nsGL) + [m_nsGL setView:[m_window contentView]]; + } + +private: +#if SK_SUPPORT_GPU + bool attachGL() { + if (!m_glCtx) { + auto ctx = new GLContextSkia(nullptr); + + m_glCtx.reset(ctx); + m_grCtx.reset(GrContext::Create(kOpenGL_GrBackend, + (GrBackendContext)m_glCtx->gl())); + + m_nsGL = [[NSOpenGLContext alloc] + initWithCGLContextObj:m_glCtx->cglContext()]; + + [m_nsGL setView:[m_window contentView]]; + } + return true; + } + + void detachGL() { + if (m_nsGL) + m_nsGL = nil; + m_glCtx.reset(nullptr); + } + + void createRenderTarget(const gfx::Size& size) { + int scale = this->scale(); + m_lastSize = size; + + GrBackendRenderTargetDesc desc; + desc.fWidth = size.w; + desc.fHeight = size.h; + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fOrigin = kBottomLeft_GrSurfaceOrigin; + desc.fSampleCnt = m_glCtx->getSampleCount(); + desc.fStencilBits = m_glCtx->getStencilBits(); + desc.fRenderTargetHandle = 0; // direct frame buffer + m_grRenderTarget.reset(m_grCtx->textureProvider()->wrapBackendRenderTarget(desc)); + m_skSurfaceDirect.reset( + SkSurface::NewRenderTargetDirect(m_grRenderTarget)); + + if (scale == 1) { + m_skSurface.reset(m_skSurfaceDirect); + } + else { + m_skSurface.reset( + SkSurface::NewRenderTarget( + m_grCtx, + SkSurface::kYes_Budgeted, + SkImageInfo::MakeN32Premul(MAX(1, size.w / scale), + MAX(1, size.h / scale)), + m_glCtx->getSampleCount())); + } + + if (!m_skSurface) + throw std::runtime_error("Error creating OpenGL surface for main display"); + + m_display->setSkiaSurface(new SkiaSurface(m_skSurface)); + + if (m_nsGL) + [m_nsGL update]; + } +#endif + + void paintGC(const gfx::Rect& rect) { + SkiaSurface* surface = static_cast(m_display->getSurface()); + const SkBitmap& bitmap = surface->bitmap(); + + ASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes()); + bitmap.lockPixels(); + + { + NSRect viewBounds = [[m_window contentView] bounds]; + NSGraphicsContext* gc = [NSGraphicsContext currentContext]; + CGContextRef cg = (CGContextRef)[gc graphicsPort]; + CGImageRef img = SkCreateCGImageRef(bitmap); + if (img) { + CGRect r = CGRectMake(viewBounds.origin.x, + viewBounds.origin.y, + viewBounds.size.width, + viewBounds.size.height); + CGContextSaveGState(cg); + CGContextSetInterpolationQuality(cg, kCGInterpolationNone); + CGContextDrawImage(cg, r, img); + CGContextRestoreGState(cg); + CGImageRelease(img); + } + } + + bitmap.unlockPixels(); + } + + SkiaDisplay* m_display; + Backend m_backend; + bool m_closing; + OSXWindow* m_window; +#if SK_SUPPORT_GPU + base::UniquePtr > m_glCtx; + NSOpenGLContext* m_nsGL; + SkAutoTUnref m_grCtx; + SkAutoTUnref m_grRenderTarget; + SkAutoTDelete m_skSurfaceDirect; + SkAutoTDelete m_skSurface; + gfx::Size m_lastSize; +#endif }; SkiaWindow::SkiaWindow(EventQueue* queue, SkiaDisplay* display) - : m_impl(nullptr) + : m_impl(new Impl(queue, display)) { - dispatch_sync( - dispatch_get_main_queue(), - ^{ - m_impl = new SkiaWindow::Impl; - }); } SkiaWindow::~SkiaWindow() +{ + destroyImpl(); +} + +void SkiaWindow::destroyImpl() { delete m_impl; + m_impl = nullptr; } int SkiaWindow::scale() const { - return m_impl->scale; + if (m_impl) + return m_impl->scale(); + else + return 1; } void SkiaWindow::setScale(int scale) { - m_impl->scale = scale; + if (m_impl) + m_impl->setScale(scale); } void SkiaWindow::setVisible(bool visible) { - if (m_impl->closing) + if (!m_impl) return; - dispatch_sync(dispatch_get_main_queue(), ^{ - if (visible) { - [m_impl->window makeKeyAndOrderFront:nil]; - } - else { - [m_impl->window close]; - } - }); + m_impl->setVisible(visible); } void SkiaWindow::maximize() @@ -102,28 +306,26 @@ bool SkiaWindow::isMaximized() const gfx::Size SkiaWindow::clientSize() const { - if (m_impl->closing) + if (!m_impl) return gfx::Size(0, 0); - return m_impl->window.clientSize; + return m_impl->clientSize(); } gfx::Size SkiaWindow::restoredSize() const { - if (m_impl->closing) + if (!m_impl) return gfx::Size(0, 0); - return m_impl->window.restoredSize; + return m_impl->restoredSize(); } void SkiaWindow::setTitle(const std::string& title) { - if (m_impl->closing) + if (!m_impl) return; - dispatch_sync(dispatch_get_main_queue(), ^{ - [m_impl->window setTitle:[NSString stringWithUTF8String:title.c_str()]]; - }); + m_impl->setTitle(title); } void SkiaWindow::captureMouse() @@ -144,11 +346,16 @@ void SkiaWindow::setNativeMouseCursor(NativeCursor cursor) void SkiaWindow::updateWindow(const gfx::Rect& bounds) { + if (m_impl) + m_impl->updateWindow(bounds); } void* SkiaWindow::handle() { - return (void*)m_impl->window; + if (m_impl) + return (void*)m_impl->handle(); + else + return nullptr; } } // namespace she