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