• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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