• 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 * found in the LICENSE file.
6 */
7 
8 //#include <tchar.h>
9 
10 #include "WindowContextFactory_unix.h"
11 
12 #include "SkUTF.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 
CreateNativeWindow(void * platformData)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 
initWindow(Display * display)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 > 1) {
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 
closeWindow()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 
get_key(KeySym keysym)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 
get_modifiers(const XEvent & event)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 
handleEvent(const XEvent & event)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 
setTitle(const char * title)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 
show()331 void Window_unix::show() {
332     XMapWindow(fDisplay, fWindow);
333 }
334 
attach(BackendType attachType)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 
onInval()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