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