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