1 ////////////////////////////////////////////////////////////
2 //
3 // SFML - Simple and Fast Multimedia Library
4 // Copyright (C) 2007-2018 Laurent Gomila (laurent@sfml-dev.org)
5 //
6 // This software is provided 'as-is', without any express or implied warranty.
7 // In no event will the authors be held liable for any damages arising from the use of this software.
8 //
9 // Permission is granted to anyone to use this software for any purpose,
10 // including commercial applications, and to alter it and redistribute it freely,
11 // subject to the following restrictions:
12 //
13 // 1. The origin of this software must not be misrepresented;
14 //    you must not claim that you wrote the original software.
15 //    If you use this software in a product, an acknowledgment
16 //    in the product documentation would be appreciated but is not required.
17 //
18 // 2. Altered source versions must be plainly marked as such,
19 //    and must not be misrepresented as being the original software.
20 //
21 // 3. This notice may not be removed or altered from any source distribution.
22 //
23 ////////////////////////////////////////////////////////////
24 
25 ////////////////////////////////////////////////////////////
26 // Headers
27 ////////////////////////////////////////////////////////////
28 #include <SFML/Window/Unix/WindowImplX11.hpp>
29 #include <SFML/Window/Unix/ClipboardImpl.hpp>
30 #include <SFML/Window/Unix/Display.hpp>
31 #include <SFML/Window/Unix/InputImpl.hpp>
32 #include <SFML/System/Utf.hpp>
33 #include <SFML/System/Err.hpp>
34 #include <SFML/System/Mutex.hpp>
35 #include <SFML/System/Lock.hpp>
36 #include <SFML/System/Sleep.hpp>
37 #include <X11/Xlibint.h>
38 #include <X11/Xutil.h>
39 #include <X11/Xatom.h>
40 #include <X11/keysym.h>
41 #include <X11/extensions/Xrandr.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <unistd.h>
45 #include <libgen.h>
46 #include <fcntl.h>
47 #include <algorithm>
48 #include <vector>
49 #include <string>
50 #include <cstring>
51 
52 #ifdef SFML_OPENGL_ES
53     #include <SFML/Window/EglContext.hpp>
54     typedef sf::priv::EglContext ContextType;
55 #else
56     #include <SFML/Window/Unix/GlxContext.hpp>
57     typedef sf::priv::GlxContext ContextType;
58 #endif
59 
60 ////////////////////////////////////////////////////////////
61 // Private data
62 ////////////////////////////////////////////////////////////
63 namespace
64 {
65     sf::priv::WindowImplX11*              fullscreenWindow = NULL;
66     std::vector<sf::priv::WindowImplX11*> allWindows;
67     sf::Mutex                             allWindowsMutex;
68     sf::String                            windowManagerName;
69 
70     sf::String                            wmAbsPosGood[] = { "Enlightenment", "FVWM", "i3" };
71 
72     static const unsigned long            eventMask = FocusChangeMask      | ButtonPressMask     |
73                                                       ButtonReleaseMask    | ButtonMotionMask    |
74                                                       PointerMotionMask    | KeyPressMask        |
75                                                       KeyReleaseMask       | StructureNotifyMask |
76                                                       EnterWindowMask      | LeaveWindowMask     |
77                                                       VisibilityChangeMask | PropertyChangeMask;
78 
79     static const unsigned int             maxTrialsCount = 5;
80 
81     // Predicate we use to find key repeat events in processEvent
82     struct KeyRepeatFinder
83     {
KeyRepeatFinder__anone4cc40d30111::KeyRepeatFinder84         KeyRepeatFinder(unsigned int keycode, Time time) : keycode(keycode), time(time) {}
85 
86         // Predicate operator that checks event type, keycode and timestamp
operator ()__anone4cc40d30111::KeyRepeatFinder87         bool operator()(const XEvent& event)
88         {
89             return ((event.type == KeyPress) && (event.xkey.keycode == keycode) && (event.xkey.time - time < 2));
90         }
91 
92         unsigned int keycode;
93         Time time;
94     };
95 
96     // Filter the events received by windows (only allow those matching a specific window)
checkEvent(::Display *,XEvent * event,XPointer userData)97     Bool checkEvent(::Display*, XEvent* event, XPointer userData)
98     {
99         // Just check if the event matches the window
100         return event->xany.window == reinterpret_cast< ::Window >(userData);
101     }
102 
103     // Find the name of the current executable
findExecutableName()104     std::string findExecutableName()
105     {
106         // We use /proc/self/cmdline to get the command line
107         // the user used to invoke this instance of the application
108         int file = ::open("/proc/self/cmdline", O_RDONLY | O_NONBLOCK);
109 
110         if (file < 0)
111             return "sfml";
112 
113         std::vector<char> buffer(256, 0);
114         std::size_t offset = 0;
115         ssize_t result = 0;
116 
117         while ((result = read(file, &buffer[offset], 256)) > 0)
118         {
119             buffer.resize(buffer.size() + result, 0);
120             offset += result;
121         }
122 
123         ::close(file);
124 
125         if (offset)
126         {
127             buffer[offset] = 0;
128 
129             // Remove the path to keep the executable name only
130             return basename(&buffer[0]);
131         }
132 
133         // Default fallback name
134         return "sfml";
135     }
136 
137     // Check if Extended Window Manager Hints are supported
ewmhSupported()138     bool ewmhSupported()
139     {
140         static bool checked = false;
141         static bool ewmhSupported = false;
142 
143         if (checked)
144             return ewmhSupported;
145 
146         checked = true;
147 
148         Atom netSupportingWmCheck = sf::priv::getAtom("_NET_SUPPORTING_WM_CHECK", true);
149         Atom netSupported = sf::priv::getAtom("_NET_SUPPORTED", true);
150 
151         if (!netSupportingWmCheck || !netSupported)
152             return false;
153 
154         ::Display* display = sf::priv::OpenDisplay();
155 
156         Atom actualType;
157         int actualFormat;
158         unsigned long numItems;
159         unsigned long numBytes;
160         unsigned char* data;
161 
162         int result = XGetWindowProperty(display,
163                                         DefaultRootWindow(display),
164                                         netSupportingWmCheck,
165                                         0,
166                                         1,
167                                         False,
168                                         XA_WINDOW,
169                                         &actualType,
170                                         &actualFormat,
171                                         &numItems,
172                                         &numBytes,
173                                         &data);
174 
175         if (result != Success || actualType != XA_WINDOW || numItems != 1)
176         {
177             if (result == Success)
178                 XFree(data);
179 
180             sf::priv::CloseDisplay(display);
181             return false;
182         }
183 
184         ::Window rootWindow = *reinterpret_cast< ::Window* >(data);
185 
186         XFree(data);
187 
188         if (!rootWindow)
189         {
190             sf::priv::CloseDisplay(display);
191             return false;
192         }
193 
194         result = XGetWindowProperty(display,
195                                     rootWindow,
196                                     netSupportingWmCheck,
197                                     0,
198                                     1,
199                                     False,
200                                     XA_WINDOW,
201                                     &actualType,
202                                     &actualFormat,
203                                     &numItems,
204                                     &numBytes,
205                                     &data);
206 
207         if (result != Success || actualType != XA_WINDOW || numItems != 1)
208         {
209             if (result == Success)
210                 XFree(data);
211 
212             sf::priv::CloseDisplay(display);
213             return false;
214         }
215 
216         ::Window childWindow = *reinterpret_cast< ::Window* >(data);
217 
218         XFree(data);
219 
220         if (!childWindow)
221         {
222             sf::priv::CloseDisplay(display);
223             return false;
224         }
225 
226         // Conforming window managers should return the same window for both queries
227         if (rootWindow != childWindow)
228         {
229             sf::priv::CloseDisplay(display);
230             return false;
231         }
232 
233         ewmhSupported = true;
234 
235         // We try to get the name of the window manager
236         // for window manager specific workarounds
237         Atom netWmName = sf::priv::getAtom("_NET_WM_NAME", true);
238 
239         if (!netWmName)
240         {
241             sf::priv::CloseDisplay(display);
242             return true;
243         }
244 
245         Atom utf8StringType = sf::priv::getAtom("UTF8_STRING");
246 
247         if (!utf8StringType)
248             utf8StringType = XA_STRING;
249 
250         result = XGetWindowProperty(display,
251                                     rootWindow,
252                                     netWmName,
253                                     0,
254                                     0x7fffffff,
255                                     False,
256                                     utf8StringType,
257                                     &actualType,
258                                     &actualFormat,
259                                     &numItems,
260                                     &numBytes,
261                                     &data);
262 
263         if (actualType && numItems)
264         {
265             // It seems the wm name string reply is not necessarily
266             // null-terminated. The work around is to get its actual
267             // length to build a proper string
268             const char* begin = reinterpret_cast<const char*>(data);
269             const char* end = begin + numItems;
270             windowManagerName = sf::String::fromUtf8(begin, end);
271         }
272 
273         if (result == Success)
274             XFree(data);
275 
276         sf::priv::CloseDisplay(display);
277 
278         return true;
279     }
280 
281     // Get the parent window.
getParentWindow(::Display * disp,::Window win)282     ::Window getParentWindow(::Display* disp, ::Window win)
283     {
284         ::Window root, parent;
285         ::Window* children = NULL;
286         unsigned int numChildren;
287 
288         XQueryTree(disp, win, &root, &parent, &children, &numChildren);
289 
290         // Children information is not used, so must be freed.
291         if (children != NULL)
292             XFree(children);
293 
294         return parent;
295     }
296 
297     // Get the Frame Extents from EWMH WMs that support it.
getEWMHFrameExtents(::Display * disp,::Window win,long & xFrameExtent,long & yFrameExtent)298     bool getEWMHFrameExtents(::Display* disp, ::Window win,
299         long& xFrameExtent, long& yFrameExtent)
300     {
301         if (!ewmhSupported())
302             return false;
303 
304         Atom frameExtents = sf::priv::getAtom("_NET_FRAME_EXTENTS", true);
305 
306         if (frameExtents == None)
307             return false;
308 
309         bool gotFrameExtents = false;
310         Atom actualType;
311         int actualFormat;
312         unsigned long numItems;
313         unsigned long numBytesLeft;
314         unsigned char* data = NULL;
315 
316         int result = XGetWindowProperty(disp,
317                                         win,
318                                         frameExtents,
319                                         0,
320                                         4,
321                                         False,
322                                         XA_CARDINAL,
323                                         &actualType,
324                                         &actualFormat,
325                                         &numItems,
326                                         &numBytesLeft,
327                                         &data);
328 
329         if ((result == Success) && (actualType == XA_CARDINAL) &&
330             (actualFormat == 32) && (numItems == 4) && (numBytesLeft == 0) &&
331             (data != NULL))
332         {
333             gotFrameExtents = true;
334 
335             long* extents = (long*) data;
336 
337             xFrameExtent = extents[0]; // Left.
338             yFrameExtent = extents[2]; // Top.
339         }
340 
341         // Always free data.
342         if (data != NULL)
343             XFree(data);
344 
345         return gotFrameExtents;
346     }
347 
348     // Check if the current WM is in the list of good WMs that provide
349     // a correct absolute position for the window when queried.
isWMAbsolutePositionGood()350     bool isWMAbsolutePositionGood()
351     {
352         // This can only work with EWMH, to get the name.
353         if (!ewmhSupported())
354             return false;
355 
356         for (size_t i = 0; i < (sizeof(wmAbsPosGood) / sizeof(wmAbsPosGood[0])); i++)
357         {
358             if (wmAbsPosGood[i] == windowManagerName)
359                 return true;
360         }
361 
362         return false;
363     }
364 
keysymToSF(KeySym symbol)365     sf::Keyboard::Key keysymToSF(KeySym symbol)
366     {
367         switch (symbol)
368         {
369             case XK_Shift_L:      return sf::Keyboard::LShift;
370             case XK_Shift_R:      return sf::Keyboard::RShift;
371             case XK_Control_L:    return sf::Keyboard::LControl;
372             case XK_Control_R:    return sf::Keyboard::RControl;
373             case XK_Alt_L:        return sf::Keyboard::LAlt;
374             case XK_Alt_R:        return sf::Keyboard::RAlt;
375             case XK_Super_L:      return sf::Keyboard::LSystem;
376             case XK_Super_R:      return sf::Keyboard::RSystem;
377             case XK_Menu:         return sf::Keyboard::Menu;
378             case XK_Escape:       return sf::Keyboard::Escape;
379             case XK_semicolon:    return sf::Keyboard::Semicolon;
380             case XK_slash:        return sf::Keyboard::Slash;
381             case XK_equal:        return sf::Keyboard::Equal;
382             case XK_minus:        return sf::Keyboard::Hyphen;
383             case XK_bracketleft:  return sf::Keyboard::LBracket;
384             case XK_bracketright: return sf::Keyboard::RBracket;
385             case XK_comma:        return sf::Keyboard::Comma;
386             case XK_period:       return sf::Keyboard::Period;
387             case XK_apostrophe:   return sf::Keyboard::Quote;
388             case XK_backslash:    return sf::Keyboard::Backslash;
389             case XK_grave:        return sf::Keyboard::Tilde;
390             case XK_space:        return sf::Keyboard::Space;
391             case XK_Return:       return sf::Keyboard::Enter;
392             case XK_KP_Enter:     return sf::Keyboard::Enter;
393             case XK_BackSpace:    return sf::Keyboard::Backspace;
394             case XK_Tab:          return sf::Keyboard::Tab;
395             case XK_Prior:        return sf::Keyboard::PageUp;
396             case XK_Next:         return sf::Keyboard::PageDown;
397             case XK_End:          return sf::Keyboard::End;
398             case XK_Home:         return sf::Keyboard::Home;
399             case XK_Insert:       return sf::Keyboard::Insert;
400             case XK_Delete:       return sf::Keyboard::Delete;
401             case XK_KP_Add:       return sf::Keyboard::Add;
402             case XK_KP_Subtract:  return sf::Keyboard::Subtract;
403             case XK_KP_Multiply:  return sf::Keyboard::Multiply;
404             case XK_KP_Divide:    return sf::Keyboard::Divide;
405             case XK_Pause:        return sf::Keyboard::Pause;
406             case XK_F1:           return sf::Keyboard::F1;
407             case XK_F2:           return sf::Keyboard::F2;
408             case XK_F3:           return sf::Keyboard::F3;
409             case XK_F4:           return sf::Keyboard::F4;
410             case XK_F5:           return sf::Keyboard::F5;
411             case XK_F6:           return sf::Keyboard::F6;
412             case XK_F7:           return sf::Keyboard::F7;
413             case XK_F8:           return sf::Keyboard::F8;
414             case XK_F9:           return sf::Keyboard::F9;
415             case XK_F10:          return sf::Keyboard::F10;
416             case XK_F11:          return sf::Keyboard::F11;
417             case XK_F12:          return sf::Keyboard::F12;
418             case XK_F13:          return sf::Keyboard::F13;
419             case XK_F14:          return sf::Keyboard::F14;
420             case XK_F15:          return sf::Keyboard::F15;
421             case XK_Left:         return sf::Keyboard::Left;
422             case XK_Right:        return sf::Keyboard::Right;
423             case XK_Up:           return sf::Keyboard::Up;
424             case XK_Down:         return sf::Keyboard::Down;
425             case XK_KP_Insert:    return sf::Keyboard::Numpad0;
426             case XK_KP_End:       return sf::Keyboard::Numpad1;
427             case XK_KP_Down:      return sf::Keyboard::Numpad2;
428             case XK_KP_Page_Down: return sf::Keyboard::Numpad3;
429             case XK_KP_Left:      return sf::Keyboard::Numpad4;
430             case XK_KP_Begin:     return sf::Keyboard::Numpad5;
431             case XK_KP_Right:     return sf::Keyboard::Numpad6;
432             case XK_KP_Home:      return sf::Keyboard::Numpad7;
433             case XK_KP_Up:        return sf::Keyboard::Numpad8;
434             case XK_KP_Page_Up:   return sf::Keyboard::Numpad9;
435             case XK_a:            return sf::Keyboard::A;
436             case XK_b:            return sf::Keyboard::B;
437             case XK_c:            return sf::Keyboard::C;
438             case XK_d:            return sf::Keyboard::D;
439             case XK_e:            return sf::Keyboard::E;
440             case XK_f:            return sf::Keyboard::F;
441             case XK_g:            return sf::Keyboard::G;
442             case XK_h:            return sf::Keyboard::H;
443             case XK_i:            return sf::Keyboard::I;
444             case XK_j:            return sf::Keyboard::J;
445             case XK_k:            return sf::Keyboard::K;
446             case XK_l:            return sf::Keyboard::L;
447             case XK_m:            return sf::Keyboard::M;
448             case XK_n:            return sf::Keyboard::N;
449             case XK_o:            return sf::Keyboard::O;
450             case XK_p:            return sf::Keyboard::P;
451             case XK_q:            return sf::Keyboard::Q;
452             case XK_r:            return sf::Keyboard::R;
453             case XK_s:            return sf::Keyboard::S;
454             case XK_t:            return sf::Keyboard::T;
455             case XK_u:            return sf::Keyboard::U;
456             case XK_v:            return sf::Keyboard::V;
457             case XK_w:            return sf::Keyboard::W;
458             case XK_x:            return sf::Keyboard::X;
459             case XK_y:            return sf::Keyboard::Y;
460             case XK_z:            return sf::Keyboard::Z;
461             case XK_0:            return sf::Keyboard::Num0;
462             case XK_1:            return sf::Keyboard::Num1;
463             case XK_2:            return sf::Keyboard::Num2;
464             case XK_3:            return sf::Keyboard::Num3;
465             case XK_4:            return sf::Keyboard::Num4;
466             case XK_5:            return sf::Keyboard::Num5;
467             case XK_6:            return sf::Keyboard::Num6;
468             case XK_7:            return sf::Keyboard::Num7;
469             case XK_8:            return sf::Keyboard::Num8;
470             case XK_9:            return sf::Keyboard::Num9;
471         }
472 
473         return sf::Keyboard::Unknown;
474     }
475 }
476 
477 
478 namespace sf
479 {
480 namespace priv
481 {
482 ////////////////////////////////////////////////////////////
WindowImplX11(WindowHandle handle)483 WindowImplX11::WindowImplX11(WindowHandle handle) :
484 m_window         (0),
485 m_screen         (0),
486 m_inputMethod    (NULL),
487 m_inputContext   (NULL),
488 m_isExternal     (true),
489 m_oldVideoMode   (0),
490 m_oldRRCrtc      (0),
491 m_hiddenCursor   (0),
492 m_lastCursor     (None),
493 m_keyRepeat      (true),
494 m_previousSize   (-1, -1),
495 m_useSizeHints   (false),
496 m_fullscreen     (false),
497 m_cursorGrabbed  (false),
498 m_windowMapped   (false),
499 m_iconPixmap     (0),
500 m_iconMaskPixmap (0),
501 m_lastInputTime  (0)
502 {
503     // Open a connection with the X server
504     m_display = OpenDisplay();
505 
506     // Make sure to check for EWMH support before we do anything
507     ewmhSupported();
508 
509     m_screen = DefaultScreen(m_display);
510 
511     // Save the window handle
512     m_window = handle;
513 
514     if (m_window)
515     {
516         // Make sure the window is listening to all the required events
517         XSetWindowAttributes attributes;
518         attributes.event_mask = eventMask;
519 
520         XChangeWindowAttributes(m_display, m_window, CWEventMask, &attributes);
521 
522         // Set the WM protocols
523         setProtocols();
524 
525         // Do some common initializations
526         initialize();
527     }
528 }
529 
530 
531 ////////////////////////////////////////////////////////////
WindowImplX11(VideoMode mode,const String & title,unsigned long style,const ContextSettings & settings)532 WindowImplX11::WindowImplX11(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings) :
533 m_window         (0),
534 m_screen         (0),
535 m_inputMethod    (NULL),
536 m_inputContext   (NULL),
537 m_isExternal     (false),
538 m_oldVideoMode   (0),
539 m_oldRRCrtc      (0),
540 m_hiddenCursor   (0),
541 m_lastCursor     (None),
542 m_keyRepeat      (true),
543 m_previousSize   (-1, -1),
544 m_useSizeHints   (false),
545 m_fullscreen     ((style & Style::Fullscreen) != 0),
546 m_cursorGrabbed  (m_fullscreen),
547 m_windowMapped   (false),
548 m_iconPixmap     (0),
549 m_iconMaskPixmap (0),
550 m_lastInputTime  (0)
551 {
552     // Open a connection with the X server
553     m_display = OpenDisplay();
554 
555     // Make sure to check for EWMH support before we do anything
556     ewmhSupported();
557 
558     m_screen = DefaultScreen(m_display);
559 
560     // Compute position and size
561     Vector2i windowPosition;
562     if(m_fullscreen)
563     {
564         windowPosition = getPrimaryMonitorPosition();
565     }
566     else
567     {
568         windowPosition.x = (DisplayWidth(m_display, m_screen)  - mode.width) / 2;
569         windowPosition.y = (DisplayWidth(m_display, m_screen)  - mode.height) / 2;
570     }
571 
572     int width  = mode.width;
573     int height = mode.height;
574 
575     // Choose the visual according to the context settings
576     XVisualInfo visualInfo = ContextType::selectBestVisual(m_display, mode.bitsPerPixel, settings);
577 
578     // Define the window attributes
579     XSetWindowAttributes attributes;
580     attributes.colormap = XCreateColormap(m_display, DefaultRootWindow(m_display), visualInfo.visual, AllocNone);
581     attributes.event_mask = eventMask;
582     attributes.override_redirect = (m_fullscreen && !ewmhSupported()) ? True : False;
583 
584     m_window = XCreateWindow(m_display,
585                              DefaultRootWindow(m_display),
586                              windowPosition.x, windowPosition.y,
587                              width, height,
588                              0,
589                              visualInfo.depth,
590                              InputOutput,
591                              visualInfo.visual,
592                              CWEventMask | CWOverrideRedirect | CWColormap,
593                              &attributes);
594 
595     if (!m_window)
596     {
597         err() << "Failed to create window" << std::endl;
598         return;
599     }
600 
601     // Set the WM protocols
602     setProtocols();
603 
604     // Set the WM initial state to the normal state
605     XWMHints* hints = XAllocWMHints();
606     hints->flags         = StateHint;
607     hints->initial_state = NormalState;
608     XSetWMHints(m_display, m_window, hints);
609     XFree(hints);
610 
611     // If not in fullscreen, set the window's style (tell the window manager to
612     // change our window's decorations and functions according to the requested style)
613     if (!m_fullscreen)
614     {
615         Atom WMHintsAtom = getAtom("_MOTIF_WM_HINTS", false);
616         if (WMHintsAtom)
617         {
618             static const unsigned long MWM_HINTS_FUNCTIONS   = 1 << 0;
619             static const unsigned long MWM_HINTS_DECORATIONS = 1 << 1;
620 
621             //static const unsigned long MWM_DECOR_ALL         = 1 << 0;
622             static const unsigned long MWM_DECOR_BORDER      = 1 << 1;
623             static const unsigned long MWM_DECOR_RESIZEH     = 1 << 2;
624             static const unsigned long MWM_DECOR_TITLE       = 1 << 3;
625             static const unsigned long MWM_DECOR_MENU        = 1 << 4;
626             static const unsigned long MWM_DECOR_MINIMIZE    = 1 << 5;
627             static const unsigned long MWM_DECOR_MAXIMIZE    = 1 << 6;
628 
629             //static const unsigned long MWM_FUNC_ALL          = 1 << 0;
630             static const unsigned long MWM_FUNC_RESIZE       = 1 << 1;
631             static const unsigned long MWM_FUNC_MOVE         = 1 << 2;
632             static const unsigned long MWM_FUNC_MINIMIZE     = 1 << 3;
633             static const unsigned long MWM_FUNC_MAXIMIZE     = 1 << 4;
634             static const unsigned long MWM_FUNC_CLOSE        = 1 << 5;
635 
636             struct WMHints
637             {
638                 unsigned long flags;
639                 unsigned long functions;
640                 unsigned long decorations;
641                 long          inputMode;
642                 unsigned long state;
643             };
644 
645             WMHints hints;
646             std::memset(&hints, 0, sizeof(hints));
647             hints.flags       = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
648             hints.decorations = 0;
649             hints.functions   = 0;
650 
651             if (style & Style::Titlebar)
652             {
653                 hints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_MINIMIZE | MWM_DECOR_MENU;
654                 hints.functions   |= MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE;
655             }
656             if (style & Style::Resize)
657             {
658                 hints.decorations |= MWM_DECOR_MAXIMIZE | MWM_DECOR_RESIZEH;
659                 hints.functions   |= MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE;
660             }
661             if (style & Style::Close)
662             {
663                 hints.decorations |= 0;
664                 hints.functions   |= MWM_FUNC_CLOSE;
665             }
666 
667             XChangeProperty(m_display,
668                             m_window,
669                             WMHintsAtom,
670                             WMHintsAtom,
671                             32,
672                             PropModeReplace,
673                             reinterpret_cast<const unsigned char*>(&hints),
674                             5);
675         }
676     }
677 
678     // This is a hack to force some windows managers to disable resizing
679     if (!(style & Style::Resize))
680     {
681         m_useSizeHints = true;
682         XSizeHints* sizeHints = XAllocSizeHints();
683         sizeHints->flags = PMinSize | PMaxSize | USPosition;
684         sizeHints->min_width = sizeHints->max_width = width;
685         sizeHints->min_height = sizeHints->max_height = height;
686         sizeHints->x = windowPosition.x;
687         sizeHints->y = windowPosition.y;
688         XSetWMNormalHints(m_display, m_window, sizeHints);
689         XFree(sizeHints);
690     }
691 
692     // Set the window's WM class (this can be used by window managers)
693     XClassHint* hint = XAllocClassHint();
694 
695     // The instance name should be something unique to this invocation
696     // of the application but is rarely if ever used these days.
697     // For simplicity, we retrieve it via the base executable name.
698     std::string executableName = findExecutableName();
699     std::vector<char> windowInstance(executableName.size() + 1, 0);
700     std::copy(executableName.begin(), executableName.end(), windowInstance.begin());
701     hint->res_name = &windowInstance[0];
702 
703     // The class name identifies a class of windows that
704     // "are of the same type". We simply use the initial window name as
705     // the class name.
706     std::string ansiTitle = title.toAnsiString();
707     std::vector<char> windowClass(ansiTitle.size() + 1, 0);
708     std::copy(ansiTitle.begin(), ansiTitle.end(), windowClass.begin());
709     hint->res_class = &windowClass[0];
710 
711     XSetClassHint(m_display, m_window, hint);
712 
713     XFree(hint);
714 
715     // Set the window's name
716     setTitle(title);
717 
718     // Do some common initializations
719     initialize();
720 
721     // Set fullscreen video mode and switch to fullscreen if necessary
722     if (m_fullscreen)
723     {
724         // Disable hint for min and max size,
725         // otherwise some windows managers will not remove window decorations
726         XSizeHints *sizeHints = XAllocSizeHints();
727         long flags = 0;
728         XGetWMNormalHints(m_display, m_window, sizeHints, &flags);
729         sizeHints->flags &= ~(PMinSize | PMaxSize);
730         XSetWMNormalHints(m_display, m_window, sizeHints);
731         XFree(sizeHints);
732 
733         setVideoMode(mode);
734         switchToFullscreen();
735     }
736 }
737 
738 
739 ////////////////////////////////////////////////////////////
~WindowImplX11()740 WindowImplX11::~WindowImplX11()
741 {
742     // Cleanup graphical resources
743     cleanup();
744 
745     // Destroy icon pixmap
746     if (m_iconPixmap)
747         XFreePixmap(m_display, m_iconPixmap);
748 
749     // Destroy icon mask pixmap
750     if (m_iconMaskPixmap)
751         XFreePixmap(m_display, m_iconMaskPixmap);
752 
753     // Destroy the cursor
754     if (m_hiddenCursor)
755         XFreeCursor(m_display, m_hiddenCursor);
756 
757     // Destroy the input context
758     if (m_inputContext)
759         XDestroyIC(m_inputContext);
760 
761     // Destroy the window
762     if (m_window && !m_isExternal)
763     {
764         XDestroyWindow(m_display, m_window);
765         XFlush(m_display);
766     }
767 
768     // Close the input method
769     if (m_inputMethod)
770         XCloseIM(m_inputMethod);
771 
772     // Close the connection with the X server
773     CloseDisplay(m_display);
774 
775     // Remove this window from the global list of windows (required for focus request)
776     Lock lock(allWindowsMutex);
777     allWindows.erase(std::find(allWindows.begin(), allWindows.end(), this));
778 }
779 
780 
781 ////////////////////////////////////////////////////////////
getSystemHandle() const782 WindowHandle WindowImplX11::getSystemHandle() const
783 {
784     return m_window;
785 }
786 
787 
788 ////////////////////////////////////////////////////////////
processEvents()789 void WindowImplX11::processEvents()
790 {
791     XEvent event;
792 
793     // Pick out the events that are interesting for this window
794     while (XCheckIfEvent(m_display, &event, &checkEvent, reinterpret_cast<XPointer>(m_window)))
795         m_events.push_back(event);
796 
797     // Handle the events for this window that we just picked out
798     while (!m_events.empty())
799     {
800         event = m_events.front();
801         m_events.pop_front();
802         processEvent(event);
803     }
804 
805     // Process clipboard window events
806     priv::ClipboardImpl::processEvents();
807 }
808 
809 
810 ////////////////////////////////////////////////////////////
getPosition() const811 Vector2i WindowImplX11::getPosition() const
812 {
813     // Get absolute position of our window relative to root window. This
814     // takes into account all information that X11 has, including X11
815     // border widths and any decorations. It corresponds to where the
816     // window actually is, but not necessarily to where we told it to
817     // go using setPosition() and XMoveWindow(). To have the two match
818     // as expected, we may have to subtract decorations and borders.
819     ::Window child;
820     int xAbsRelToRoot, yAbsRelToRoot;
821 
822     XTranslateCoordinates(m_display, m_window, DefaultRootWindow(m_display),
823         0, 0, &xAbsRelToRoot, &yAbsRelToRoot, &child);
824 
825     // CASE 1: some rare WMs actually put the window exactly where we tell
826     // it to, even with decorations and such, which get shifted back.
827     // In these rare cases, we can use the absolute value directly.
828     if (isWMAbsolutePositionGood())
829         return Vector2i(xAbsRelToRoot, yAbsRelToRoot);
830 
831     // CASE 2: most modern WMs support EWMH and can define _NET_FRAME_EXTENTS
832     // with the exact frame size to subtract, so if present, we prefer it and
833     // query it first. According to spec, this already includes any borders.
834     long xFrameExtent, yFrameExtent;
835 
836     if (getEWMHFrameExtents(m_display, m_window, xFrameExtent, yFrameExtent))
837     {
838         // Get final X/Y coordinates: subtract EWMH frame extents from
839         // absolute window position.
840         return Vector2i((xAbsRelToRoot - xFrameExtent), (yAbsRelToRoot - yFrameExtent));
841     }
842 
843     // CASE 3: EWMH frame extents were not available, use geometry.
844     // We climb back up to the window before the root and use its
845     // geometry information to extract X/Y position. This because
846     // re-parenting WMs may re-parent the window multiple times, so
847     // we'd have to climb up to the furthest ancestor and sum the
848     // relative differences and borders anyway; and doing that to
849     // subtract those values from the absolute coordinates of the
850     // window is equivalent to going up the tree and asking the
851     // furthest ancestor what it's relative distance to the root is.
852     // So we use that approach because it's simpler.
853     // This approach assumes that any window between the root and
854     // our window is part of decorations/borders in some way. This
855     // seems to hold true for most reasonable WM implementations.
856     ::Window ancestor = m_window;
857     ::Window root = DefaultRootWindow(m_display);
858 
859     while (getParentWindow(m_display, ancestor) != root)
860     {
861         // Next window up (parent window).
862         ancestor = getParentWindow(m_display, ancestor);
863     }
864 
865     // Get final X/Y coordinates: take the relative position to
866     // the root of the furthest ancestor window.
867     int xRelToRoot, yRelToRoot;
868     unsigned int width, height, borderWidth, depth;
869 
870     XGetGeometry(m_display, ancestor, &root, &xRelToRoot, &yRelToRoot,
871         &width, &height, &borderWidth, &depth);
872 
873     return Vector2i(xRelToRoot, yRelToRoot);
874 }
875 
876 
877 ////////////////////////////////////////////////////////////
setPosition(const Vector2i & position)878 void WindowImplX11::setPosition(const Vector2i& position)
879 {
880     XMoveWindow(m_display, m_window, position.x, position.y);
881     XFlush(m_display);
882 }
883 
884 
885 ////////////////////////////////////////////////////////////
getSize() const886 Vector2u WindowImplX11::getSize() const
887 {
888     XWindowAttributes attributes;
889     XGetWindowAttributes(m_display, m_window, &attributes);
890     return Vector2u(attributes.width, attributes.height);
891 }
892 
893 
894 ////////////////////////////////////////////////////////////
setSize(const Vector2u & size)895 void WindowImplX11::setSize(const Vector2u& size)
896 {
897     // If resizing is disable for the window we have to update the size hints (required by some window managers).
898     if (m_useSizeHints)
899     {
900         XSizeHints* sizeHints = XAllocSizeHints();
901         sizeHints->flags = PMinSize | PMaxSize;
902         sizeHints->min_width = sizeHints->max_width = size.x;
903         sizeHints->min_height = sizeHints->max_height = size.y;
904         XSetWMNormalHints(m_display, m_window, sizeHints);
905         XFree(sizeHints);
906     }
907 
908     XResizeWindow(m_display, m_window, size.x, size.y);
909     XFlush(m_display);
910 }
911 
912 
913 ////////////////////////////////////////////////////////////
setTitle(const String & title)914 void WindowImplX11::setTitle(const String& title)
915 {
916     // Bare X11 has no Unicode window title support.
917     // There is however an option to tell the window manager your Unicode title via hints.
918 
919     // Convert to UTF-8 encoding.
920     std::basic_string<Uint8> utf8Title;
921     Utf32::toUtf8(title.begin(), title.end(), std::back_inserter(utf8Title));
922 
923     Atom useUtf8 = getAtom("UTF8_STRING", false);
924 
925     // Set the _NET_WM_NAME atom, which specifies a UTF-8 encoded window title.
926     Atom wmName = getAtom("_NET_WM_NAME", false);
927     XChangeProperty(m_display, m_window, wmName, useUtf8, 8,
928                     PropModeReplace, utf8Title.c_str(), utf8Title.size());
929 
930     // Set the _NET_WM_ICON_NAME atom, which specifies a UTF-8 encoded window title.
931     Atom wmIconName = getAtom("_NET_WM_ICON_NAME", false);
932     XChangeProperty(m_display, m_window, wmIconName, useUtf8, 8,
933                     PropModeReplace, utf8Title.c_str(), utf8Title.size());
934 
935     // Set the non-Unicode title as a fallback for window managers who don't support _NET_WM_NAME.
936     #ifdef X_HAVE_UTF8_STRING
937     Xutf8SetWMProperties(m_display,
938                          m_window,
939                          title.toAnsiString().c_str(),
940                          title.toAnsiString().c_str(),
941                          NULL,
942                          0,
943                          NULL,
944                          NULL,
945                          NULL);
946     #else
947     XmbSetWMProperties(m_display,
948                        m_window,
949                        title.toAnsiString().c_str(),
950                        title.toAnsiString().c_str(),
951                        NULL,
952                        0,
953                        NULL,
954                        NULL,
955                        NULL);
956     #endif
957 }
958 
959 
960 ////////////////////////////////////////////////////////////
setIcon(unsigned int width,unsigned int height,const Uint8 * pixels)961 void WindowImplX11::setIcon(unsigned int width, unsigned int height, const Uint8* pixels)
962 {
963     // X11 wants BGRA pixels: swap red and blue channels
964     // Note: this memory will be freed by XDestroyImage
965     Uint8* iconPixels = static_cast<Uint8*>(std::malloc(width * height * 4));
966     for (std::size_t i = 0; i < width * height; ++i)
967     {
968         iconPixels[i * 4 + 0] = pixels[i * 4 + 2];
969         iconPixels[i * 4 + 1] = pixels[i * 4 + 1];
970         iconPixels[i * 4 + 2] = pixels[i * 4 + 0];
971         iconPixels[i * 4 + 3] = pixels[i * 4 + 3];
972     }
973 
974     // Create the icon pixmap
975     Visual*      defVisual = DefaultVisual(m_display, m_screen);
976     unsigned int defDepth  = DefaultDepth(m_display, m_screen);
977     XImage* iconImage = XCreateImage(m_display, defVisual, defDepth, ZPixmap, 0, (char*)iconPixels, width, height, 32, 0);
978     if (!iconImage)
979     {
980         err() << "Failed to set the window's icon" << std::endl;
981         return;
982     }
983 
984     if (m_iconPixmap)
985         XFreePixmap(m_display, m_iconPixmap);
986 
987     if (m_iconMaskPixmap)
988         XFreePixmap(m_display, m_iconMaskPixmap);
989 
990     m_iconPixmap = XCreatePixmap(m_display, RootWindow(m_display, m_screen), width, height, defDepth);
991     XGCValues values;
992     GC iconGC = XCreateGC(m_display, m_iconPixmap, 0, &values);
993     XPutImage(m_display, m_iconPixmap, iconGC, iconImage, 0, 0, 0, 0, width, height);
994     XFreeGC(m_display, iconGC);
995     XDestroyImage(iconImage);
996 
997     // Create the mask pixmap (must have 1 bit depth)
998     std::size_t pitch = (width + 7) / 8;
999     std::vector<Uint8> maskPixels(pitch * height, 0);
1000     for (std::size_t j = 0; j < height; ++j)
1001     {
1002         for (std::size_t i = 0; i < pitch; ++i)
1003         {
1004             for (std::size_t k = 0; k < 8; ++k)
1005             {
1006                 if (i * 8 + k < width)
1007                 {
1008                     Uint8 opacity = (pixels[(i * 8 + k + j * width) * 4 + 3] > 0) ? 1 : 0;
1009                     maskPixels[i + j * pitch] |= (opacity << k);
1010                 }
1011             }
1012         }
1013     }
1014     m_iconMaskPixmap = XCreatePixmapFromBitmapData(m_display, m_window, (char*)&maskPixels[0], width, height, 1, 0, 1);
1015 
1016     // Send our new icon to the window through the WMHints
1017     XWMHints* hints = XAllocWMHints();
1018     hints->flags       = IconPixmapHint | IconMaskHint;
1019     hints->icon_pixmap = m_iconPixmap;
1020     hints->icon_mask   = m_iconMaskPixmap;
1021     XSetWMHints(m_display, m_window, hints);
1022     XFree(hints);
1023 
1024     // ICCCM wants BGRA pixels: swap red and blue channels
1025     // ICCCM also wants the first 2 unsigned 32-bit values to be width and height
1026     std::vector<unsigned long> icccmIconPixels(2 + width * height, 0);
1027     unsigned long* ptr = &icccmIconPixels[0];
1028 
1029     *ptr++ = width;
1030     *ptr++ = height;
1031 
1032     for (std::size_t i = 0; i < width * height; ++i)
1033     {
1034         *ptr++ = (pixels[i * 4 + 2] << 0 ) |
1035                  (pixels[i * 4 + 1] << 8 ) |
1036                  (pixels[i * 4 + 0] << 16) |
1037                  (pixels[i * 4 + 3] << 24);
1038     }
1039 
1040     Atom netWmIcon = getAtom("_NET_WM_ICON");
1041 
1042     XChangeProperty(m_display,
1043                     m_window,
1044                     netWmIcon,
1045                     XA_CARDINAL,
1046                     32,
1047                     PropModeReplace,
1048                     reinterpret_cast<const unsigned char*>(&icccmIconPixels[0]),
1049                     2 + width * height);
1050 
1051     XFlush(m_display);
1052 }
1053 
1054 
1055 ////////////////////////////////////////////////////////////
setVisible(bool visible)1056 void WindowImplX11::setVisible(bool visible)
1057 {
1058     if (visible)
1059     {
1060         XMapWindow(m_display, m_window);
1061 
1062         if(m_fullscreen)
1063             switchToFullscreen();
1064 
1065         XFlush(m_display);
1066 
1067         // Before continuing, make sure the WM has
1068         // internally marked the window as viewable
1069         while (!m_windowMapped && !m_isExternal)
1070             processEvents();
1071     }
1072     else
1073     {
1074         XUnmapWindow(m_display, m_window);
1075 
1076         XFlush(m_display);
1077 
1078         // Before continuing, make sure the WM has
1079         // internally marked the window as unviewable
1080         while (m_windowMapped && !m_isExternal)
1081             processEvents();
1082     }
1083 }
1084 
1085 
1086 ////////////////////////////////////////////////////////////
setMouseCursorVisible(bool visible)1087 void WindowImplX11::setMouseCursorVisible(bool visible)
1088 {
1089     XDefineCursor(m_display, m_window, visible ? m_lastCursor : m_hiddenCursor);
1090     XFlush(m_display);
1091 }
1092 
1093 
1094 ////////////////////////////////////////////////////////////
setMouseCursor(const CursorImpl & cursor)1095 void WindowImplX11::setMouseCursor(const CursorImpl& cursor)
1096 {
1097     m_lastCursor = cursor.m_cursor;
1098     XDefineCursor(m_display, m_window, m_lastCursor);
1099 }
1100 
1101 
1102 ////////////////////////////////////////////////////////////
setMouseCursorGrabbed(bool grabbed)1103 void WindowImplX11::setMouseCursorGrabbed(bool grabbed)
1104 {
1105     // This has no effect in fullscreen mode
1106     if (m_fullscreen || (m_cursorGrabbed == grabbed))
1107         return;
1108 
1109     if (grabbed)
1110     {
1111         // Try multiple times to grab the cursor
1112         for (unsigned int trial = 0; trial < maxTrialsCount; ++trial)
1113         {
1114             int result = XGrabPointer(m_display, m_window, True, None, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime);
1115 
1116             if (result == GrabSuccess)
1117             {
1118                 m_cursorGrabbed = true;
1119                 break;
1120             }
1121 
1122             // The cursor grab failed, trying again after a small sleep
1123             sf::sleep(sf::milliseconds(50));
1124         }
1125 
1126         if (!m_cursorGrabbed)
1127             err() << "Failed to grab mouse cursor" << std::endl;
1128     }
1129     else
1130     {
1131         XUngrabPointer(m_display, CurrentTime);
1132     }
1133 }
1134 
1135 
1136 ////////////////////////////////////////////////////////////
setKeyRepeatEnabled(bool enabled)1137 void WindowImplX11::setKeyRepeatEnabled(bool enabled)
1138 {
1139     m_keyRepeat = enabled;
1140 }
1141 
1142 
1143 ////////////////////////////////////////////////////////////
requestFocus()1144 void WindowImplX11::requestFocus()
1145 {
1146     // Focus is only stolen among SFML windows, not between applications
1147     // Check the global list of windows to find out whether an SFML window has the focus
1148     // Note: can't handle console and other non-SFML windows belonging to the application.
1149     bool sfmlWindowFocused = false;
1150 
1151     {
1152         Lock lock(allWindowsMutex);
1153         for (std::vector<WindowImplX11*>::iterator itr = allWindows.begin(); itr != allWindows.end(); ++itr)
1154         {
1155             if ((*itr)->hasFocus())
1156             {
1157                 sfmlWindowFocused = true;
1158                 break;
1159             }
1160         }
1161     }
1162 
1163     // Check if window is viewable (not on other desktop, ...)
1164     // TODO: Check also if minimized
1165     XWindowAttributes attributes;
1166     if (XGetWindowAttributes(m_display, m_window, &attributes) == 0)
1167     {
1168         sf::err() << "Failed to check if window is viewable while requesting focus" << std::endl;
1169         return; // error getting attribute
1170     }
1171 
1172     bool windowViewable = (attributes.map_state == IsViewable);
1173 
1174     if (sfmlWindowFocused && windowViewable)
1175     {
1176         // Another SFML window of this application has the focus and the current window is viewable:
1177         // steal focus (i.e. bring window to the front and give it input focus)
1178         grabFocus();
1179     }
1180     else
1181     {
1182         // Otherwise: display urgency hint (flashing application logo)
1183         // Ensure WM hints exist, allocate if necessary
1184         XWMHints* hints = XGetWMHints(m_display, m_window);
1185         if (hints == NULL)
1186             hints = XAllocWMHints();
1187 
1188         // Add urgency (notification) flag to hints
1189         hints->flags |= XUrgencyHint;
1190         XSetWMHints(m_display, m_window, hints);
1191         XFree(hints);
1192     }
1193 }
1194 
1195 
1196 ////////////////////////////////////////////////////////////
hasFocus() const1197 bool WindowImplX11::hasFocus() const
1198 {
1199     ::Window focusedWindow = 0;
1200     int revertToReturn = 0;
1201     XGetInputFocus(m_display, &focusedWindow, &revertToReturn);
1202 
1203     return (m_window == focusedWindow);
1204 }
1205 
1206 
1207 ////////////////////////////////////////////////////////////
grabFocus()1208 void WindowImplX11::grabFocus()
1209 {
1210     Atom netActiveWindow = None;
1211 
1212     if (ewmhSupported())
1213         netActiveWindow = getAtom("_NET_ACTIVE_WINDOW");
1214 
1215     // Only try to grab focus if the window is mapped
1216     XWindowAttributes attr;
1217 
1218     XGetWindowAttributes(m_display, m_window, &attr);
1219 
1220     if (attr.map_state == IsUnmapped)
1221         return;
1222 
1223     if (netActiveWindow)
1224     {
1225         XEvent event;
1226         std::memset(&event, 0, sizeof(event));
1227 
1228         event.type = ClientMessage;
1229         event.xclient.window = m_window;
1230         event.xclient.format = 32;
1231         event.xclient.message_type = netActiveWindow;
1232         event.xclient.data.l[0] = 1; // Normal application
1233         event.xclient.data.l[1] = m_lastInputTime;
1234         event.xclient.data.l[2] = 0; // We don't know the currently active window
1235 
1236         int result = XSendEvent(m_display,
1237                                 DefaultRootWindow(m_display),
1238                                 False,
1239                                 SubstructureNotifyMask | SubstructureRedirectMask,
1240                                 &event);
1241 
1242         XFlush(m_display);
1243 
1244         if (!result)
1245             err() << "Setting fullscreen failed, could not send \"_NET_ACTIVE_WINDOW\" event" << std::endl;
1246     }
1247     else
1248     {
1249         XRaiseWindow(m_display, m_window);
1250         XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime);
1251         XFlush(m_display);
1252     }
1253 }
1254 
1255 
1256 ////////////////////////////////////////////////////////////
setVideoMode(const VideoMode & mode)1257 void WindowImplX11::setVideoMode(const VideoMode& mode)
1258 {
1259     // Skip mode switching if the new mode is equal to the desktop mode
1260     if (mode == VideoMode::getDesktopMode())
1261         return;
1262 
1263     // Check if the XRandR extension is present
1264     int xRandRMajor, xRandRMinor;
1265     if (!checkXRandR(xRandRMajor, xRandRMinor))
1266     {
1267         // XRandR extension is not supported: we cannot use fullscreen mode
1268         err() << "Fullscreen is not supported, switching to window mode" << std::endl;
1269         return;
1270     }
1271 
1272     // Get root window
1273     ::Window rootWindow = RootWindow(m_display, m_screen);
1274 
1275     // Get the screen resources
1276     XRRScreenResources* res = XRRGetScreenResources(m_display, rootWindow);
1277     if (!res)
1278     {
1279         err() << "Failed to get the current screen resources for fullscreen mode, switching to window mode" << std::endl;
1280         return;
1281     }
1282 
1283     RROutput output = getOutputPrimary(rootWindow, res, xRandRMajor, xRandRMinor);
1284 
1285     // Get output info from output
1286     XRROutputInfo* outputInfo = XRRGetOutputInfo(m_display, res, output);
1287     if (!outputInfo || outputInfo->connection == RR_Disconnected)
1288     {
1289         XRRFreeScreenResources(res);
1290 
1291         // If outputInfo->connection == RR_Disconnected, free output info
1292         if (outputInfo)
1293             XRRFreeOutputInfo(outputInfo);
1294 
1295         err() << "Failed to get output info for fullscreen mode, switching to window mode" << std::endl;
1296         return;
1297     }
1298 
1299     // Retreive current RRMode, screen position and rotation
1300     XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, outputInfo->crtc);
1301     if (!crtcInfo)
1302     {
1303         XRRFreeScreenResources(res);
1304         XRRFreeOutputInfo(outputInfo);
1305         err() << "Failed to get crtc info for fullscreen mode, switching to window mode" << std::endl;
1306         return;
1307     }
1308 
1309     // Find RRMode to set
1310     bool modeFound = false;
1311     RRMode xRandMode;
1312 
1313     for (int i = 0; (i < res->nmode) && !modeFound; i++)
1314     {
1315         if (crtcInfo->rotation == RR_Rotate_90 || crtcInfo->rotation == RR_Rotate_270)
1316             std::swap(res->modes[i].height, res->modes[i].width);
1317 
1318         // Check if screen size match
1319         if (res->modes[i].width == static_cast<int>(mode.width) &&
1320             res->modes[i].height == static_cast<int>(mode.height))
1321         {
1322             xRandMode = res->modes[i].id;
1323             modeFound = true;
1324         }
1325     }
1326 
1327     if (!modeFound)
1328     {
1329         XRRFreeScreenResources(res);
1330         XRRFreeOutputInfo(outputInfo);
1331         err() << "Failed to find a matching RRMode for fullscreen mode, switching to window mode" << std::endl;
1332         return;
1333     }
1334 
1335     // Save the current video mode before we switch to fullscreen
1336     m_oldVideoMode = crtcInfo->mode;
1337     m_oldRRCrtc = outputInfo->crtc;
1338 
1339     // Switch to fullscreen mode
1340     XRRSetCrtcConfig(m_display,
1341                      res,
1342                      outputInfo->crtc,
1343                      CurrentTime,
1344                      crtcInfo->x,
1345                      crtcInfo->y,
1346                      xRandMode,
1347                      crtcInfo->rotation,
1348                      &output,
1349                      1);
1350 
1351     // Set "this" as the current fullscreen window
1352     fullscreenWindow = this;
1353 
1354     XRRFreeScreenResources(res);
1355     XRRFreeOutputInfo(outputInfo);
1356     XRRFreeCrtcInfo(crtcInfo);
1357 }
1358 
1359 
1360 ////////////////////////////////////////////////////////////
resetVideoMode()1361 void WindowImplX11::resetVideoMode()
1362 {
1363     if (fullscreenWindow == this)
1364     {
1365         // Try to set old configuration
1366         // Check if the XRandR extension
1367         int xRandRMajor, xRandRMinor;
1368         if (checkXRandR(xRandRMajor, xRandRMinor))
1369         {
1370             XRRScreenResources* res = XRRGetScreenResources(m_display, DefaultRootWindow(m_display));
1371             if (!res)
1372             {
1373                 err() << "Failed to get the current screen resources to reset the video mode" << std::endl;
1374                 return;
1375             }
1376 
1377             // Retreive current screen position and rotation
1378             XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, m_oldRRCrtc);
1379             if (!crtcInfo)
1380             {
1381                 XRRFreeScreenResources(res);
1382                 err() << "Failed to get crtc info to reset the video mode" << std::endl;
1383                 return;
1384             }
1385 
1386             RROutput output;
1387 
1388             // if version >= 1.3 get the primary screen else take the first screen
1389             if ((xRandRMajor == 1 && xRandRMinor >= 3) || xRandRMajor > 1)
1390             {
1391                 output = XRRGetOutputPrimary(m_display, DefaultRootWindow(m_display));
1392 
1393                 // Check if returned output is valid, otherwise use the first screen
1394                 if (output == None)
1395                     output = res->outputs[0];
1396             }
1397             else{
1398                 output = res->outputs[0];
1399             }
1400 
1401             XRRSetCrtcConfig(m_display,
1402                              res,
1403                              m_oldRRCrtc,
1404                              CurrentTime,
1405                              crtcInfo->x,
1406                              crtcInfo->y,
1407                              m_oldVideoMode,
1408                              crtcInfo->rotation,
1409                              &output,
1410                              1);
1411 
1412             XRRFreeCrtcInfo(crtcInfo);
1413             XRRFreeScreenResources(res);
1414         }
1415 
1416         // Reset the fullscreen window
1417         fullscreenWindow = NULL;
1418     }
1419 }
1420 
1421 
1422 ////////////////////////////////////////////////////////////
switchToFullscreen()1423 void WindowImplX11::switchToFullscreen()
1424 {
1425     grabFocus();
1426 
1427     if (ewmhSupported())
1428     {
1429         Atom netWmBypassCompositor = getAtom("_NET_WM_BYPASS_COMPOSITOR");
1430 
1431         if (netWmBypassCompositor)
1432         {
1433             static const unsigned long bypassCompositor = 1;
1434 
1435             XChangeProperty(m_display,
1436                             m_window,
1437                             netWmBypassCompositor,
1438                             XA_CARDINAL,
1439                             32,
1440                             PropModeReplace,
1441                             reinterpret_cast<const unsigned char*>(&bypassCompositor),
1442                             1);
1443         }
1444 
1445         Atom netWmState = getAtom("_NET_WM_STATE", true);
1446         Atom netWmStateFullscreen = getAtom("_NET_WM_STATE_FULLSCREEN", true);
1447 
1448         if (!netWmState || !netWmStateFullscreen)
1449         {
1450             err() << "Setting fullscreen failed. Could not get required atoms" << std::endl;
1451             return;
1452         }
1453 
1454         XEvent event;
1455         std::memset(&event, 0, sizeof(event));
1456 
1457         event.type = ClientMessage;
1458         event.xclient.window = m_window;
1459         event.xclient.format = 32;
1460         event.xclient.message_type = netWmState;
1461         event.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
1462         event.xclient.data.l[1] = netWmStateFullscreen;
1463         event.xclient.data.l[2] = 0; // No second property
1464         event.xclient.data.l[3] = 1; // Normal window
1465 
1466         int result = XSendEvent(m_display,
1467                                 DefaultRootWindow(m_display),
1468                                 False,
1469                                 SubstructureNotifyMask | SubstructureRedirectMask,
1470                                 &event);
1471 
1472         if (!result)
1473             err() << "Setting fullscreen failed, could not send \"_NET_WM_STATE\" event" << std::endl;
1474     }
1475 }
1476 
1477 
1478 ////////////////////////////////////////////////////////////
setProtocols()1479 void WindowImplX11::setProtocols()
1480 {
1481     Atom wmProtocols = getAtom("WM_PROTOCOLS");
1482     Atom wmDeleteWindow = getAtom("WM_DELETE_WINDOW");
1483 
1484     if (!wmProtocols)
1485     {
1486         err() << "Failed to request WM_PROTOCOLS atom." << std::endl;
1487         return;
1488     }
1489 
1490     std::vector<Atom> atoms;
1491 
1492     if (wmDeleteWindow)
1493     {
1494         atoms.push_back(wmDeleteWindow);
1495     }
1496     else
1497     {
1498         err() << "Failed to request WM_DELETE_WINDOW atom." << std::endl;
1499     }
1500 
1501     Atom netWmPing = None;
1502     Atom netWmPid = None;
1503 
1504     if (ewmhSupported())
1505     {
1506         netWmPing = getAtom("_NET_WM_PING", true);
1507         netWmPid = getAtom("_NET_WM_PID", true);
1508     }
1509 
1510     if (netWmPing && netWmPid)
1511     {
1512         const long pid = getpid();
1513 
1514         XChangeProperty(m_display,
1515                         m_window,
1516                         netWmPid,
1517                         XA_CARDINAL,
1518                         32,
1519                         PropModeReplace,
1520                         reinterpret_cast<const unsigned char*>(&pid),
1521                         1);
1522 
1523         atoms.push_back(netWmPing);
1524     }
1525 
1526     if (!atoms.empty())
1527     {
1528         XChangeProperty(m_display,
1529                         m_window,
1530                         wmProtocols,
1531                         XA_ATOM,
1532                         32,
1533                         PropModeReplace,
1534                         reinterpret_cast<const unsigned char*>(&atoms[0]),
1535                         atoms.size());
1536     }
1537     else
1538     {
1539         err() << "Didn't set any window protocols" << std::endl;
1540     }
1541 }
1542 
1543 
1544 ////////////////////////////////////////////////////////////
initialize()1545 void WindowImplX11::initialize()
1546 {
1547     // Create the input context
1548     m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL);
1549 
1550     if (m_inputMethod)
1551     {
1552         m_inputContext = XCreateIC(m_inputMethod,
1553                                    XNClientWindow,
1554                                    m_window,
1555                                    XNFocusWindow,
1556                                    m_window,
1557                                    XNInputStyle,
1558                                    XIMPreeditNothing | XIMStatusNothing,
1559                                    static_cast<void*>(NULL));
1560     }
1561     else
1562     {
1563         m_inputContext = NULL;
1564     }
1565 
1566     if (!m_inputContext)
1567         err() << "Failed to create input context for window -- TextEntered event won't be able to return unicode" << std::endl;
1568 
1569     Atom wmWindowType = getAtom("_NET_WM_WINDOW_TYPE", false);
1570     Atom wmWindowTypeNormal = getAtom("_NET_WM_WINDOW_TYPE_NORMAL", false);
1571 
1572     if (wmWindowType && wmWindowTypeNormal)
1573     {
1574         XChangeProperty(m_display,
1575                         m_window,
1576                         wmWindowType,
1577                         XA_ATOM,
1578                         32,
1579                         PropModeReplace,
1580                         reinterpret_cast<const unsigned char*>(&wmWindowTypeNormal),
1581                         1);
1582     }
1583 
1584     // Show the window
1585     setVisible(true);
1586 
1587     // Raise the window and grab input focus
1588     grabFocus();
1589 
1590     // Create the hidden cursor
1591     createHiddenCursor();
1592 
1593     // Flush the commands queue
1594     XFlush(m_display);
1595 
1596     // Add this window to the global list of windows (required for focus request)
1597     Lock lock(allWindowsMutex);
1598     allWindows.push_back(this);
1599 }
1600 
1601 
1602 ////////////////////////////////////////////////////////////
updateLastInputTime(::Time time)1603 void WindowImplX11::updateLastInputTime(::Time time)
1604 {
1605     if (time && (time != m_lastInputTime))
1606     {
1607         Atom netWmUserTime = getAtom("_NET_WM_USER_TIME", true);
1608 
1609         if (netWmUserTime)
1610         {
1611             XChangeProperty(m_display,
1612                             m_window,
1613                             netWmUserTime,
1614                             XA_CARDINAL,
1615                             32,
1616                             PropModeReplace,
1617                             reinterpret_cast<const unsigned char*>(&time),
1618                             1);
1619         }
1620 
1621         m_lastInputTime = time;
1622     }
1623 }
1624 
1625 
1626 ////////////////////////////////////////////////////////////
createHiddenCursor()1627 void WindowImplX11::createHiddenCursor()
1628 {
1629     // Create the cursor's pixmap (1x1 pixels)
1630     Pixmap cursorPixmap = XCreatePixmap(m_display, m_window, 1, 1, 1);
1631     GC graphicsContext = XCreateGC(m_display, cursorPixmap, 0, NULL);
1632     XDrawPoint(m_display, cursorPixmap, graphicsContext, 0, 0);
1633     XFreeGC(m_display, graphicsContext);
1634 
1635     // Create the cursor, using the pixmap as both the shape and the mask of the cursor
1636     XColor color;
1637     color.flags = DoRed | DoGreen | DoBlue;
1638     color.red = color.blue = color.green = 0;
1639     m_hiddenCursor = XCreatePixmapCursor(m_display, cursorPixmap, cursorPixmap, &color, &color, 0, 0);
1640 
1641     // We don't need the pixmap any longer, free it
1642     XFreePixmap(m_display, cursorPixmap);
1643 }
1644 
1645 
1646 ////////////////////////////////////////////////////////////
cleanup()1647 void WindowImplX11::cleanup()
1648 {
1649     // Restore the previous video mode (in case we were running in fullscreen)
1650     resetVideoMode();
1651 
1652     // Unhide the mouse cursor (in case it was hidden)
1653     setMouseCursorVisible(true);
1654 }
1655 
1656 
1657 ////////////////////////////////////////////////////////////
processEvent(XEvent & windowEvent)1658 bool WindowImplX11::processEvent(XEvent& windowEvent)
1659 {
1660     // This function implements a workaround to properly discard
1661     // repeated key events when necessary. The problem is that the
1662     // system's key events policy doesn't match SFML's one: X server will generate
1663     // both repeated KeyPress and KeyRelease events when maintaining a key down, while
1664     // SFML only wants repeated KeyPress events. Thus, we have to:
1665     // - Discard duplicated KeyRelease events when KeyRepeatEnabled is true
1666     // - Discard both duplicated KeyPress and KeyRelease events when KeyRepeatEnabled is false
1667 
1668     // Detect repeated key events
1669     if (windowEvent.type == KeyRelease)
1670     {
1671         // Find the next KeyPress event with matching keycode and time
1672         std::deque<XEvent>::iterator iter = std::find_if(
1673             m_events.begin(),
1674             m_events.end(),
1675             KeyRepeatFinder(windowEvent.xkey.keycode, windowEvent.xkey.time)
1676         );
1677 
1678         if (iter != m_events.end())
1679         {
1680             // If we don't want repeated events, remove the next KeyPress from the queue
1681             if (!m_keyRepeat)
1682                 m_events.erase(iter);
1683 
1684             // This KeyRelease is a repeated event and we don't want it
1685             return false;
1686         }
1687     }
1688 
1689     // Convert the X11 event to a sf::Event
1690     switch (windowEvent.type)
1691     {
1692         // Destroy event
1693         case DestroyNotify:
1694         {
1695             // The window is about to be destroyed: we must cleanup resources
1696             cleanup();
1697             break;
1698         }
1699 
1700         // Gain focus event
1701         case FocusIn:
1702         {
1703             // Update the input context
1704             if (m_inputContext)
1705                 XSetICFocus(m_inputContext);
1706 
1707             // Grab cursor
1708             if (m_cursorGrabbed)
1709             {
1710                 // Try multiple times to grab the cursor
1711                 for (unsigned int trial = 0; trial < maxTrialsCount; ++trial)
1712                 {
1713                     int result = XGrabPointer(m_display, m_window, True, None, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime);
1714 
1715                     if (result == GrabSuccess)
1716                     {
1717                         m_cursorGrabbed = true;
1718                         break;
1719                     }
1720 
1721                     // The cursor grab failed, trying again after a small sleep
1722                     sf::sleep(sf::milliseconds(50));
1723                 }
1724 
1725                 if (!m_cursorGrabbed)
1726                     err() << "Failed to grab mouse cursor" << std::endl;
1727             }
1728 
1729             Event event;
1730             event.type = Event::GainedFocus;
1731             pushEvent(event);
1732 
1733             // If the window has been previously marked urgent (notification) as a result of a focus request, undo that
1734             XWMHints* hints = XGetWMHints(m_display, m_window);
1735             if (hints != NULL)
1736             {
1737                 // Remove urgency (notification) flag from hints
1738                 hints->flags &= ~XUrgencyHint;
1739                 XSetWMHints(m_display, m_window, hints);
1740                 XFree(hints);
1741             }
1742 
1743             break;
1744         }
1745 
1746         // Lost focus event
1747         case FocusOut:
1748         {
1749             // Update the input context
1750             if (m_inputContext)
1751                 XUnsetICFocus(m_inputContext);
1752 
1753             // Release cursor
1754             if (m_cursorGrabbed)
1755                 XUngrabPointer(m_display, CurrentTime);
1756 
1757             Event event;
1758             event.type = Event::LostFocus;
1759             pushEvent(event);
1760             break;
1761         }
1762 
1763         // Resize event
1764         case ConfigureNotify:
1765         {
1766             // ConfigureNotify can be triggered for other reasons, check if the size has actually changed
1767             if ((windowEvent.xconfigure.width != m_previousSize.x) || (windowEvent.xconfigure.height != m_previousSize.y))
1768             {
1769                 Event event;
1770                 event.type        = Event::Resized;
1771                 event.size.width  = windowEvent.xconfigure.width;
1772                 event.size.height = windowEvent.xconfigure.height;
1773                 pushEvent(event);
1774 
1775                 m_previousSize.x = windowEvent.xconfigure.width;
1776                 m_previousSize.y = windowEvent.xconfigure.height;
1777             }
1778             break;
1779         }
1780 
1781         // Close event
1782         case ClientMessage:
1783         {
1784             static Atom wmProtocols = getAtom("WM_PROTOCOLS");
1785 
1786             // Handle window manager protocol messages we support
1787             if (windowEvent.xclient.message_type == wmProtocols)
1788             {
1789                 static Atom wmDeleteWindow = getAtom("WM_DELETE_WINDOW");
1790                 static Atom netWmPing = ewmhSupported() ? getAtom("_NET_WM_PING", true) : None;
1791 
1792                 if ((windowEvent.xclient.format == 32) && (windowEvent.xclient.data.l[0]) == static_cast<long>(wmDeleteWindow))
1793                 {
1794                     // Handle the WM_DELETE_WINDOW message
1795                     Event event;
1796                     event.type = Event::Closed;
1797                     pushEvent(event);
1798                 }
1799                 else if (netWmPing && (windowEvent.xclient.format == 32) && (windowEvent.xclient.data.l[0]) == static_cast<long>(netWmPing))
1800                 {
1801                     // Handle the _NET_WM_PING message, send pong back to WM to show that we are responsive
1802                     windowEvent.xclient.window = DefaultRootWindow(m_display);
1803 
1804                     XSendEvent(m_display, DefaultRootWindow(m_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &windowEvent);
1805                 }
1806             }
1807             break;
1808         }
1809 
1810         // Key down event
1811         case KeyPress:
1812         {
1813             Keyboard::Key key = Keyboard::Unknown;
1814 
1815             // Try each KeySym index (modifier group) until we get a match
1816             for (int i = 0; i < 4; ++i)
1817             {
1818                 // Get the SFML keyboard code from the keysym of the key that has been pressed
1819                 key = keysymToSF(XLookupKeysym(&windowEvent.xkey, i));
1820 
1821                 if (key != Keyboard::Unknown)
1822                     break;
1823             }
1824 
1825             // Fill the event parameters
1826             // TODO: if modifiers are wrong, use XGetModifierMapping to retrieve the actual modifiers mapping
1827             Event event;
1828             event.type        = Event::KeyPressed;
1829             event.key.code    = key;
1830             event.key.alt     = windowEvent.xkey.state & Mod1Mask;
1831             event.key.control = windowEvent.xkey.state & ControlMask;
1832             event.key.shift   = windowEvent.xkey.state & ShiftMask;
1833             event.key.system  = windowEvent.xkey.state & Mod4Mask;
1834             pushEvent(event);
1835 
1836             // Generate a TextEntered event
1837             if (!XFilterEvent(&windowEvent, None))
1838             {
1839                 #ifdef X_HAVE_UTF8_STRING
1840                 if (m_inputContext)
1841                 {
1842                     Status status;
1843                     Uint8  keyBuffer[16];
1844 
1845                     int length = Xutf8LookupString(
1846                         m_inputContext,
1847                         &windowEvent.xkey,
1848                         reinterpret_cast<char*>(keyBuffer),
1849                         sizeof(keyBuffer),
1850                         NULL,
1851                         &status
1852                     );
1853 
1854                     if (length > 0)
1855                     {
1856                         Uint32 unicode = 0;
1857                         Utf8::decode(keyBuffer, keyBuffer + length, unicode, 0);
1858                         if (unicode != 0)
1859                         {
1860                             Event textEvent;
1861                             textEvent.type         = Event::TextEntered;
1862                             textEvent.text.unicode = unicode;
1863                             pushEvent(textEvent);
1864                         }
1865                     }
1866                 }
1867                 else
1868                 #endif
1869                 {
1870                     static XComposeStatus status;
1871                     char keyBuffer[16];
1872                     if (XLookupString(&windowEvent.xkey, keyBuffer, sizeof(keyBuffer), NULL, &status))
1873                     {
1874                         Event textEvent;
1875                         textEvent.type         = Event::TextEntered;
1876                         textEvent.text.unicode = static_cast<Uint32>(keyBuffer[0]);
1877                         pushEvent(textEvent);
1878                     }
1879                 }
1880             }
1881 
1882             updateLastInputTime(windowEvent.xkey.time);
1883 
1884             break;
1885         }
1886 
1887         // Key up event
1888         case KeyRelease:
1889         {
1890             Keyboard::Key key = Keyboard::Unknown;
1891 
1892             // Try each KeySym index (modifier group) until we get a match
1893             for (int i = 0; i < 4; ++i)
1894             {
1895                 // Get the SFML keyboard code from the keysym of the key that has been released
1896                 key = keysymToSF(XLookupKeysym(&windowEvent.xkey, i));
1897 
1898                 if (key != Keyboard::Unknown)
1899                     break;
1900             }
1901 
1902             // Fill the event parameters
1903             Event event;
1904             event.type        = Event::KeyReleased;
1905             event.key.code    = key;
1906             event.key.alt     = windowEvent.xkey.state & Mod1Mask;
1907             event.key.control = windowEvent.xkey.state & ControlMask;
1908             event.key.shift   = windowEvent.xkey.state & ShiftMask;
1909             event.key.system  = windowEvent.xkey.state & Mod4Mask;
1910             pushEvent(event);
1911 
1912             break;
1913         }
1914 
1915         // Mouse button pressed
1916         case ButtonPress:
1917         {
1918             // XXX: Why button 8 and 9?
1919             // Because 4 and 5 are the vertical wheel and 6 and 7 are horizontal wheel ;)
1920             unsigned int button = windowEvent.xbutton.button;
1921             if ((button == Button1) ||
1922                 (button == Button2) ||
1923                 (button == Button3) ||
1924                 (button == 8) ||
1925                 (button == 9))
1926             {
1927                 Event event;
1928                 event.type          = Event::MouseButtonPressed;
1929                 event.mouseButton.x = windowEvent.xbutton.x;
1930                 event.mouseButton.y = windowEvent.xbutton.y;
1931                 switch(button)
1932                 {
1933                     case Button1: event.mouseButton.button = Mouse::Left;     break;
1934                     case Button2: event.mouseButton.button = Mouse::Middle;   break;
1935                     case Button3: event.mouseButton.button = Mouse::Right;    break;
1936                     case 8:       event.mouseButton.button = Mouse::XButton1; break;
1937                     case 9:       event.mouseButton.button = Mouse::XButton2; break;
1938                 }
1939                 pushEvent(event);
1940             }
1941 
1942             updateLastInputTime(windowEvent.xbutton.time);
1943 
1944             break;
1945         }
1946 
1947         // Mouse button released
1948         case ButtonRelease:
1949         {
1950             unsigned int button = windowEvent.xbutton.button;
1951             if ((button == Button1) ||
1952                 (button == Button2) ||
1953                 (button == Button3) ||
1954                 (button == 8) ||
1955                 (button == 9))
1956             {
1957                 Event event;
1958                 event.type          = Event::MouseButtonReleased;
1959                 event.mouseButton.x = windowEvent.xbutton.x;
1960                 event.mouseButton.y = windowEvent.xbutton.y;
1961                 switch(button)
1962                 {
1963                     case Button1: event.mouseButton.button = Mouse::Left;     break;
1964                     case Button2: event.mouseButton.button = Mouse::Middle;   break;
1965                     case Button3: event.mouseButton.button = Mouse::Right;    break;
1966                     case 8:       event.mouseButton.button = Mouse::XButton1; break;
1967                     case 9:       event.mouseButton.button = Mouse::XButton2; break;
1968                 }
1969                 pushEvent(event);
1970             }
1971             else if ((button == Button4) || (button == Button5))
1972             {
1973                 Event event;
1974 
1975                 event.type             = Event::MouseWheelMoved;
1976                 event.mouseWheel.delta = (button == Button4) ? 1 : -1;
1977                 event.mouseWheel.x     = windowEvent.xbutton.x;
1978                 event.mouseWheel.y     = windowEvent.xbutton.y;
1979                 pushEvent(event);
1980 
1981                 event.type                   = Event::MouseWheelScrolled;
1982                 event.mouseWheelScroll.wheel = Mouse::VerticalWheel;
1983                 event.mouseWheelScroll.delta = (button == Button4) ? 1 : -1;
1984                 event.mouseWheelScroll.x     = windowEvent.xbutton.x;
1985                 event.mouseWheelScroll.y     = windowEvent.xbutton.y;
1986                 pushEvent(event);
1987             }
1988             else if ((button == 6) || (button == 7))
1989             {
1990                 Event event;
1991                 event.type                   = Event::MouseWheelScrolled;
1992                 event.mouseWheelScroll.wheel = Mouse::HorizontalWheel;
1993                 event.mouseWheelScroll.delta = (button == 6) ? 1 : -1;
1994                 event.mouseWheelScroll.x     = windowEvent.xbutton.x;
1995                 event.mouseWheelScroll.y     = windowEvent.xbutton.y;
1996                 pushEvent(event);
1997             }
1998             break;
1999         }
2000 
2001         // Mouse moved
2002         case MotionNotify:
2003         {
2004             Event event;
2005             event.type        = Event::MouseMoved;
2006             event.mouseMove.x = windowEvent.xmotion.x;
2007             event.mouseMove.y = windowEvent.xmotion.y;
2008             pushEvent(event);
2009             break;
2010         }
2011 
2012         // Mouse entered
2013         case EnterNotify:
2014         {
2015             if (windowEvent.xcrossing.mode == NotifyNormal)
2016             {
2017                 Event event;
2018                 event.type = Event::MouseEntered;
2019                 pushEvent(event);
2020             }
2021             break;
2022         }
2023 
2024         // Mouse left
2025         case LeaveNotify:
2026         {
2027             if (windowEvent.xcrossing.mode == NotifyNormal)
2028             {
2029                 Event event;
2030                 event.type = Event::MouseLeft;
2031                 pushEvent(event);
2032             }
2033             break;
2034         }
2035 
2036         // Window unmapped
2037         case UnmapNotify:
2038         {
2039             if (windowEvent.xunmap.window == m_window)
2040                 m_windowMapped = false;
2041 
2042             break;
2043         }
2044 
2045         // Window visibility change
2046         case VisibilityNotify:
2047         {
2048             // We prefer using VisibilityNotify over MapNotify because
2049             // some window managers like awesome don't internally flag a
2050             // window as viewable even after it is mapped but before it
2051             // is visible leading to certain function calls failing with
2052             // an unviewable error if called before VisibilityNotify arrives
2053 
2054             // Empirical testing on most widely used window managers shows
2055             // that mapping a window will always lead to a VisibilityNotify
2056             // event that is not VisibilityFullyObscured
2057             if (windowEvent.xvisibility.window == m_window)
2058             {
2059                 if (windowEvent.xvisibility.state != VisibilityFullyObscured)
2060                     m_windowMapped = true;
2061             }
2062 
2063             break;
2064         }
2065 
2066         // Window property change
2067         case PropertyNotify:
2068         {
2069             if (!m_lastInputTime)
2070                 m_lastInputTime = windowEvent.xproperty.time;
2071 
2072             break;
2073         }
2074     }
2075 
2076     return true;
2077 }
2078 
2079 
2080 ////////////////////////////////////////////////////////////
checkXRandR(int & xRandRMajor,int & xRandRMinor)2081 bool WindowImplX11::checkXRandR(int& xRandRMajor, int& xRandRMinor)
2082 {
2083     // Check if the XRandR extension is present
2084     int version;
2085     if (!XQueryExtension(m_display, "RANDR", &version, &version, &version))
2086     {
2087         err() << "XRandR extension is not supported" << std::endl;
2088         return false;
2089     }
2090 
2091     // Check XRandR version, 1.2 required
2092     if (!XRRQueryVersion(m_display, &xRandRMajor, &xRandRMinor) || xRandRMajor < 1 || (xRandRMajor == 1 && xRandRMinor < 2 ))
2093     {
2094         err() << "XRandR is too old" << std::endl;
2095         return false;
2096     }
2097 
2098     return true;
2099 }
2100 
2101 
2102 ////////////////////////////////////////////////////////////
getOutputPrimary(::Window & rootWindow,XRRScreenResources * res,int xRandRMajor,int xRandRMinor)2103 RROutput WindowImplX11::getOutputPrimary(::Window& rootWindow, XRRScreenResources* res, int xRandRMajor, int xRandRMinor)
2104 {
2105     // if xRandR version >= 1.3 get the primary screen else take the first screen
2106     if ((xRandRMajor == 1 && xRandRMinor >= 3) || xRandRMajor > 1)
2107     {
2108         RROutput output = XRRGetOutputPrimary(m_display, rootWindow);
2109 
2110         // Check if returned output is valid, otherwise use the first screen
2111         if (output == None)
2112             return res->outputs[0];
2113         else
2114             return output;
2115     }
2116 
2117     // xRandr version can't get the primary screen, use the first screen
2118     return res->outputs[0];
2119 }
2120 
2121 
2122 ////////////////////////////////////////////////////////////
getPrimaryMonitorPosition()2123 Vector2i WindowImplX11::getPrimaryMonitorPosition()
2124 {
2125     Vector2i monitorPosition;
2126 
2127     // Get root window
2128     ::Window rootWindow = RootWindow(m_display, m_screen);
2129 
2130     // Get the screen resources
2131     XRRScreenResources* res = XRRGetScreenResources(m_display, rootWindow);
2132     if (!res)
2133     {
2134         err() << "Failed to get the current screen resources for.primary monitor position" << std::endl;
2135         return monitorPosition;
2136     }
2137 
2138     // Get xRandr version
2139     int xRandRMajor, xRandRMinor;
2140     if (!checkXRandR(xRandRMajor, xRandRMinor))
2141         xRandRMajor = xRandRMinor = 0;
2142 
2143     RROutput output = getOutputPrimary(rootWindow, res, xRandRMajor, xRandRMinor);
2144 
2145     // Get output info from output
2146     XRROutputInfo* outputInfo = XRRGetOutputInfo(m_display, res, output);
2147     if (!outputInfo || outputInfo->connection == RR_Disconnected)
2148     {
2149         XRRFreeScreenResources(res);
2150 
2151         // If outputInfo->connection == RR_Disconnected, free output info
2152         if (outputInfo)
2153             XRRFreeOutputInfo(outputInfo);
2154 
2155         err() << "Failed to get output info for.primary monitor position" << std::endl;
2156         return monitorPosition;
2157     }
2158 
2159     // Retreive current RRMode, screen position and rotation
2160     XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(m_display, res, outputInfo->crtc);
2161     if (!crtcInfo)
2162     {
2163         XRRFreeScreenResources(res);
2164         XRRFreeOutputInfo(outputInfo);
2165         err() << "Failed to get crtc info for.primary monitor position" << std::endl;
2166         return monitorPosition;
2167     }
2168 
2169     monitorPosition.x = crtcInfo->x;
2170     monitorPosition.y = crtcInfo->y;
2171 
2172     XRRFreeCrtcInfo(crtcInfo);
2173     XRRFreeOutputInfo(outputInfo);
2174     XRRFreeScreenResources(res);
2175 
2176     return monitorPosition;
2177 }
2178 
2179 } // namespace priv
2180 
2181 } // namespace sf
2182