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