diff options
author | Brian Osman <brianosman@google.com> | 2017-11-21 13:18:02 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-11-21 18:37:19 +0000 |
commit | eff04b5ec287e0fee0d44207c10d2d11f7eade8a (patch) | |
tree | ea2cf00ea329c81611536aaa9a9f1eca47c67e9a /tools/sk_app/unix | |
parent | 2aa09dbe8aced37aa6bb285e62df45deb0e81650 (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.cpp | 149 | ||||
-rw-r--r-- | tools/sk_app/unix/RasterWindowContext_unix.cpp | 104 | ||||
-rw-r--r-- | tools/sk_app/unix/VulkanWindowContext_unix.cpp | 86 | ||||
-rw-r--r-- | tools/sk_app/unix/WindowContextFactory_unix.h | 42 | ||||
-rw-r--r-- | tools/sk_app/unix/Window_unix.cpp | 387 | ||||
-rw-r--r-- | tools/sk_app/unix/Window_unix.h | 95 | ||||
-rw-r--r-- | tools/sk_app/unix/main_unix.cpp | 94 |
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; +} |