Switch to new IOS windowing system.
This replaces the SDL-based system and should allow Metal to work on iOS.
OpenGL and raster will render but there's no touch input yet.
Bug: skia:8737
Change-Id: I863accc47f0e1781192d567dbe54d1e321c3cd2e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/231561
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 61baf01..cae7672 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2375,11 +2375,12 @@
]
} else if (is_ios) {
sources += [
- "tools/sk_app/ios/GLWindowContext_ios.cpp",
- "tools/sk_app/ios/RasterWindowContext_ios.cpp",
- "tools/sk_app/ios/Window_ios.cpp",
- "tools/sk_app/ios/main_ios.cpp",
+ "tools/sk_app/ios/GLWindowContext_ios.mm",
+ "tools/sk_app/ios/RasterWindowContext_ios.mm",
+ "tools/sk_app/ios/Window_ios.mm",
+ "tools/sk_app/ios/main_ios.mm",
]
+ libs += [ "QuartzCore.framework" ]
}
if (skia_use_vulkan) {
@@ -2401,8 +2402,9 @@
sources += [ "tools/sk_app/MetalWindowContext.mm" ]
if (is_mac) {
sources += [ "tools/sk_app/mac/MetalWindowContext_mac.mm" ]
+ } else if (is_ios) {
+ # sources += [ "tools/sk_app/mac/MetalWindowContext_ios.mm" ]
}
- libs += [ "MetalKit.framework" ]
}
deps = [
@@ -2410,8 +2412,6 @@
]
if (is_android) {
deps += [ "//third_party/native_app_glue" ]
- } else if (is_ios) {
- deps += [ "//third_party/libsdl" ]
}
if (skia_use_angle) {
deps += [ "//third_party/angle2" ]
diff --git a/gn/gen_plist_ios.py b/gn/gen_plist_ios.py
index e4041be..f3c24d5 100644
--- a/gn/gen_plist_ios.py
+++ b/gn/gen_plist_ios.py
@@ -24,6 +24,11 @@
<key>CFBundleExecutable</key> <string>{app}</string>
<key>CFBundleIdentifier</key> <string>com.google.{app}</string>
<key>CFBundlePackageType</key> <string>APPL</string>
+ <key>LSRequiresIPhoneOS</key> <true/>
+ <key>UIDeviceFamily</key> <array>
+ <integer>1</integer>
+ <integer>2</integer>
+ </array>
</dict>
</plist>
'''.format(app=app))
diff --git a/tools/sk_app/ios/GLWindowContext_ios.cpp b/tools/sk_app/ios/GLWindowContext_ios.cpp
deleted file mode 100644
index c442f6d..0000000
--- a/tools/sk_app/ios/GLWindowContext_ios.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <OpenGLES/ES2/gl.h>
-#include "tools/sk_app/GLWindowContext.h"
-#include "SDL.h"
-#include "include/gpu/gl/GrGLInterface.h"
-#include "tools/sk_app/ios/WindowContextFactory_ios.h"
-
-using sk_app::DisplayParams;
-using sk_app::window_context_factory::IOSWindowInfo;
-using sk_app::GLWindowContext;
-
-namespace {
-
-class GLWindowContext_ios : public GLWindowContext {
-public:
- GLWindowContext_ios(const IOSWindowInfo&, const DisplayParams&);
-
- ~GLWindowContext_ios() override;
-
- void onSwapBuffers() override;
-
- sk_sp<const GrGLInterface> onInitializeContext() override;
- void onDestroyContext() override {}
-
-private:
- SDL_Window* fWindow;
- SDL_GLContext fGLContext;
-
- typedef GLWindowContext INHERITED;
-};
-
-GLWindowContext_ios::GLWindowContext_ios(const IOSWindowInfo& info, const DisplayParams& params)
- : INHERITED(params)
- , fWindow(info.fWindow)
- , fGLContext(info.fGLContext) {
-
- // any config code here (particularly for msaa)?
-
- this->initializeContext();
-}
-
-GLWindowContext_ios::~GLWindowContext_ios() {
- this->destroyContext();
-}
-
-sk_sp<const GrGLInterface> GLWindowContext_ios::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_GL_GetDrawableSize(fWindow, &fWidth, &fHeight);
- glViewport(0, 0, fWidth, fHeight);
- } else {
- SkDebugf("MakeCurrent failed: %s\n", SDL_GetError());
- }
- return GrGLMakeNativeInterface();
-}
-
-void GLWindowContext_ios::onSwapBuffers() {
- if (fWindow && fGLContext) {
- SDL_GL_SwapWindow(fWindow);
- }
-}
-
-} // anonymous namespace
-
-namespace sk_app {
-namespace window_context_factory {
-
-std::unique_ptr<WindowContext> MakeGLForIOS(const IOSWindowInfo& info,
- const DisplayParams& params) {
- std::unique_ptr<WindowContext> ctx(new GLWindowContext_ios(info, params));
- if (!ctx->isValid()) {
- return nullptr;
- }
- return ctx;
-}
-
-} // namespace window_context_factory
-} // namespace sk_app
diff --git a/tools/sk_app/ios/GLWindowContext_ios.mm b/tools/sk_app/ios/GLWindowContext_ios.mm
new file mode 100644
index 0000000..9e52cd8
--- /dev/null
+++ b/tools/sk_app/ios/GLWindowContext_ios.mm
@@ -0,0 +1,169 @@
+
+/*
+ * 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 "include/gpu/gl/GrGLInterface.h"
+#include "tools/sk_app/GLWindowContext.h"
+#include "tools/sk_app/ios/WindowContextFactory_ios.h"
+
+#include <OpenGLES/ES3/gl.h>
+#include <UIKit/UIKit.h>
+
+using sk_app::DisplayParams;
+using sk_app::window_context_factory::IOSWindowInfo;
+using sk_app::GLWindowContext;
+
+@interface RasterView : MainView
+@end
+
+@implementation RasterView
++ (Class) layerClass {
+ return [CAEAGLLayer class];
+}
+@end
+
+namespace {
+
+class GLWindowContext_ios : public GLWindowContext {
+public:
+ GLWindowContext_ios(const IOSWindowInfo&, const DisplayParams&);
+
+ ~GLWindowContext_ios() override;
+
+ void onSwapBuffers() override;
+
+ sk_sp<const GrGLInterface> onInitializeContext() override;
+ void onDestroyContext() override;
+
+ void resize(int w, int h) override;
+
+private:
+ sk_app::Window_ios* fWindow;
+ UIViewController* fViewController;
+ RasterView* fRasterView;
+ EAGLContext* fGLContext;
+ GLuint fFramebuffer;
+ GLuint fRenderbuffer;
+
+ typedef GLWindowContext INHERITED;
+};
+
+GLWindowContext_ios::GLWindowContext_ios(const IOSWindowInfo& info, const DisplayParams& params)
+ : INHERITED(params)
+ , fWindow(info.fWindow)
+ , fViewController(info.fViewController)
+ , fGLContext(nil) {
+
+ // any config code here (particularly for msaa)?
+
+ this->initializeContext();
+}
+
+GLWindowContext_ios::~GLWindowContext_ios() {
+ this->onDestroyContext();
+ [fRasterView removeFromSuperview];
+ [fRasterView release];
+}
+
+sk_sp<const GrGLInterface> GLWindowContext_ios::onInitializeContext() {
+ SkASSERT(nil != fViewController);
+ SkASSERT(!fGLContext);
+
+ CGRect frameRect = [fViewController.view frame];
+ fRasterView = [[[RasterView alloc] initWithFrame:frameRect] initWithWindow:fWindow];
+ [fViewController.view addSubview:fRasterView];
+
+ fGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
+
+ if (!fGLContext)
+ {
+ SkDebugf("Could Not Create OpenGL ES Context\n");
+ return nullptr;
+ }
+
+ if (![EAGLContext setCurrentContext:fGLContext]) {
+ SkDebugf("Could Not Set OpenGL ES Context As Current\n");
+ this->onDestroyContext();
+ return nullptr;
+ }
+
+ // Set up EAGLLayer
+ CAEAGLLayer* eaglLayer = (CAEAGLLayer*)fRasterView.layer;
+ eaglLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking : @NO,
+ kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8 };
+ eaglLayer.opaque = YES;
+ eaglLayer.frame = frameRect;
+ eaglLayer.contentsGravity = kCAGravityTopLeft;
+
+ // Set up framebuffer
+ glGenFramebuffers(1, &fFramebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, fFramebuffer);
+
+ glGenRenderbuffers(1, &fRenderbuffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, fRenderbuffer);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fRenderbuffer);
+
+ [fGLContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer];
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ SkDebugf("Invalid Framebuffer\n");
+ this->onDestroyContext();
+ return nullptr;
+ }
+
+ glClearStencil(0);
+ glClearColor(0, 0, 0, 255);
+ glStencilMask(0xffffffff);
+ glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+ fStencilBits = 8;
+ fSampleCount = 1; // TODO: handle multisampling
+
+ fWidth = fViewController.view.frame.size.width;
+ fHeight = fViewController.view.frame.size.height;
+
+ glViewport(0, 0, fWidth, fHeight);
+
+ return GrGLMakeNativeInterface();
+}
+
+void GLWindowContext_ios::onDestroyContext() {
+ glDeleteFramebuffers(1, &fFramebuffer);
+ glDeleteRenderbuffers(1, &fRenderbuffer);
+ [EAGLContext setCurrentContext:nil];
+ [fGLContext release];
+ fGLContext = nil;
+}
+
+void GLWindowContext_ios::onSwapBuffers() {
+ glBindRenderbuffer(GL_RENDERBUFFER, fRenderbuffer);
+ [fGLContext presentRenderbuffer:GL_RENDERBUFFER];
+}
+
+void GLWindowContext_ios::resize(int w, int h) {
+ // TODO: handle rotation
+ // [fGLContext update];
+ INHERITED::resize(w, h);
+}
+
+} // anonymous namespace
+
+namespace sk_app {
+namespace window_context_factory {
+
+std::unique_ptr<WindowContext> MakeGLForIOS(const IOSWindowInfo& info,
+ const DisplayParams& params) {
+ std::unique_ptr<WindowContext> ctx(new GLWindowContext_ios(info, params));
+ if (!ctx->isValid()) {
+ return nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+} // namespace sk_app
diff --git a/tools/sk_app/ios/RasterWindowContext_ios.cpp b/tools/sk_app/ios/RasterWindowContext_ios.cpp
deleted file mode 100644
index 61ddbf5..0000000
--- a/tools/sk_app/ios/RasterWindowContext_ios.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "include/core/SkCanvas.h"
-#include "include/core/SkColorFilter.h"
-#include "include/gpu/gl/GrGLInterface.h"
-#include "tools/ToolUtils.h"
-#include "tools/sk_app/GLWindowContext.h"
-#include "tools/sk_app/ios/WindowContextFactory_ios.h"
-
-#include <OpenGLES/ES2/gl.h>
-
-#include "SDL.h"
-
-using sk_app::DisplayParams;
-using sk_app::window_context_factory::IOSWindowInfo;
-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_ios : public GLWindowContext {
-public:
- RasterWindowContext_ios(const IOSWindowInfo&, const DisplayParams&);
-
- ~RasterWindowContext_ios() 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_ios::RasterWindowContext_ios(const IOSWindowInfo& info,
- const DisplayParams& params)
- : INHERITED(params)
- , fWindow(info.fWindow)
- , fGLContext(nullptr) {
-
- // any config code here (particularly for msaa)?
-
- this->initializeContext();
-}
-
-RasterWindowContext_ios::~RasterWindowContext_ios() {
- this->destroyContext();
-}
-
-sk_sp<const GrGLInterface> RasterWindowContext_ios::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_GL_GetDrawableSize(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_ios::onDestroyContext() {
- fBackbufferSurface.reset(nullptr);
-}
-
-sk_sp<SkSurface> RasterWindowContext_ios::getBackbufferSurface() { return fBackbufferSurface; }
-
-void RasterWindowContext_ios::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 {
-
-std::unique_ptr<WindowContext> MakeRasterForIOS(const IOSWindowInfo& info,
- const DisplayParams& params) {
- std::unique_ptr<WindowContext> ctx(new RasterWindowContext_ios(info, params));
- if (!ctx->isValid()) {
- return nullptr;
- }
- return ctx;
-}
-
-} // namespace window_context_factory
-} // namespace sk_app
diff --git a/tools/sk_app/ios/RasterWindowContext_ios.mm b/tools/sk_app/ios/RasterWindowContext_ios.mm
new file mode 100644
index 0000000..bddcf9d
--- /dev/null
+++ b/tools/sk_app/ios/RasterWindowContext_ios.mm
@@ -0,0 +1,197 @@
+
+/*
+ * 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 "include/core/SkCanvas.h"
+#include "include/core/SkColorFilter.h"
+#include "include/gpu/gl/GrGLInterface.h"
+#include "tools/ToolUtils.h"
+#include "tools/sk_app/GLWindowContext.h"
+#include "tools/sk_app/ios/WindowContextFactory_ios.h"
+
+#include <OpenGLES/ES3/gl.h>
+
+#include <UIKit/UIKit.h>
+
+using sk_app::DisplayParams;
+using sk_app::window_context_factory::IOSWindowInfo;
+using sk_app::GLWindowContext;
+
+@interface GLView : MainView
+@end
+
+@implementation GLView
++ (Class) layerClass {
+ return [CAEAGLLayer class];
+}
+@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_ios : public GLWindowContext {
+public:
+ RasterWindowContext_ios(const IOSWindowInfo&, const DisplayParams&);
+
+ ~RasterWindowContext_ios() override;
+
+ sk_sp<SkSurface> getBackbufferSurface() override;
+
+ void onSwapBuffers() override;
+
+ sk_sp<const GrGLInterface> onInitializeContext() override;
+ void onDestroyContext() override;
+
+ void resize(int w, int h) override;
+
+private:
+ sk_app::Window_ios* fWindow;
+ UIViewController* fViewController;
+ GLView* fGLView;
+ EAGLContext* fGLContext;
+ GLuint fFramebuffer;
+ GLuint fRenderbuffer;
+ sk_sp<SkSurface> fBackbufferSurface;
+
+ typedef GLWindowContext INHERITED;
+};
+
+RasterWindowContext_ios::RasterWindowContext_ios(const IOSWindowInfo& info,
+ const DisplayParams& params)
+ : INHERITED(params)
+ , fWindow(info.fWindow)
+ , fViewController(info.fViewController)
+ , fGLContext(nil) {
+
+ // any config code here (particularly for msaa)?
+
+ this->initializeContext();
+}
+
+RasterWindowContext_ios::~RasterWindowContext_ios() {
+ this->onDestroyContext();
+ [fGLView removeFromSuperview];
+ [fGLView release];
+}
+
+sk_sp<const GrGLInterface> RasterWindowContext_ios::onInitializeContext() {
+ SkASSERT(nil != fViewController);
+ SkASSERT(!fGLContext);
+
+ CGRect frameRect = [fViewController.view frame];
+ fGLView = [[[GLView alloc] initWithFrame:frameRect] initWithWindow:fWindow];
+ [fViewController.view addSubview:fGLView];
+
+ fGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
+
+ if (!fGLContext)
+ {
+ SkDebugf("Could Not Create OpenGL ES Context\n");
+ return nullptr;
+ }
+
+ if (![EAGLContext setCurrentContext:fGLContext]) {
+ SkDebugf("Could Not Set OpenGL ES Context As Current\n");
+ this->onDestroyContext();
+ return nullptr;
+ }
+
+ // Set up EAGLLayer
+ CAEAGLLayer* eaglLayer = (CAEAGLLayer*)fGLView.layer;
+ eaglLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking : @NO,
+ kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8 };
+ eaglLayer.opaque = YES;
+ eaglLayer.frame = frameRect;
+ eaglLayer.contentsGravity = kCAGravityTopLeft;
+
+ // Set up framebuffer
+ glGenFramebuffers(1, &fFramebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, fFramebuffer);
+
+ glGenRenderbuffers(1, &fRenderbuffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, fRenderbuffer);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fRenderbuffer);
+
+ [fGLContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer];
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ SkDebugf("Invalid Framebuffer\n");
+ this->onDestroyContext();
+ return nullptr;
+ }
+
+ glClearStencil(0);
+ glClearColor(0, 0, 0, 255);
+ glStencilMask(0xffffffff);
+ glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+ fStencilBits = 8;
+ fSampleCount = 1; // TODO: handle multisampling
+
+ fWidth = fViewController.view.frame.size.width;
+ fHeight = fViewController.view.frame.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_ios::onDestroyContext() {
+ glDeleteFramebuffers(1, &fFramebuffer);
+ glDeleteRenderbuffers(1, &fRenderbuffer);
+ [EAGLContext setCurrentContext:nil];
+ [fGLContext release];
+ fGLContext = nil;
+}
+
+sk_sp<SkSurface> RasterWindowContext_ios::getBackbufferSurface() {
+ return fBackbufferSurface;
+}
+
+void RasterWindowContext_ios::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();
+ glBindRenderbuffer(GL_RENDERBUFFER, fRenderbuffer);
+ [fGLContext presentRenderbuffer:GL_RENDERBUFFER];
+ }
+}
+
+void RasterWindowContext_ios::resize(int w, int h) {
+ // TODO: handle rotation
+ // [fGLContext update];
+ INHERITED::resize(w, h);
+}
+
+} // anonymous namespace
+
+namespace sk_app {
+namespace window_context_factory {
+
+std::unique_ptr<WindowContext> MakeRasterForIOS(const IOSWindowInfo& info,
+ const DisplayParams& params) {
+ std::unique_ptr<WindowContext> ctx(new RasterWindowContext_ios(info, params));
+ if (!ctx->isValid()) {
+ return nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+} // namespace sk_app
diff --git a/tools/sk_app/ios/WindowContextFactory_ios.h b/tools/sk_app/ios/WindowContextFactory_ios.h
index 87604b2..64df86f 100644
--- a/tools/sk_app/ios/WindowContextFactory_ios.h
+++ b/tools/sk_app/ios/WindowContextFactory_ios.h
@@ -9,7 +9,9 @@
#ifndef WindowContextFactory_ios_DEFINED
#define WindowContextFactory_ios_DEFINED
-#include "SDL.h"
+#include "tools/sk_app/ios/Window_ios.h"
+
+#import <UIKit/UIKit.h>
#include "tools/sk_app/WindowContext.h"
@@ -22,12 +24,17 @@
namespace window_context_factory {
struct IOSWindowInfo {
- SDL_Window* fWindow;
- SDL_GLContext fGLContext;
+ sk_app::Window_ios* fWindow;
+ UIViewController* fViewController;
};
inline std::unique_ptr<WindowContext> MakeVulkanForIOS(const IOSWindowInfo&, const DisplayParams&) {
- // No Vulkan support on iOS.
+ // No Vulkan support on iOS yet.
+ return nullptr;
+}
+
+inline std::unique_ptr<WindowContext> MakeMetalForIOS(const IOSWindowInfo&, const DisplayParams&) {
+ // No Metal support on iOS yet.
return nullptr;
}
diff --git a/tools/sk_app/ios/Window_ios.cpp b/tools/sk_app/ios/Window_ios.cpp
deleted file mode 100644
index d7ef20b..0000000
--- a/tools/sk_app/ios/Window_ios.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
-* Copyright 2017 Google Inc.
-*
-* Use of this source code is governed by a BSD-style license that can be
-* found in the LICENSE file.
-*/
-
-#include "src/core/SkUtils.h"
-#include "tools/sk_app/ios/WindowContextFactory_ios.h"
-#include "tools/sk_app/ios/Window_ios.h"
-#include "tools/skui/ModifierKey.h"
-#include "tools/timer/Timer.h"
-
-namespace sk_app {
-
-SkTDynamicHash<Window_ios, Uint32> Window_ios::gWindowMap;
-
-Window* Window::CreateNativeWindow(void*) {
- Window_ios* window = new Window_ios();
- if (!window->initWindow()) {
- delete window;
- return nullptr;
- }
-
- return window;
-}
-
-bool Window_ios::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_FULLSCREEN | SDL_WINDOW_ALLOW_HIGHDPI;
- 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;
- }
-
- return true;
-}
-
-void Window_ios::closeWindow() {
- if (fGLContext) {
- SDL_GL_DeleteContext(fGLContext);
- fGLContext = nullptr;
- }
-
- if (fWindow) {
- gWindowMap.remove(fWindowID);
- SDL_DestroyWindow(fWindow);
- fWindowID = 0;
- fWindow = nullptr;
- }
-}
-
-static skui::Key get_key(const SDL_Keysym& keysym) {
- static const struct {
- SDL_Keycode fSDLK;
- skui::Key fKey;
- } gPair[] = {
- { SDLK_BACKSPACE, skui::Key::kBack },
- { SDLK_CLEAR, skui::Key::kBack },
- { SDLK_RETURN, skui::Key::kOK },
- { SDLK_UP, skui::Key::kUp },
- { SDLK_DOWN, skui::Key::kDown },
- { SDLK_LEFT, skui::Key::kLeft },
- { SDLK_RIGHT, skui::Key::kRight },
- { SDLK_TAB, skui::Key::kTab },
- { SDLK_PAGEUP, skui::Key::kPageUp },
- { SDLK_PAGEDOWN, skui::Key::kPageDown },
- { SDLK_HOME, skui::Key::kHome },
- { SDLK_END, skui::Key::kEnd },
- { SDLK_DELETE, skui::Key::kDelete },
- { SDLK_ESCAPE, skui::Key::kEscape },
- { SDLK_LSHIFT, skui::Key::kShift },
- { SDLK_RSHIFT, skui::Key::kShift },
- { SDLK_LCTRL, skui::Key::kCtrl },
- { SDLK_RCTRL, skui::Key::kCtrl },
- { SDLK_LALT, skui::Key::kOption },
- { SDLK_LALT, skui::Key::kOption },
- { 'A', skui::Key::kA },
- { 'C', skui::Key::kC },
- { 'V', skui::Key::kV },
- { 'X', skui::Key::kX },
- { 'Y', skui::Key::kY },
- { 'Z', skui::Key::kZ },
- };
- for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
- if (gPair[i].fSDLK == keysym.sym) {
- return gPair[i].fKey;
- }
- }
- return skui::Key::kNONE;
-}
-
-static skui::ModifierKey get_modifiers(const SDL_Event& event) {
- static const struct {
- unsigned fSDLMask;
- skui::ModifierKey fSkMask;
- } gModifiers[] = {
- { KMOD_SHIFT, skui::ModifierKey::kShift },
- { KMOD_CTRL, skui::ModifierKey::kControl },
- { KMOD_ALT, skui::ModifierKey::kOption },
- };
-
- skui::ModifierKey modifiers = skui::ModifierKey::kNone;
-
- 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 |= skui::ModifierKey::kFirstPress;
- }
- 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_ios::HandleWindowEvent(const SDL_Event& event) {
- Window_ios* win = gWindowMap.find(event.window.windowID);
- if (win && win->handleEvent(event)) {
- return true;
- }
-
- return false;
-}
-
-bool Window_ios::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_FINGERDOWN:
- this->onTouch(event.tfinger.fingerId, skui::InputState::kDown,
- (int)(this->width()*event.tfinger.x),
- (int)(this->height()*event.tfinger.y));
- break;
-
- case SDL_FINGERUP:
- this->onTouch(event.tfinger.fingerId, skui::InputState::kUp,
- (int)(this->width()*event.tfinger.x),
- (int)(this->height()*event.tfinger.y));
- break;
-
- case SDL_FINGERMOTION:
- this->onTouch(event.tfinger.fingerId, skui::InputState::kMove,
- (int)(this->width()*event.tfinger.x),
- (int)(this->height()*event.tfinger.y));
- break;
-
- case SDL_KEYDOWN: {
- skui::Key key = get_key(event.key.keysym);
- if (key != skui::Key::kNONE) {
- if (!this->onKey(key, skui::InputState::kDown, get_modifiers(event))) {
- if (event.key.keysym.sym == SDLK_ESCAPE) {
- return true;
- }
- }
- }
- } break;
-
- case SDL_KEYUP: {
- skui::Key key = get_key(event.key.keysym);
- if (key != skui::Key::kNONE) {
- (void) this->onKey(key, skui::InputState::kUp,
- 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_ios::setTitle(const char* title) {
- SDL_SetWindowTitle(fWindow, title);
-}
-
-void Window_ios::show() {
- SDL_ShowWindow(fWindow);
-}
-
-bool Window_ios::attach(BackendType attachType) {
- this->initWindow();
-
- window_context_factory::IOSWindowInfo info;
- info.fWindow = fWindow;
- info.fGLContext = fGLContext;
- switch (attachType) {
- case kRaster_BackendType:
- fWindowContext = MakeRasterForIOS(info, fRequestedDisplayParams);
- break;
-
- case kNativeGL_BackendType:
- default:
- fWindowContext = MakeGLForIOS(info, fRequestedDisplayParams);
- break;
- }
- this->onBackendCreated();
-
- return (SkToBool(fWindowContext));
-}
-
-void Window_ios::onInval() {
- SDL_Event sdlevent;
- sdlevent.type = SDL_WINDOWEVENT;
- sdlevent.window.windowID = fWindowID;
- sdlevent.window.event = SDL_WINDOWEVENT_EXPOSED;
- SDL_PushEvent(&sdlevent);
-}
-
-} // namespace sk_app
diff --git a/tools/sk_app/ios/Window_ios.h b/tools/sk_app/ios/Window_ios.h
index a548253..037f992 100644
--- a/tools/sk_app/ios/Window_ios.h
+++ b/tools/sk_app/ios/Window_ios.h
@@ -12,7 +12,7 @@
#include "src/core/SkTDynamicHash.h"
#include "tools/sk_app/Window.h"
-#include "SDL.h"
+#import <UIKit/UIKit.h>
namespace sk_app {
@@ -20,47 +20,42 @@
public:
Window_ios()
: INHERITED()
- , fWindow(nullptr)
- , fWindowID(0)
- , fGLContext(nullptr)
- , fMSAASampleCount(1) {}
+ , fWindow(nil) {}
~Window_ios() override { this->closeWindow(); }
bool initWindow();
- void setTitle(const char*) override;
- void show() override;
+ void setTitle(const char*) override {}
+ void show() override {}
bool attach(BackendType) override;
void onInval() override;
- static bool HandleWindowEvent(const SDL_Event& event);
+ static void PaintWindow();
- static const Uint32& GetKey(const Window_ios& w) {
- return w.fWindowID;
- }
+ UIWindow* uiWindow() { return fWindow; }
- static uint32_t Hash(const Uint32& winID) {
- return winID;
- }
+ static Window_ios* MainWindow() { return gWindow; }
private:
- bool handleEvent(const SDL_Event& event);
-
void closeWindow();
- static SkTDynamicHash<Window_ios, Uint32> gWindowMap;
+ UIWindow* fWindow;
- SDL_Window* fWindow;
- Uint32 fWindowID;
- SDL_GLContext fGLContext;
-
- int fMSAASampleCount;
+ static Window_ios* gWindow; // there should be only one
typedef Window INHERITED;
};
} // namespace sk_app
+//////////////////////////////////////////////////////////////////////////
+
+@interface MainView : UIView
+
+- (MainView*)initWithWindow:(sk_app::Window_ios*)initWindow;
+
+@end
+
#endif
diff --git a/tools/sk_app/ios/Window_ios.mm b/tools/sk_app/ios/Window_ios.mm
new file mode 100644
index 0000000..e70d15f
--- /dev/null
+++ b/tools/sk_app/ios/Window_ios.mm
@@ -0,0 +1,148 @@
+/*
+* Copyright 2017 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include "tools/sk_app/ios/WindowContextFactory_ios.h"
+#include "tools/sk_app/ios/Window_ios.h"
+
+@interface WindowViewController : UIViewController
+
+- (WindowViewController*)initWithWindow:(sk_app::Window_ios*)initWindow;
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////
+
+using sk_app::Window;
+
+namespace sk_app {
+
+Window_ios* Window_ios::gWindow = nullptr;
+
+Window* Window::CreateNativeWindow(void*) {
+ // already have a window
+ if (Window_ios::MainWindow()) {
+ return nullptr;
+ }
+
+ Window_ios* window = new Window_ios();
+ if (!window->initWindow()) {
+ delete window;
+ return nullptr;
+ }
+
+ return window;
+}
+
+bool Window_ios::initWindow() {
+ // we already have a window
+ if (fWindow) {
+ return true;
+ }
+
+ // Create a delegate to track certain events
+ WindowViewController* viewController = [[WindowViewController alloc] initWithWindow:this];
+ if (nil == viewController) {
+ return false;
+ }
+
+ fWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ if (nil == fWindow) {
+ [viewController release];
+ return false;
+ }
+ fWindow.backgroundColor = [UIColor whiteColor];
+
+ viewController.view = nil;
+ [fWindow setRootViewController:viewController];
+ [fWindow makeKeyAndVisible];
+
+ gWindow = this;
+
+ return true;
+}
+
+void Window_ios::closeWindow() {
+ if (nil != fWindow) {
+ gWindow = nullptr;
+ [fWindow release];
+ fWindow = nil;
+ }
+}
+
+bool Window_ios::attach(BackendType attachType) {
+ this->initWindow();
+
+ window_context_factory::IOSWindowInfo info;
+ info.fWindow = this;
+ info.fViewController = fWindow.rootViewController;
+ switch (attachType) {
+ case kRaster_BackendType:
+ fWindowContext = MakeRasterForIOS(info, fRequestedDisplayParams);
+ break;
+//#ifdef SK_METAL
+// case kMetal_BackendType:
+// fWindowContext = MakeMetalForMac(info, fRequestedDisplayParams);
+// break;
+//#endif
+ case kNativeGL_BackendType:
+ default:
+ fWindowContext = MakeGLForIOS(info, fRequestedDisplayParams);
+ break;
+ }
+ this->onBackendCreated();
+
+ return (SkToBool(fWindowContext));
+}
+
+void Window_ios::PaintWindow() {
+ gWindow->onPaint();
+}
+
+void Window_ios::onInval() {
+ // TODO: send expose event
+}
+
+} // namespace sk_app
+
+///////////////////////////////////////////////////////////////////////////////
+
+@implementation WindowViewController {
+ sk_app::Window_ios* fWindow;
+}
+
+- (WindowViewController*)initWithWindow:(sk_app::Window_ios *)initWindow {
+ fWindow = initWindow;
+
+ return self;
+}
+
+- (void)viewDidLoad {
+ // nothing yet
+}
+
+- (void)didReceiveMemoryWarning {
+ // nothing yet
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////
+
+@implementation MainView {
+ sk_app::Window_ios* fWindow;
+}
+
+- (MainView*)initWithWindow:(sk_app::Window_ios *)initWindow {
+ self = [super init];
+
+ fWindow = initWindow;
+
+ return self;
+}
+
+@end
+
diff --git a/tools/sk_app/ios/main_ios.cpp b/tools/sk_app/ios/main_ios.cpp
deleted file mode 100644
index 6982a5a..0000000
--- a/tools/sk_app/ios/main_ios.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-* Copyright 2017 Google Inc.
-*
-* Use of this source code is governed by a BSD-style license that can be
-* found in the LICENSE file.
-*/
-
-#include "include/core/SkTypes.h"
-#include "include/private/SkTHash.h"
-#include "tools/sk_app/Application.h"
-#include "tools/sk_app/ios/Window_ios.h"
-#include "tools/timer/Timer.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_FINGERDOWN:
- case SDL_FINGERMOTION:
- case SDL_FINGERUP:
- case SDL_KEYDOWN:
- case SDL_KEYUP:
- case SDL_TEXTINPUT:
- done = sk_app::Window_ios::HandleWindowEvent(event);
- break;
-
- case SDL_QUIT:
- done = true;
- break;
-
- default:
- break;
- }
- }
-
- app->onIdle();
- }
- delete app;
-
- SDL_Quit();
-
- return 0;
-}
diff --git a/tools/sk_app/ios/main_ios.mm b/tools/sk_app/ios/main_ios.mm
new file mode 100644
index 0000000..3bcc236
--- /dev/null
+++ b/tools/sk_app/ios/main_ios.mm
@@ -0,0 +1,106 @@
+/*
+* Copyright 2017 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include "include/core/SkTypes.h"
+#include "include/private/SkTHash.h"
+#include "tools/sk_app/Application.h"
+#include "tools/sk_app/ios/Window_ios.h"
+#include "tools/timer/Timer.h"
+
+#import <UIKit/UIKit.h>
+
+using sk_app::Application;
+
+////////////////////////////////////////////////////////////////////
+
+@interface AppDelegate : UIResponder<UIApplicationDelegate>
+
+@property (nonatomic, assign) BOOL done;
+@property (strong, nonatomic) UIWindow *window;
+
+@end
+
+@implementation AppDelegate
+
+@synthesize done = _done;
+@synthesize window = _window;
+
+- (void)applicationWillTerminate:(UIApplication *)sender {
+ _done = TRUE;
+}
+
+- (void)launchApp {
+ // Extract argc and argv from NSProcessInfo
+ NSArray *arguments = [[NSProcessInfo processInfo] arguments];
+ int argc = arguments.count;
+ char** argv = (char **)malloc((argc+1) * sizeof(char *));
+ int i = 0;
+ for (NSString* string in arguments) {
+ size_t bufferSize = (string.length+1) * sizeof(char);
+ argv[i] = (char*)malloc(bufferSize);
+ [string getCString:argv[i]
+ maxLength:bufferSize
+ encoding:NSUTF8StringEncoding];
+ ++i;
+ }
+ argv[i] = NULL;
+
+ Application* app = Application::Create(argc, argv, nullptr);
+
+ // Free the memory we used for argc and argv
+ for (i = 0; i < argc; i++) {
+ free(argv[i]);
+ }
+ free(argv);
+
+ sk_app::Window_ios* mainWindow = sk_app::Window_ios::MainWindow();
+ if (!mainWindow) {
+ return;
+ }
+ self.window = mainWindow->uiWindow();
+
+ // take over the main event loop
+ bool done = false;
+ while (!done) {
+ // TODO: consider using a dispatch queue or CADisplayLink instead of this
+ const CFTimeInterval kSeconds = 0.000002;
+ CFRunLoopRunResult result;
+ do {
+ result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, kSeconds, TRUE);
+ } while (result == kCFRunLoopRunHandledSource);
+
+ // TODO: is this the right approach for iOS?
+ // Rather than depending on an iOS event to drive this, we treat our window
+ // invalidation flag as a separate event stream. Window::onPaint() will clear
+ // the invalidation flag, effectively removing it from the stream.
+ sk_app::Window_ios::PaintWindow();
+
+ app->onIdle();
+ }
+ delete app;
+}
+
+- (BOOL)application:(UIApplication *)application
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ // let the system event loop run once, then launch into our main loop
+ [self performSelector:@selector(launchApp) withObject:nil afterDelay:0.0];
+
+ return YES;
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////
+
+int main(int argc, char **argv) {
+ /* Give over control to run loop, AppDelegate will handle most things from here */
+ @autoreleasepool {
+ UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+
+ return EXIT_SUCCESS;
+}