1 /* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 //#include <tchar.h> 9 10 #include "WindowContextFactory_unix.h" 11 12 #include "SkUtils.h" 13 #include "Timer.h" 14 #include "../GLWindowContext.h" 15 #include "Window_unix.h" 16 17 extern "C" { 18 #include "keysym2ucs.h" 19 } 20 #include <X11/Xutil.h> 21 #include <X11/XKBlib.h> 22 23 namespace sk_app { 24 25 SkTDynamicHash<Window_unix, XWindow> Window_unix::gWindowMap; 26 27 Window* Window::CreateNativeWindow(void* platformData) { 28 Display* display = (Display*)platformData; 29 SkASSERT(display); 30 31 Window_unix* window = new Window_unix(); 32 if (!window->initWindow(display)) { 33 delete window; 34 return nullptr; 35 } 36 37 return window; 38 } 39 40 const long kEventMask = ExposureMask | StructureNotifyMask | 41 KeyPressMask | KeyReleaseMask | 42 PointerMotionMask | ButtonPressMask | ButtonReleaseMask; 43 44 bool Window_unix::initWindow(Display* display) { 45 if (fRequestedDisplayParams.fMSAASampleCount != fMSAASampleCount) { 46 this->closeWindow(); 47 } 48 // we already have a window 49 if (fDisplay) { 50 return true; 51 } 52 fDisplay = display; 53 54 constexpr int initialWidth = 1280; 55 constexpr int initialHeight = 960; 56 57 // Attempt to create a window that supports GL 58 59 // We prefer the more recent glXChooseFBConfig but fall back to glXChooseVisual. They have 60 // slight differences in how attributes are specified. 61 static int constexpr kChooseFBConfigAtt[] = { 62 GLX_RENDER_TYPE, GLX_RGBA_BIT, 63 GLX_DOUBLEBUFFER, True, 64 GLX_STENCIL_SIZE, 8, 65 None 66 }; 67 // For some reason glXChooseVisual takes a non-const pointer to the attributes. 68 int chooseVisualAtt[] = { 69 GLX_RGBA, 70 GLX_DOUBLEBUFFER, 71 GLX_STENCIL_SIZE, 8, 72 None 73 }; 74 SkASSERT(nullptr == fVisualInfo); 75 if (fRequestedDisplayParams.fMSAASampleCount > 0) { 76 static const GLint kChooseFBConifgAttCnt = SK_ARRAY_COUNT(kChooseFBConfigAtt); 77 GLint msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 4]; 78 memcpy(msaaChooseFBConfigAtt, kChooseFBConfigAtt, sizeof(kChooseFBConfigAtt)); 79 SkASSERT(None == msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1]); 80 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB; 81 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 0] = 1; 82 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 1] = GLX_SAMPLES_ARB; 83 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 2] = fRequestedDisplayParams.fMSAASampleCount; 84 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 3] = None; 85 int n; 86 fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), msaaChooseFBConfigAtt, &n); 87 if (n > 0) { 88 fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig); 89 } else { 90 static const GLint kChooseVisualAttCnt = SK_ARRAY_COUNT(chooseVisualAtt); 91 GLint msaaChooseVisualAtt[kChooseVisualAttCnt + 4]; 92 memcpy(msaaChooseVisualAtt, chooseVisualAtt, sizeof(chooseVisualAtt)); 93 SkASSERT(None == msaaChooseVisualAtt[kChooseVisualAttCnt - 1]); 94 msaaChooseFBConfigAtt[kChooseVisualAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB; 95 msaaChooseFBConfigAtt[kChooseVisualAttCnt + 0] = 1; 96 msaaChooseFBConfigAtt[kChooseVisualAttCnt + 1] = GLX_SAMPLES_ARB; 97 msaaChooseFBConfigAtt[kChooseVisualAttCnt + 2] = 98 fRequestedDisplayParams.fMSAASampleCount; 99 msaaChooseFBConfigAtt[kChooseVisualAttCnt + 3] = None; 100 fVisualInfo = glXChooseVisual(display, DefaultScreen(display), msaaChooseVisualAtt); 101 fFBConfig = nullptr; 102 } 103 } 104 if (nullptr == fVisualInfo) { 105 int n; 106 fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), kChooseFBConfigAtt, &n); 107 if (n > 0) { 108 fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig); 109 } else { 110 fVisualInfo = glXChooseVisual(display, DefaultScreen(display), chooseVisualAtt); 111 fFBConfig = nullptr; 112 } 113 } 114 115 if (fVisualInfo) { 116 Colormap colorMap = XCreateColormap(display, 117 RootWindow(display, fVisualInfo->screen), 118 fVisualInfo->visual, 119 AllocNone); 120 XSetWindowAttributes swa; 121 swa.colormap = colorMap; 122 swa.event_mask = kEventMask; 123 fWindow = XCreateWindow(display, 124 RootWindow(display, fVisualInfo->screen), 125 0, 0, // x, y 126 initialWidth, initialHeight, 127 0, // border width 128 fVisualInfo->depth, 129 InputOutput, 130 fVisualInfo->visual, 131 CWEventMask | CWColormap, 132 &swa); 133 } else { 134 // Create a simple window instead. We will not be able to show GL 135 fWindow = XCreateSimpleWindow(display, 136 DefaultRootWindow(display), 137 0, 0, // x, y 138 initialWidth, initialHeight, 139 0, // border width 140 0, // border value 141 0); // background value 142 XSelectInput(display, fWindow, kEventMask); 143 } 144 145 if (!fWindow) { 146 return false; 147 } 148 149 fMSAASampleCount = fRequestedDisplayParams.fMSAASampleCount; 150 151 // set up to catch window delete message 152 fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); 153 XSetWMProtocols(display, fWindow, &fWmDeleteMessage, 1); 154 155 // add to hashtable of windows 156 gWindowMap.add(this); 157 158 // init event variables 159 fPendingPaint = false; 160 fPendingResize = false; 161 162 return true; 163 } 164 165 void Window_unix::closeWindow() { 166 if (fDisplay) { 167 this->detach(); 168 if (fGC) { 169 XFreeGC(fDisplay, fGC); 170 fGC = nullptr; 171 } 172 gWindowMap.remove(fWindow); 173 XDestroyWindow(fDisplay, fWindow); 174 fWindow = 0; 175 if (fFBConfig) { 176 XFree(fFBConfig); 177 fFBConfig = nullptr; 178 } 179 if (fVisualInfo) { 180 XFree(fVisualInfo); 181 fVisualInfo = nullptr; 182 } 183 fDisplay = nullptr; 184 } 185 } 186 187 static Window::Key get_key(KeySym keysym) { 188 static const struct { 189 KeySym fXK; 190 Window::Key fKey; 191 } gPair[] = { 192 { XK_BackSpace, Window::Key::kBack }, 193 { XK_Clear, Window::Key::kBack }, 194 { XK_Return, Window::Key::kOK }, 195 { XK_Up, Window::Key::kUp }, 196 { XK_Down, Window::Key::kDown }, 197 { XK_Left, Window::Key::kLeft }, 198 { XK_Right, Window::Key::kRight }, 199 { XK_Tab, Window::Key::kTab }, 200 { XK_Page_Up, Window::Key::kPageUp }, 201 { XK_Page_Down, Window::Key::kPageDown }, 202 { XK_Home, Window::Key::kHome }, 203 { XK_End, Window::Key::kEnd }, 204 { XK_Delete, Window::Key::kDelete }, 205 { XK_Escape, Window::Key::kEscape }, 206 { XK_Shift_L, Window::Key::kShift }, 207 { XK_Shift_R, Window::Key::kShift }, 208 { XK_Control_L, Window::Key::kCtrl }, 209 { XK_Control_R, Window::Key::kCtrl }, 210 { XK_Alt_L, Window::Key::kOption }, 211 { XK_Alt_R, Window::Key::kOption }, 212 { 'A', Window::Key::kA }, 213 { 'C', Window::Key::kC }, 214 { 'V', Window::Key::kV }, 215 { 'X', Window::Key::kX }, 216 { 'Y', Window::Key::kY }, 217 { 'Z', Window::Key::kZ }, 218 }; 219 for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { 220 if (gPair[i].fXK == keysym) { 221 return gPair[i].fKey; 222 } 223 } 224 return Window::Key::kNONE; 225 } 226 227 static uint32_t get_modifiers(const XEvent& event) { 228 static const struct { 229 unsigned fXMask; 230 unsigned fSkMask; 231 } gModifiers[] = { 232 { ShiftMask, Window::kShift_ModifierKey }, 233 { ControlMask, Window::kControl_ModifierKey }, 234 { Mod1Mask, Window::kOption_ModifierKey }, 235 }; 236 237 auto modifiers = 0; 238 for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) { 239 if (event.xkey.state & gModifiers[i].fXMask) { 240 modifiers |= gModifiers[i].fSkMask; 241 } 242 } 243 return modifiers; 244 } 245 246 bool Window_unix::handleEvent(const XEvent& event) { 247 switch (event.type) { 248 case MapNotify: 249 if (!fGC) { 250 fGC = XCreateGC(fDisplay, fWindow, 0, nullptr); 251 } 252 break; 253 254 case ClientMessage: 255 if ((Atom)event.xclient.data.l[0] == fWmDeleteMessage && 256 gWindowMap.count() == 1) { 257 return true; 258 } 259 break; 260 261 case ButtonPress: 262 switch (event.xbutton.button) { 263 case Button1: 264 this->onMouse(event.xbutton.x, event.xbutton.y, 265 Window::kDown_InputState, get_modifiers(event)); 266 break; 267 case Button4: 268 this->onMouseWheel(1.0f, get_modifiers(event)); 269 break; 270 case Button5: 271 this->onMouseWheel(-1.0f, get_modifiers(event)); 272 break; 273 } 274 break; 275 276 case ButtonRelease: 277 if (event.xbutton.button == Button1) { 278 this->onMouse(event.xbutton.x, event.xbutton.y, 279 Window::kUp_InputState, get_modifiers(event)); 280 } 281 break; 282 283 case MotionNotify: 284 this->onMouse(event.xmotion.x, event.xmotion.y, 285 Window::kMove_InputState, get_modifiers(event)); 286 break; 287 288 case KeyPress: { 289 int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0; 290 KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode, 0, shiftLevel); 291 Window::Key key = get_key(keysym); 292 if (key != Window::Key::kNONE) { 293 if (!this->onKey(key, Window::kDown_InputState, get_modifiers(event))) { 294 if (keysym == XK_Escape) { 295 return true; 296 } 297 } 298 } 299 300 long uni = keysym2ucs(keysym); 301 if (uni != -1) { 302 (void) this->onChar((SkUnichar) uni, get_modifiers(event)); 303 } 304 } break; 305 306 case KeyRelease: { 307 int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0; 308 KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode, 309 0, shiftLevel); 310 Window::Key key = get_key(keysym); 311 (void) this->onKey(key, Window::kUp_InputState, 312 get_modifiers(event)); 313 } break; 314 315 316 default: 317 // these events should be handled in the main event loop 318 SkASSERT(event.type != Expose && event.type != ConfigureNotify); 319 break; 320 } 321 322 return false; 323 } 324 325 void Window_unix::setTitle(const char* title) { 326 XTextProperty textproperty; 327 XStringListToTextProperty(const_cast<char**>(&title), 1, &textproperty); 328 XSetWMName(fDisplay, fWindow, &textproperty); 329 } 330 331 void Window_unix::show() { 332 XMapWindow(fDisplay, fWindow); 333 } 334 335 bool Window_unix::attach(BackendType attachType) { 336 this->initWindow(fDisplay); 337 338 window_context_factory::XlibWindowInfo winInfo; 339 winInfo.fDisplay = fDisplay; 340 winInfo.fWindow = fWindow; 341 winInfo.fFBConfig = fFBConfig; 342 winInfo.fVisualInfo = fVisualInfo; 343 344 XWindowAttributes attrs; 345 if (XGetWindowAttributes(fDisplay, fWindow, &attrs)) { 346 winInfo.fWidth = attrs.width; 347 winInfo.fHeight = attrs.height; 348 } else { 349 winInfo.fWidth = winInfo.fHeight = 0; 350 } 351 352 switch (attachType) { 353 #ifdef SK_VULKAN 354 case kVulkan_BackendType: 355 fWindowContext = window_context_factory::NewVulkanForXlib(winInfo, 356 fRequestedDisplayParams); 357 break; 358 #endif 359 case kNativeGL_BackendType: 360 fWindowContext = window_context_factory::NewGLForXlib(winInfo, fRequestedDisplayParams); 361 break; 362 case kRaster_BackendType: 363 fWindowContext = window_context_factory::NewRasterForXlib(winInfo, 364 fRequestedDisplayParams); 365 break; 366 } 367 this->onBackendCreated(); 368 369 return (SkToBool(fWindowContext)); 370 } 371 372 void Window_unix::onInval() { 373 XEvent event; 374 event.type = Expose; 375 event.xexpose.send_event = True; 376 event.xexpose.display = fDisplay; 377 event.xexpose.window = fWindow; 378 event.xexpose.x = 0; 379 event.xexpose.y = 0; 380 event.xexpose.width = this->width(); 381 event.xexpose.height = this->height(); 382 event.xexpose.count = 0; 383 384 XSendEvent(fDisplay, fWindow, False, 0, &event); 385 } 386 387 } // namespace sk_app 388