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