aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/sk_app/unix/Window_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/sk_app/unix/Window_unix.cpp')
-rw-r--r--tools/sk_app/unix/Window_unix.cpp387
1 files changed, 387 insertions, 0 deletions
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