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
This commit is contained in:
David Capello 2015-10-09 19:45:39 -03:00
parent f38fd4eb5e
commit 247a8a7174
17 changed files with 574 additions and 275 deletions

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -13,10 +13,9 @@
#include <AppKit/AppKit.h>
@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

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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 <AppKit/AppKit.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
@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

184
src/she/osx/view.mm Normal file
View File

@ -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

View File

@ -14,22 +14,32 @@
#include <stdio.h>
#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

View File

@ -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;

View File

@ -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;
}

View File

@ -17,7 +17,8 @@
#include "she/skia/skia_system.h"
#if __APPLE__
#include "she/osx/app.h"
#include "she/osx/app.h"
#include <CoreServices/CoreServices.h>
#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
}

View File

@ -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 {

View File

@ -41,6 +41,8 @@ public:
void* handle();
private:
void destroyImpl();
class Impl;
Impl* m_impl;

View File

@ -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<GLContextCGL> 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<SkiaSurface*>(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<GLContextCGL>(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<SkiaSurface*>(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<GLContextSkia<GLContextCGL> > m_glCtx;
NSOpenGLContext* m_nsGL;
SkAutoTUnref<GrContext> m_grCtx;
SkAutoTUnref<GrRenderTarget> m_grRenderTarget;
SkAutoTDelete<SkSurface> m_skSurfaceDirect;
SkAutoTDelete<SkSurface> 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