1 //
2 // Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // X11Window.cpp: Implementation of OSWindow for X11
8
9 #include "x11/X11Window.h"
10
11 #include "aemu/base/system/System.h"
12
13 #include <assert.h>
14
15 namespace {
16
WaitForMapNotify(Display * dpy,XEvent * event,XPointer window)17 Bool WaitForMapNotify(Display *dpy, XEvent *event, XPointer window)
18 {
19 return event->type == MapNotify && event->xmap.window == reinterpret_cast<Window>(window);
20 }
21
X11CodeToKey(Display * display,unsigned int scancode)22 static Key X11CodeToKey(Display *display, unsigned int scancode)
23 {
24 int temp;
25 KeySym *keySymbols;
26 keySymbols = XGetKeyboardMapping(display, scancode, 1, &temp);
27
28 unsigned int keySymbol = keySymbols[0];
29 XFree(keySymbols);
30
31 switch (keySymbol)
32 {
33 case XK_Shift_L: return KEY_LSHIFT;
34 case XK_Shift_R: return KEY_RSHIFT;
35 case XK_Alt_L: return KEY_LALT;
36 case XK_Alt_R: return KEY_RALT;
37 case XK_Control_L: return KEY_LCONTROL;
38 case XK_Control_R: return KEY_RCONTROL;
39 case XK_Super_L: return KEY_LSYSTEM;
40 case XK_Super_R: return KEY_RSYSTEM;
41 case XK_Menu: return KEY_MENU;
42
43 case XK_semicolon: return KEY_SEMICOLON;
44 case XK_slash: return KEY_SLASH;
45 case XK_equal: return KEY_EQUAL;
46 case XK_minus: return KEY_DASH;
47 case XK_bracketleft: return KEY_LBRACKET;
48 case XK_bracketright:return KEY_RBRACKET;
49 case XK_comma: return KEY_COMMA;
50 case XK_period: return KEY_PERIOD;
51 case XK_backslash: return KEY_BACKSLASH;
52 case XK_asciitilde: return KEY_TILDE;
53 case XK_Escape: return KEY_ESCAPE;
54 case XK_space: return KEY_SPACE;
55 case XK_Return: return KEY_RETURN;
56 case XK_BackSpace: return KEY_BACK;
57 case XK_Tab: return KEY_TAB;
58 case XK_Page_Up: return KEY_PAGEUP;
59 case XK_Page_Down: return KEY_PAGEDOWN;
60 case XK_End: return KEY_END;
61 case XK_Home: return KEY_HOME;
62 case XK_Insert: return KEY_INSERT;
63 case XK_Delete: return KEY_DELETE;
64 case XK_KP_Add: return KEY_ADD;
65 case XK_KP_Subtract: return KEY_SUBTRACT;
66 case XK_KP_Multiply: return KEY_MULTIPLY;
67 case XK_KP_Divide: return KEY_DIVIDE;
68 case XK_Pause: return KEY_PAUSE;
69
70 case XK_F1: return KEY_F1;
71 case XK_F2: return KEY_F2;
72 case XK_F3: return KEY_F3;
73 case XK_F4: return KEY_F4;
74 case XK_F5: return KEY_F5;
75 case XK_F6: return KEY_F6;
76 case XK_F7: return KEY_F7;
77 case XK_F8: return KEY_F8;
78 case XK_F9: return KEY_F9;
79 case XK_F10: return KEY_F10;
80 case XK_F11: return KEY_F11;
81 case XK_F12: return KEY_F12;
82 case XK_F13: return KEY_F13;
83 case XK_F14: return KEY_F14;
84 case XK_F15: return KEY_F15;
85
86 case XK_Left: return KEY_LEFT;
87 case XK_Right: return KEY_RIGHT;
88 case XK_Down: return KEY_DOWN;
89 case XK_Up: return KEY_UP;
90
91 case XK_KP_Insert: return KEY_NUMPAD0;
92 case XK_KP_End: return KEY_NUMPAD1;
93 case XK_KP_Down: return KEY_NUMPAD2;
94 case XK_KP_Page_Down:return KEY_NUMPAD3;
95 case XK_KP_Left: return KEY_NUMPAD4;
96 case XK_KP_5: return KEY_NUMPAD5;
97 case XK_KP_Right: return KEY_NUMPAD6;
98 case XK_KP_Home: return KEY_NUMPAD7;
99 case XK_KP_Up: return KEY_NUMPAD8;
100 case XK_KP_Page_Up: return KEY_NUMPAD9;
101
102 case XK_a: return KEY_A;
103 case XK_b: return KEY_B;
104 case XK_c: return KEY_C;
105 case XK_d: return KEY_D;
106 case XK_e: return KEY_E;
107 case XK_f: return KEY_F;
108 case XK_g: return KEY_G;
109 case XK_h: return KEY_H;
110 case XK_i: return KEY_I;
111 case XK_j: return KEY_J;
112 case XK_k: return KEY_K;
113 case XK_l: return KEY_L;
114 case XK_m: return KEY_M;
115 case XK_n: return KEY_N;
116 case XK_o: return KEY_O;
117 case XK_p: return KEY_P;
118 case XK_q: return KEY_Q;
119 case XK_r: return KEY_R;
120 case XK_s: return KEY_S;
121 case XK_t: return KEY_T;
122 case XK_u: return KEY_U;
123 case XK_v: return KEY_V;
124 case XK_w: return KEY_W;
125 case XK_x: return KEY_X;
126 case XK_y: return KEY_Y;
127 case XK_z: return KEY_Z;
128
129 case XK_1: return KEY_NUM1;
130 case XK_2: return KEY_NUM2;
131 case XK_3: return KEY_NUM3;
132 case XK_4: return KEY_NUM4;
133 case XK_5: return KEY_NUM5;
134 case XK_6: return KEY_NUM6;
135 case XK_7: return KEY_NUM7;
136 case XK_8: return KEY_NUM8;
137 case XK_9: return KEY_NUM9;
138 case XK_0: return KEY_NUM0;
139 }
140
141 return Key(0);
142 }
143
AddX11KeyStateToEvent(Event * event,unsigned int state)144 static void AddX11KeyStateToEvent(Event *event, unsigned int state)
145 {
146 event->key.shift = state & ShiftMask;
147 event->key.control = state & ControlMask;
148 event->key.alt = state & Mod1Mask;
149 event->key.system = state & Mod4Mask;
150 }
151
152 }
153
X11Window()154 X11Window::X11Window()
155 : WM_DELETE_WINDOW(None),
156 WM_PROTOCOLS(None),
157 TEST_EVENT(None),
158 mDisplay(nullptr),
159 mWindow(0),
160 mRequestedVisualId(-1),
161 mVisible(false)
162 {
163 }
164
X11Window(int visualId)165 X11Window::X11Window(int visualId)
166 : WM_DELETE_WINDOW(None),
167 WM_PROTOCOLS(None),
168 TEST_EVENT(None),
169 mDisplay(nullptr),
170 mWindow(0),
171 mRequestedVisualId(visualId),
172 mVisible(false)
173 {
174 }
175
~X11Window()176 X11Window::~X11Window()
177 {
178 destroy();
179 }
180
initialize(const std::string & name,size_t width,size_t height)181 bool X11Window::initialize(const std::string &name, size_t width, size_t height)
182 {
183 destroy();
184
185 mDisplay = XOpenDisplay(nullptr);
186 if (!mDisplay)
187 {
188 return false;
189 }
190
191 {
192 int screen = DefaultScreen(mDisplay);
193 Window root = RootWindow(mDisplay, screen);
194
195 Visual *visual;
196 if (mRequestedVisualId == -1)
197 {
198 visual = DefaultVisual(mDisplay, screen);
199 }
200 else
201 {
202 XVisualInfo visualTemplate;
203 visualTemplate.visualid = mRequestedVisualId;
204
205 int numVisuals = 0;
206 XVisualInfo *visuals = XGetVisualInfo(mDisplay, VisualIDMask, &visualTemplate, &numVisuals);
207 if (numVisuals <= 0)
208 {
209 return false;
210 }
211 assert(numVisuals == 1);
212
213 visual = visuals[0].visual;
214 XFree(visuals);
215 }
216
217 int depth = DefaultDepth(mDisplay, screen);
218 Colormap colormap = XCreateColormap(mDisplay, root, visual, AllocNone);
219
220 XSetWindowAttributes attributes;
221 unsigned long attributeMask = CWBorderPixel | CWColormap | CWEventMask;
222
223 attributes.event_mask = StructureNotifyMask | PointerMotionMask | ButtonPressMask |
224 ButtonReleaseMask | FocusChangeMask | EnterWindowMask |
225 LeaveWindowMask | KeyPressMask | KeyReleaseMask;
226 attributes.border_pixel = 0;
227 attributes.colormap = colormap;
228
229 mWindow = XCreateWindow(mDisplay, root, 0, 0, width, height, 0, depth, InputOutput,
230 visual, attributeMask, &attributes);
231 XFreeColormap(mDisplay, colormap);
232 }
233
234 if (!mWindow)
235 {
236 destroy();
237 return false;
238 }
239
240 // Tell the window manager to notify us when the user wants to close the
241 // window so we can do it ourselves.
242 WM_DELETE_WINDOW = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
243 WM_PROTOCOLS = XInternAtom(mDisplay, "WM_PROTOCOLS", False);
244 if (WM_DELETE_WINDOW == None || WM_PROTOCOLS == None)
245 {
246 destroy();
247 return false;
248 }
249
250 if(XSetWMProtocols(mDisplay, mWindow, &WM_DELETE_WINDOW, 1) == 0)
251 {
252 destroy();
253 return false;
254 }
255
256 // Create an atom to identify our test event
257 TEST_EVENT = XInternAtom(mDisplay, "ANGLE_TEST_EVENT", False);
258 if (TEST_EVENT == None)
259 {
260 destroy();
261 return false;
262 }
263
264 XFlush(mDisplay);
265
266 mX = 0;
267 mY = 0;
268 mWidth = width;
269 mHeight = height;
270
271 return true;
272 }
273
destroy()274 void X11Window::destroy()
275 {
276 if (mWindow)
277 {
278 XDestroyWindow(mDisplay, mWindow);
279 mWindow = 0;
280 }
281 if (mDisplay)
282 {
283 XCloseDisplay(mDisplay);
284 mDisplay = nullptr;
285 }
286 WM_DELETE_WINDOW = None;
287 WM_PROTOCOLS = None;
288 }
289
getNativeWindow() const290 EGLNativeWindowType X11Window::getNativeWindow() const
291 {
292 return mWindow;
293 }
294
getNativeDisplay() const295 EGLNativeDisplayType X11Window::getNativeDisplay() const
296 {
297 return mDisplay;
298 }
299
getFramebufferNativeWindow() const300 void* X11Window::getFramebufferNativeWindow() const
301 {
302 int screen = DefaultScreen(mDisplay);
303 Window root = RootWindow(mDisplay, screen);
304 return (void*)(uintptr_t)root;
305 }
306
messageLoop()307 void X11Window::messageLoop()
308 {
309 int eventCount = XPending(mDisplay);
310 while (eventCount--)
311 {
312 XEvent event;
313 XNextEvent(mDisplay, &event);
314 processEvent(event);
315 }
316 }
317
setMousePosition(int x,int y)318 void X11Window::setMousePosition(int x, int y)
319 {
320 XWarpPointer(mDisplay, None, mWindow, 0, 0, 0, 0, x, y);
321 }
322
CreateOSWindow()323 OSWindow *CreateOSWindow()
324 {
325 return new X11Window();
326 }
327
setPosition(int x,int y)328 bool X11Window::setPosition(int x, int y)
329 {
330 XMoveWindow(mDisplay, mWindow, x, y);
331 XFlush(mDisplay);
332 return true;
333 }
334
resize(int width,int height)335 bool X11Window::resize(int width, int height)
336 {
337 XResizeWindow(mDisplay, mWindow, width, height);
338 XFlush(mDisplay);
339
340 // Wait until the window as actually been resized so that the code calling resize
341 // can assume the window has been resized.
342 const double kResizeWaitDelay = 0.2;
343 while (mHeight != height && mWidth != width)
344 {
345 messageLoop();
346 android::base::sleepMs(100);
347 }
348
349 return true;
350 }
351
setVisible(bool isVisible)352 void X11Window::setVisible(bool isVisible)
353 {
354 if (mVisible == isVisible)
355 {
356 return;
357 }
358
359 if (isVisible)
360 {
361 XMapWindow(mDisplay, mWindow);
362
363 // Wait until we get an event saying this window is mapped so that the
364 // code calling setVisible can assume the window is visible.
365 // This is important when creating a framebuffer as the framebuffer content
366 // is undefined when the window is not visible.
367 XEvent dummyEvent;
368 XIfEvent(mDisplay, &dummyEvent, WaitForMapNotify, reinterpret_cast<XPointer>(mWindow));
369 }
370 else
371 {
372 XUnmapWindow(mDisplay, mWindow);
373 XFlush(mDisplay);
374 }
375 mVisible = isVisible;
376 }
377
signalTestEvent()378 void X11Window::signalTestEvent()
379 {
380 XEvent event;
381 event.type = ClientMessage;
382 event.xclient.message_type = TEST_EVENT;
383 // Format needs to be valid or a BadValue is generated
384 event.xclient.format = 32;
385
386 // Hijack StructureNotifyMask as we know we will be listening for it.
387 XSendEvent(mDisplay, mWindow, False, StructureNotifyMask, &event);
388 }
389
processEvent(const XEvent & xEvent)390 void X11Window::processEvent(const XEvent &xEvent)
391 {
392 // TODO(cwallez) text events
393 switch (xEvent.type)
394 {
395 case ButtonPress:
396 {
397 Event event;
398 MouseButton button = MOUSEBUTTON_UNKNOWN;
399 int wheelY = 0;
400
401 // The mouse wheel updates are sent via button events.
402 switch (xEvent.xbutton.button)
403 {
404 case Button4:
405 wheelY = 1;
406 break;
407 case Button5:
408 wheelY = -1;
409 break;
410 case 6:
411 break;
412 case 7:
413 break;
414
415 case Button1:
416 button = MOUSEBUTTON_LEFT;
417 break;
418 case Button2:
419 button = MOUSEBUTTON_MIDDLE;
420 break;
421 case Button3:
422 button = MOUSEBUTTON_RIGHT;
423 break;
424 case 8:
425 button = MOUSEBUTTON_BUTTON4;
426 break;
427 case 9:
428 button = MOUSEBUTTON_BUTTON5;
429 break;
430
431 default:
432 break;
433 }
434
435 if (wheelY != 0)
436 {
437 event.type = Event::EVENT_MOUSE_WHEEL_MOVED;
438 event.mouseWheel.delta = wheelY;
439 pushEvent(event);
440 }
441
442 if (button != MOUSEBUTTON_UNKNOWN)
443 {
444 event.type = Event::EVENT_MOUSE_BUTTON_RELEASED;
445 event.mouseButton.button = button;
446 event.mouseButton.x = xEvent.xbutton.x;
447 event.mouseButton.y = xEvent.xbutton.y;
448 pushEvent(event);
449 }
450 }
451 break;
452
453 case ButtonRelease:
454 {
455 Event event;
456 MouseButton button = MOUSEBUTTON_UNKNOWN;
457
458 switch (xEvent.xbutton.button)
459 {
460 case Button1:
461 button = MOUSEBUTTON_LEFT;
462 break;
463 case Button2:
464 button = MOUSEBUTTON_MIDDLE;
465 break;
466 case Button3:
467 button = MOUSEBUTTON_RIGHT;
468 break;
469 case 8:
470 button = MOUSEBUTTON_BUTTON4;
471 break;
472 case 9:
473 button = MOUSEBUTTON_BUTTON5;
474 break;
475
476 default:
477 break;
478 }
479
480 if (button != MOUSEBUTTON_UNKNOWN)
481 {
482 event.type = Event::EVENT_MOUSE_BUTTON_RELEASED;
483 event.mouseButton.button = button;
484 event.mouseButton.x = xEvent.xbutton.x;
485 event.mouseButton.y = xEvent.xbutton.y;
486 pushEvent(event);
487 }
488 }
489 break;
490
491 case KeyPress:
492 {
493 Event event;
494 event.type = Event::EVENT_KEY_PRESSED;
495 event.key.code = X11CodeToKey(mDisplay, xEvent.xkey.keycode);
496 AddX11KeyStateToEvent(&event, xEvent.xkey.state);
497 pushEvent(event);
498 }
499 break;
500
501 case KeyRelease:
502 {
503 Event event;
504 event.type = Event::EVENT_KEY_RELEASED;
505 event.key.code = X11CodeToKey(mDisplay, xEvent.xkey.keycode);
506 AddX11KeyStateToEvent(&event, xEvent.xkey.state);
507 pushEvent(event);
508 }
509 break;
510
511 case EnterNotify:
512 {
513 Event event;
514 event.type = Event::EVENT_MOUSE_ENTERED;
515 pushEvent(event);
516 }
517 break;
518
519 case LeaveNotify:
520 {
521 Event event;
522 event.type = Event::EVENT_MOUSE_LEFT;
523 pushEvent(event);
524 }
525 break;
526
527 case MotionNotify:
528 {
529 Event event;
530 event.type = Event::EVENT_MOUSE_MOVED;
531 event.mouseMove.x = xEvent.xmotion.x;
532 event.mouseMove.y = xEvent.xmotion.y;
533 pushEvent(event);
534 }
535 break;
536
537 case ConfigureNotify:
538 {
539 if (xEvent.xconfigure.width != mWidth || xEvent.xconfigure.height != mHeight)
540 {
541 Event event;
542 event.type = Event::EVENT_RESIZED;
543 event.size.width = xEvent.xconfigure.width;
544 event.size.height = xEvent.xconfigure.height;
545 pushEvent(event);
546 }
547 if (xEvent.xconfigure.x != mX || xEvent.xconfigure.y != mY)
548 {
549 // Sometimes, the window manager reparents our window (for example
550 // when resizing) then the X and Y coordinates will be with respect to
551 // the new parent and not what the user wants to know. Use
552 // XTranslateCoordinates to get the coordinates on the screen.
553 int screen = DefaultScreen(mDisplay);
554 Window root = RootWindow(mDisplay, screen);
555
556 int x, y;
557 Window child;
558 XTranslateCoordinates(mDisplay, mWindow, root, 0, 0, &x, &y, &child);
559
560 if (x != mX || y != mY)
561 {
562 Event event;
563 event.type = Event::EVENT_MOVED;
564 event.move.x = x;
565 event.move.y = y;
566 pushEvent(event);
567 }
568 }
569 }
570 break;
571
572 case FocusIn:
573 if (xEvent.xfocus.mode == NotifyNormal || xEvent.xfocus.mode == NotifyWhileGrabbed)
574 {
575 Event event;
576 event.type = Event::EVENT_GAINED_FOCUS;
577 pushEvent(event);
578 }
579 break;
580
581 case FocusOut:
582 if (xEvent.xfocus.mode == NotifyNormal || xEvent.xfocus.mode == NotifyWhileGrabbed)
583 {
584 Event event;
585 event.type = Event::EVENT_LOST_FOCUS;
586 pushEvent(event);
587 }
588 break;
589
590 case DestroyNotify:
591 // We already received WM_DELETE_WINDOW
592 break;
593
594 case ClientMessage:
595 if (xEvent.xclient.message_type == WM_PROTOCOLS &&
596 static_cast<Atom>(xEvent.xclient.data.l[0]) == WM_DELETE_WINDOW)
597 {
598 Event event;
599 event.type = Event::EVENT_CLOSED;
600 pushEvent(event);
601 }
602 else if (xEvent.xclient.message_type == TEST_EVENT)
603 {
604 Event event;
605 event.type = Event::EVENT_TEST;
606 pushEvent(event);
607 }
608 break;
609 }
610 }
611