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