CMake: add WITH_LINKER_LLD option for unix platforms
[blender-staging.git] / intern / ghost / intern / GHOST_WindowX11.cpp
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup GHOST
22  */
23
24 /* For standard X11 cursors */
25 #include <X11/Xatom.h>
26 #include <X11/Xmd.h>
27 #include <X11/Xutil.h>
28 #include <X11/cursorfont.h>
29 #ifdef WITH_X11_ALPHA
30 #  include <X11/extensions/Xrender.h>
31 #endif
32 #include "GHOST_Debug.h"
33 #include "GHOST_IconX11.h"
34 #include "GHOST_SystemX11.h"
35 #include "GHOST_WindowX11.h"
36 #include "STR_String.h"
37
38 #ifdef WITH_XDND
39 #  include "GHOST_DropTargetX11.h"
40 #endif
41
42 #ifdef WITH_GL_EGL
43 #  include "GHOST_ContextEGL.h"
44 #  include <EGL/eglext.h>
45 #else
46 #  include "GHOST_ContextGLX.h"
47 #endif
48
49 /* for XIWarpPointer */
50 #ifdef WITH_X11_XINPUT
51 #  include <X11/extensions/XInput2.h>
52 #endif
53
54 // For DPI value
55 #include <X11/Xresource.h>
56
57 #include <cstdio>
58 #include <cstring>
59
60 /* gethostname */
61 #include <unistd.h>
62
63 #include <algorithm>
64 #include <math.h>
65 #include <string>
66
67 /* For obscure full screen mode stuff
68  * lifted verbatim from blut. */
69
70 typedef struct {
71   long flags;
72   long functions;
73   long decorations;
74   long input_mode;
75 } MotifWmHints;
76
77 enum {
78   MWM_HINTS_FUNCTIONS = (1L << 0),
79   MWM_HINTS_DECORATIONS = (1L << 1),
80 };
81 enum {
82   MWM_FUNCTION_ALL = (1L << 0),
83   MWM_FUNCTION_RESIZE = (1L << 1),
84   MWM_FUNCTION_MOVE = (1L << 2),
85   MWM_FUNCTION_MINIMIZE = (1L << 3),
86   MWM_FUNCTION_MAXIMIZE = (1L << 4),
87   MWM_FUNCTION_CLOSE = (1L << 5),
88 };
89
90 #ifndef HOST_NAME_MAX
91 #  define HOST_NAME_MAX 64
92 #endif
93
94 // #define GHOST_X11_GRAB
95
96 /*
97  * A Client can't change the window property, that is
98  * the work of the window manager. In case, we send
99  * a ClientMessage to the RootWindow with the property
100  * and the Action (WM-spec define this):
101  */
102 #define _NET_WM_STATE_REMOVE 0
103 #define _NET_WM_STATE_ADD 1
104 // #define _NET_WM_STATE_TOGGLE 2 // UNUSED
105
106 #ifdef WITH_GL_EGL
107
108 static XVisualInfo *x11_visualinfo_from_egl(Display *display)
109 {
110   int num_visuals;
111   XVisualInfo vinfo_template;
112   vinfo_template.screen = DefaultScreen(display);
113   return XGetVisualInfo(display, VisualScreenMask, &vinfo_template, &num_visuals);
114 }
115
116 #else
117
118 static XVisualInfo *x11_visualinfo_from_glx(Display *display,
119                                             bool stereoVisual,
120                                             bool needAlpha,
121                                             GLXFBConfig *fbconfig)
122 {
123   int glx_major, glx_minor, glx_version; /* GLX version: major.minor */
124   int glx_attribs[64];
125
126   *fbconfig = NULL;
127
128   /* Set up the minimum attributes that we require and see if
129    * X can find us a visual matching those requirements. */
130
131   if (!glXQueryVersion(display, &glx_major, &glx_minor)) {
132     fprintf(stderr,
133             "%s:%d: X11 glXQueryVersion() failed, "
134             "verify working openGL system!\n",
135             __FILE__,
136             __LINE__);
137
138     return NULL;
139   }
140   glx_version = glx_major * 100 + glx_minor;
141 #  ifndef WITH_X11_ALPHA
142   (void)glx_version;
143 #  endif
144
145 #  ifdef WITH_X11_ALPHA
146   if (needAlpha && glx_version >= 103 &&
147       (glXChooseFBConfig || (glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)glXGetProcAddressARB(
148                                  (const GLubyte *)"glXChooseFBConfig")) != NULL) &&
149       (glXGetVisualFromFBConfig ||
150        (glXGetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC)glXGetProcAddressARB(
151             (const GLubyte *)"glXGetVisualFromFBConfig")) != NULL)) {
152
153     GHOST_X11_GL_GetAttributes(glx_attribs, 64, stereoVisual, needAlpha, true);
154
155     int nbfbconfig;
156     GLXFBConfig *fbconfigs = glXChooseFBConfig(
157         display, DefaultScreen(display), glx_attribs, &nbfbconfig);
158
159     /* Any sample level or even zero, which means oversampling disabled, is good
160      * but we need a valid visual to continue */
161     if (nbfbconfig > 0) {
162       /* take a frame buffer config that has alpha cap */
163       for (int i = 0; i < nbfbconfig; i++) {
164         XVisualInfo *visual = (XVisualInfo *)glXGetVisualFromFBConfig(display, fbconfigs[i]);
165         if (!visual)
166           continue;
167         /* if we don't need a alpha background, the first config will do, otherwise
168          * test the alphaMask as it won't necessarily be present */
169         if (needAlpha) {
170           XRenderPictFormat *pict_format = XRenderFindVisualFormat(display, visual->visual);
171           if (!pict_format)
172             continue;
173           if (pict_format->direct.alphaMask <= 0)
174             continue;
175         }
176
177         *fbconfig = fbconfigs[i];
178         XFree(fbconfigs);
179
180         return visual;
181       }
182
183       XFree(fbconfigs);
184     }
185   }
186   else
187 #  endif
188   {
189     /* legacy, don't use extension */
190     GHOST_X11_GL_GetAttributes(glx_attribs, 64, stereoVisual, needAlpha, false);
191
192     XVisualInfo *visual = glXChooseVisual(display, DefaultScreen(display), glx_attribs);
193
194     /* Any sample level or even zero, which means oversampling disabled, is good
195      * but we need a valid visual to continue */
196     if (visual != NULL) {
197       return visual;
198     }
199   }
200
201   /* All options exhausted, cannot continue */
202   fprintf(stderr,
203           "%s:%d: X11 glXChooseVisual() failed, "
204           "verify working openGL system!\n",
205           __FILE__,
206           __LINE__);
207
208   return NULL;
209 }
210
211 #endif  // WITH_GL_EGL
212
213 GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
214                                  Display *display,
215                                  const STR_String &title,
216                                  GHOST_TInt32 left,
217                                  GHOST_TInt32 top,
218                                  GHOST_TUns32 width,
219                                  GHOST_TUns32 height,
220                                  GHOST_TWindowState state,
221                                  GHOST_WindowX11 *parentWindow,
222                                  GHOST_TDrawingContextType type,
223                                  const bool is_dialog,
224                                  const bool stereoVisual,
225                                  const bool exclusive,
226                                  const bool alphaBackground,
227                                  const bool is_debug)
228     : GHOST_Window(width, height, state, stereoVisual, exclusive),
229       m_display(display),
230       m_visualInfo(NULL),
231       m_fbconfig(NULL),
232       m_normal_state(GHOST_kWindowStateNormal),
233       m_system(system),
234       m_invalid_window(false),
235       m_empty_cursor(None),
236       m_custom_cursor(None),
237       m_visible_cursor(None),
238       m_taskbar("blender.desktop"),
239 #ifdef WITH_XDND
240       m_dropTarget(NULL),
241 #endif
242       m_tabletData(GHOST_TABLET_DATA_NONE),
243 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
244       m_xic(NULL),
245 #endif
246       m_valid_setup(false),
247       m_is_debug_context(is_debug)
248 {
249   if (type == GHOST_kDrawingContextTypeOpenGL) {
250 #ifdef WITH_GL_EGL
251     m_visualInfo = x11_visualinfo_from_egl(m_display);
252     (void)alphaBackground;
253 #else
254     m_visualInfo = x11_visualinfo_from_glx(
255         m_display, stereoVisual, alphaBackground, (GLXFBConfig *)&m_fbconfig);
256 #endif
257   }
258   else {
259     XVisualInfo tmp = {0};
260     int n;
261     m_visualInfo = XGetVisualInfo(m_display, 0, &tmp, &n);
262   }
263
264   /* caller needs to check 'getValid()' */
265   if (m_visualInfo == NULL) {
266     fprintf(stderr, "initial window could not find the GLX extension\n");
267     return;
268   }
269
270   unsigned int xattributes_valuemask = 0;
271
272   XSetWindowAttributes xattributes;
273   memset(&xattributes, 0, sizeof(xattributes));
274
275   xattributes_valuemask |= CWBorderPixel;
276   xattributes.border_pixel = 0;
277
278   /* Specify which events we are interested in hearing. */
279
280   xattributes_valuemask |= CWEventMask;
281   xattributes.event_mask = ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
282                            EnterWindowMask | LeaveWindowMask | ButtonPressMask |
283                            ButtonReleaseMask | PointerMotionMask | FocusChangeMask |
284                            PropertyChangeMask | KeymapStateMask;
285
286   if (exclusive) {
287     xattributes_valuemask |= CWOverrideRedirect;
288     xattributes.override_redirect = True;
289   }
290
291   xattributes_valuemask |= CWColormap;
292   xattributes.colormap = XCreateColormap(
293       m_display, RootWindow(m_display, m_visualInfo->screen), m_visualInfo->visual, AllocNone);
294
295   /* create the window! */
296   if ((parentWindow == 0) || is_dialog) {
297     m_window = XCreateWindow(m_display,
298                              RootWindow(m_display, m_visualInfo->screen),
299                              left,
300                              top,
301                              width,
302                              height,
303                              0, /* no border. */
304                              m_visualInfo->depth,
305                              InputOutput,
306                              m_visualInfo->visual,
307                              xattributes_valuemask,
308                              &xattributes);
309   }
310   else {
311     Window root_return;
312     int x_return, y_return;
313     unsigned int w_return, h_return, border_w_return, depth_return;
314
315     XGetGeometry(m_display,
316                  parentWindow->m_window,
317                  &root_return,
318                  &x_return,
319                  &y_return,
320                  &w_return,
321                  &h_return,
322                  &border_w_return,
323                  &depth_return);
324
325     left = 0;
326     top = 0;
327     width = w_return;
328     height = h_return;
329
330     m_window = XCreateWindow(m_display,
331                              parentWindow->m_window, /* reparent against embedder */
332                              left,
333                              top,
334                              width,
335                              height,
336                              0, /* no border. */
337                              m_visualInfo->depth,
338                              InputOutput,
339                              m_visualInfo->visual,
340                              xattributes_valuemask,
341                              &xattributes);
342
343     XSelectInput(m_display, parentWindow->m_window, SubstructureNotifyMask);
344   }
345
346 #ifdef WITH_XDND
347   /* initialize drop target for newly created window */
348   m_dropTarget = new GHOST_DropTargetX11(this, m_system);
349   GHOST_PRINT("Set drop target\n");
350 #endif
351
352   if (state == GHOST_kWindowStateMaximized || state == GHOST_kWindowStateFullScreen) {
353     Atom atoms[2];
354     int count = 0;
355     if (state == GHOST_kWindowStateMaximized) {
356       atoms[count++] = m_system->m_atom._NET_WM_STATE_MAXIMIZED_VERT;
357       atoms[count++] = m_system->m_atom._NET_WM_STATE_MAXIMIZED_HORZ;
358     }
359     else {
360       atoms[count++] = m_system->m_atom._NET_WM_STATE_FULLSCREEN;
361     }
362
363     XChangeProperty(m_display,
364                     m_window,
365                     m_system->m_atom._NET_WM_STATE,
366                     XA_ATOM,
367                     32,
368                     PropModeReplace,
369                     (unsigned char *)atoms,
370                     count);
371     m_post_init = False;
372   }
373   /*
374    * One of the problem with WM-spec is that can't set a property
375    * to a window that isn't mapped. That is why we can't "just
376    * call setState" here.
377    *
378    * To fix this, we first need know that the window is really
379    * map waiting for the MapNotify event.
380    *
381    * So, m_post_init indicate that we need wait for the MapNotify
382    * event and then set the Window state to the m_post_state.
383    */
384   else if ((state != GHOST_kWindowStateNormal) && (state != GHOST_kWindowStateMinimized)) {
385     m_post_init = True;
386     m_post_state = state;
387   }
388   else {
389     m_post_init = False;
390     m_post_state = GHOST_kWindowStateNormal;
391   }
392
393   if (is_dialog && parentWindow) {
394     setDialogHints(parentWindow);
395   }
396
397   /* Create some hints for the window manager on how
398    * we want this window treated. */
399   {
400     XSizeHints *xsizehints = XAllocSizeHints();
401     xsizehints->flags = PPosition | PSize | PMinSize | PMaxSize;
402     xsizehints->x = left;
403     xsizehints->y = top;
404     xsizehints->width = width;
405     xsizehints->height = height;
406     xsizehints->min_width = 320;  /* size hints, could be made apart of the ghost api */
407     xsizehints->min_height = 240; /* limits are also arbitrary, but should not allow 1x1 window */
408     xsizehints->max_width = 65535;
409     xsizehints->max_height = 65535;
410     XSetWMNormalHints(m_display, m_window, xsizehints);
411     XFree(xsizehints);
412   }
413
414   /* XClassHint, title */
415   {
416     XClassHint *xclasshint = XAllocClassHint();
417     const int len = title.Length() + 1;
418     char *wmclass = (char *)malloc(sizeof(char) * len);
419     memcpy(wmclass, title.ReadPtr(), len * sizeof(char));
420     xclasshint->res_name = wmclass;
421     xclasshint->res_class = wmclass;
422     XSetClassHint(m_display, m_window, xclasshint);
423     free(wmclass);
424     XFree(xclasshint);
425   }
426
427   /* The basic for a good ICCCM "work" */
428   if (m_system->m_atom.WM_PROTOCOLS) {
429     Atom atoms[2];
430     int natom = 0;
431
432     if (m_system->m_atom.WM_DELETE_WINDOW) {
433       atoms[natom] = m_system->m_atom.WM_DELETE_WINDOW;
434       natom++;
435     }
436
437     if (m_system->m_atom.WM_TAKE_FOCUS && m_system->m_windowFocus) {
438       atoms[natom] = m_system->m_atom.WM_TAKE_FOCUS;
439       natom++;
440     }
441
442     if (natom) {
443       /* printf("Register atoms: %d\n", natom); */
444       XSetWMProtocols(m_display, m_window, atoms, natom);
445     }
446   }
447
448   /* Set the window hints */
449   {
450     XWMHints *xwmhints = XAllocWMHints();
451     xwmhints->initial_state = NormalState;
452     xwmhints->input = (m_system->m_windowFocus) ? True : False;
453     xwmhints->flags = InputHint | StateHint;
454     XSetWMHints(display, m_window, xwmhints);
455     XFree(xwmhints);
456   }
457
458   /* set the icon */
459   {
460     Atom _NET_WM_ICON = XInternAtom(m_display, "_NET_WM_ICON", False);
461     XChangeProperty(m_display,
462                     m_window,
463                     _NET_WM_ICON,
464                     XA_CARDINAL,
465                     32,
466                     PropModeReplace,
467                     (unsigned char *)BLENDER_ICONS_WM_X11,
468                     sizeof(BLENDER_ICONS_WM_X11) / sizeof(unsigned long));
469   }
470
471   /* set the process ID (_NET_WM_PID) */
472   {
473     Atom _NET_WM_PID = XInternAtom(m_display, "_NET_WM_PID", False);
474     pid_t pid = getpid();
475     XChangeProperty(m_display,
476                     m_window,
477                     _NET_WM_PID,
478                     XA_CARDINAL,
479                     32,
480                     PropModeReplace,
481                     (unsigned char *)&pid,
482                     1);
483   }
484
485   /* set the hostname (WM_CLIENT_MACHINE) */
486   {
487     char hostname[HOST_NAME_MAX];
488     char *text_array[1];
489     XTextProperty text_prop;
490
491     gethostname(hostname, sizeof(hostname));
492     hostname[sizeof(hostname) - 1] = '\0';
493     text_array[0] = hostname;
494
495     XStringListToTextProperty(text_array, 1, &text_prop);
496     XSetWMClientMachine(m_display, m_window, &text_prop);
497     XFree(text_prop.value);
498   }
499
500 #ifdef WITH_X11_XINPUT
501   refreshXInputDevices();
502 #endif
503
504   /* now set up the rendering context. */
505   if (setDrawingContextType(type) == GHOST_kSuccess) {
506     m_valid_setup = true;
507     GHOST_PRINT("Created window\n");
508   }
509
510   setTitle(title);
511
512   if (exclusive && system->m_windowFocus) {
513     XMapRaised(m_display, m_window);
514   }
515   else {
516     XMapWindow(m_display, m_window);
517
518     if (!system->m_windowFocus) {
519       XLowerWindow(m_display, m_window);
520     }
521   }
522   GHOST_PRINT("Mapped window\n");
523
524   XFlush(m_display);
525 }
526
527 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
528 static Bool destroyICCallback(XIC /*xic*/, XPointer ptr, XPointer /*data*/)
529 {
530   GHOST_PRINT("XIM input context destroyed\n");
531
532   if (ptr) {
533     *(XIC *)ptr = NULL;
534   }
535   /* Ignored by X11. */
536   return True;
537 }
538
539 bool GHOST_WindowX11::createX11_XIC()
540 {
541   XIM xim = m_system->getX11_XIM();
542   if (!xim)
543     return false;
544
545   XICCallback destroy;
546   destroy.callback = (XICProc)destroyICCallback;
547   destroy.client_data = (XPointer)&m_xic;
548   m_xic = XCreateIC(xim,
549                     XNClientWindow,
550                     m_window,
551                     XNFocusWindow,
552                     m_window,
553                     XNInputStyle,
554                     XIMPreeditNothing | XIMStatusNothing,
555                     XNResourceName,
556                     GHOST_X11_RES_NAME,
557                     XNResourceClass,
558                     GHOST_X11_RES_CLASS,
559                     XNDestroyCallback,
560                     &destroy,
561                     NULL);
562   if (!m_xic)
563     return false;
564
565   unsigned long fevent;
566   XGetICValues(m_xic, XNFilterEvents, &fevent, NULL);
567   XSelectInput(m_display,
568                m_window,
569                ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
570                    EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask |
571                    PointerMotionMask | FocusChangeMask | PropertyChangeMask | KeymapStateMask |
572                    fevent);
573   return true;
574 }
575 #endif
576
577 #ifdef WITH_X11_XINPUT
578 void GHOST_WindowX11::refreshXInputDevices()
579 {
580   if (m_system->m_xinput_version.present) {
581     std::vector<XEventClass> xevents;
582
583     for (GHOST_SystemX11::GHOST_TabletX11 &xtablet : m_system->GetXTablets()) {
584       /* With modern XInput (xlib 1.6.2 at least and/or evdev 2.9.0) and some 'no-name' tablets
585        * like 'UC-LOGIC Tablet WP5540U', we also need to 'select' ButtonPress for motion event,
586        * otherwise we do not get any tablet motion event once pen is pressed... See T43367.
587        */
588       XEventClass ev;
589
590       DeviceMotionNotify(xtablet.Device, xtablet.MotionEvent, ev);
591       if (ev)
592         xevents.push_back(ev);
593       DeviceButtonPress(xtablet.Device, xtablet.PressEvent, ev);
594       if (ev)
595         xevents.push_back(ev);
596       ProximityIn(xtablet.Device, xtablet.ProxInEvent, ev);
597       if (ev)
598         xevents.push_back(ev);
599       ProximityOut(xtablet.Device, xtablet.ProxOutEvent, ev);
600       if (ev)
601         xevents.push_back(ev);
602     }
603
604     XSelectExtensionEvent(m_display, m_window, xevents.data(), (int)xevents.size());
605   }
606 }
607
608 #endif /* WITH_X11_XINPUT */
609
610 Window GHOST_WindowX11::getXWindow()
611 {
612   return m_window;
613 }
614
615 bool GHOST_WindowX11::getValid() const
616 {
617   return GHOST_Window::getValid() && m_valid_setup;
618 }
619
620 void GHOST_WindowX11::setTitle(const STR_String &title)
621 {
622   Atom name = XInternAtom(m_display, "_NET_WM_NAME", 0);
623   Atom utf8str = XInternAtom(m_display, "UTF8_STRING", 0);
624   XChangeProperty(m_display,
625                   m_window,
626                   name,
627                   utf8str,
628                   8,
629                   PropModeReplace,
630                   (const unsigned char *)title.ReadPtr(),
631                   title.Length());
632
633   /* This should convert to valid x11 string
634    * and getTitle would need matching change */
635   XStoreName(m_display, m_window, title);
636
637   XFlush(m_display);
638 }
639
640 void GHOST_WindowX11::getTitle(STR_String &title) const
641 {
642   char *name = NULL;
643
644   XFetchName(m_display, m_window, &name);
645   title = name ? name : "untitled";
646   XFree(name);
647 }
648
649 void GHOST_WindowX11::getWindowBounds(GHOST_Rect &bounds) const
650 {
651   /* Getting the window bounds under X11 is not
652    * really supported (nor should it be desired). */
653   getClientBounds(bounds);
654 }
655
656 void GHOST_WindowX11::getClientBounds(GHOST_Rect &bounds) const
657 {
658   Window root_return;
659   int x_return, y_return;
660   unsigned int w_return, h_return, border_w_return, depth_return;
661   GHOST_TInt32 screen_x, screen_y;
662
663   XGetGeometry(m_display,
664                m_window,
665                &root_return,
666                &x_return,
667                &y_return,
668                &w_return,
669                &h_return,
670                &border_w_return,
671                &depth_return);
672
673   clientToScreen(0, 0, screen_x, screen_y);
674
675   bounds.m_l = screen_x;
676   bounds.m_r = bounds.m_l + w_return;
677   bounds.m_t = screen_y;
678   bounds.m_b = bounds.m_t + h_return;
679 }
680
681 GHOST_TSuccess GHOST_WindowX11::setClientWidth(GHOST_TUns32 width)
682 {
683   XWindowChanges values;
684   unsigned int value_mask = CWWidth;
685   values.width = width;
686   XConfigureWindow(m_display, m_window, value_mask, &values);
687
688   return GHOST_kSuccess;
689 }
690
691 GHOST_TSuccess GHOST_WindowX11::setClientHeight(GHOST_TUns32 height)
692 {
693   XWindowChanges values;
694   unsigned int value_mask = CWHeight;
695   values.height = height;
696   XConfigureWindow(m_display, m_window, value_mask, &values);
697   return GHOST_kSuccess;
698 }
699
700 GHOST_TSuccess GHOST_WindowX11::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
701 {
702   XWindowChanges values;
703   unsigned int value_mask = CWWidth | CWHeight;
704   values.width = width;
705   values.height = height;
706   XConfigureWindow(m_display, m_window, value_mask, &values);
707   return GHOST_kSuccess;
708 }
709
710 void GHOST_WindowX11::screenToClient(GHOST_TInt32 inX,
711                                      GHOST_TInt32 inY,
712                                      GHOST_TInt32 &outX,
713                                      GHOST_TInt32 &outY) const
714 {
715   /* This is correct! */
716
717   int ax, ay;
718   Window temp;
719
720   XTranslateCoordinates(
721       m_display, RootWindow(m_display, m_visualInfo->screen), m_window, inX, inY, &ax, &ay, &temp);
722   outX = ax;
723   outY = ay;
724 }
725
726 void GHOST_WindowX11::clientToScreen(GHOST_TInt32 inX,
727                                      GHOST_TInt32 inY,
728                                      GHOST_TInt32 &outX,
729                                      GHOST_TInt32 &outY) const
730 {
731   int ax, ay;
732   Window temp;
733
734   XTranslateCoordinates(
735       m_display, m_window, RootWindow(m_display, m_visualInfo->screen), inX, inY, &ax, &ay, &temp);
736   outX = ax;
737   outY = ay;
738 }
739
740 GHOST_TSuccess GHOST_WindowX11::setDialogHints(GHOST_WindowX11 *parentWindow)
741 {
742
743   Atom atom_window_type = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE", False);
744   Atom atom_dialog = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
745   MotifWmHints hints = {0};
746
747   XChangeProperty(m_display,
748                   m_window,
749                   atom_window_type,
750                   XA_ATOM,
751                   32,
752                   PropModeReplace,
753                   (unsigned char *)&atom_dialog,
754                   1);
755   XSetTransientForHint(m_display, m_window, parentWindow->m_window);
756
757   /* Disable minimizing of the window for now.
758    * Actually, most window managers disable minimizing and maximizing for dialogs, ignoring this.
759    * Leaving it here anyway in the hope it brings back maximizing on some window managers at least,
760    * we'd preferably have it even for dialog windows (e.g. file browser). */
761   hints.flags = MWM_HINTS_FUNCTIONS;
762   hints.functions = MWM_FUNCTION_RESIZE | MWM_FUNCTION_MOVE | MWM_FUNCTION_MAXIMIZE |
763                     MWM_FUNCTION_CLOSE;
764   XChangeProperty(m_display,
765                   m_window,
766                   m_system->m_atom._MOTIF_WM_HINTS,
767                   m_system->m_atom._MOTIF_WM_HINTS,
768                   32,
769                   PropModeReplace,
770                   (unsigned char *)&hints,
771                   4);
772
773   return GHOST_kSuccess;
774 }
775
776 void GHOST_WindowX11::icccmSetState(int state)
777 {
778   XEvent xev;
779
780   if (state != IconicState)
781     return;
782
783   xev.xclient.type = ClientMessage;
784   xev.xclient.serial = 0;
785   xev.xclient.send_event = True;
786   xev.xclient.display = m_display;
787   xev.xclient.window = m_window;
788   xev.xclient.format = 32;
789   xev.xclient.message_type = m_system->m_atom.WM_CHANGE_STATE;
790   xev.xclient.data.l[0] = state;
791   XSendEvent(m_display,
792              RootWindow(m_display, m_visualInfo->screen),
793              False,
794              SubstructureNotifyMask | SubstructureRedirectMask,
795              &xev);
796 }
797
798 int GHOST_WindowX11::icccmGetState(void) const
799 {
800   struct {
801     CARD32 state;
802     XID icon;
803   } * prop_ret;
804   unsigned long bytes_after, num_ret;
805   Atom type_ret;
806   int ret, format_ret;
807   CARD32 st;
808
809   prop_ret = NULL;
810   ret = XGetWindowProperty(m_display,
811                            m_window,
812                            m_system->m_atom.WM_STATE,
813                            0,
814                            2,
815                            False,
816                            m_system->m_atom.WM_STATE,
817                            &type_ret,
818                            &format_ret,
819                            &num_ret,
820                            &bytes_after,
821                            ((unsigned char **)&prop_ret));
822   if ((ret == Success) && (prop_ret != NULL) && (num_ret == 2)) {
823     st = prop_ret->state;
824   }
825   else {
826     st = NormalState;
827   }
828
829   if (prop_ret) {
830     XFree(prop_ret);
831   }
832
833   return st;
834 }
835
836 void GHOST_WindowX11::netwmMaximized(bool set)
837 {
838   XEvent xev;
839
840   xev.xclient.type = ClientMessage;
841   xev.xclient.serial = 0;
842   xev.xclient.send_event = True;
843   xev.xclient.window = m_window;
844   xev.xclient.message_type = m_system->m_atom._NET_WM_STATE;
845   xev.xclient.format = 32;
846
847   if (set == True)
848     xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
849   else
850     xev.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
851
852   xev.xclient.data.l[1] = m_system->m_atom._NET_WM_STATE_MAXIMIZED_HORZ;
853   xev.xclient.data.l[2] = m_system->m_atom._NET_WM_STATE_MAXIMIZED_VERT;
854   xev.xclient.data.l[3] = 0;
855   xev.xclient.data.l[4] = 0;
856   XSendEvent(m_display,
857              RootWindow(m_display, m_visualInfo->screen),
858              False,
859              SubstructureRedirectMask | SubstructureNotifyMask,
860              &xev);
861 }
862
863 bool GHOST_WindowX11::netwmIsMaximized(void) const
864 {
865   Atom *prop_ret;
866   unsigned long bytes_after, num_ret, i;
867   Atom type_ret;
868   bool st;
869   int format_ret, ret, count;
870
871   prop_ret = NULL;
872   st = False;
873   ret = XGetWindowProperty(m_display,
874                            m_window,
875                            m_system->m_atom._NET_WM_STATE,
876                            0,
877                            INT_MAX,
878                            False,
879                            XA_ATOM,
880                            &type_ret,
881                            &format_ret,
882                            &num_ret,
883                            &bytes_after,
884                            (unsigned char **)&prop_ret);
885   if ((ret == Success) && (prop_ret) && (format_ret == 32)) {
886     count = 0;
887     for (i = 0; i < num_ret; i++) {
888       if (prop_ret[i] == m_system->m_atom._NET_WM_STATE_MAXIMIZED_HORZ) {
889         count++;
890       }
891       if (prop_ret[i] == m_system->m_atom._NET_WM_STATE_MAXIMIZED_VERT) {
892         count++;
893       }
894       if (count == 2) {
895         st = True;
896         break;
897       }
898     }
899   }
900
901   if (prop_ret)
902     XFree(prop_ret);
903   return (st);
904 }
905
906 void GHOST_WindowX11::netwmFullScreen(bool set)
907 {
908   XEvent xev;
909
910   xev.xclient.type = ClientMessage;
911   xev.xclient.serial = 0;
912   xev.xclient.send_event = True;
913   xev.xclient.window = m_window;
914   xev.xclient.message_type = m_system->m_atom._NET_WM_STATE;
915   xev.xclient.format = 32;
916
917   if (set == True)
918     xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
919   else
920     xev.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
921
922   xev.xclient.data.l[1] = m_system->m_atom._NET_WM_STATE_FULLSCREEN;
923   xev.xclient.data.l[2] = 0;
924   xev.xclient.data.l[3] = 0;
925   xev.xclient.data.l[4] = 0;
926   XSendEvent(m_display,
927              RootWindow(m_display, m_visualInfo->screen),
928              False,
929              SubstructureRedirectMask | SubstructureNotifyMask,
930              &xev);
931 }
932
933 bool GHOST_WindowX11::netwmIsFullScreen(void) const
934 {
935   Atom *prop_ret;
936   unsigned long bytes_after, num_ret, i;
937   Atom type_ret;
938   bool st;
939   int format_ret, ret;
940
941   prop_ret = NULL;
942   st = False;
943   ret = XGetWindowProperty(m_display,
944                            m_window,
945                            m_system->m_atom._NET_WM_STATE,
946                            0,
947                            INT_MAX,
948                            False,
949                            XA_ATOM,
950                            &type_ret,
951                            &format_ret,
952                            &num_ret,
953                            &bytes_after,
954                            (unsigned char **)&prop_ret);
955   if ((ret == Success) && (prop_ret) && (format_ret == 32)) {
956     for (i = 0; i < num_ret; i++) {
957       if (prop_ret[i] == m_system->m_atom._NET_WM_STATE_FULLSCREEN) {
958         st = True;
959         break;
960       }
961     }
962   }
963
964   if (prop_ret)
965     XFree(prop_ret);
966   return (st);
967 }
968
969 void GHOST_WindowX11::motifFullScreen(bool set)
970 {
971   MotifWmHints hints;
972
973   hints.flags = MWM_HINTS_DECORATIONS;
974   if (set == True)
975     hints.decorations = 0;
976   else
977     hints.decorations = 1;
978
979   XChangeProperty(m_display,
980                   m_window,
981                   m_system->m_atom._MOTIF_WM_HINTS,
982                   m_system->m_atom._MOTIF_WM_HINTS,
983                   32,
984                   PropModeReplace,
985                   (unsigned char *)&hints,
986                   4);
987 }
988
989 bool GHOST_WindowX11::motifIsFullScreen(void) const
990 {
991   MotifWmHints *prop_ret;
992   unsigned long bytes_after, num_ret;
993   Atom type_ret;
994   bool state;
995   int format_ret, st;
996
997   prop_ret = NULL;
998   state = False;
999   st = XGetWindowProperty(m_display,
1000                           m_window,
1001                           m_system->m_atom._MOTIF_WM_HINTS,
1002                           0,
1003                           INT_MAX,
1004                           False,
1005                           m_system->m_atom._MOTIF_WM_HINTS,
1006                           &type_ret,
1007                           &format_ret,
1008                           &num_ret,
1009                           &bytes_after,
1010                           (unsigned char **)&prop_ret);
1011   if ((st == Success) && prop_ret) {
1012     if (prop_ret->flags & MWM_HINTS_DECORATIONS) {
1013       if (!prop_ret->decorations)
1014         state = True;
1015     }
1016   }
1017
1018   if (prop_ret)
1019     XFree(prop_ret);
1020   return (state);
1021 }
1022
1023 GHOST_TWindowState GHOST_WindowX11::getState() const
1024 {
1025   GHOST_TWindowState state_ret;
1026   int state;
1027
1028   state_ret = GHOST_kWindowStateNormal;
1029   state = icccmGetState();
1030   /*
1031    * In the Iconic and Withdrawn state, the window
1032    * is unmapped, so only need return a Minimized state.
1033    */
1034   if ((state == IconicState) || (state == WithdrawnState))
1035     state_ret = GHOST_kWindowStateMinimized;
1036   else if (netwmIsFullScreen() == True)
1037     state_ret = GHOST_kWindowStateFullScreen;
1038   else if (motifIsFullScreen() == True)
1039     state_ret = GHOST_kWindowStateFullScreen;
1040   else if (netwmIsMaximized() == True)
1041     state_ret = GHOST_kWindowStateMaximized;
1042   return (state_ret);
1043 }
1044
1045 GHOST_TSuccess GHOST_WindowX11::setState(GHOST_TWindowState state)
1046 {
1047   GHOST_TWindowState cur_state;
1048   bool is_max, is_full, is_motif_full;
1049
1050   cur_state = getState();
1051   if (state == (int)cur_state)
1052     return GHOST_kSuccess;
1053
1054   if (cur_state != GHOST_kWindowStateMinimized) {
1055     /*
1056      * The window don't have this property's
1057      * if it's not mapped.
1058      */
1059     is_max = netwmIsMaximized();
1060     is_full = netwmIsFullScreen();
1061   }
1062   else {
1063     is_max = False;
1064     is_full = False;
1065   }
1066
1067   is_motif_full = motifIsFullScreen();
1068
1069   if (state == GHOST_kWindowStateNormal)
1070     state = m_normal_state;
1071
1072   if (state == GHOST_kWindowStateNormal) {
1073     if (is_max == True)
1074       netwmMaximized(False);
1075     if (is_full == True)
1076       netwmFullScreen(False);
1077     if (is_motif_full == True)
1078       motifFullScreen(False);
1079     icccmSetState(NormalState);
1080     return (GHOST_kSuccess);
1081   }
1082
1083   if (state == GHOST_kWindowStateFullScreen) {
1084     /*
1085      * We can't change to full screen if the window
1086      * isn't mapped.
1087      */
1088     if (cur_state == GHOST_kWindowStateMinimized)
1089       return (GHOST_kFailure);
1090
1091     m_normal_state = cur_state;
1092
1093     if (is_max == True)
1094       netwmMaximized(False);
1095     if (is_full == False)
1096       netwmFullScreen(True);
1097     if (is_motif_full == False)
1098       motifFullScreen(True);
1099     return (GHOST_kSuccess);
1100   }
1101
1102   if (state == GHOST_kWindowStateMaximized) {
1103     /*
1104      * We can't change to Maximized if the window
1105      * isn't mapped.
1106      */
1107     if (cur_state == GHOST_kWindowStateMinimized)
1108       return (GHOST_kFailure);
1109
1110     if (is_full == True)
1111       netwmFullScreen(False);
1112     if (is_motif_full == True)
1113       motifFullScreen(False);
1114     if (is_max == False)
1115       netwmMaximized(True);
1116     return (GHOST_kSuccess);
1117   }
1118
1119   if (state == GHOST_kWindowStateMinimized) {
1120     /*
1121      * The window manager need save the current state of
1122      * the window (maximized, full screen, etc).
1123      */
1124     icccmSetState(IconicState);
1125     return (GHOST_kSuccess);
1126   }
1127
1128   return (GHOST_kFailure);
1129 }
1130
1131 GHOST_TSuccess GHOST_WindowX11::setOrder(GHOST_TWindowOrder order)
1132 {
1133   if (order == GHOST_kWindowOrderTop) {
1134     XWindowAttributes attr;
1135     Atom atom;
1136
1137     /* We use both XRaiseWindow and _NET_ACTIVE_WINDOW, since some
1138      * window managers ignore the former (e.g. kwin from kde) and others
1139      * don't implement the latter (e.g. fluxbox pre 0.9.9) */
1140
1141     XRaiseWindow(m_display, m_window);
1142
1143     atom = XInternAtom(m_display, "_NET_ACTIVE_WINDOW", True);
1144
1145     if (atom != None) {
1146       Window root;
1147       XEvent xev;
1148       long eventmask;
1149
1150       xev.xclient.type = ClientMessage;
1151       xev.xclient.serial = 0;
1152       xev.xclient.send_event = True;
1153       xev.xclient.window = m_window;
1154       xev.xclient.message_type = atom;
1155
1156       xev.xclient.format = 32;
1157       xev.xclient.data.l[0] = 1;
1158       xev.xclient.data.l[1] = CurrentTime;
1159       xev.xclient.data.l[2] = m_window;
1160       xev.xclient.data.l[3] = 0;
1161       xev.xclient.data.l[4] = 0;
1162
1163       root = RootWindow(m_display, m_visualInfo->screen);
1164       eventmask = SubstructureRedirectMask | SubstructureNotifyMask;
1165
1166       XSendEvent(m_display, root, False, eventmask, &xev);
1167     }
1168
1169     XGetWindowAttributes(m_display, m_window, &attr);
1170
1171     /* iconized windows give bad match error */
1172     if (attr.map_state == IsViewable)
1173       XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime);
1174     XFlush(m_display);
1175   }
1176   else if (order == GHOST_kWindowOrderBottom) {
1177     XLowerWindow(m_display, m_window);
1178     XFlush(m_display);
1179   }
1180   else {
1181     return GHOST_kFailure;
1182   }
1183
1184   return GHOST_kSuccess;
1185 }
1186
1187 bool GHOST_WindowX11::isDialog() const
1188 {
1189   Atom atom_window_type = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE", False);
1190   Atom atom_dialog = XInternAtom(m_display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
1191
1192   Atom *prop_ret;
1193   unsigned long bytes_after, num_ret;
1194   Atom type_ret;
1195   bool st;
1196   int format_ret, ret;
1197
1198   prop_ret = NULL;
1199   st = False;
1200   ret = XGetWindowProperty(m_display,
1201                            m_window,
1202                            atom_window_type,
1203                            0,
1204                            INT_MAX,
1205                            False,
1206                            XA_ATOM,
1207                            &type_ret,
1208                            &format_ret,
1209                            &num_ret,
1210                            &bytes_after,
1211                            (unsigned char **)&prop_ret);
1212   if ((ret == Success) && (prop_ret) && (format_ret == 32)) {
1213     if (prop_ret[0] == atom_dialog) {
1214       st = True;
1215     }
1216   }
1217
1218   if (prop_ret) {
1219     XFree(prop_ret);
1220   }
1221
1222   return st;
1223 }
1224
1225 GHOST_TSuccess GHOST_WindowX11::invalidate()
1226 {
1227   /* So the idea of this function is to generate an expose event
1228    * for the window.
1229    * Unfortunately X does not handle expose events for you and
1230    * it is the client's job to refresh the dirty part of the window.
1231    * We need to queue up invalidate calls and generate GHOST events
1232    * for them in the system.
1233    *
1234    * We implement this by setting a boolean in this class to concatenate
1235    * all such calls into a single event for this window.
1236    *
1237    * At the same time we queue the dirty windows in the system class
1238    * and generate events for them at the next processEvents call. */
1239
1240   if (m_invalid_window == false) {
1241     m_system->addDirtyWindow(this);
1242     m_invalid_window = true;
1243   }
1244
1245   return GHOST_kSuccess;
1246 }
1247
1248 /**
1249  * called by the X11 system implementation when expose events
1250  * for the window have been pushed onto the GHOST queue
1251  */
1252
1253 void GHOST_WindowX11::validate()
1254 {
1255   m_invalid_window = false;
1256 }
1257
1258 /**
1259  * Destructor.
1260  * Closes the window and disposes resources allocated.
1261  */
1262
1263 GHOST_WindowX11::~GHOST_WindowX11()
1264 {
1265   std::map<unsigned int, Cursor>::iterator it = m_standard_cursors.begin();
1266   for (; it != m_standard_cursors.end(); ++it) {
1267     XFreeCursor(m_display, it->second);
1268   }
1269
1270   if (m_empty_cursor) {
1271     XFreeCursor(m_display, m_empty_cursor);
1272   }
1273   if (m_custom_cursor) {
1274     XFreeCursor(m_display, m_custom_cursor);
1275   }
1276
1277   if (m_valid_setup) {
1278     static Atom Primary_atom, Clipboard_atom;
1279     Window p_owner, c_owner;
1280     /*Change the owner of the Atoms to None if we are the owner*/
1281     Primary_atom = XInternAtom(m_display, "PRIMARY", False);
1282     Clipboard_atom = XInternAtom(m_display, "CLIPBOARD", False);
1283
1284     p_owner = XGetSelectionOwner(m_display, Primary_atom);
1285     c_owner = XGetSelectionOwner(m_display, Clipboard_atom);
1286
1287     if (p_owner == m_window) {
1288       XSetSelectionOwner(m_display, Primary_atom, None, CurrentTime);
1289     }
1290     if (c_owner == m_window) {
1291       XSetSelectionOwner(m_display, Clipboard_atom, None, CurrentTime);
1292     }
1293   }
1294
1295   if (m_visualInfo) {
1296     XFree(m_visualInfo);
1297   }
1298
1299 #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1300   if (m_xic) {
1301     XDestroyIC(m_xic);
1302   }
1303 #endif
1304
1305 #ifdef WITH_XDND
1306   delete m_dropTarget;
1307 #endif
1308
1309   releaseNativeHandles();
1310
1311   if (m_valid_setup) {
1312     XDestroyWindow(m_display, m_window);
1313   }
1314 }
1315
1316 GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type)
1317 {
1318   if (type == GHOST_kDrawingContextTypeOpenGL) {
1319
1320     // During development:
1321     //   try 4.x compatibility profile
1322     //   try 3.3 compatibility profile
1323     //   fall back to 3.0 if needed
1324     //
1325     // Final Blender 2.8:
1326     //   try 4.x core profile
1327     //   try 3.3 core profile
1328     //   no fallbacks
1329
1330 #if defined(WITH_GL_PROFILE_CORE)
1331     {
1332       const char *version_major = (char *)glewGetString(GLEW_VERSION_MAJOR);
1333       if (version_major != NULL && version_major[0] == '1') {
1334         fprintf(stderr, "Error: GLEW version 2.0 and above is required.\n");
1335         abort();
1336       }
1337     }
1338 #endif
1339
1340     const int profile_mask =
1341 #ifdef WITH_GL_EGL
1342 #  if defined(WITH_GL_PROFILE_CORE)
1343         EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT;
1344 #  elif defined(WITH_GL_PROFILE_COMPAT)
1345         EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT;
1346 #  else
1347 #    error  // must specify either core or compat at build time
1348 #  endif
1349 #else
1350 #  if defined(WITH_GL_PROFILE_CORE)
1351         GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
1352 #  elif defined(WITH_GL_PROFILE_COMPAT)
1353         GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
1354 #  else
1355 #    error  // must specify either core or compat at build time
1356 #  endif
1357 #endif
1358
1359     GHOST_Context *context;
1360
1361     for (int minor = 5; minor >= 0; --minor) {
1362 #ifdef WITH_GL_EGL
1363       context = new GHOST_ContextEGL(
1364           m_wantStereoVisual,
1365           EGLNativeWindowType(m_window),
1366           EGLNativeDisplayType(m_display),
1367           profile_mask,
1368           4,
1369           minor,
1370           GHOST_OPENGL_EGL_CONTEXT_FLAGS |
1371               (m_is_debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
1372           GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
1373           EGL_OPENGL_API);
1374 #else
1375       context = new GHOST_ContextGLX(m_wantStereoVisual,
1376                                      m_window,
1377                                      m_display,
1378                                      (GLXFBConfig)m_fbconfig,
1379                                      profile_mask,
1380                                      4,
1381                                      minor,
1382                                      GHOST_OPENGL_GLX_CONTEXT_FLAGS |
1383                                          (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
1384                                      GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
1385 #endif
1386
1387       if (context->initializeDrawingContext())
1388         return context;
1389       else
1390         delete context;
1391     }
1392
1393 #ifdef WITH_GL_EGL
1394     context = new GHOST_ContextEGL(m_wantStereoVisual,
1395                                    EGLNativeWindowType(m_window),
1396                                    EGLNativeDisplayType(m_display),
1397                                    profile_mask,
1398                                    3,
1399                                    3,
1400                                    GHOST_OPENGL_EGL_CONTEXT_FLAGS |
1401                                        (m_is_debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
1402                                    GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
1403                                    EGL_OPENGL_API);
1404 #else
1405     context = new GHOST_ContextGLX(m_wantStereoVisual,
1406                                    m_window,
1407                                    m_display,
1408                                    (GLXFBConfig)m_fbconfig,
1409                                    profile_mask,
1410                                    3,
1411                                    3,
1412                                    GHOST_OPENGL_GLX_CONTEXT_FLAGS |
1413                                        (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
1414                                    GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
1415 #endif
1416
1417     if (context->initializeDrawingContext())
1418       return context;
1419     else
1420       delete context;
1421
1422     /* Ugly, but we get crashes unless a whole bunch of systems are patched. */
1423     fprintf(stderr, "Error! Unsupported graphics card or driver.\n");
1424     fprintf(stderr,
1425             "A graphics card and driver with support for OpenGL 3.3 or higher is required.\n");
1426     fprintf(stderr, "The program will now close.\n");
1427     fflush(stderr);
1428     exit(1);
1429   }
1430
1431   return NULL;
1432 }
1433
1434 GHOST_TSuccess GHOST_WindowX11::getStandardCursor(GHOST_TStandardCursor g_cursor, Cursor &xcursor)
1435 {
1436   unsigned int xcursor_id;
1437
1438   switch (g_cursor) {
1439     case GHOST_kStandardCursorHelp:
1440       xcursor_id = XC_question_arrow;
1441       break;
1442     case GHOST_kStandardCursorWait:
1443       xcursor_id = XC_watch;
1444       break;
1445     case GHOST_kStandardCursorText:
1446       xcursor_id = XC_xterm;
1447       break;
1448     case GHOST_kStandardCursorCrosshair:
1449       xcursor_id = XC_crosshair;
1450       break;
1451     case GHOST_kStandardCursorUpDown:
1452       xcursor_id = XC_sb_v_double_arrow;
1453       break;
1454     case GHOST_kStandardCursorLeftRight:
1455       xcursor_id = XC_sb_h_double_arrow;
1456       break;
1457     case GHOST_kStandardCursorTopSide:
1458       xcursor_id = XC_top_side;
1459       break;
1460     case GHOST_kStandardCursorBottomSide:
1461       xcursor_id = XC_bottom_side;
1462       break;
1463     case GHOST_kStandardCursorLeftSide:
1464       xcursor_id = XC_left_side;
1465       break;
1466     case GHOST_kStandardCursorRightSide:
1467       xcursor_id = XC_right_side;
1468       break;
1469     case GHOST_kStandardCursorTopLeftCorner:
1470       xcursor_id = XC_top_left_corner;
1471       break;
1472     case GHOST_kStandardCursorTopRightCorner:
1473       xcursor_id = XC_top_right_corner;
1474       break;
1475     case GHOST_kStandardCursorBottomRightCorner:
1476       xcursor_id = XC_bottom_right_corner;
1477       break;
1478     case GHOST_kStandardCursorBottomLeftCorner:
1479       xcursor_id = XC_bottom_left_corner;
1480       break;
1481     case GHOST_kStandardCursorDefault:
1482       xcursor = None;
1483       return GHOST_kSuccess;
1484     default:
1485       xcursor = None;
1486       return GHOST_kFailure;
1487   }
1488
1489   xcursor = m_standard_cursors[xcursor_id];
1490
1491   if (!xcursor) {
1492     xcursor = XCreateFontCursor(m_display, xcursor_id);
1493
1494     m_standard_cursors[xcursor_id] = xcursor;
1495   }
1496
1497   return GHOST_kSuccess;
1498 }
1499
1500 Cursor GHOST_WindowX11::getEmptyCursor()
1501 {
1502   if (!m_empty_cursor) {
1503     Pixmap blank;
1504     XColor dummy = {0};
1505     char data[1] = {0};
1506
1507     /* make a blank cursor */
1508     blank = XCreateBitmapFromData(
1509         m_display, RootWindow(m_display, m_visualInfo->screen), data, 1, 1);
1510
1511     m_empty_cursor = XCreatePixmapCursor(m_display, blank, blank, &dummy, &dummy, 0, 0);
1512     XFreePixmap(m_display, blank);
1513   }
1514
1515   return m_empty_cursor;
1516 }
1517
1518 GHOST_TSuccess GHOST_WindowX11::setWindowCursorVisibility(bool visible)
1519 {
1520   Cursor xcursor;
1521
1522   if (visible) {
1523     if (m_visible_cursor) {
1524       xcursor = m_visible_cursor;
1525     }
1526     else if (getStandardCursor(getCursorShape(), xcursor) == GHOST_kFailure) {
1527       getStandardCursor(getCursorShape(), xcursor);
1528     }
1529   }
1530   else {
1531     xcursor = getEmptyCursor();
1532   }
1533
1534   XDefineCursor(m_display, m_window, xcursor);
1535   XFlush(m_display);
1536
1537   return GHOST_kSuccess;
1538 }
1539
1540 GHOST_TSuccess GHOST_WindowX11::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
1541 {
1542   if (mode != GHOST_kGrabDisable) {
1543     if (mode != GHOST_kGrabNormal) {
1544       m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1545       setCursorGrabAccum(0, 0);
1546
1547       if (mode == GHOST_kGrabHide)
1548         setWindowCursorVisibility(false);
1549     }
1550 #ifdef GHOST_X11_GRAB
1551     XGrabPointer(m_display,
1552                  m_window,
1553                  False,
1554                  ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
1555                  GrabModeAsync,
1556                  GrabModeAsync,
1557                  None,
1558                  None,
1559                  CurrentTime);
1560 #endif
1561   }
1562   else {
1563     if (m_cursorGrab == GHOST_kGrabHide) {
1564       m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1565     }
1566
1567     if (m_cursorGrab != GHOST_kGrabNormal) {
1568       /* use to generate a mouse move event, otherwise the last event
1569        * blender gets can be outside the screen causing menus not to show
1570        * properly unless the user moves the mouse */
1571
1572 #if defined(WITH_X11_XINPUT) && defined(USE_X11_XINPUT_WARP)
1573       if ((m_system->m_xinput_version.present) &&
1574           (m_system->m_xinput_version.major_version >= 2)) {
1575         int device_id;
1576         if (XIGetClientPointer(m_display, None, &device_id) != False) {
1577           XIWarpPointer(m_display, device_id, None, None, 0, 0, 0, 0, 0, 0);
1578         }
1579       }
1580       else
1581 #endif
1582       {
1583         XWarpPointer(m_display, None, None, 0, 0, 0, 0, 0, 0);
1584       }
1585     }
1586
1587     /* Perform this last so to workaround XWayland bug, see: T53004. */
1588     if (m_cursorGrab == GHOST_kGrabHide) {
1589       setWindowCursorVisibility(true);
1590     }
1591
1592     /* Almost works without but important
1593      * otherwise the mouse GHOST location can be incorrect on exit. */
1594     setCursorGrabAccum(0, 0);
1595     m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
1596 #ifdef GHOST_X11_GRAB
1597     XUngrabPointer(m_display, CurrentTime);
1598 #endif
1599   }
1600
1601   XFlush(m_display);
1602
1603   return GHOST_kSuccess;
1604 }
1605
1606 GHOST_TSuccess GHOST_WindowX11::setWindowCursorShape(GHOST_TStandardCursor shape)
1607 {
1608   Cursor xcursor;
1609   if (getStandardCursor(shape, xcursor) == GHOST_kFailure) {
1610     getStandardCursor(GHOST_kStandardCursorDefault, xcursor);
1611   }
1612
1613   m_visible_cursor = xcursor;
1614
1615   XDefineCursor(m_display, m_window, xcursor);
1616   XFlush(m_display);
1617
1618   return GHOST_kSuccess;
1619 }
1620
1621 GHOST_TSuccess GHOST_WindowX11::hasCursorShape(GHOST_TStandardCursor shape)
1622 {
1623   Cursor xcursor;
1624   return getStandardCursor(shape, xcursor);
1625 }
1626
1627 GHOST_TSuccess GHOST_WindowX11::setWindowCustomCursorShape(GHOST_TUns8 *bitmap,
1628                                                            GHOST_TUns8 *mask,
1629                                                            int sizex,
1630                                                            int sizey,
1631                                                            int hotX,
1632                                                            int hotY,
1633                                                            bool /*canInvertColor*/)
1634 {
1635   Colormap colormap = DefaultColormap(m_display, m_visualInfo->screen);
1636   Pixmap bitmap_pix, mask_pix;
1637   XColor fg, bg;
1638
1639   if (XAllocNamedColor(m_display, colormap, "White", &fg, &fg) == 0)
1640     return GHOST_kFailure;
1641   if (XAllocNamedColor(m_display, colormap, "Black", &bg, &bg) == 0)
1642     return GHOST_kFailure;
1643
1644   if (m_custom_cursor) {
1645     XFreeCursor(m_display, m_custom_cursor);
1646   }
1647
1648   bitmap_pix = XCreateBitmapFromData(m_display, m_window, (char *)bitmap, sizex, sizey);
1649   mask_pix = XCreateBitmapFromData(m_display, m_window, (char *)mask, sizex, sizey);
1650
1651   m_custom_cursor = XCreatePixmapCursor(m_display, bitmap_pix, mask_pix, &fg, &bg, hotX, hotY);
1652   XDefineCursor(m_display, m_window, m_custom_cursor);
1653   XFlush(m_display);
1654
1655   m_visible_cursor = m_custom_cursor;
1656
1657   XFreePixmap(m_display, bitmap_pix);
1658   XFreePixmap(m_display, mask_pix);
1659
1660   XFreeColors(m_display, colormap, &fg.pixel, 1, 0L);
1661   XFreeColors(m_display, colormap, &bg.pixel, 1, 0L);
1662
1663   return GHOST_kSuccess;
1664 }
1665
1666 GHOST_TSuccess GHOST_WindowX11::beginFullScreen() const
1667 {
1668   {
1669     Window root_return;
1670     int x_return, y_return;
1671     unsigned int w_return, h_return, border_w_return, depth_return;
1672
1673     XGetGeometry(m_display,
1674                  m_window,
1675                  &root_return,
1676                  &x_return,
1677                  &y_return,
1678                  &w_return,
1679                  &h_return,
1680                  &border_w_return,
1681                  &depth_return);
1682
1683     m_system->setCursorPosition(w_return / 2, h_return / 2);
1684   }
1685
1686   /* Grab Keyboard & Mouse */
1687   int err;
1688
1689   err = XGrabKeyboard(m_display, m_window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
1690   if (err != GrabSuccess)
1691     printf("XGrabKeyboard failed %d\n", err);
1692
1693   err = XGrabPointer(m_display,
1694                      m_window,
1695                      False,
1696                      PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
1697                      GrabModeAsync,
1698                      GrabModeAsync,
1699                      m_window,
1700                      None,
1701                      CurrentTime);
1702   if (err != GrabSuccess)
1703     printf("XGrabPointer failed %d\n", err);
1704
1705   return GHOST_kSuccess;
1706 }
1707
1708 GHOST_TSuccess GHOST_WindowX11::endFullScreen() const
1709 {
1710   XUngrabKeyboard(m_display, CurrentTime);
1711   XUngrabPointer(m_display, CurrentTime);
1712
1713   return GHOST_kSuccess;
1714 }
1715
1716 GHOST_TUns16 GHOST_WindowX11::getDPIHint()
1717 {
1718   /* Try to read DPI setting set using xrdb */
1719   char *resMan = XResourceManagerString(m_display);
1720   if (resMan) {
1721     XrmDatabase xrdb = XrmGetStringDatabase(resMan);
1722     if (xrdb) {
1723       char *type = NULL;
1724       XrmValue val;
1725
1726       int success = XrmGetResource(xrdb, "Xft.dpi", "Xft.Dpi", &type, &val);
1727       if (success && type) {
1728         if (strcmp(type, "String") == 0) {
1729           return atoi((char *)val.addr);
1730         }
1731       }
1732     }
1733     XrmDestroyDatabase(xrdb);
1734   }
1735
1736   /* Fallback to calculating DPI using X reported DPI, set using xrandr --dpi */
1737   XWindowAttributes attr;
1738   if (!XGetWindowAttributes(m_display, m_window, &attr)) {
1739     /* Failed to get window attributes, return X11 default DPI */
1740     return 96;
1741   }
1742
1743   Screen *screen = attr.screen;
1744   int pixelWidth = WidthOfScreen(screen);
1745   int pixelHeight = HeightOfScreen(screen);
1746   int mmWidth = WidthMMOfScreen(screen);
1747   int mmHeight = HeightMMOfScreen(screen);
1748
1749   double pixelDiagonal = sqrt((pixelWidth * pixelWidth) + (pixelHeight * pixelHeight));
1750   double mmDiagonal = sqrt((mmWidth * mmWidth) + (mmHeight * mmHeight));
1751   float inchDiagonal = mmDiagonal * 0.039f;
1752   int dpi = pixelDiagonal / inchDiagonal;
1753   return dpi;
1754 }
1755
1756 GHOST_TSuccess GHOST_WindowX11::setProgressBar(float progress)
1757 {
1758   if (m_taskbar.is_valid()) {
1759     m_taskbar.set_progress(progress);
1760     m_taskbar.set_progress_enabled(true);
1761     return GHOST_kSuccess;
1762   }
1763
1764   return GHOST_kFailure;
1765 }
1766
1767 GHOST_TSuccess GHOST_WindowX11::endProgressBar()
1768 {
1769   if (m_taskbar.is_valid()) {
1770     m_taskbar.set_progress_enabled(false);
1771     return GHOST_kSuccess;
1772   }
1773
1774   return GHOST_kFailure;
1775 }