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