Switch sk_app to use native window creation on MacOS.

Bug: skia:
Change-Id: I1763aab0b4bdb650128c1fcc3aa3a05d194496ca
Reviewed-on: https://skia-review.googlesource.com/c/186360
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
Jim Van Verth 2019-01-28 14:46:04 -05:00 committed by Skia Commit-Bot
parent 8c0a1cad37
commit 1f086ca625
11 changed files with 850 additions and 615 deletions

View File

@ -2124,10 +2124,10 @@ if (skia_enable_tools) {
}
} else if (is_mac) {
sources += [
"tools/sk_app/mac/GLWindowContext_mac.cpp",
"tools/sk_app/mac/RasterWindowContext_mac.cpp",
"tools/sk_app/mac/Window_mac.cpp",
"tools/sk_app/mac/main_mac.cpp",
"tools/sk_app/mac/GLWindowContext_mac.mm",
"tools/sk_app/mac/RasterWindowContext_mac.mm",
"tools/sk_app/mac/Window_mac.mm",
"tools/sk_app/mac/main_mac.mm",
]
libs += [
"QuartzCore.framework",
@ -2162,7 +2162,7 @@ if (skia_enable_tools) {
]
if (is_android) {
deps += [ "//third_party/native_app_glue" ]
} else if (is_mac || is_ios) {
} else if (is_ios) {
deps += [ "//third_party/libsdl" ]
}
if (skia_use_angle) {

View File

@ -1,97 +0,0 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "../GLWindowContext.h"
#include "WindowContextFactory_mac.h"
#include "gl/GrGLInterface.h"
#include <OpenGL/gl.h>
#include "SDL.h"
using sk_app::DisplayParams;
using sk_app::window_context_factory::MacWindowInfo;
using sk_app::GLWindowContext;
namespace {
class GLWindowContext_mac : public GLWindowContext {
public:
GLWindowContext_mac(const MacWindowInfo&, const DisplayParams&);
~GLWindowContext_mac() override;
void onSwapBuffers() override;
sk_sp<const GrGLInterface> onInitializeContext() override;
void onDestroyContext() override {}
private:
SDL_Window* fWindow;
SDL_GLContext fGLContext;
typedef GLWindowContext INHERITED;
};
GLWindowContext_mac::GLWindowContext_mac(const MacWindowInfo& info, const DisplayParams& params)
: INHERITED(params)
, fWindow(info.fWindow)
, fGLContext(info.fGLContext) {
// any config code here (particularly for msaa)?
this->initializeContext();
}
GLWindowContext_mac::~GLWindowContext_mac() {
this->destroyContext();
}
sk_sp<const GrGLInterface> GLWindowContext_mac::onInitializeContext() {
SkASSERT(fWindow);
SkASSERT(fGLContext);
if (0 == SDL_GL_MakeCurrent(fWindow, fGLContext)) {
glClearStencil(0);
glClearColor(0, 0, 0, 0);
glStencilMask(0xffffffff);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &fStencilBits);
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fSampleCount);
fSampleCount = SkTMax(fSampleCount, 1);
SDL_GetWindowSize(fWindow, &fWidth, &fHeight);
glViewport(0, 0, fWidth, fHeight);
} else {
SkDebugf("MakeCurrent failed: %s\n", SDL_GetError());
}
return GrGLMakeNativeInterface();
}
void GLWindowContext_mac::onSwapBuffers() {
if (fWindow && fGLContext) {
SDL_GL_SwapWindow(fWindow);
}
}
} // anonymous namespace
namespace sk_app {
namespace window_context_factory {
WindowContext* NewGLForMac(const MacWindowInfo& info, const DisplayParams& params) {
WindowContext* ctx = new GLWindowContext_mac(info, params);
if (!ctx->isValid()) {
delete ctx;
return nullptr;
}
return ctx;
}
} // namespace window_context_factory
} // namespace sk_app

View File

@ -0,0 +1,200 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "../GLWindowContext.h"
#include "WindowContextFactory_mac.h"
#include "gl/GrGLInterface.h"
#include <OpenGL/gl.h>
#include <Cocoa/Cocoa.h>
using sk_app::DisplayParams;
using sk_app::window_context_factory::MacWindowInfo;
using sk_app::GLWindowContext;
@interface GLView : NSOpenGLView
@end
@implementation GLView
- (void)drawRect:(NSRect)dirtyRect {
// not sure why the parent isn't getting this, but we'll pass it up
[[self superview] drawRect:dirtyRect];
}
@end
namespace {
class GLWindowContext_mac : public GLWindowContext {
public:
GLWindowContext_mac(const MacWindowInfo&, const DisplayParams&);
~GLWindowContext_mac() override;
void onSwapBuffers() override;
sk_sp<const GrGLInterface> onInitializeContext() override;
void onDestroyContext() override;
private:
NSView* fMainView;
GLView* fGLView;
NSOpenGLContext* fGLContext;
NSOpenGLPixelFormat* fPixelFormat;
typedef GLWindowContext INHERITED;
};
GLWindowContext_mac::GLWindowContext_mac(const MacWindowInfo& info, const DisplayParams& params)
: INHERITED(params)
, fMainView(info.fMainView) {
// any config code here (particularly for msaa)?
this->initializeContext();
}
GLWindowContext_mac::~GLWindowContext_mac() {
this->destroyContext();
}
sk_sp<const GrGLInterface> GLWindowContext_mac::onInitializeContext() {
SkASSERT(nil != fMainView);
// set up pixel format
constexpr int kMaxAttributes = 18;
NSOpenGLPixelFormatAttribute attributes[kMaxAttributes];
int numAttributes = 0;
attributes[numAttributes++] = NSOpenGLPFAAccelerated;
attributes[numAttributes++] = NSOpenGLPFAClosestPolicy;
attributes[numAttributes++] = NSOpenGLPFADoubleBuffer;
attributes[numAttributes++] = NSOpenGLPFAOpenGLProfile;
attributes[numAttributes++] = NSOpenGLProfileVersion3_2Core;
attributes[numAttributes++] = NSOpenGLPFAColorSize;
attributes[numAttributes++] = 24;
attributes[numAttributes++] = NSOpenGLPFAAlphaSize;
attributes[numAttributes++] = 8;
attributes[numAttributes++] = NSOpenGLPFADepthSize;
attributes[numAttributes++] = 0;
attributes[numAttributes++] = NSOpenGLPFAStencilSize;
attributes[numAttributes++] = 8;
if (fDisplayParams.fMSAASampleCount > 1) {
attributes[numAttributes++] = NSOpenGLPFASampleBuffers;
attributes[numAttributes++] = 1;
attributes[numAttributes++] = NSOpenGLPFASamples;
attributes[numAttributes++] = fDisplayParams.fMSAASampleCount;
} else {
attributes[numAttributes++] = NSOpenGLPFASampleBuffers;
attributes[numAttributes++] = 0;
}
attributes[numAttributes++] = 0;
SkASSERT(numAttributes <= kMaxAttributes);
fPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
if (nil == fPixelFormat) {
return nullptr;
}
// create context
fGLContext = [[NSOpenGLContext alloc] initWithFormat:fPixelFormat shareContext:nil];
if (nil == fGLContext) {
[fPixelFormat release];
fPixelFormat = nil;
return nullptr;
}
// create view
NSRect rect = fMainView.bounds;
fGLView = [[GLView alloc] initWithFrame:rect];
if (nil == fGLView) {
[fGLContext release];
fGLContext = nil;
[fPixelFormat release];
fPixelFormat = nil;
return nullptr;
}
[fGLView setTranslatesAutoresizingMaskIntoConstraints:NO];
// attach OpenGL view to main view
[fMainView addSubview:fGLView];
NSDictionary *views = NSDictionaryOfVariableBindings(fGLView);
[fMainView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[fGLView]|"
options:0
metrics:nil
views:views]];
[fMainView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[fGLView]|"
options:0
metrics:nil
views:views]];
// make context current
GLint swapInterval = 1;
[fGLContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
[fGLView setOpenGLContext:fGLContext];
[fGLView setPixelFormat:fPixelFormat];
[fGLView setWantsBestResolutionOpenGLSurface:YES];
[fGLContext setView:fGLView];
[fGLContext makeCurrentContext];
glClearStencil(0);
glClearColor(0, 0, 0, 255);
glStencilMask(0xffffffff);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
GLint stencilBits;
[fPixelFormat getValues:&stencilBits forAttribute:NSOpenGLPFAStencilSize forVirtualScreen:0];
fStencilBits = stencilBits;
GLint sampleCount;
[fPixelFormat getValues:&sampleCount forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
fSampleCount = sampleCount;
fSampleCount = SkTMax(fSampleCount, 1);
const NSRect backingRect = [fGLView convertRectToBacking:fGLView.bounds];
fWidth = backingRect.size.width;
fHeight = backingRect.size.height;
glViewport(0, 0, fWidth, fHeight);
return GrGLMakeNativeInterface();
}
void GLWindowContext_mac::onDestroyContext() {
[fGLView removeFromSuperview];
[fGLView release];
fGLView = nil;
[fGLContext release];
fGLContext = nil;
[fPixelFormat release];
fPixelFormat = nil;
}
void GLWindowContext_mac::onSwapBuffers() {
[fGLContext flushBuffer];
}
} // anonymous namespace
namespace sk_app {
namespace window_context_factory {
WindowContext* NewGLForMac(const MacWindowInfo& info, const DisplayParams& params) {
WindowContext* ctx = new GLWindowContext_mac(info, params);
if (!ctx->isValid()) {
delete ctx;
return nullptr;
}
return ctx;
}
} // namespace window_context_factory
} // namespace sk_app

View File

@ -1,129 +0,0 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "../GLWindowContext.h"
#include "SkCanvas.h"
#include "SkColorFilter.h"
#include "WindowContextFactory_mac.h"
#include "gl/GrGLInterface.h"
#include "sk_tool_utils.h"
#include <OpenGL/gl.h>
#include "SDL.h"
using sk_app::DisplayParams;
using sk_app::window_context_factory::MacWindowInfo;
using sk_app::GLWindowContext;
namespace {
// We use SDL to support Mac windowing mainly for convenience's sake. However, it
// does not allow us to support a purely raster backend because we have no hooks into
// the NSWindow's drawRect: method. Hence we use GL to handle the update. Should we
// want to avoid this, we will probably need to write our own windowing backend.
class RasterWindowContext_mac : public GLWindowContext {
public:
RasterWindowContext_mac(const MacWindowInfo&, const DisplayParams&);
~RasterWindowContext_mac() override;
sk_sp<SkSurface> getBackbufferSurface() override;
void onSwapBuffers() override;
sk_sp<const GrGLInterface> onInitializeContext() override;
void onDestroyContext() override;
private:
SDL_Window* fWindow;
SDL_GLContext fGLContext;
sk_sp<SkSurface> fBackbufferSurface;
typedef GLWindowContext INHERITED;
};
RasterWindowContext_mac::RasterWindowContext_mac(const MacWindowInfo& info,
const DisplayParams& params)
: INHERITED(params)
, fWindow(info.fWindow)
, fGLContext(info.fGLContext) {
// any config code here (particularly for msaa)?
this->initializeContext();
}
RasterWindowContext_mac::~RasterWindowContext_mac() {
this->destroyContext();
}
sk_sp<const GrGLInterface> RasterWindowContext_mac::onInitializeContext() {
SkASSERT(fWindow);
SkASSERT(fGLContext);
if (0 == SDL_GL_MakeCurrent(fWindow, fGLContext)) {
glClearStencil(0);
glClearColor(0, 0, 0, 0);
glStencilMask(0xffffffff);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &fStencilBits);
SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fSampleCount);
fSampleCount = SkTMax(fSampleCount, 1);
SDL_GetWindowSize(fWindow, &fWidth, &fHeight);
glViewport(0, 0, fWidth, fHeight);
} else {
SkDebugf("MakeCurrent failed: %s\n", SDL_GetError());
}
// make the offscreen image
SkImageInfo info = SkImageInfo::Make(fWidth, fHeight, fDisplayParams.fColorType,
kPremul_SkAlphaType, fDisplayParams.fColorSpace);
fBackbufferSurface = SkSurface::MakeRaster(info);
return GrGLMakeNativeInterface();
}
void RasterWindowContext_mac::onDestroyContext() {
fBackbufferSurface.reset(nullptr);
}
sk_sp<SkSurface> RasterWindowContext_mac::getBackbufferSurface() { return fBackbufferSurface; }
void RasterWindowContext_mac::onSwapBuffers() {
if (fWindow && fGLContext) {
// We made/have an off-screen surface. Get the contents as an SkImage:
sk_sp<SkImage> snapshot = fBackbufferSurface->makeImageSnapshot();
sk_sp<SkSurface> gpuSurface = INHERITED::getBackbufferSurface();
SkCanvas* gpuCanvas = gpuSurface->getCanvas();
gpuCanvas->drawImage(snapshot, 0, 0);
gpuCanvas->flush();
SDL_GL_SwapWindow(fWindow);
}
}
} // anonymous namespace
namespace sk_app {
namespace window_context_factory {
WindowContext* NewRasterForMac(const MacWindowInfo& info, const DisplayParams& params) {
WindowContext* ctx = new RasterWindowContext_mac(info, params);
if (!ctx->isValid()) {
delete ctx;
return nullptr;
}
return ctx;
}
} // namespace window_context_factory
} // namespace sk_app

View File

@ -0,0 +1,229 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "../GLWindowContext.h"
#include "SkCanvas.h"
#include "SkColorFilter.h"
#include "WindowContextFactory_mac.h"
#include "gl/GrGLInterface.h"
#include "sk_tool_utils.h"
#include <OpenGL/gl.h>
#include <Cocoa/Cocoa.h>
using sk_app::DisplayParams;
using sk_app::window_context_factory::MacWindowInfo;
using sk_app::GLWindowContext;
@interface RasterView : NSOpenGLView
@end
@implementation RasterView
- (void)drawRect:(NSRect)dirtyRect {
// not sure why the parent isn't getting this, but we'll pass it up
[[self superview] drawRect:dirtyRect];
}
@end
namespace {
// TODO: This still uses GL to handle the update rather than using a purely raster backend,
// for historical reasons. Writing a pure raster backend would be better in the long run.
class RasterWindowContext_mac : public GLWindowContext {
public:
RasterWindowContext_mac(const MacWindowInfo&, const DisplayParams&);
~RasterWindowContext_mac() override;
sk_sp<SkSurface> getBackbufferSurface() override;
void onSwapBuffers() override;
sk_sp<const GrGLInterface> onInitializeContext() override;
void onDestroyContext() override;
private:
NSView* fMainView;
RasterView* fRasterView;
NSOpenGLContext* fGLContext;
NSOpenGLPixelFormat* fPixelFormat;
sk_sp<SkSurface> fBackbufferSurface;
typedef GLWindowContext INHERITED;
};
RasterWindowContext_mac::RasterWindowContext_mac(const MacWindowInfo& info,
const DisplayParams& params)
: INHERITED(params)
, fMainView(info.fMainView) {
// any config code here (particularly for msaa)?
this->initializeContext();
}
RasterWindowContext_mac::~RasterWindowContext_mac() {
this->destroyContext();
}
sk_sp<const GrGLInterface> RasterWindowContext_mac::onInitializeContext() {
SkASSERT(nil != fMainView);
// set up pixel format
constexpr int kMaxAttributes = 18;
NSOpenGLPixelFormatAttribute attributes[kMaxAttributes];
int numAttributes = 0;
attributes[numAttributes++] = NSOpenGLPFAAccelerated;
attributes[numAttributes++] = NSOpenGLPFAClosestPolicy;
attributes[numAttributes++] = NSOpenGLPFAOpenGLProfile;
attributes[numAttributes++] = NSOpenGLProfileVersion3_2Core;
attributes[numAttributes++] = NSOpenGLPFAColorSize;
attributes[numAttributes++] = 24;
attributes[numAttributes++] = NSOpenGLPFAAlphaSize;
attributes[numAttributes++] = 8;
attributes[numAttributes++] = NSOpenGLPFADepthSize;
attributes[numAttributes++] = 0;
attributes[numAttributes++] = NSOpenGLPFAStencilSize;
attributes[numAttributes++] = 8;
attributes[numAttributes++] = NSOpenGLPFADoubleBuffer;
if (fDisplayParams.fMSAASampleCount > 1) {
attributes[numAttributes++] = NSOpenGLPFASampleBuffers;
attributes[numAttributes++] = 1;
attributes[numAttributes++] = NSOpenGLPFASamples;
attributes[numAttributes++] = fDisplayParams.fMSAASampleCount;
} else {
attributes[numAttributes++] = NSOpenGLPFASampleBuffers;
attributes[numAttributes++] = 0;
}
attributes[numAttributes++] = 0;
SkASSERT(numAttributes <= kMaxAttributes);
fPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
if (nil == fPixelFormat) {
return nullptr;
}
// create context
fGLContext = [[NSOpenGLContext alloc] initWithFormat:fPixelFormat shareContext:nil];
if (nil == fGLContext) {
[fPixelFormat release];
fPixelFormat = nil;
return nullptr;
}
// create view
NSRect rect = fMainView.bounds;
fRasterView = [[RasterView alloc] initWithFrame:rect];
if (nil == fRasterView) {
[fGLContext release];
fGLContext = nil;
[fPixelFormat release];
fPixelFormat = nil;
return nullptr;
}
[fRasterView setTranslatesAutoresizingMaskIntoConstraints:NO];
// attach OpenGL view to main view
[fMainView addSubview:fRasterView];
NSDictionary *views = NSDictionaryOfVariableBindings(fRasterView);
[fMainView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[fRasterView]|"
options:0
metrics:nil
views:views]];
[fMainView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[fRasterView]|"
options:0
metrics:nil
views:views]];
// make context current
GLint swapInterval = 1;
[fGLContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
[fRasterView setOpenGLContext:fGLContext];
[fRasterView setPixelFormat:fPixelFormat];
[fRasterView setWantsBestResolutionOpenGLSurface:YES];
[fGLContext setView:fRasterView];
[fGLContext makeCurrentContext];
glClearStencil(0);
glClearColor(0, 0, 0, 0);
glStencilMask(0xffffffff);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
GLint stencilBits;
[fPixelFormat getValues:&stencilBits forAttribute:NSOpenGLPFAStencilSize forVirtualScreen:0];
fStencilBits = stencilBits;
GLint sampleCount;
[fPixelFormat getValues:&sampleCount forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
fSampleCount = sampleCount;
fSampleCount = SkTMax(fSampleCount, 1);
const NSRect backingRect = [fRasterView convertRectToBacking:fRasterView.bounds];
fWidth = backingRect.size.width;
fHeight = backingRect.size.height;
glViewport(0, 0, fWidth, fHeight);
// make the offscreen image
SkImageInfo info = SkImageInfo::Make(fWidth, fHeight, fDisplayParams.fColorType,
kPremul_SkAlphaType, fDisplayParams.fColorSpace);
fBackbufferSurface = SkSurface::MakeRaster(info);
return GrGLMakeNativeInterface();
}
void RasterWindowContext_mac::onDestroyContext() {
fBackbufferSurface.reset(nullptr);
[fRasterView removeFromSuperview];
[fRasterView release];
fRasterView = nil;
[fGLContext release];
fGLContext = nil;
[fPixelFormat release];
fPixelFormat = nil;
}
sk_sp<SkSurface> RasterWindowContext_mac::getBackbufferSurface() { return fBackbufferSurface; }
void RasterWindowContext_mac::onSwapBuffers() {
if (fBackbufferSurface) {
// We made/have an off-screen surface. Get the contents as an SkImage:
sk_sp<SkImage> snapshot = fBackbufferSurface->makeImageSnapshot();
sk_sp<SkSurface> gpuSurface = INHERITED::getBackbufferSurface();
SkCanvas* gpuCanvas = gpuSurface->getCanvas();
gpuCanvas->drawImage(snapshot, 0, 0);
gpuCanvas->flush();
[fGLContext flushBuffer];
}
}
} // anonymous namespace
namespace sk_app {
namespace window_context_factory {
WindowContext* NewRasterForMac(const MacWindowInfo& info, const DisplayParams& params) {
WindowContext* ctx = new RasterWindowContext_mac(info, params);
if (!ctx->isValid()) {
delete ctx;
return nullptr;
}
return ctx;
}
} // namespace window_context_factory
} // namespace sk_app

View File

@ -9,7 +9,7 @@
#ifndef WindowContextFactory_mac_DEFINED
#define WindowContextFactory_mac_DEFINED
#include "SDL.h"
#include <Cocoa/Cocoa.h>
namespace sk_app {
@ -19,8 +19,7 @@ struct DisplayParams;
namespace window_context_factory {
struct MacWindowInfo {
SDL_Window* fWindow;
SDL_GLContext fGLContext;
NSView* fMainView;
};
inline WindowContext* NewVulkanForMac(const MacWindowInfo&, const DisplayParams&) {

View File

@ -1,296 +0,0 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkUtils.h"
#include "Timer.h"
#include "WindowContextFactory_mac.h"
#include "Window_mac.h"
namespace sk_app {
SkTDynamicHash<Window_mac, Uint32> Window_mac::gWindowMap;
Window* Window::CreateNativeWindow(void*) {
Window_mac* window = new Window_mac();
if (!window->initWindow()) {
delete window;
return nullptr;
}
return window;
}
bool Window_mac::initWindow() {
if (fRequestedDisplayParams.fMSAASampleCount != fMSAASampleCount) {
this->closeWindow();
}
// we already have a window
if (fWindow) {
return true;
}
constexpr int initialWidth = 1280;
constexpr int initialHeight = 960;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
if (fRequestedDisplayParams.fMSAASampleCount > 1) {
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fRequestedDisplayParams.fMSAASampleCount);
} else {
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
}
// TODO: handle other display params
uint32_t windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
fWindow = SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
initialWidth, initialHeight, windowFlags);
if (!fWindow) {
return false;
}
fMSAASampleCount = fRequestedDisplayParams.fMSAASampleCount;
// add to hashtable of windows
fWindowID = SDL_GetWindowID(fWindow);
gWindowMap.add(this);
fGLContext = SDL_GL_CreateContext(fWindow);
if (!fGLContext) {
SkDebugf("%s\n", SDL_GetError());
this->closeWindow();
return false;
}
// Workaround for a bug in SDL that causes a black screen until you move the window on 10.14.
SDL_PumpEvents();
int actualWidth, actualHeight;
SDL_GetWindowSize(fWindow, &actualWidth, &actualHeight);
SDL_SetWindowSize(fWindow, actualWidth, actualHeight);
return true;
}
void Window_mac::closeWindow() {
if (fGLContext) {
SDL_GL_DeleteContext(fGLContext);
fGLContext = nullptr;
}
if (fWindow) {
gWindowMap.remove(fWindowID);
SDL_DestroyWindow(fWindow);
fWindowID = 0;
fWindow = nullptr;
}
}
static Window::Key get_key(const SDL_Keysym& keysym) {
static const struct {
SDL_Keycode fSDLK;
Window::Key fKey;
} gPair[] = {
{ SDLK_BACKSPACE, Window::Key::kBack },
{ SDLK_CLEAR, Window::Key::kBack },
{ SDLK_RETURN, Window::Key::kOK },
{ SDLK_UP, Window::Key::kUp },
{ SDLK_DOWN, Window::Key::kDown },
{ SDLK_LEFT, Window::Key::kLeft },
{ SDLK_RIGHT, Window::Key::kRight },
{ SDLK_TAB, Window::Key::kTab },
{ SDLK_PAGEUP, Window::Key::kPageUp },
{ SDLK_PAGEDOWN, Window::Key::kPageDown },
{ SDLK_HOME, Window::Key::kHome },
{ SDLK_END, Window::Key::kEnd },
{ SDLK_DELETE, Window::Key::kDelete },
{ SDLK_ESCAPE, Window::Key::kEscape },
{ SDLK_LSHIFT, Window::Key::kShift },
{ SDLK_RSHIFT, Window::Key::kShift },
{ SDLK_LCTRL, Window::Key::kCtrl },
{ SDLK_RCTRL, Window::Key::kCtrl },
{ SDLK_LALT, Window::Key::kOption },
{ SDLK_LALT, Window::Key::kOption },
{ 'A', Window::Key::kA },
{ 'C', Window::Key::kC },
{ 'V', Window::Key::kV },
{ 'X', Window::Key::kX },
{ 'Y', Window::Key::kY },
{ 'Z', Window::Key::kZ },
};
for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
if (gPair[i].fSDLK == keysym.sym) {
return gPair[i].fKey;
}
}
return Window::Key::kNONE;
}
static uint32_t get_modifiers(const SDL_Event& event) {
static const struct {
unsigned fSDLMask;
unsigned fSkMask;
} gModifiers[] = {
{ KMOD_SHIFT, Window::kShift_ModifierKey },
{ KMOD_CTRL, Window::kControl_ModifierKey },
{ KMOD_ALT, Window::kOption_ModifierKey },
};
auto modifiers = 0;
switch (event.type) {
case SDL_KEYDOWN:
// fall through
case SDL_KEYUP: {
for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) {
if (event.key.keysym.mod & gModifiers[i].fSDLMask) {
modifiers |= gModifiers[i].fSkMask;
}
}
if (0 == event.key.repeat) {
modifiers |= Window::kFirstPress_ModifierKey;
}
break;
}
default: {
SDL_Keymod mod = SDL_GetModState();
for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) {
if (mod & gModifiers[i].fSDLMask) {
modifiers |= gModifiers[i].fSkMask;
}
}
break;
}
}
return modifiers;
}
bool Window_mac::HandleWindowEvent(const SDL_Event& event) {
Window_mac* win = gWindowMap.find(event.window.windowID);
if (win && win->handleEvent(event)) {
return true;
}
return false;
}
bool Window_mac::handleEvent(const SDL_Event& event) {
switch (event.type) {
case SDL_WINDOWEVENT:
if (SDL_WINDOWEVENT_EXPOSED == event.window.event) {
this->onPaint();
} else if (SDL_WINDOWEVENT_RESIZED == event.window.event) {
this->onResize(event.window.data1, event.window.data2);
}
break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT) {
this->onMouse(event.button.x, event.button.y,
Window::kDown_InputState, get_modifiers(event));
}
break;
case SDL_MOUSEBUTTONUP:
if (event.button.button == SDL_BUTTON_LEFT) {
this->onMouse(event.button.x, event.button.y,
Window::kUp_InputState, get_modifiers(event));
}
break;
case SDL_MOUSEMOTION:
this->onMouse(event.motion.x, event.motion.y,
Window::kMove_InputState, get_modifiers(event));
break;
case SDL_MOUSEWHEEL:
this->onMouseWheel(event.wheel.y, get_modifiers(event));
break;
case SDL_KEYDOWN: {
Window::Key key = get_key(event.key.keysym);
if (key != Window::Key::kNONE) {
if (!this->onKey(key, Window::kDown_InputState, get_modifiers(event))) {
if (event.key.keysym.sym == SDLK_ESCAPE) {
return true;
}
}
}
} break;
case SDL_KEYUP: {
Window::Key key = get_key(event.key.keysym);
if (key != Window::Key::kNONE) {
(void) this->onKey(key, Window::kUp_InputState,
get_modifiers(event));
}
} break;
case SDL_TEXTINPUT: {
const char* textIter = &event.text.text[0];
while (SkUnichar c = SkUTF8_NextUnichar(&textIter)) {
(void) this->onChar(c, get_modifiers(event));
}
} break;
default:
break;
}
return false;
}
void Window_mac::setTitle(const char* title) {
SDL_SetWindowTitle(fWindow, title);
}
void Window_mac::show() {
SDL_ShowWindow(fWindow);
}
bool Window_mac::attach(BackendType attachType) {
this->initWindow();
window_context_factory::MacWindowInfo info;
info.fWindow = fWindow;
info.fGLContext = fGLContext;
switch (attachType) {
case kRaster_BackendType:
fWindowContext = NewRasterForMac(info, fRequestedDisplayParams);
break;
case kNativeGL_BackendType:
default:
fWindowContext = NewGLForMac(info, fRequestedDisplayParams);
break;
}
this->onBackendCreated();
return (SkToBool(fWindowContext));
}
void Window_mac::onInval() {
SDL_Event sdlevent;
sdlevent.type = SDL_WINDOWEVENT;
sdlevent.window.windowID = fWindowID;
sdlevent.window.event = SDL_WINDOWEVENT_EXPOSED;
SDL_PushEvent(&sdlevent);
}
} // namespace sk_app

View File

@ -9,10 +9,8 @@
#define Window_mac_DEFINED
#include "../Window.h"
#include "SkChecksum.h"
#include "SkTDynamicHash.h"
#include "SDL.h"
#import <Cocoa/Cocoa.h>
namespace sk_app {
@ -20,11 +18,11 @@ class Window_mac : public Window {
public:
Window_mac()
: INHERITED()
, fWindow(nullptr)
, fWindowID(0)
, fGLContext(nullptr)
, fWindow(nil)
, fMSAASampleCount(1) {}
~Window_mac() override { this->closeWindow(); }
~Window_mac() override {
this->closeWindow();
}
bool initWindow();
@ -35,27 +33,11 @@ public:
void onInval() override;
static bool HandleWindowEvent(const SDL_Event& event);
static const Uint32& GetKey(const Window_mac& w) {
return w.fWindowID;
}
static uint32_t Hash(const Uint32& winID) {
return winID;
}
private:
bool handleEvent(const SDL_Event& event);
NSView* view() { return [fWindow contentView]; }
void closeWindow();
static SkTDynamicHash<Window_mac, Uint32> gWindowMap;
SDL_Window* fWindow;
Uint32 fWindowID;
SDL_GLContext fGLContext;
private:
NSWindow* fWindow;
int fMSAASampleCount;
typedef Window INHERITED;

View File

@ -0,0 +1,305 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkUtils.h"
#include "Timer.h"
#include "WindowContextFactory_mac.h"
#include "Window_mac.h"
@interface MainView : NSView
- (MainView*)initWithWindow:(sk_app::Window*)initWindow;
@end
@interface WindowDelegate : NSObject<NSWindowDelegate>
- (WindowDelegate*)initWithWindow:(sk_app::Window*)initWindow;
@end
///////////////////////////////////////////////////////////////////////////////
using sk_app::Window;
namespace sk_app {
static int gWindowCount = 0;
Window* Window::CreateNativeWindow(void*) {
Window_mac* window = new Window_mac();
if (!window->initWindow()) {
delete window;
return nullptr;
}
++gWindowCount;
return window;
}
bool Window_mac::initWindow() {
if (fRequestedDisplayParams.fMSAASampleCount != fMSAASampleCount) {
this->closeWindow();
}
// we already have a window
if (fWindow) {
return true;
}
constexpr int initialWidth = 1280;
constexpr int initialHeight = 960;
NSUInteger windowStyle = (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask |
NSMiniaturizableWindowMask);
NSRect windowRect = NSMakeRect(100, 100, initialWidth, initialHeight);
fWindow = [[NSWindow alloc] initWithContentRect:windowRect styleMask:windowStyle
backing:NSBackingStoreBuffered defer:NO];
if (nil == fWindow) {
return false;
}
WindowDelegate* delegate = [[WindowDelegate alloc] initWithWindow:this];
[fWindow setDelegate:delegate];
[delegate release];
// create view
MainView* view = [[[MainView alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)] initWithWindow:this];
if (nil == view) {
[fWindow release];
fWindow = nil;
return false;
}
// attach view to window
[fWindow setContentView:view];
return true;
}
void Window_mac::closeWindow() {
[fWindow release];
fWindow = nil;
}
void Window_mac::setTitle(const char* title) {
NSString *titleString = [NSString stringWithCString:title encoding:NSUTF8StringEncoding];
[fWindow setTitle:titleString];
}
void Window_mac::show() {
[NSApp activateIgnoringOtherApps:YES];
[fWindow makeKeyAndOrderFront:NSApp];
}
bool Window_mac::attach(BackendType attachType) {
this->initWindow();
window_context_factory::MacWindowInfo info;
info.fMainView = this->view();
switch (attachType) {
case kRaster_BackendType:
fWindowContext = NewRasterForMac(info, fRequestedDisplayParams);
break;
case kNativeGL_BackendType:
default:
fWindowContext = NewGLForMac(info, fRequestedDisplayParams);
break;
}
this->onBackendCreated();
return (SkToBool(fWindowContext));
}
void Window_mac::onInval() {
[[fWindow contentView] setNeedsDisplay:YES];
}
} // namespace sk_app
///////////////////////////////////////////////////////////////////////////////
@implementation WindowDelegate {
sk_app::Window* fWindow;
}
- (WindowDelegate*)initWithWindow:(sk_app::Window *)initWindow {
fWindow = initWindow;
return self;
}
- (void)windowDidResize:(NSNotification *)notification {
sk_app::Window_mac* macWindow = reinterpret_cast<sk_app::Window_mac*>(fWindow);
const NSRect mainRect = [macWindow->view() frame];
const NSRect backingRect = [macWindow->view() convertRectToBacking:mainRect];
fWindow->onResize(backingRect.size.width, backingRect.size.height);
}
- (BOOL)windowShouldClose:(NSWindow*)sender {
--sk_app::gWindowCount;
if (sk_app::gWindowCount < 1) {
[NSApp terminate:self];
}
reinterpret_cast<sk_app::Window_mac*>(fWindow)->closeWindow();
return FALSE;
}
@end
///////////////////////////////////////////////////////////////////////////////
@implementation MainView {
sk_app::Window* fWindow;
}
- (MainView*)initWithWindow:(sk_app::Window *)initWindow {
fWindow = initWindow;
return self;
}
- (BOOL)isOpaque {
return YES;
}
- (BOOL)canBecomeKeyView {
return YES;
}
- (BOOL)acceptsFirstResponder {
return YES;
}
- (void)drawRect:(NSRect)dirtyRect {
fWindow->onPaint();
}
static Window::Key get_key(unsigned short vk) {
// This will work with an ANSI QWERTY keyboard.
// Something more robust would be needed to support alternate keyboards.
static const struct {
unsigned short fVK;
Window::Key fKey;
} gPair[] = {
{ 0x33, Window::Key::kBack },
{ 0x24, Window::Key::kOK },
{ 0x7E, Window::Key::kUp },
{ 0x7D, Window::Key::kDown },
{ 0x7B, Window::Key::kLeft },
{ 0x7C, Window::Key::kRight },
{ 0x30, Window::Key::kTab },
{ 0x74, Window::Key::kPageUp },
{ 0x79, Window::Key::kPageDown },
{ 0x73, Window::Key::kHome },
{ 0x77, Window::Key::kEnd },
{ 0x75, Window::Key::kDelete },
{ 0x35, Window::Key::kEscape },
{ 0x38, Window::Key::kShift },
{ 0x3C, Window::Key::kShift },
{ 0x3B, Window::Key::kCtrl },
{ 0x3E, Window::Key::kCtrl },
{ 0x3A, Window::Key::kOption },
{ 0x3D, Window::Key::kOption },
{ 0x00, Window::Key::kA },
{ 0x08, Window::Key::kC },
{ 0x09, Window::Key::kV },
{ 0x07, Window::Key::kX },
{ 0x10, Window::Key::kY },
{ 0x06, Window::Key::kZ },
};
for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
if (gPair[i].fVK == vk) {
return gPair[i].fKey;
}
}
return Window::Key::kNONE;
}
static uint32_t get_modifiers(const NSEvent* event) {
NSUInteger modifierFlags = [event modifierFlags];
auto modifiers = 0;
if (modifierFlags & NSEventModifierFlagShift) {
modifiers |= Window::kShift_ModifierKey;
}
if (modifierFlags & NSEventModifierFlagControl) {
modifiers |= Window::kControl_ModifierKey;
}
if (modifierFlags & NSEventModifierFlagOption) {
modifiers |= Window::kOption_ModifierKey;
}
if ((NSKeyDown == [event type] || NSKeyUp == [event type]) &&
NO == [event isARepeat]) {
modifiers |= Window::kFirstPress_ModifierKey;
}
return modifiers;
}
- (void)keyDown:(NSEvent *)event {
Window::Key key = get_key([event keyCode]);
if (key != Window::Key::kNONE) {
if (!fWindow->onKey(key, Window::kDown_InputState, get_modifiers(event))) {
[[self superview] keyDown:event];
}
} else {
NSString* characters = [event charactersIgnoringModifiers];
if ([characters length] > 0) {
unichar firstChar = [characters characterAtIndex:0];
(void) fWindow->onChar((SkUnichar) firstChar, get_modifiers(event));
}
}
}
- (void)keyUp:(NSEvent *)event {
Window::Key key = get_key([event keyCode]);
if (key != Window::Key::kNONE) {
(void) fWindow->onKey(key, Window::kUp_InputState, get_modifiers(event));
}
}
- (void)mouseDown:(NSEvent *)event {
const NSPoint pos = [event locationInWindow];
const NSRect rect = [self frame];
fWindow->onMouse(pos.x, rect.size.height - pos.y, Window::kDown_InputState,
get_modifiers(event));
}
- (void)mouseDragged:(NSEvent *)event {
[self mouseMoved:event];
}
- (void)mouseUp:(NSEvent *)event {
const NSPoint pos = [event locationInWindow];
const NSRect rect = [self frame];
fWindow->onMouse(pos.x, rect.size.height - pos.y, Window::kUp_InputState,
get_modifiers(event));
}
- (void)mouseMoved:(NSEvent *)event {
const NSPoint pos = [event locationInWindow];
const NSRect rect = [self frame];
fWindow->onMouse(pos.x, rect.size.height - pos.y, Window::kMove_InputState,
get_modifiers(event));
}
- (void)scrollWheel:(NSEvent *)event {
// TODO: support hasPreciseScrollingDeltas?
fWindow->onMouseWheel([event scrollingDeltaY], get_modifiers(event));
}
@end

View File

@ -1,59 +0,0 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkTypes.h"
#include "SkTHash.h"
#include "Timer.h"
#include "Window_mac.h"
#include "../Application.h"
#include "SDL.h"
using sk_app::Application;
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
SkDebugf("Could not initialize SDL!\n");
return 1;
}
Application* app = Application::Create(argc, argv, nullptr);
SDL_Event event;
bool done = false;
while (!done) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
// events handled by the windows
case SDL_WINDOWEVENT:
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEWHEEL:
case SDL_KEYDOWN:
case SDL_KEYUP:
case SDL_TEXTINPUT:
done = sk_app::Window_mac::HandleWindowEvent(event);
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
app->onIdle();
}
delete app;
SDL_Quit();
return 0;
}

View File

@ -0,0 +1,101 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#import <Cocoa/Cocoa.h>
#include "Window_mac.h"
#include "../Application.h"
@interface AppDelegate : NSObject<NSApplicationDelegate, NSWindowDelegate>
@property (nonatomic, assign) BOOL done;
@end
@implementation AppDelegate : NSObject
@synthesize done = _done;
- (id)init {
self = [super init];
_done = FALSE;
return self;
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
_done = TRUE;
return NSTerminateCancel;
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[NSApp stop:nil];
}
@end
///////////////////////////////////////////////////////////////////////////////////////////
using sk_app::Application;
int main(int argc, char * argv[]) {
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
// we only run on systems that support at least Core Profile 3.2
return EXIT_FAILURE;
#endif
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
//Create the application menu.
NSMenu* menuBar=[[NSMenu alloc] initWithTitle:@"AMainMenu"];
[NSApp setMenu:menuBar];
NSMenuItem* item;
NSMenu* subMenu;
item=[[NSMenuItem alloc] initWithTitle:@"Apple" action:NULL keyEquivalent:@""];
[menuBar addItem:item];
subMenu=[[NSMenu alloc] initWithTitle:@"Apple"];
[menuBar setSubmenu:subMenu forItem:item];
[item release];
item=[[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
[subMenu addItem:item];
[item release];
[subMenu release];
// Set AppDelegate to catch certain global events
AppDelegate* appDelegate = [[[AppDelegate alloc] init] autorelease];
[NSApp setDelegate:appDelegate];
Application* app = Application::Create(argc, argv, nullptr);
// This will run until the application finishes launching, then lets us take over
[NSApp run];
// Now we process the events
while (![appDelegate done]) {
NSEvent* event;
do {
event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[NSApp sendEvent:event];
} while (event != nil);
app->onIdle();
}
delete app;
[menuBar release];
[pool release];
return EXIT_SUCCESS;
}