mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
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:
parent
f38fd4eb5e
commit
247a8a7174
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
184
src/she/osx/view.mm
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -41,6 +41,8 @@ public:
|
||||
void* handle();
|
||||
|
||||
private:
|
||||
void destroyImpl();
|
||||
|
||||
class Impl;
|
||||
Impl* m_impl;
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user