aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/sk_app/unix
diff options
context:
space:
mode:
authorGravatar Brian Osman <brianosman@google.com>2017-11-21 13:18:02 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-11-21 18:37:19 +0000
commiteff04b5ec287e0fee0d44207c10d2d11f7eade8a (patch)
treeea2cf00ea329c81611536aaa9a9f1eca47c67e9a /tools/sk_app/unix
parent2aa09dbe8aced37aa6bb285e62df45deb0e81650 (diff)
Remove SampleApp and convert HelloWorld to sk_app
There is still a large amount of views code that could be trimmed down, but which is used to implement samples (in viewer). Seemed simpler to remove some of this code in pieces. Bug: skia: Change-Id: Ia3415060d03c8de604a154e3dc38379b754daab6 Reviewed-on: https://skia-review.googlesource.com/72801 Commit-Queue: Brian Osman <brianosman@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
Diffstat (limited to 'tools/sk_app/unix')
-rw-r--r--tools/sk_app/unix/GLWindowContext_unix.cpp149
-rw-r--r--tools/sk_app/unix/RasterWindowContext_unix.cpp104
-rw-r--r--tools/sk_app/unix/VulkanWindowContext_unix.cpp86
-rw-r--r--tools/sk_app/unix/WindowContextFactory_unix.h42
-rw-r--r--tools/sk_app/unix/Window_unix.cpp387
-rw-r--r--tools/sk_app/unix/Window_unix.h95
-rw-r--r--tools/sk_app/unix/main_unix.cpp94
7 files changed, 957 insertions, 0 deletions
diff --git a/tools/sk_app/unix/GLWindowContext_unix.cpp b/tools/sk_app/unix/GLWindowContext_unix.cpp
new file mode 100644
index 0000000000..d7a4387880
--- /dev/null
+++ b/tools/sk_app/unix/GLWindowContext_unix.cpp
@@ -0,0 +1,149 @@
+
+/*
+ * 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 <GL/gl.h>
+#include "../GLWindowContext.h"
+#include "WindowContextFactory_unix.h"
+#include "gl/GrGLInterface.h"
+
+using sk_app::window_context_factory::XlibWindowInfo;
+using sk_app::DisplayParams;
+using sk_app::GLWindowContext;
+
+namespace {
+
+class GLWindowContext_xlib : public GLWindowContext {
+public:
+ GLWindowContext_xlib(const XlibWindowInfo&, const DisplayParams&);
+ ~GLWindowContext_xlib() override;
+
+ void onSwapBuffers() override;
+
+ void onDestroyContext() override;
+
+protected:
+ sk_sp<const GrGLInterface> onInitializeContext() override;
+
+private:
+ GLWindowContext_xlib(void*, const DisplayParams&);
+
+ Display* fDisplay;
+ XWindow fWindow;
+ GLXFBConfig* fFBConfig;
+ XVisualInfo* fVisualInfo;
+ GLXContext fGLContext;
+
+ typedef GLWindowContext INHERITED;
+};
+
+GLWindowContext_xlib::GLWindowContext_xlib(const XlibWindowInfo& winInfo, const DisplayParams& params)
+ : INHERITED(params)
+ , fDisplay(winInfo.fDisplay)
+ , fWindow(winInfo.fWindow)
+ , fFBConfig(winInfo.fFBConfig)
+ , fVisualInfo(winInfo.fVisualInfo)
+ , fGLContext() {
+ fWidth = winInfo.fWidth;
+ fHeight = winInfo.fHeight;
+ this->initializeContext();
+}
+
+using CreateContextAttribsFn = GLXContext(Display*, GLXFBConfig, GLXContext, Bool, const int*);
+
+sk_sp<const GrGLInterface> GLWindowContext_xlib::onInitializeContext() {
+ SkASSERT(fDisplay);
+ SkASSERT(!fGLContext);
+ // We attempt to use glXCreateContextAttribsARB as RenderDoc requires that the context be
+ // created with this rather than glXCreateContext.
+ CreateContextAttribsFn* createContextAttribs = (CreateContextAttribsFn*)glXGetProcAddressARB(
+ (const GLubyte*)"glXCreateContextAttribsARB");
+ if (createContextAttribs && fFBConfig) {
+ // Specifying 3.2 allows an arbitrarily high context version (so long as no 3.2 features
+ // have been removed).
+ for (int minor = 2; minor >= 0 && !fGLContext; --minor) {
+ // Ganesh prefers a compatibility profile for possible NVPR support. However, RenderDoc
+ // requires a core profile. Edit this code to use RenderDoc.
+ for (int profile : {GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
+ GLX_CONTEXT_CORE_PROFILE_BIT_ARB}) {
+ int attribs[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, minor,
+ GLX_CONTEXT_PROFILE_MASK_ARB, profile,
+ 0
+ };
+ fGLContext = createContextAttribs(fDisplay, *fFBConfig, nullptr, True, attribs);
+ if (fGLContext) {
+ break;
+ }
+ }
+ }
+ }
+ if (!fGLContext) {
+ fGLContext = glXCreateContext(fDisplay, fVisualInfo, nullptr, GL_TRUE);
+ }
+ if (!fGLContext) {
+ return nullptr;
+ }
+
+ if (!glXMakeCurrent(fDisplay, fWindow, fGLContext)) {
+ return nullptr;
+ }
+ glClearStencil(0);
+ glClearColor(0, 0, 0, 0);
+ glStencilMask(0xffffffff);
+ glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+ glXGetConfig(fDisplay, fVisualInfo, GLX_STENCIL_SIZE, &fStencilBits);
+ glXGetConfig(fDisplay, fVisualInfo, GLX_SAMPLES_ARB, &fSampleCount);
+
+ XWindow root;
+ int x, y;
+ unsigned int border_width, depth;
+ XGetGeometry(fDisplay, fWindow, &root, &x, &y, (unsigned int*)&fWidth, (unsigned int*)&fHeight,
+ &border_width, &depth);
+ glViewport(0, 0, fWidth, fHeight);
+
+ return sk_sp<const GrGLInterface>(GrGLCreateNativeInterface());
+}
+
+GLWindowContext_xlib::~GLWindowContext_xlib() {
+ this->destroyContext();
+}
+
+void GLWindowContext_xlib::onDestroyContext() {
+ if (!fDisplay || !fGLContext) {
+ return;
+ }
+ glXMakeCurrent(fDisplay, None, nullptr);
+ glXDestroyContext(fDisplay, fGLContext);
+ fGLContext = nullptr;
+}
+
+void GLWindowContext_xlib::onSwapBuffers() {
+ if (fDisplay && fGLContext) {
+ glXSwapBuffers(fDisplay, fWindow);
+ }
+}
+
+} // anonymous namespace
+
+namespace sk_app {
+
+namespace window_context_factory {
+
+WindowContext* NewGLForXlib(const XlibWindowInfo& winInfo, const DisplayParams& params) {
+ WindowContext* ctx = new GLWindowContext_xlib(winInfo, params);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+
+} // namespace sk_app
diff --git a/tools/sk_app/unix/RasterWindowContext_unix.cpp b/tools/sk_app/unix/RasterWindowContext_unix.cpp
new file mode 100644
index 0000000000..6bfa6fd0be
--- /dev/null
+++ b/tools/sk_app/unix/RasterWindowContext_unix.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 "WindowContextFactory_unix.h"
+#include "../RasterWindowContext.h"
+#include "SkSurface.h"
+
+using sk_app::RasterWindowContext;
+using sk_app::DisplayParams;
+
+namespace {
+
+class RasterWindowContext_xlib : public RasterWindowContext {
+public:
+ RasterWindowContext_xlib(Display*, XWindow, int width, int height, const DisplayParams&);
+
+ sk_sp<SkSurface> getBackbufferSurface() override;
+ void swapBuffers() override;
+ bool isValid() override { return SkToBool(fWindow); }
+ void resize(int w, int h) override;
+ void setDisplayParams(const DisplayParams& params) override;
+
+protected:
+ sk_sp<SkSurface> fBackbufferSurface;
+ Display* fDisplay;
+ XWindow fWindow;
+ GC fGC;
+
+ typedef RasterWindowContext INHERITED;
+};
+
+RasterWindowContext_xlib::RasterWindowContext_xlib(Display* display, XWindow window, int width,
+ int height, const DisplayParams& params)
+ : INHERITED(params)
+ , fDisplay(display)
+ , fWindow(window) {
+ fGC = XCreateGC(fDisplay, fWindow, 0, nullptr);
+ this->resize(width, height);
+ fWidth = width;
+ fHeight = height;
+}
+
+void RasterWindowContext_xlib::setDisplayParams(const DisplayParams& params) {
+ fDisplayParams = params;
+ XWindowAttributes attrs;
+ XGetWindowAttributes(fDisplay, fWindow, &attrs);
+ this->resize(attrs.width, attrs.height);
+}
+
+void RasterWindowContext_xlib::resize(int w, int h) {
+ SkImageInfo info = SkImageInfo::Make(w, h, fDisplayParams.fColorType, kPremul_SkAlphaType,
+ fDisplayParams.fColorSpace);
+ fBackbufferSurface = SkSurface::MakeRaster(info);
+
+}
+
+sk_sp<SkSurface> RasterWindowContext_xlib::getBackbufferSurface() { return fBackbufferSurface; }
+
+void RasterWindowContext_xlib::swapBuffers() {
+ SkPixmap pm;
+ if (!fBackbufferSurface->peekPixels(&pm)) {
+ return;
+ }
+ int bitsPerPixel = pm.info().bytesPerPixel() * 8;
+ XImage image;
+ memset(&image, 0, sizeof(image));
+ image.width = pm.width();
+ image.height = pm.height();
+ image.format = ZPixmap;
+ image.data = (char*) pm.addr();
+ image.byte_order = LSBFirst;
+ image.bitmap_unit = bitsPerPixel;
+ image.bitmap_bit_order = LSBFirst;
+ image.bitmap_pad = bitsPerPixel;
+ image.depth = 24;
+ image.bytes_per_line = pm.rowBytes() - pm.width() * pm.info().bytesPerPixel();
+ image.bits_per_pixel = bitsPerPixel;
+ if (!XInitImage(&image)) {
+ return;
+ }
+ XPutImage(fDisplay, fWindow, fGC, &image, 0, 0, 0, 0, pm.width(), pm.height());
+}
+
+} // anonymous namespace
+
+namespace sk_app {
+namespace window_context_factory {
+
+WindowContext* NewRasterForXlib(const XlibWindowInfo& info, const DisplayParams& params) {
+ WindowContext* ctx = new RasterWindowContext_xlib(info.fDisplay, info.fWindow, info.fWidth,
+ info.fHeight, params);
+ if (!ctx->isValid()) {
+ delete ctx;
+ ctx = nullptr;
+ }
+ return ctx;
+}
+
+} // namespace window_context_factory
+} // namespace sk_app
diff --git a/tools/sk_app/unix/VulkanWindowContext_unix.cpp b/tools/sk_app/unix/VulkanWindowContext_unix.cpp
new file mode 100644
index 0000000000..b2f1ffc763
--- /dev/null
+++ b/tools/sk_app/unix/VulkanWindowContext_unix.cpp
@@ -0,0 +1,86 @@
+
+/*
+ * 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 "vk/GrVkInterface.h"
+#include "vk/GrVkUtil.h"
+
+#include "vk/VkTestUtils.h"
+
+#include <X11/Xlib-xcb.h>
+
+#include "WindowContextFactory_unix.h"
+#include "../VulkanWindowContext.h"
+
+namespace sk_app {
+
+namespace window_context_factory {
+
+WindowContext* NewVulkanForXlib(const XlibWindowInfo& info, const DisplayParams& displayParams) {
+ PFN_vkGetInstanceProcAddr instProc;
+ PFN_vkGetDeviceProcAddr devProc;
+ if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) {
+ return nullptr;
+ }
+
+ auto createVkSurface = [&info, instProc](VkInstance instance) -> VkSurfaceKHR {
+ static PFN_vkCreateXcbSurfaceKHR createXcbSurfaceKHR = nullptr;
+ if (!createXcbSurfaceKHR) {
+ createXcbSurfaceKHR =
+ (PFN_vkCreateXcbSurfaceKHR) instProc(instance, "vkCreateXcbSurfaceKHR");
+ }
+
+ VkSurfaceKHR surface;
+
+ VkXcbSurfaceCreateInfoKHR surfaceCreateInfo;
+ memset(&surfaceCreateInfo, 0, sizeof(VkXcbSurfaceCreateInfoKHR));
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.pNext = nullptr;
+ surfaceCreateInfo.flags = 0;
+ surfaceCreateInfo.connection = XGetXCBConnection(info.fDisplay);
+ surfaceCreateInfo.window = info.fWindow;
+
+ VkResult res = createXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
+ if (VK_SUCCESS != res) {
+ return VK_NULL_HANDLE;
+ }
+
+ return surface;
+ };
+
+ auto canPresent = [&info, instProc](VkInstance instance, VkPhysicalDevice physDev,
+ uint32_t queueFamilyIndex) {
+ static PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
+ getPhysicalDeviceXcbPresentationSupportKHR = nullptr;
+ if (!getPhysicalDeviceXcbPresentationSupportKHR) {
+ getPhysicalDeviceXcbPresentationSupportKHR =
+ (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
+ instProc(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
+ }
+
+
+ Display* display = info.fDisplay;
+ VisualID visualID = info.fVisualInfo->visualid;
+ VkBool32 check = getPhysicalDeviceXcbPresentationSupportKHR(physDev,
+ queueFamilyIndex,
+ XGetXCBConnection(display),
+ visualID);
+ return (VK_FALSE != check);
+ };
+ WindowContext* context = new VulkanWindowContext(displayParams, createVkSurface, canPresent,
+ instProc, devProc);
+ if (!context->isValid()) {
+ delete context;
+ return nullptr;
+ }
+ return context;
+}
+
+} // namespace VulkanWindowContextFactory
+
+} // namespace sk_app
diff --git a/tools/sk_app/unix/WindowContextFactory_unix.h b/tools/sk_app/unix/WindowContextFactory_unix.h
new file mode 100644
index 0000000000..e6d033b4cd
--- /dev/null
+++ b/tools/sk_app/unix/WindowContextFactory_unix.h
@@ -0,0 +1,42 @@
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef WindowContextFactory_unix_DEFINED
+#define WindowContextFactory_unix_DEFINED
+
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+typedef Window XWindow;
+
+namespace sk_app {
+
+class WindowContext;
+struct DisplayParams;
+
+namespace window_context_factory {
+
+struct XlibWindowInfo {
+ Display* fDisplay;
+ XWindow fWindow;
+ GLXFBConfig* fFBConfig;
+ XVisualInfo* fVisualInfo;
+ int fWidth;
+ int fHeight;
+};
+
+WindowContext* NewVulkanForXlib(const XlibWindowInfo&, const DisplayParams&);
+
+WindowContext* NewGLForXlib(const XlibWindowInfo&, const DisplayParams&);
+
+WindowContext* NewRasterForXlib(const XlibWindowInfo&, const DisplayParams&);
+
+} // namespace window_context_factory
+
+} // namespace sk_app
+
+#endif
diff --git a/tools/sk_app/unix/Window_unix.cpp b/tools/sk_app/unix/Window_unix.cpp
new file mode 100644
index 0000000000..f5ca5ee073
--- /dev/null
+++ b/tools/sk_app/unix/Window_unix.cpp
@@ -0,0 +1,387 @@
+/*
+* 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 <tchar.h>
+
+#include "WindowContextFactory_unix.h"
+
+#include "SkUtils.h"
+#include "Timer.h"
+#include "../GLWindowContext.h"
+#include "Window_unix.h"
+
+extern "C" {
+ #include "keysym2ucs.h"
+}
+#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
+
+namespace sk_app {
+
+SkTDynamicHash<Window_unix, XWindow> Window_unix::gWindowMap;
+
+Window* Window::CreateNativeWindow(void* platformData) {
+ Display* display = (Display*)platformData;
+ SkASSERT(display);
+
+ Window_unix* window = new Window_unix();
+ if (!window->initWindow(display)) {
+ delete window;
+ return nullptr;
+ }
+
+ return window;
+}
+
+const long kEventMask = ExposureMask | StructureNotifyMask |
+ KeyPressMask | KeyReleaseMask |
+ PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
+
+bool Window_unix::initWindow(Display* display) {
+ if (fRequestedDisplayParams.fMSAASampleCount != fMSAASampleCount) {
+ this->closeWindow();
+ }
+ // we already have a window
+ if (fDisplay) {
+ return true;
+ }
+ fDisplay = display;
+
+ constexpr int initialWidth = 1280;
+ constexpr int initialHeight = 960;
+
+ // Attempt to create a window that supports GL
+
+ // We prefer the more recent glXChooseFBConfig but fall back to glXChooseVisual. They have
+ // slight differences in how attributes are specified.
+ static int constexpr kChooseFBConfigAtt[] = {
+ GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DOUBLEBUFFER, True,
+ GLX_STENCIL_SIZE, 8,
+ None
+ };
+ // For some reason glXChooseVisual takes a non-const pointer to the attributes.
+ int chooseVisualAtt[] = {
+ GLX_RGBA,
+ GLX_DOUBLEBUFFER,
+ GLX_STENCIL_SIZE, 8,
+ None
+ };
+ SkASSERT(nullptr == fVisualInfo);
+ if (fRequestedDisplayParams.fMSAASampleCount > 0) {
+ static const GLint kChooseFBConifgAttCnt = SK_ARRAY_COUNT(kChooseFBConfigAtt);
+ GLint msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 4];
+ memcpy(msaaChooseFBConfigAtt, kChooseFBConfigAtt, sizeof(kChooseFBConfigAtt));
+ SkASSERT(None == msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1]);
+ msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB;
+ msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 0] = 1;
+ msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 1] = GLX_SAMPLES_ARB;
+ msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 2] = fRequestedDisplayParams.fMSAASampleCount;
+ msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 3] = None;
+ int n;
+ fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), msaaChooseFBConfigAtt, &n);
+ if (n > 0) {
+ fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig);
+ } else {
+ static const GLint kChooseVisualAttCnt = SK_ARRAY_COUNT(chooseVisualAtt);
+ GLint msaaChooseVisualAtt[kChooseVisualAttCnt + 4];
+ memcpy(msaaChooseVisualAtt, chooseVisualAtt, sizeof(chooseVisualAtt));
+ SkASSERT(None == msaaChooseVisualAtt[kChooseVisualAttCnt - 1]);
+ msaaChooseFBConfigAtt[kChooseVisualAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB;
+ msaaChooseFBConfigAtt[kChooseVisualAttCnt + 0] = 1;
+ msaaChooseFBConfigAtt[kChooseVisualAttCnt + 1] = GLX_SAMPLES_ARB;
+ msaaChooseFBConfigAtt[kChooseVisualAttCnt + 2] =
+ fRequestedDisplayParams.fMSAASampleCount;
+ msaaChooseFBConfigAtt[kChooseVisualAttCnt + 3] = None;
+ fVisualInfo = glXChooseVisual(display, DefaultScreen(display), msaaChooseVisualAtt);
+ fFBConfig = nullptr;
+ }
+ }
+ if (nullptr == fVisualInfo) {
+ int n;
+ fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), kChooseFBConfigAtt, &n);
+ if (n > 0) {
+ fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig);
+ } else {
+ fVisualInfo = glXChooseVisual(display, DefaultScreen(display), chooseVisualAtt);
+ fFBConfig = nullptr;
+ }
+ }
+
+ if (fVisualInfo) {
+ Colormap colorMap = XCreateColormap(display,
+ RootWindow(display, fVisualInfo->screen),
+ fVisualInfo->visual,
+ AllocNone);
+ XSetWindowAttributes swa;
+ swa.colormap = colorMap;
+ swa.event_mask = kEventMask;
+ fWindow = XCreateWindow(display,
+ RootWindow(display, fVisualInfo->screen),
+ 0, 0, // x, y
+ initialWidth, initialHeight,
+ 0, // border width
+ fVisualInfo->depth,
+ InputOutput,
+ fVisualInfo->visual,
+ CWEventMask | CWColormap,
+ &swa);
+ } else {
+ // Create a simple window instead. We will not be able to show GL
+ fWindow = XCreateSimpleWindow(display,
+ DefaultRootWindow(display),
+ 0, 0, // x, y
+ initialWidth, initialHeight,
+ 0, // border width
+ 0, // border value
+ 0); // background value
+ XSelectInput(display, fWindow, kEventMask);
+ }
+
+ if (!fWindow) {
+ return false;
+ }
+
+ fMSAASampleCount = fRequestedDisplayParams.fMSAASampleCount;
+
+ // set up to catch window delete message
+ fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(display, fWindow, &fWmDeleteMessage, 1);
+
+ // add to hashtable of windows
+ gWindowMap.add(this);
+
+ // init event variables
+ fPendingPaint = false;
+ fPendingResize = false;
+
+ return true;
+}
+
+void Window_unix::closeWindow() {
+ if (fDisplay) {
+ this->detach();
+ if (fGC) {
+ XFreeGC(fDisplay, fGC);
+ fGC = nullptr;
+ }
+ gWindowMap.remove(fWindow);
+ XDestroyWindow(fDisplay, fWindow);
+ fWindow = 0;
+ if (fFBConfig) {
+ XFree(fFBConfig);
+ fFBConfig = nullptr;
+ }
+ if (fVisualInfo) {
+ XFree(fVisualInfo);
+ fVisualInfo = nullptr;
+ }
+ fDisplay = nullptr;
+ }
+}
+
+static Window::Key get_key(KeySym keysym) {
+ static const struct {
+ KeySym fXK;
+ Window::Key fKey;
+ } gPair[] = {
+ { XK_BackSpace, Window::Key::kBack },
+ { XK_Clear, Window::Key::kBack },
+ { XK_Return, Window::Key::kOK },
+ { XK_Up, Window::Key::kUp },
+ { XK_Down, Window::Key::kDown },
+ { XK_Left, Window::Key::kLeft },
+ { XK_Right, Window::Key::kRight },
+ { XK_Tab, Window::Key::kTab },
+ { XK_Page_Up, Window::Key::kPageUp },
+ { XK_Page_Down, Window::Key::kPageDown },
+ { XK_Home, Window::Key::kHome },
+ { XK_End, Window::Key::kEnd },
+ { XK_Delete, Window::Key::kDelete },
+ { XK_Escape, Window::Key::kEscape },
+ { XK_Shift_L, Window::Key::kShift },
+ { XK_Shift_R, Window::Key::kShift },
+ { XK_Control_L, Window::Key::kCtrl },
+ { XK_Control_R, Window::Key::kCtrl },
+ { XK_Alt_L, Window::Key::kOption },
+ { XK_Alt_R, 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].fXK == keysym) {
+ return gPair[i].fKey;
+ }
+ }
+ return Window::Key::kNONE;
+}
+
+static uint32_t get_modifiers(const XEvent& event) {
+ static const struct {
+ unsigned fXMask;
+ unsigned fSkMask;
+ } gModifiers[] = {
+ { ShiftMask, Window::kShift_ModifierKey },
+ { ControlMask, Window::kControl_ModifierKey },
+ { Mod1Mask, Window::kOption_ModifierKey },
+ };
+
+ auto modifiers = 0;
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) {
+ if (event.xkey.state & gModifiers[i].fXMask) {
+ modifiers |= gModifiers[i].fSkMask;
+ }
+ }
+ return modifiers;
+}
+
+bool Window_unix::handleEvent(const XEvent& event) {
+ switch (event.type) {
+ case MapNotify:
+ if (!fGC) {
+ fGC = XCreateGC(fDisplay, fWindow, 0, nullptr);
+ }
+ break;
+
+ case ClientMessage:
+ if ((Atom)event.xclient.data.l[0] == fWmDeleteMessage &&
+ gWindowMap.count() == 1) {
+ return true;
+ }
+ break;
+
+ case ButtonPress:
+ switch (event.xbutton.button) {
+ case Button1:
+ this->onMouse(event.xbutton.x, event.xbutton.y,
+ Window::kDown_InputState, get_modifiers(event));
+ break;
+ case Button4:
+ this->onMouseWheel(1.0f, get_modifiers(event));
+ break;
+ case Button5:
+ this->onMouseWheel(-1.0f, get_modifiers(event));
+ break;
+ }
+ break;
+
+ case ButtonRelease:
+ if (event.xbutton.button == Button1) {
+ this->onMouse(event.xbutton.x, event.xbutton.y,
+ Window::kUp_InputState, get_modifiers(event));
+ }
+ break;
+
+ case MotionNotify:
+ this->onMouse(event.xmotion.x, event.xmotion.y,
+ Window::kMove_InputState, get_modifiers(event));
+ break;
+
+ case KeyPress: {
+ int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
+ KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode, 0, shiftLevel);
+ Window::Key key = get_key(keysym);
+ if (key != Window::Key::kNONE) {
+ if (!this->onKey(key, Window::kDown_InputState, get_modifiers(event))) {
+ if (keysym == XK_Escape) {
+ return true;
+ }
+ }
+ }
+
+ long uni = keysym2ucs(keysym);
+ if (uni != -1) {
+ (void) this->onChar((SkUnichar) uni, get_modifiers(event));
+ }
+ } break;
+
+ case KeyRelease: {
+ int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
+ KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode,
+ 0, shiftLevel);
+ Window::Key key = get_key(keysym);
+ (void) this->onKey(key, Window::kUp_InputState,
+ get_modifiers(event));
+ } break;
+
+
+ default:
+ // these events should be handled in the main event loop
+ SkASSERT(event.type != Expose && event.type != ConfigureNotify);
+ break;
+ }
+
+ return false;
+}
+
+void Window_unix::setTitle(const char* title) {
+ XTextProperty textproperty;
+ XStringListToTextProperty(const_cast<char**>(&title), 1, &textproperty);
+ XSetWMName(fDisplay, fWindow, &textproperty);
+}
+
+void Window_unix::show() {
+ XMapWindow(fDisplay, fWindow);
+}
+
+bool Window_unix::attach(BackendType attachType) {
+ this->initWindow(fDisplay);
+
+ window_context_factory::XlibWindowInfo winInfo;
+ winInfo.fDisplay = fDisplay;
+ winInfo.fWindow = fWindow;
+ winInfo.fFBConfig = fFBConfig;
+ winInfo.fVisualInfo = fVisualInfo;
+
+ XWindowAttributes attrs;
+ if (XGetWindowAttributes(fDisplay, fWindow, &attrs)) {
+ winInfo.fWidth = attrs.width;
+ winInfo.fHeight = attrs.height;
+ } else {
+ winInfo.fWidth = winInfo.fHeight = 0;
+ }
+
+ switch (attachType) {
+#ifdef SK_VULKAN
+ case kVulkan_BackendType:
+ fWindowContext = window_context_factory::NewVulkanForXlib(winInfo,
+ fRequestedDisplayParams);
+ break;
+#endif
+ case kNativeGL_BackendType:
+ fWindowContext = window_context_factory::NewGLForXlib(winInfo, fRequestedDisplayParams);
+ break;
+ case kRaster_BackendType:
+ fWindowContext = window_context_factory::NewRasterForXlib(winInfo,
+ fRequestedDisplayParams);
+ break;
+ }
+ this->onBackendCreated();
+
+ return (SkToBool(fWindowContext));
+}
+
+void Window_unix::onInval() {
+ XEvent event;
+ event.type = Expose;
+ event.xexpose.send_event = True;
+ event.xexpose.display = fDisplay;
+ event.xexpose.window = fWindow;
+ event.xexpose.x = 0;
+ event.xexpose.y = 0;
+ event.xexpose.width = this->width();
+ event.xexpose.height = this->height();
+ event.xexpose.count = 0;
+
+ XSendEvent(fDisplay, fWindow, False, 0, &event);
+}
+
+} // namespace sk_app
diff --git a/tools/sk_app/unix/Window_unix.h b/tools/sk_app/unix/Window_unix.h
new file mode 100644
index 0000000000..b59f502eb9
--- /dev/null
+++ b/tools/sk_app/unix/Window_unix.h
@@ -0,0 +1,95 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#ifndef Window_unix_DEFINED
+#define Window_unix_DEFINED
+
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+#include "../Window.h"
+#include "SkChecksum.h"
+#include "SkTDynamicHash.h"
+
+typedef Window XWindow;
+
+namespace sk_app {
+
+class Window_unix : public Window {
+public:
+ Window_unix() : Window()
+ , fDisplay(nullptr)
+ , fWindow(0)
+ , fGC(nullptr)
+ , fFBConfig(nullptr)
+ , fVisualInfo(nullptr)
+ , fMSAASampleCount(0) {}
+ ~Window_unix() override { this->closeWindow(); }
+
+ bool initWindow(Display* display);
+
+ void setTitle(const char*) override;
+ void show() override;
+
+ bool attach(BackendType) override;
+
+ void onInval() override;
+
+ bool handleEvent(const XEvent& event);
+
+ static const XWindow& GetKey(const Window_unix& w) {
+ return w.fWindow;
+ }
+
+ static uint32_t Hash(const XWindow& w) {
+ return SkChecksum::Mix(w);
+ }
+
+ static SkTDynamicHash<Window_unix, XWindow> gWindowMap;
+
+ void markPendingPaint() { fPendingPaint = true; }
+ void finishPaint() {
+ if (fPendingPaint) {
+ this->onPaint();
+ fPendingPaint = false;
+ }
+ }
+
+ void markPendingResize(int width, int height) {
+ if (width != this->width() || height != this->height()){
+ fPendingResize = true;
+ fPendingWidth = width;
+ fPendingHeight = height;
+ }
+ }
+ void finishResize() {
+ if (fPendingResize) {
+ this->onResize(fPendingWidth, fPendingHeight);
+ fPendingResize = false;
+ }
+ }
+
+private:
+ void closeWindow();
+
+ Display* fDisplay;
+ XWindow fWindow;
+ GC fGC;
+ GLXFBConfig* fFBConfig;
+ XVisualInfo* fVisualInfo;
+ int fMSAASampleCount;
+
+ Atom fWmDeleteMessage;
+
+ bool fPendingPaint;
+ int fPendingWidth;
+ int fPendingHeight;
+ bool fPendingResize;
+};
+
+} // namespace sk_app
+
+#endif
diff --git a/tools/sk_app/unix/main_unix.cpp b/tools/sk_app/unix/main_unix.cpp
new file mode 100644
index 0000000000..4d9a64d6b6
--- /dev/null
+++ b/tools/sk_app/unix/main_unix.cpp
@@ -0,0 +1,94 @@
+/*
+
+* 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_unix.h"
+#include "../Application.h"
+
+using sk_app::Application;
+
+void finishWindow(sk_app::Window_unix* win) {
+ win->finishResize();
+ win->finishPaint();
+}
+
+int main(int argc, char**argv) {
+
+ Display* display = XOpenDisplay(nullptr);
+
+ Application* app = Application::Create(argc, argv, (void*)display);
+
+ // Get the file descriptor for the X display
+ int x11_fd = ConnectionNumber(display);
+ int count = x11_fd + 1;
+
+ SkTHashSet<sk_app::Window_unix*> pendingWindows;
+ bool done = false;
+ while (!done) {
+ // Create a file description set containing x11_fd
+ fd_set in_fds;
+ FD_ZERO(&in_fds);
+ FD_SET(x11_fd, &in_fds);
+
+ // Set a sleep timer
+ struct timeval tv;
+ tv.tv_usec = 100;
+ tv.tv_sec = 0;
+
+ while (!XPending(display)) {
+ // Wait for an event on the file descriptor or for timer expiration
+ (void) select(count, &in_fds, nullptr, nullptr, &tv);
+ }
+
+ // Handle XEvents (if any) and flush the input
+ int count = XPending(display);
+ while (count-- && !done) {
+ XEvent event;
+ XNextEvent(display, &event);
+
+ sk_app::Window_unix* win = sk_app::Window_unix::gWindowMap.find(event.xany.window);
+ if (!win) {
+ continue;
+ }
+
+ // paint and resize events get collapsed
+ switch (event.type) {
+ case Expose:
+ win->markPendingPaint();
+ pendingWindows.add(win);
+ break;
+ case ConfigureNotify:
+ win->markPendingResize(event.xconfigurerequest.width,
+ event.xconfigurerequest.height);
+ pendingWindows.add(win);
+ break;
+ default:
+ if (win->handleEvent(event)) {
+ done = true;
+ }
+ break;
+ }
+ }
+
+ pendingWindows.foreach(finishWindow);
+ if (pendingWindows.count() > 0) {
+ app->onIdle();
+ }
+ pendingWindows.reset();
+
+ XFlush(display);
+ }
+
+ delete app;
+
+ XCloseDisplay(display);
+
+ return 0;
+}