Panda3D
x11GraphicsWindow.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file x11GraphicsWindow.cxx
10  * @author rdb
11  * @date 2009-07-07
12  */
13 
14 #include "x11GraphicsWindow.h"
15 #include "config_x11display.h"
16 #include "x11GraphicsPipe.h"
17 
18 #include "graphicsPipe.h"
19 #include "keyboardButton.h"
20 #include "mouseButton.h"
21 #include "buttonMap.h"
22 #include "clockObject.h"
23 #include "pStatTimer.h"
24 #include "textEncoder.h"
25 #include "throw_event.h"
26 #include "lightReMutexHolder.h"
27 #include "nativeWindowHandle.h"
28 #include "virtualFileSystem.h"
29 #include "get_x11.h"
30 #include "pnmImage.h"
31 #include "pnmFileTypeRegistry.h"
32 #include "evdevInputDevice.h"
33 
34 #include <sys/time.h>
35 #include <fcntl.h>
36 
37 using std::istream;
38 using std::ostringstream;
39 using std::string;
40 
41 struct _XcursorFile {
42  void *closure;
43  int (*read)(XcursorFile *, unsigned char *, int);
44  int (*write)(XcursorFile *, unsigned char *, int);
45  int (*seek)(XcursorFile *, long, int);
46 };
47 
48 typedef struct _XcursorImage {
49  unsigned int version;
50  unsigned int size;
51  unsigned int width;
52  unsigned int height;
53  unsigned int xhot;
54  unsigned int yhot;
55  unsigned int delay;
56  unsigned int *pixels;
57 } XcursorImage;
58 
59 static int xcursor_read(XcursorFile *file, unsigned char *buf, int len) {
60  istream* str = (istream*) file->closure;
61  str->read((char*) buf, len);
62  return str->gcount();
63 }
64 
65 static int xcursor_write(XcursorFile *file, unsigned char *buf, int len) {
66  // Not implemented, we don't need it.
67  nassertr_always(false, 0);
68  return 0;
69 }
70 
71 static int xcursor_seek(XcursorFile *file, long offset, int whence) {
72  istream* str = (istream*) file->closure;
73  switch (whence) {
74  case SEEK_SET:
75  str->seekg(offset, istream::beg);
76  break;
77  case SEEK_CUR:
78  str->seekg(offset, istream::cur);
79  break;
80  case SEEK_END:
81  str->seekg(offset, istream::end);
82  }
83 
84  return str->tellg();
85 }
86 
87 TypeHandle x11GraphicsWindow::_type_handle;
88 
89 /**
90  *
91  */
92 x11GraphicsWindow::
93 x11GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
94  const string &name,
95  const FrameBufferProperties &fb_prop,
96  const WindowProperties &win_prop,
97  int flags,
99  GraphicsOutput *host) :
100  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
101 {
102  x11GraphicsPipe *x11_pipe;
103  DCAST_INTO_V(x11_pipe, _pipe);
104  _display = x11_pipe->get_display();
105  _screen = x11_pipe->get_screen();
106  _xwindow = (X11_Window)nullptr;
107  _ic = (XIC)nullptr;
108  _visual_info = nullptr;
109  _orig_size_id = -1;
110 
111  if (x11_pipe->_have_xrandr) {
112  // We may still need these functions after the pipe is already destroyed,
113  // so we copy them into the x11GraphicsWindow.
114  _XRRGetScreenInfo = x11_pipe->_XRRGetScreenInfo;
115  _XRRSetScreenConfig = x11_pipe->_XRRSetScreenConfig;
116  }
117 
118  _awaiting_configure = false;
119  _dga_mouse_enabled = false;
120  _override_redirect = False;
121  _wm_delete_window = x11_pipe->_wm_delete_window;
122 
123  PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
124  add_input_device(device);
125  _input = device;
126 }
127 
128 /**
129  *
130  */
131 x11GraphicsWindow::
132 ~x11GraphicsWindow() {
133  if (!_cursor_filenames.empty()) {
134  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
135  for (auto item : _cursor_filenames) {
136  XFreeCursor(_display, item.second);
137  }
138  }
139 }
140 
141 /**
142  * Returns the MouseData associated with the nth input device's pointer. This
143  * is deprecated; use get_pointer_device().get_pointer() instead, or for raw
144  * mice, use the InputDeviceManager interface.
145  */
147 get_pointer(int device) const {
148  MouseData result;
149  {
150  LightMutexHolder holder(_input_lock);
151  nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
152 
153  result = ((const GraphicsWindowInputDevice *)_input_devices[device].p())->get_pointer();
154 
155  // We recheck this immediately to get the most up-to-date value, but we
156  // won't bother waiting for the lock if we can't.
157  if (device == 0 && !_dga_mouse_enabled && result._in_window &&
158  x11GraphicsPipe::_x_mutex.try_lock()) {
159  XEvent event;
160  if (_xwindow != None &&
161  XQueryPointer(_display, _xwindow, &event.xbutton.root,
162  &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
163  &event.xbutton.x, &event.xbutton.y, &event.xbutton.state)) {
164  double time = ClockObject::get_global_clock()->get_real_time();
165  result._xpos = event.xbutton.x;
166  result._ypos = event.xbutton.y;
167  ((GraphicsWindowInputDevice *)_input_devices[0].p())->set_pointer_in_window(result._xpos, result._ypos, time);
168  }
169  x11GraphicsPipe::_x_mutex.release();
170  }
171  }
172  return result;
173 }
174 
175 /**
176  * Forces the pointer to the indicated position within the window, if
177  * possible.
178  *
179  * Returns true if successful, false on failure. This may fail if the mouse
180  * is not currently within the window, or if the API doesn't support this
181  * operation.
182  */
184 move_pointer(int device, int x, int y) {
185  // Note: this is not thread-safe; it should be called only from App.
186  // Probably not an issue.
187  if (device == 0) {
188  // Move the system mouse pointer.
189  PointerData md = _input->get_pointer();
190  if (!_properties.get_foreground() || !md.get_in_window()) {
191  // If the window doesn't have input focus, or the mouse isn't currently
192  // within the window, forget it.
193  return false;
194  }
195 
196  if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) {
197  if (!_dga_mouse_enabled) {
198  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
199  XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y);
200  }
201  _input->set_pointer_in_window(x, y);
202  }
203  return true;
204  } else {
205  // Can't move a raw mouse.
206  return false;
207  }
208 }
209 
210 /**
211  * Clears the entire framebuffer before rendering, according to the settings
212  * of get_color_clear_active() and get_depth_clear_active() (inherited from
213  * DrawableRegion).
214  *
215  * This function is called only within the draw thread.
216  */
218 clear(Thread *current_thread) {
219  if (is_any_clear_active()) {
220  // Evidently the NVIDIA driver may call glXCreateNewContext inside
221  // prepare_display_region, so we need to hold the X11 lock.
222  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
223  GraphicsOutput::clear(current_thread);
224  }
225 }
226 
227 /**
228  * This function will be called within the draw thread before beginning
229  * rendering for a given frame. It should do whatever setup is required, and
230  * return true if the frame should be rendered, or false if it should be
231  * skipped.
232  */
234 begin_frame(FrameMode mode, Thread *current_thread) {
235  PStatTimer timer(_make_current_pcollector, current_thread);
236 
237  begin_frame_spam(mode);
238  if (_gsg == nullptr) {
239  return false;
240  }
241  if (_awaiting_configure) {
242  // Don't attempt to draw while we have just reconfigured the window and we
243  // haven't got the notification back yet.
244  return false;
245  }
246 
247  // Reset the GSG state if this is the first time it has been used. (We
248  // can't just call reset() when we construct the GSG, because reset()
249  // requires having a current context.)
250  _gsg->reset_if_new();
251 
252  if (mode == FM_render) {
253  // begin_render_texture();
254  clear_cube_map_selection();
255  }
256 
257  _gsg->set_current_properties(&get_fb_properties());
258  return _gsg->begin_frame(current_thread);
259 }
260 
261 /**
262  * This function will be called within the draw thread after rendering is
263  * completed for a given frame. It should do whatever finalization is
264  * required.
265  */
267 end_frame(FrameMode mode, Thread *current_thread) {
268  end_frame_spam(mode);
269  nassertv(_gsg != nullptr);
270 
271  if (mode == FM_render) {
272  // end_render_texture();
273  copy_to_textures();
274  }
275 
276  _gsg->end_frame(current_thread);
277 
278  if (mode == FM_render) {
279  trigger_flip();
280  clear_cube_map_selection();
281  }
282 }
283 
284 /**
285  * Do whatever processing is necessary to ensure that the window responds to
286  * user events. Also, honor any requests recently made via
287  * request_properties()
288  *
289  * This function is called only within the window thread.
290  */
293  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
294 
296 
297  if (_xwindow == (X11_Window)0) {
298  return;
299  }
300 
301  XEvent event;
302  XKeyEvent keyrelease_event;
303  bool got_keyrelease_event = false;
304 
305  XConfigureEvent configure_event;
306  bool got_configure_event = false;
307 
308  WindowProperties properties;
309  bool changed_properties = false;
310 
311  while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
312  if (XFilterEvent(&event, None)) {
313  continue;
314  }
315 
316  if (got_keyrelease_event) {
317  // If a keyrelease event is immediately followed by a matching keypress
318  // event, that's just key repeat and we should treat the two events
319  // accordingly. It would be nice if X provided a way to differentiate
320  // between keyrepeat and explicit keypresses more generally.
321  got_keyrelease_event = false;
322 
323  if (event.type == KeyPress &&
324  event.xkey.keycode == keyrelease_event.keycode &&
325  (event.xkey.time - keyrelease_event.time <= 1)) {
326  // In particular, we only generate down messages for the repeated
327  // keys, not down-and-up messages.
328  handle_keystroke(event.xkey);
329 
330  // We thought about not generating the keypress event, but we need
331  // that repeat for backspace. Rethink later.
332  handle_keypress(event.xkey);
333  continue;
334 
335  } else {
336  // This keyrelease event is not immediately followed by a matching
337  // keypress event, so it's a genuine release.
338  handle_keyrelease(keyrelease_event);
339  }
340  }
341 
342  ButtonHandle button;
343 
344  switch (event.type) {
345  case ReparentNotify:
346  break;
347 
348  case ConfigureNotify:
349  // When resizing or moving the window, multiple ConfigureNotify events
350  // may be sent in rapid succession. We only respond to the last one.
351  configure_event = event.xconfigure;
352  got_configure_event = true;
353  break;
354 
355  case ButtonPress:
356  // This refers to the mouse buttons.
357  button = get_mouse_button(event.xbutton);
358  if (!_dga_mouse_enabled) {
359  _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
360  }
361  _input->button_down(button);
362  break;
363 
364  case ButtonRelease:
365  button = get_mouse_button(event.xbutton);
366  if (!_dga_mouse_enabled) {
367  _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
368  }
369  _input->button_up(button);
370  break;
371 
372  case MotionNotify:
373  if (_dga_mouse_enabled) {
374  PointerData md = _input->get_pointer();
375  _input->set_pointer_in_window(md.get_x() + event.xmotion.x_root, md.get_y() + event.xmotion.y_root);
376  } else {
377  _input->set_pointer_in_window(event.xmotion.x, event.xmotion.y);
378  }
379  break;
380 
381  case KeyPress:
382  handle_keystroke(event.xkey);
383  handle_keypress(event.xkey);
384  break;
385 
386  case KeyRelease:
387  // The KeyRelease can't be processed immediately, because we have to
388  // check first if it's immediately followed by a matching KeyPress
389  // event.
390  keyrelease_event = event.xkey;
391  got_keyrelease_event = true;
392  break;
393 
394  case EnterNotify:
395  if (_dga_mouse_enabled) {
396  PointerData md = _input->get_pointer();
397  _input->set_pointer_in_window(md.get_x(), md.get_y());
398  } else {
399  _input->set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
400  }
401  break;
402 
403  case LeaveNotify:
404  _input->set_pointer_out_of_window();
405  break;
406 
407  case FocusIn:
408  properties.set_foreground(true);
409  changed_properties = true;
410  break;
411 
412  case FocusOut:
413  _input->focus_lost();
414  properties.set_foreground(false);
415  changed_properties = true;
416  break;
417 
418  case UnmapNotify:
419  properties.set_minimized(true);
420  changed_properties = true;
421  break;
422 
423  case MapNotify:
424  properties.set_minimized(false);
425  changed_properties = true;
426 
427  // Auto-focus the window when it is mapped.
428  XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
429  break;
430 
431  case ClientMessage:
432  if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
433  // This is a message from the window manager indicating that the user
434  // has requested to close the window.
435  string close_request_event = get_close_request_event();
436  if (!close_request_event.empty()) {
437  // In this case, the app has indicated a desire to intercept the
438  // request and process it directly.
439  throw_event(close_request_event);
440 
441  } else {
442  // In this case, the default case, the app does not intend to
443  // service the request, so we do by closing the window.
444 
445  // TODO: don't release the gsg in the window thread.
446  close_window();
447  properties.set_open(false);
448  system_changed_properties(properties);
449  }
450  }
451  break;
452 
453  case DestroyNotify:
454  // Apparently, we never get a DestroyNotify on a toplevel window.
455  // Instead, we rely on hints from the window manager (see above).
456  x11display_cat.info()
457  << "DestroyNotify\n";
458  break;
459 
460  default:
461  x11display_cat.warning()
462  << "unhandled X event type " << event.type << "\n";
463  }
464  }
465 
466  if (got_configure_event) {
467  // Now handle the last configure event we found.
468  _awaiting_configure = false;
469 
470  // Is this the inner corner or the outer corner? The Xlib docs say it
471  // should be the outer corner, but it appears to be the inner corner on my
472  // own implementation, which is inconsistent with XConfigureWindow.
473  // (Panda really wants to work with the inner corner, anyway, but that
474  // means we need to fix XConfigureWindow too.)
475  properties.set_origin(configure_event.x, configure_event.y);
476  properties.set_size(configure_event.width, configure_event.height);
477 
478  if (_properties.get_fixed_size()) {
479  // If the window properties indicate a fixed size only, undo any attempt
480  // by the user to change them. In X, there doesn't appear to be a way
481  // to universally disallow this directly (although we do set the
482  // min_size and max_size to the same value, which seems to work for most
483  // window managers.)
484  if (configure_event.width != _fixed_size.get_x() ||
485  configure_event.height != _fixed_size.get_y()) {
486  XWindowChanges changes;
487  changes.width = _fixed_size.get_x();
488  changes.height = _fixed_size.get_y();
489  int value_mask = (CWWidth | CWHeight);
490  XConfigureWindow(_display, _xwindow, value_mask, &changes);
491  }
492  }
493 
494  // If the window was reconfigured, we may need to re-confine the mouse
495  // pointer. See GitHub bug #280.
496  if (_properties.get_mouse_mode() == WindowProperties::M_confined) {
497  X11_Cursor cursor = None;
498  if (_properties.get_cursor_hidden()) {
499  x11GraphicsPipe *x11_pipe;
500  DCAST_INTO_V(x11_pipe, _pipe);
501  cursor = x11_pipe->get_hidden_cursor();
502  }
503 
504  XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync, GrabModeAsync,
505  _xwindow, cursor, CurrentTime);
506  }
507 
508  changed_properties = true;
509  }
510 
511  if (properties.has_foreground() && (
512  _properties.get_mouse_mode() == WindowProperties::M_confined ||
513  _dga_mouse_enabled)) {
514  x11GraphicsPipe *x11_pipe;
515  DCAST_INTO_V(x11_pipe, _pipe);
516 
517  // Focus has changed, let's let go of the pointer if we've grabbed or re-grab it if needed
518  if (properties.get_foreground()) {
519  // Window is going to the foreground, re-grab the pointer
520  X11_Cursor cursor = None;
521  if (_properties.get_cursor_hidden()) {
522  cursor = x11_pipe->get_hidden_cursor();
523  }
524 
525  XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync, GrabModeAsync,
526  _xwindow, cursor, CurrentTime);
527  if (_dga_mouse_enabled) {
528  x11_pipe->enable_relative_mouse();
529  }
530  }
531  else {
532  // window is leaving the foreground, ungrab the pointer
533  if (_dga_mouse_enabled) {
534  x11_pipe->disable_relative_mouse();
535  } else if (_properties.get_mouse_mode() == WindowProperties::M_confined) {
536  XUngrabPointer(_display, CurrentTime);
537  }
538  }
539  }
540 
541  if (changed_properties) {
542  system_changed_properties(properties);
543  }
544 
545  if (got_keyrelease_event) {
546  // This keyrelease event is not immediately followed by a matching
547  // keypress event, so it's a genuine release.
548  handle_keyrelease(keyrelease_event);
549  }
550 }
551 
552 /**
553  * Applies the requested set of properties to the window, if possible, for
554  * instance to request a change in size or minimization status.
555  *
556  * The window properties are applied immediately, rather than waiting until
557  * the next frame. This implies that this method may *only* be called from
558  * within the window thread.
559  *
560  * The return value is true if the properties are set, false if they are
561  * ignored. This is mainly useful for derived classes to implement extensions
562  * to this function.
563  */
566  if (_pipe == nullptr) {
567  // If the pipe is null, we're probably closing down.
569  return;
570  }
571 
572  x11GraphicsPipe *x11_pipe;
573  DCAST_INTO_V(x11_pipe, _pipe);
574 
575  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
576 
577  // We're either going into or out of fullscreen, or are in fullscreen and
578  // are changing the resolution.
579  bool is_fullscreen = _properties.has_fullscreen() && _properties.get_fullscreen();
580  bool want_fullscreen = properties.has_fullscreen() ? properties.get_fullscreen() : is_fullscreen;
581 
582  if (want_fullscreen && properties.has_origin()) {
583  // If we're fullscreen, reject changes to the origin.
584  properties.clear_origin();
585  }
586 
587  if (is_fullscreen != want_fullscreen || (is_fullscreen && properties.has_size())) {
588  if (want_fullscreen) {
589  // OK, first figure out which CRTC the window is on. It may be on more
590  // than one, actually, so grab a point in the center in order to figure
591  // out which one it's more-or-less mostly on.
592  LPoint2i center(0, 0);
593  if (_properties.has_origin()) {
594  center = _properties.get_origin();
595  if (_properties.has_size()) {
596  center += _properties.get_size() / 2;
597  }
598  }
599  int x, y, width, height;
600  x11_pipe->find_fullscreen_crtc(center, x, y, width, height);
601 
602  // Which size should we go fullscreen in?
603  int reqsizex, reqsizey;
604  if (properties.has_size()) {
605  reqsizex = properties.get_x_size();
606  reqsizey = properties.get_y_size();
607  } else if (_properties.has_size()) {
608  reqsizex = _properties.get_x_size();
609  reqsizey = _properties.get_y_size();
610  } else {
611  reqsizex = width;
612  reqsizey = height;
613  }
614 
615  // Are we passing in pipe.display_width/height? This is actually the
616  // size of the virtual desktop, which may not be a real resolution, so
617  // if that is passed in, we have to assume that the user means to just
618  // fullscreen without changing the screen resolution.
619  if ((reqsizex == x11_pipe->get_display_width() &&
620  reqsizey == x11_pipe->get_display_height())
621  || (width == reqsizex && height == reqsizey)
622  || !x11_pipe->_have_xrandr) {
623 
624  // Cover the current CRTC.
625  properties.set_origin(x, y);
626  properties.set_size(width, height);
627 
628  if (x11display_cat.is_debug()) {
629  x11display_cat.debug()
630  << "Setting window to fullscreen on CRTC "
631  << width << "x" << height << "+" << x << "+" << y << "\n";
632  }
633  } else {
634  // We may need to change the screen resolution. The code below is
635  // suboptimal; in the future, we probably want to only touch the CRTC
636  // that the window is on.
637  XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, _xwindow ? _xwindow : x11_pipe->get_root());
638  SizeID old_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation);
639  SizeID new_size_id = (SizeID) -1;
640  int num_sizes = 0;
641 
642  XRRScreenSize *xrrs;
643  xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes);
644  for (int i = 0; i < num_sizes; ++i) {
645  if (xrrs[i].width == reqsizex &&
646  xrrs[i].height == reqsizey) {
647  new_size_id = i;
648  }
649  }
650  if (new_size_id == (SizeID) -1) {
651  x11display_cat.error()
652  << "Videocard has no supported display resolutions at specified res ("
653  << reqsizex << " x " << reqsizey << ")\n";
654 
655  // Just go fullscreen at native resolution, then.
656  properties.set_origin(x, y);
657  properties.set_size(width, height);
658  } else {
659  if (x11display_cat.is_debug()) {
660  x11display_cat.debug()
661  << "Switching to fullscreen with resolution "
662  << reqsizex << "x" << reqsizey << "\n";
663  }
664 
665  if (new_size_id != old_size_id) {
666  _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime);
667  if (_orig_size_id == (SizeID) -1) {
668  // Remember the original resolution so we can switch back to it.
669  _orig_size_id = old_size_id;
670  }
671 
672  // Since the above changes the entire screen configuration, we
673  // have to set the origin to 0, 0.
674  properties.set_origin(0, 0);
675  }
676  }
677  }
678  } else {
679  // Change the resolution back to what it was. Don't remove the SizeID
680  // typecast!
681  if (_orig_size_id != (SizeID) -1) {
682  XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, x11_pipe->get_root());
683  _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), _orig_size_id, _orig_rotation, CurrentTime);
684  _orig_size_id = (SizeID) -1;
685  }
686  // Set the origin back to what it was
687  if (!properties.has_origin() && _properties.has_origin()) {
688  properties.set_origin(_properties.get_x_origin(), _properties.get_y_origin());
689  }
690  }
691  }
692 
693  if (properties.has_origin()) {
694  // A coordinate of -2 means to center the window on screen.
695  if (properties.get_x_origin() == -2 || properties.get_y_origin() == -2) {
696  int x_origin = properties.get_x_origin();
697  int y_origin = properties.get_y_origin();
698  if (properties.has_size()) {
699  if (x_origin == -2) {
700  x_origin = 0.5 * (x11_pipe->get_display_width() - properties.get_x_size());
701  }
702  if (y_origin == -2) {
703  y_origin = 0.5 * (x11_pipe->get_display_height() - properties.get_y_size());
704  }
705  } else {
706  if (x_origin == -2) {
707  x_origin = 0.5 * (x11_pipe->get_display_width() - _properties.get_x_size());
708  }
709  if (y_origin == -2) {
710  y_origin = 0.5 * (x11_pipe->get_display_height() - _properties.get_y_size());
711  }
712  }
713  properties.set_origin(x_origin, y_origin);
714  }
715  }
716 
718  if (!properties.is_any_specified()) {
719  // The base class has already handled this case.
720  return;
721  }
722 
723  // The window is already open; we are limited to what we can change on the
724  // fly.
725 
726  // We'll pass some property requests on as a window manager hint.
727  set_wm_properties(properties, true);
728 
729  // The window title may be changed by issuing another hint request. Assume
730  // this will be honored.
731  if (properties.has_title()) {
732  _properties.set_title(properties.get_title());
733  properties.clear_title();
734  }
735 
736  // Same for fullscreen.
737  if (properties.has_fullscreen()) {
738  _properties.set_fullscreen(properties.get_fullscreen());
739  properties.clear_fullscreen();
740  }
741 
742  // The size and position of an already-open window are changed via explicit
743  // X calls. These may still get intercepted by the window manager. Rather
744  // than changing _properties immediately, we'll wait for the ConfigureNotify
745  // message to come back.
746  XWindowChanges changes;
747  int value_mask = 0;
748 
749  if (_properties.get_fullscreen()) {
750  changes.x = 0;
751  changes.y = 0;
752  value_mask |= CWX | CWY;
753  properties.clear_origin();
754 
755  } else if (properties.has_origin()) {
756  changes.x = properties.get_x_origin();
757  changes.y = properties.get_y_origin();
758  if (changes.x != -1) value_mask |= CWX;
759  if (changes.y != -1) value_mask |= CWY;
760  properties.clear_origin();
761  }
762 
763  // This, too. But we can't currently change out of fixed_size mode.
764  if (properties.has_fixed_size() && properties.get_fixed_size()) {
765  _properties.set_fixed_size(properties.get_fixed_size());
766  properties.clear_fixed_size();
767  _fixed_size = _properties.get_size();
768  }
769 
770  if (properties.has_size()) {
771  changes.width = properties.get_x_size();
772  changes.height = properties.get_y_size();
773  value_mask |= (CWWidth | CWHeight);
774 
775  if (_properties.get_fixed_size()) {
776  _fixed_size = properties.get_size();
777  }
778  properties.clear_size();
779  }
780 
781  if (properties.has_z_order()) {
782  // We'll send the classic stacking request through the standard interface,
783  // for users of primitive window managers; but we'll also send it as a
784  // window manager hint, for users of modern window managers.
785  _properties.set_z_order(properties.get_z_order());
786  switch (properties.get_z_order()) {
787  case WindowProperties::Z_bottom:
788  changes.stack_mode = Below;
789  break;
790 
791  case WindowProperties::Z_normal:
792  changes.stack_mode = TopIf;
793  break;
794 
795  case WindowProperties::Z_top:
796  changes.stack_mode = Above;
797  break;
798  }
799 
800  value_mask |= (CWStackMode);
801  properties.clear_z_order();
802  }
803 
804  // We hide the cursor by setting it to an invisible pixmap. We can also
805  // load a custom cursor from a file.
806  if (properties.has_cursor_hidden() || properties.has_cursor_filename()) {
807  if (properties.has_cursor_hidden()) {
808  _properties.set_cursor_hidden(properties.get_cursor_hidden());
809  properties.clear_cursor_hidden();
810  }
811  Filename cursor_filename;
812  if (properties.has_cursor_filename()) {
813  cursor_filename = properties.get_cursor_filename();
814  _properties.set_cursor_filename(cursor_filename);
815  properties.clear_cursor_filename();
816  }
817  Filename filename = properties.get_cursor_filename();
818  _properties.set_cursor_filename(filename);
819 
820  if (_properties.get_cursor_hidden()) {
821  XDefineCursor(_display, _xwindow, x11_pipe->get_hidden_cursor());
822 
823  } else if (!cursor_filename.empty()) {
824  // Note that if the cursor fails to load, cursor will be None
825  X11_Cursor cursor = get_cursor(cursor_filename);
826  XDefineCursor(_display, _xwindow, cursor);
827 
828  } else {
829  XDefineCursor(_display, _xwindow, None);
830  }
831 
832  // Regrab the mouse if we changed the cursor, otherwise it won't update.
833  if (!properties.has_mouse_mode() &&
834  _properties.get_mouse_mode() != WindowProperties::M_absolute) {
835  properties.set_mouse_mode(_properties.get_mouse_mode());
836  }
837  }
838 
839  if (properties.has_foreground()) {
840  if (properties.get_foreground()) {
841  XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
842  } else {
843  XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
844  }
845  properties.clear_foreground();
846  }
847 
848  if (properties.has_mouse_mode()) {
849  switch (properties.get_mouse_mode()) {
850  case WindowProperties::M_absolute:
851  XUngrabPointer(_display, CurrentTime);
852  if (_dga_mouse_enabled) {
853  x11_pipe->disable_relative_mouse();
854  _dga_mouse_enabled = false;
855  }
856  _properties.set_mouse_mode(WindowProperties::M_absolute);
857  properties.clear_mouse_mode();
858  break;
859 
860  case WindowProperties::M_relative:
861  if (!_dga_mouse_enabled) {
862  if (x11_pipe->supports_relative_mouse()) {
863  X11_Cursor cursor = None;
864  if (_properties.get_cursor_hidden()) {
865  x11GraphicsPipe *x11_pipe;
866  DCAST_INTO_V(x11_pipe, _pipe);
867  cursor = x11_pipe->get_hidden_cursor();
868  }
869 
870  if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
871  GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
872  x11display_cat.error() << "Failed to grab pointer!\n";
873  } else {
874  x11_pipe->enable_relative_mouse();
875 
876  _properties.set_mouse_mode(WindowProperties::M_relative);
877  properties.clear_mouse_mode();
878  _dga_mouse_enabled = true;
879 
880  // Get the real mouse position, so we can addsubtract our relative
881  // coordinates later.
882  XEvent event;
883  XQueryPointer(_display, _xwindow, &event.xbutton.root,
884  &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
885  &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
886  _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
887  }
888  } else {
889  x11display_cat.warning()
890  << "XF86DGA extension not available, cannot enable relative mouse mode\n";
891  _dga_mouse_enabled = false;
892  }
893  }
894  break;
895 
896  case WindowProperties::M_confined:
897  {
898  x11GraphicsPipe *x11_pipe;
899  DCAST_INTO_V(x11_pipe, _pipe);
900 
901  if (_dga_mouse_enabled) {
902  x11_pipe->disable_relative_mouse();
903  _dga_mouse_enabled = false;
904  }
905  X11_Cursor cursor = None;
906  if (_properties.get_cursor_hidden()) {
907  cursor = x11_pipe->get_hidden_cursor();
908  }
909 
910  if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
911  GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
912  x11display_cat.error() << "Failed to grab pointer!\n";
913  } else {
914  _properties.set_mouse_mode(WindowProperties::M_confined);
915  properties.clear_mouse_mode();
916  }
917  }
918  break;
919  }
920  }
921 
922  if (value_mask != 0) {
923  // We must call this after changing the WM properties, otherwise we may
924  // get misleading ConfigureNotify events in the wrong order.
925  XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
926 
927  // Don't draw anything until this is done reconfiguring.
928  _awaiting_configure = true;
929  }
930 }
931 
932 /**
933  * Overridden from GraphicsWindow.
934  */
935 void x11GraphicsWindow::
936 mouse_mode_absolute() {
937  // unused: remove in 1.10!
938 }
939 
940 /**
941  * Overridden from GraphicsWindow.
942  */
943 void x11GraphicsWindow::
944 mouse_mode_relative() {
945  // unused: remove in 1.10!
946 }
947 
948 /**
949  * Closes the window right now. Called from the window thread.
950  */
951 void x11GraphicsWindow::
952 close_window() {
953  if (_gsg != nullptr) {
954  _gsg.clear();
955  }
956 
957  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
958  if (_ic != (XIC)nullptr) {
959  XDestroyIC(_ic);
960  _ic = (XIC)nullptr;
961  }
962 
963  if (_xwindow != (X11_Window)nullptr) {
964  XDestroyWindow(_display, _xwindow);
965  _xwindow = (X11_Window)nullptr;
966 
967  // This may be necessary if we just closed the last X window in an
968  // application, so the server hears the close request.
969  XFlush(_display);
970  }
971 
972  // Change the resolution back to what it was. Don't remove the SizeID
973  // typecast!
974  if (_orig_size_id != (SizeID) -1) {
975  X11_Window root;
976  if (_pipe != nullptr) {
977  x11GraphicsPipe *x11_pipe;
978  DCAST_INTO_V(x11_pipe, _pipe);
979  root = x11_pipe->get_root();
980  } else {
981  // Oops. Looks like the pipe was destroyed before the window gets
982  // closed. Oh well, let's get the root window by ourselves.
983  root = RootWindow(_display, _screen);
984  }
985  XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, root);
986  _XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime);
987  _orig_size_id = -1;
988  }
989 
990  GraphicsWindow::close_window();
991 }
992 
993 /**
994  * Opens the window right now. Called from the window thread. Returns true
995  * if the window is successfully opened, or false if there was a problem.
996  */
997 bool x11GraphicsWindow::
998 open_window() {
999  if (_visual_info == nullptr) {
1000  // No X visual for this fbconfig; how can we open the window?
1001  x11display_cat.error()
1002  << "No X visual: cannot open window.\n";
1003  return false;
1004  }
1005 
1006  x11GraphicsPipe *x11_pipe;
1007  DCAST_INTO_R(x11_pipe, _pipe, false);
1008 
1009  if (!_properties.has_origin()) {
1010  _properties.set_origin(0, 0);
1011  }
1012  if (!_properties.has_size()) {
1013  _properties.set_size(100, 100);
1014  }
1015 
1016  // Make sure we are not making X11 calls from other threads.
1017  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
1018 
1019  X11_Window parent_window = x11_pipe->get_root();
1020  WindowHandle *window_handle = _properties.get_parent_window();
1021  if (window_handle != nullptr) {
1022  x11display_cat.info()
1023  << "Got parent_window " << *window_handle << "\n";
1024  WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
1025  if (os_handle != nullptr) {
1026  x11display_cat.info()
1027  << "os_handle type " << os_handle->get_type() << "\n";
1028 
1029  if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
1030  NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
1031  parent_window = x11_handle->get_handle();
1032  } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
1033  NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
1034  parent_window = (X11_Window)int_handle->get_handle();
1035  }
1036  }
1037  }
1038  _parent_window_handle = window_handle;
1039 
1040  _event_mask =
1041  ButtonPressMask | ButtonReleaseMask |
1042  KeyPressMask | KeyReleaseMask |
1043  EnterWindowMask | LeaveWindowMask |
1044  PointerMotionMask |
1045  FocusChangeMask | StructureNotifyMask;
1046 
1047  // Initialize window attributes
1048  XSetWindowAttributes wa;
1049  wa.background_pixel = XBlackPixel(_display, _screen);
1050  wa.border_pixel = 0;
1051  wa.colormap = _colormap;
1052  wa.event_mask = _event_mask;
1053  wa.override_redirect = _override_redirect;
1054 
1055  unsigned long attrib_mask =
1056  CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
1057 
1058  _xwindow = XCreateWindow
1059  (_display, parent_window,
1060  _properties.get_x_origin(), _properties.get_y_origin(),
1061  _properties.get_x_size(), _properties.get_y_size(),
1062  0, _visual_info->depth, InputOutput,
1063  _visual_info->visual, attrib_mask, &wa);
1064 
1065  if (_xwindow == (X11_Window)0) {
1066  x11display_cat.error()
1067  << "failed to create X window.\n";
1068  return false;
1069  }
1070 
1071  if (_properties.get_fixed_size()) {
1072  _fixed_size = _properties.get_size();
1073  }
1074 
1075  set_wm_properties(_properties, false);
1076 
1077  // We don't specify any fancy properties of the XIC. It would be nicer if
1078  // we could support fancy IM's that want preedit callbacks, etc., but that
1079  // can wait until we have an X server that actually supports these to test
1080  // it on.
1081  XIM im = x11_pipe->get_im();
1082  _ic = nullptr;
1083  if (im) {
1084  _ic = XCreateIC
1085  (im,
1086  XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
1087  nullptr);
1088  if (_ic == (XIC)nullptr) {
1089  x11display_cat.warning()
1090  << "Couldn't create input context.\n";
1091  }
1092  }
1093 
1094  if (_properties.get_cursor_hidden()) {
1095  XDefineCursor(_display, _xwindow, x11_pipe->get_hidden_cursor());
1096 
1097  } else if (_properties.has_cursor_filename() && !_properties.get_cursor_filename().empty()) {
1098  // Note that if the cursor fails to load, cursor will be None
1099  X11_Cursor cursor = get_cursor(_properties.get_cursor_filename());
1100  XDefineCursor(_display, _xwindow, cursor);
1101  }
1102 
1103  XMapWindow(_display, _xwindow);
1104 
1105  if (_properties.get_raw_mice()) {
1106  open_raw_mice();
1107  } else {
1108  if (x11display_cat.is_debug()) {
1109  x11display_cat.debug()
1110  << "Raw mice not requested.\n";
1111  }
1112  }
1113 
1114  // Create a WindowHandle for ourselves
1115  _window_handle = NativeWindowHandle::make_x11(_xwindow);
1116 
1117  // And tell our parent window that we're now its child.
1118  if (_parent_window_handle != nullptr) {
1119  _parent_window_handle->attach_child(_window_handle);
1120  }
1121 
1122  return true;
1123 }
1124 
1125 /**
1126  * Asks the window manager to set the appropriate properties. In X, these
1127  * properties cannot be specified directly by the application; they must be
1128  * requested via the window manager, which may or may not choose to honor the
1129  * request.
1130  *
1131  * If already_mapped is true, the window has already been mapped (manifested)
1132  * on the display. This means we may need to use a different action in some
1133  * cases.
1134  *
1135  * Assumes the X11 lock is held.
1136  */
1137 void x11GraphicsWindow::
1138 set_wm_properties(const WindowProperties &properties, bool already_mapped) {
1139  x11GraphicsPipe *x11_pipe;
1140  DCAST_INTO_V(x11_pipe, _pipe);
1141 
1142  // Name the window if there is a name
1143  XTextProperty window_name;
1144  XTextProperty *window_name_p = nullptr;
1145  if (properties.has_title()) {
1146  const char *name = properties.get_title().c_str();
1147  if (XStringListToTextProperty((char **)&name, 1, &window_name) != 0) {
1148  window_name_p = &window_name;
1149  }
1150  }
1151 
1152  // The size hints request a window of a particular size andor a particular
1153  // placement onscreen.
1154  XSizeHints *size_hints_p = nullptr;
1155  if (properties.has_origin() || properties.has_size()) {
1156  size_hints_p = XAllocSizeHints();
1157  if (size_hints_p != nullptr) {
1158  if (properties.has_origin()) {
1159  size_hints_p->x = properties.get_x_origin();
1160  size_hints_p->y = properties.get_y_origin();
1161  size_hints_p->flags |= USPosition;
1162  }
1163  LVecBase2i size = _properties.get_size();
1164  if (properties.has_size()) {
1165  size = properties.get_size();
1166  size_hints_p->width = size.get_x();
1167  size_hints_p->height = size.get_y();
1168  size_hints_p->flags |= USSize;
1169  }
1170  if (properties.get_fixed_size()) {
1171  size_hints_p->min_width = size.get_x();
1172  size_hints_p->min_height = size.get_y();
1173  size_hints_p->max_width = size.get_x();
1174  size_hints_p->max_height = size.get_y();
1175  size_hints_p->flags |= (PMinSize | PMaxSize);
1176  }
1177  }
1178  }
1179 
1180  // The window manager hints include requests to the window manager other
1181  // than those specific to window geometry.
1182  XWMHints *wm_hints_p = nullptr;
1183  wm_hints_p = XAllocWMHints();
1184  if (wm_hints_p != nullptr) {
1185  if (properties.has_minimized() && properties.get_minimized()) {
1186  wm_hints_p->initial_state = IconicState;
1187  } else {
1188  wm_hints_p->initial_state = NormalState;
1189  }
1190  wm_hints_p->flags = StateHint;
1191  }
1192 
1193  // Two competing window manager interfaces have evolved. One of them allows
1194  // to set certain properties as a "type"; the other one as a "state". We'll
1195  // try to honor both.
1196  static const int max_type_data = 32;
1197  int32_t type_data[max_type_data];
1198  int next_type_data = 0;
1199 
1200  static const int max_state_data = 32;
1201  int32_t state_data[max_state_data];
1202  int next_state_data = 0;
1203 
1204  static const int max_set_data = 32;
1205  class SetAction {
1206  public:
1207  inline SetAction() { }
1208  inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
1209  Atom _state;
1210  Atom _action;
1211  };
1212  SetAction set_data[max_set_data];
1213  int next_set_data = 0;
1214 
1215  if (properties.has_fullscreen()) {
1216  if (properties.get_fullscreen()) {
1217  // For a "fullscreen" request, we pass this through, hoping the window
1218  // manager will support EWMH.
1219  type_data[next_type_data++] = x11_pipe->_net_wm_window_type_fullscreen;
1220 
1221  // We also request it as a state.
1222  state_data[next_state_data++] = x11_pipe->_net_wm_state_fullscreen;
1223  // Don't ask me why this has to be 10 and not _net_wm_state_add. It
1224  // doesn't seem to work otherwise.
1225  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 1);
1226 
1227  } else {
1228  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 0);
1229  }
1230  }
1231 
1232  // If we asked for a window without a border, there's no excellent way to
1233  // arrange that. For users whose window managers follow the EWMH
1234  // specification, we can ask for a "splash" screen, which is usually
1235  // undecorated. It's not exactly right, but the spec doesn't give us an
1236  // exactly-right option.
1237 
1238  // For other users, we'll totally punt and just set the window's Class to
1239  // "Undecorated", and let the user configure hisher window manager not to
1240  // put a border around windows of this class.
1241  XClassHint *class_hints_p = nullptr;
1242  if (!x_wm_class.empty()) {
1243  // Unless the user wanted to use his own WM_CLASS, of course.
1244  class_hints_p = XAllocClassHint();
1245  class_hints_p->res_class = (char*) x_wm_class.c_str();
1246  if (!x_wm_class_name.empty()) {
1247  class_hints_p->res_name = (char*) x_wm_class_name.c_str();
1248  }
1249 
1250  } else if (properties.get_undecorated() || properties.get_fullscreen()) {
1251  class_hints_p = XAllocClassHint();
1252  class_hints_p->res_class = (char*) "Undecorated";
1253  }
1254 
1255  if (properties.get_undecorated() && !properties.get_fullscreen()) {
1256  type_data[next_type_data++] = x11_pipe->_net_wm_window_type_splash;
1257  }
1258 
1259  if (properties.has_z_order()) {
1260  switch (properties.get_z_order()) {
1261  case WindowProperties::Z_bottom:
1262  state_data[next_state_data++] = x11_pipe->_net_wm_state_below;
1263  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1264  x11_pipe->_net_wm_state_add);
1265  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1266  x11_pipe->_net_wm_state_remove);
1267  break;
1268 
1269  case WindowProperties::Z_normal:
1270  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1271  x11_pipe->_net_wm_state_remove);
1272  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1273  x11_pipe->_net_wm_state_remove);
1274  break;
1275 
1276  case WindowProperties::Z_top:
1277  state_data[next_state_data++] = x11_pipe->_net_wm_state_above;
1278  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1279  x11_pipe->_net_wm_state_remove);
1280  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1281  x11_pipe->_net_wm_state_add);
1282  break;
1283  }
1284  }
1285 
1286  nassertv(next_type_data < max_type_data);
1287  nassertv(next_state_data < max_state_data);
1288  nassertv(next_set_data < max_set_data);
1289 
1290  // Add the process ID as a convenience for other applications.
1291  int32_t pid = getpid();
1292  XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_pid,
1293  XA_CARDINAL, 32, PropModeReplace,
1294  (unsigned char *)&pid, 1);
1295 
1296  // Disable compositing effects in fullscreen mode.
1297  if (properties.has_fullscreen()) {
1298  int32_t compositor = properties.get_fullscreen() ? 1 : 0;
1299  XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_bypass_compositor,
1300  XA_CARDINAL, 32, PropModeReplace,
1301  (unsigned char *)&compositor, 1);
1302  }
1303 
1304  XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type,
1305  XA_ATOM, 32, PropModeReplace,
1306  (unsigned char *)type_data, next_type_data);
1307 
1308  // Request the state properties all at once.
1309  XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_state,
1310  XA_ATOM, 32, PropModeReplace,
1311  (unsigned char *)state_data, next_state_data);
1312 
1313  if (already_mapped) {
1314  // We have to request state changes differently when the window has been
1315  // mapped. To do this, we need to send a client message to the root
1316  // window for each change.
1317 
1318  x11GraphicsPipe *x11_pipe;
1319  DCAST_INTO_V(x11_pipe, _pipe);
1320 
1321  for (int i = 0; i < next_set_data; ++i) {
1322  XClientMessageEvent event;
1323  memset(&event, 0, sizeof(event));
1324  event.type = ClientMessage;
1325  event.send_event = True;
1326  event.display = _display;
1327  event.window = _xwindow;
1328  event.message_type = x11_pipe->_net_wm_state;
1329  event.format = 32;
1330  event.data.l[0] = set_data[i]._action;
1331  event.data.l[1] = set_data[i]._state;
1332  event.data.l[2] = 0;
1333  event.data.l[3] = 1;
1334 
1335  XSendEvent(_display, x11_pipe->get_root(), True, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&event);
1336  }
1337  }
1338 
1339  XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
1340  nullptr, 0, size_hints_p, wm_hints_p, class_hints_p);
1341 
1342  if (size_hints_p != nullptr) {
1343  XFree(size_hints_p);
1344  }
1345  if (wm_hints_p != nullptr) {
1346  XFree(wm_hints_p);
1347  }
1348  if (class_hints_p != nullptr) {
1349  XFree(class_hints_p);
1350  }
1351 
1352  // Also, indicate to the window manager that we'd like to get a chance to
1353  // close our windows cleanly, rather than being rudely disconnected from the
1354  // X server if the user requests a window close.
1355  Atom protocols[] = {
1356  _wm_delete_window,
1357  };
1358 
1359  XSetWMProtocols(_display, _xwindow, protocols,
1360  sizeof(protocols) / sizeof(Atom));
1361 }
1362 
1363 /**
1364  * Allocates a colormap appropriate to the visual and stores in in the
1365  * _colormap method.
1366  */
1367 void x11GraphicsWindow::
1368 setup_colormap(XVisualInfo *visual) {
1369  x11GraphicsPipe *x11_pipe;
1370  DCAST_INTO_V(x11_pipe, _pipe);
1371  X11_Window root_window = x11_pipe->get_root();
1372 
1373  _colormap = XCreateColormap(_display, root_window,
1374  visual->visual, AllocNone);
1375 }
1376 
1377 /**
1378  * Adds raw mice to the _input_devices list.
1379  * @deprecated obtain raw devices via the device manager instead.
1380  */
1381 void x11GraphicsWindow::
1382 open_raw_mice() {
1383 #ifdef PHAVE_LINUX_INPUT_H
1384  bool any_present = false;
1385  bool any_mice = false;
1386 
1387  for (int i=0; i<64; i++) {
1388  ostringstream fnb;
1389  fnb << "/dev/input/event" << i;
1390  string fn = fnb.str();
1391  int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
1392  if (fd >= 0) {
1393  EvdevInputDevice *device = new EvdevInputDevice(nullptr, fd);
1394  nassertd(device != NULL) continue;
1395 
1396  if (device->has_pointer()) {
1397  add_input_device(device);
1398 
1399  x11display_cat.info()
1400  << "Raw mouse " << _input_devices.size()
1401  << " detected: " << device->get_name() << "\n";
1402 
1403  any_mice = true;
1404  any_present = true;
1405  }
1406  } else {
1407  if (errno == ENOENT || errno == ENOTDIR) {
1408  break;
1409  } else {
1410  any_present = true;
1411  x11display_cat.error()
1412  << "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
1413  }
1414  }
1415  }
1416 
1417  if (any_mice) {
1418  _properties.set_raw_mice(true);
1419 
1420  } else if (!any_present) {
1421  x11display_cat.error() <<
1422  "Opening raw mice: files not found: /dev/input/event*\n";
1423 
1424  } else {
1425  x11display_cat.error() <<
1426  "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
1427  }
1428 #else
1429  x11display_cat.error() <<
1430  "Opening raw mice: panda not compiled with raw mouse support.\n";
1431 #endif
1432 }
1433 
1434 /**
1435  * Generates a keystroke corresponding to the indicated X KeyPress event.
1436  */
1437 void x11GraphicsWindow::
1438 handle_keystroke(XKeyEvent &event) {
1439  if (!_dga_mouse_enabled) {
1440  _input->set_pointer_in_window(event.x, event.y);
1441  }
1442 
1443  if (_ic) {
1444  // First, get the keystroke as a wide-character sequence.
1445  static const int buffer_size = 256;
1446  wchar_t buffer[buffer_size];
1447  Status status;
1448  int len = XwcLookupString(_ic, &event, buffer, buffer_size, nullptr,
1449  &status);
1450  if (status == XBufferOverflow) {
1451  x11display_cat.error()
1452  << "Overflowed input buffer.\n";
1453  }
1454 
1455  // Now each of the returned wide characters represents a keystroke.
1456  for (int i = 0; i < len; i++) {
1457  _input->keystroke(buffer[i]);
1458  }
1459 
1460  } else {
1461  // Without an input context, just get the ascii keypress.
1462  ButtonHandle button = get_button(event, true);
1463  if (button.has_ascii_equivalent()) {
1464  _input->keystroke(button.get_ascii_equivalent());
1465  }
1466  }
1467 }
1468 
1469 /**
1470  * Generates a keypress corresponding to the indicated X KeyPress event.
1471  */
1472 void x11GraphicsWindow::
1473 handle_keypress(XKeyEvent &event) {
1474  if (!_dga_mouse_enabled) {
1475  _input->set_pointer_in_window(event.x, event.y);
1476  }
1477 
1478  // Now get the raw unshifted button.
1479  ButtonHandle button = get_button(event, false);
1480  if (button != ButtonHandle::none()) {
1481  if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1482  _input->button_down(KeyboardButton::control());
1483  }
1484  if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1485  _input->button_down(KeyboardButton::shift());
1486  }
1487  if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1488  _input->button_down(KeyboardButton::alt());
1489  }
1490  if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1491  _input->button_down(KeyboardButton::meta());
1492  }
1493  _input->button_down(button);
1494  }
1495 
1496  if (event.keycode >= 9 && event.keycode <= 135) {
1497  ButtonHandle raw_button = map_raw_button(event.keycode);
1498  if (raw_button != ButtonHandle::none()) {
1499  _input->raw_button_down(raw_button);
1500  }
1501  }
1502 }
1503 
1504 /**
1505  * Generates a keyrelease corresponding to the indicated X KeyRelease event.
1506  */
1507 void x11GraphicsWindow::
1508 handle_keyrelease(XKeyEvent &event) {
1509  if (!_dga_mouse_enabled) {
1510  _input->set_pointer_in_window(event.x, event.y);
1511  }
1512 
1513  // Now get the raw unshifted button.
1514  ButtonHandle button = get_button(event, false);
1515  if (button != ButtonHandle::none()) {
1516  if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1517  _input->button_up(KeyboardButton::control());
1518  }
1519  if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1520  _input->button_up(KeyboardButton::shift());
1521  }
1522  if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1523  _input->button_up(KeyboardButton::alt());
1524  }
1525  if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1526  _input->button_up(KeyboardButton::meta());
1527  }
1528  _input->button_up(button);
1529  }
1530 
1531  if (event.keycode >= 9 && event.keycode <= 135) {
1532  ButtonHandle raw_button = map_raw_button(event.keycode);
1533  if (raw_button != ButtonHandle::none()) {
1534  _input->raw_button_up(raw_button);
1535  }
1536  }
1537 }
1538 
1539 /**
1540  * Returns the Panda ButtonHandle corresponding to the keyboard button
1541  * indicated by the given key event.
1542  */
1543 ButtonHandle x11GraphicsWindow::
1544 get_button(XKeyEvent &key_event, bool allow_shift) {
1545  KeySym key = XLookupKeysym(&key_event, 0);
1546 
1547  if ((key_event.state & Mod2Mask) != 0) {
1548  // Mod2Mask corresponds to NumLock being in effect. In this case, we want
1549  // to get the alternate keysym associated with any keypad keys. Weird
1550  // system.
1551  KeySym k2;
1552  ButtonHandle button;
1553  switch (key) {
1554  case XK_KP_Space:
1555  case XK_KP_Tab:
1556  case XK_KP_Enter:
1557  case XK_KP_F1:
1558  case XK_KP_F2:
1559  case XK_KP_F3:
1560  case XK_KP_F4:
1561  case XK_KP_Equal:
1562  case XK_KP_Multiply:
1563  case XK_KP_Add:
1564  case XK_KP_Separator:
1565  case XK_KP_Subtract:
1566  case XK_KP_Divide:
1567  case XK_KP_Left:
1568  case XK_KP_Up:
1569  case XK_KP_Right:
1570  case XK_KP_Down:
1571  case XK_KP_Begin:
1572  case XK_KP_Prior:
1573  case XK_KP_Next:
1574  case XK_KP_Home:
1575  case XK_KP_End:
1576  case XK_KP_Insert:
1577  case XK_KP_Delete:
1578  case XK_KP_0:
1579  case XK_KP_1:
1580  case XK_KP_2:
1581  case XK_KP_3:
1582  case XK_KP_4:
1583  case XK_KP_5:
1584  case XK_KP_6:
1585  case XK_KP_7:
1586  case XK_KP_8:
1587  case XK_KP_9:
1588  k2 = XLookupKeysym(&key_event, 1);
1589  button = map_button(k2);
1590  if (button != ButtonHandle::none()) {
1591  return button;
1592  }
1593  // If that didn't produce a button we know, just fall through and handle
1594  // the normal, un-numlocked key.
1595  break;
1596 
1597  default:
1598  break;
1599  }
1600  }
1601 
1602  if (allow_shift) {
1603  // If shift is held down, get the shifted keysym.
1604  if ((key_event.state & ShiftMask) != 0) {
1605  KeySym k2 = XLookupKeysym(&key_event, 1);
1606  ButtonHandle button = map_button(k2);
1607  if (button != ButtonHandle::none()) {
1608  return button;
1609  }
1610  }
1611 
1612  // If caps lock is down, shift lowercase letters to uppercase. We can do
1613  // this in just the ASCII set, because we handle international keyboards
1614  // elsewhere (via an input context).
1615  if ((key_event.state & (ShiftMask | LockMask)) != 0) {
1616  if (key >= XK_a && key <= XK_z) {
1617  key += (XK_A - XK_a);
1618  }
1619  }
1620  }
1621 
1622  return map_button(key);
1623 }
1624 
1625 /**
1626  * Maps from a single X keysym to Panda's ButtonHandle. Called by
1627  * get_button(), above.
1628  */
1629 ButtonHandle x11GraphicsWindow::
1630 map_button(KeySym key) const {
1631  switch (key) {
1632  case NoSymbol:
1633  return ButtonHandle::none();
1634  case XK_BackSpace:
1635  return KeyboardButton::backspace();
1636  case XK_Tab:
1637  case XK_KP_Tab:
1638  return KeyboardButton::tab();
1639  case XK_Return:
1640  case XK_KP_Enter:
1641  return KeyboardButton::enter();
1642  case XK_Escape:
1643  return KeyboardButton::escape();
1644  case XK_KP_Space:
1645  case XK_space:
1646  return KeyboardButton::space();
1647  case XK_exclam:
1648  return KeyboardButton::ascii_key('!');
1649  case XK_quotedbl:
1650  return KeyboardButton::ascii_key('"');
1651  case XK_numbersign:
1652  return KeyboardButton::ascii_key('#');
1653  case XK_dollar:
1654  return KeyboardButton::ascii_key('$');
1655  case XK_percent:
1656  return KeyboardButton::ascii_key('%');
1657  case XK_ampersand:
1658  return KeyboardButton::ascii_key('&');
1659  case XK_apostrophe: // == XK_quoteright
1660  case XK_dead_acute: // on int'l keyboards
1661  return KeyboardButton::ascii_key('\'');
1662  case XK_parenleft:
1663  return KeyboardButton::ascii_key('(');
1664  case XK_parenright:
1665  return KeyboardButton::ascii_key(')');
1666  case XK_asterisk:
1667  case XK_KP_Multiply:
1668  return KeyboardButton::ascii_key('*');
1669  case XK_plus:
1670  case XK_KP_Add:
1671  return KeyboardButton::ascii_key('+');
1672  case XK_comma:
1673  case XK_KP_Separator:
1674  return KeyboardButton::ascii_key(',');
1675  case XK_minus:
1676  case XK_KP_Subtract:
1677  return KeyboardButton::ascii_key('-');
1678  case XK_period:
1679  case XK_KP_Decimal:
1680  return KeyboardButton::ascii_key('.');
1681  case XK_slash:
1682  case XK_KP_Divide:
1683  return KeyboardButton::ascii_key('/');
1684  case XK_0:
1685  case XK_KP_0:
1686  return KeyboardButton::ascii_key('0');
1687  case XK_1:
1688  case XK_KP_1:
1689  return KeyboardButton::ascii_key('1');
1690  case XK_2:
1691  case XK_KP_2:
1692  return KeyboardButton::ascii_key('2');
1693  case XK_3:
1694  case XK_KP_3:
1695  return KeyboardButton::ascii_key('3');
1696  case XK_4:
1697  case XK_KP_4:
1698  return KeyboardButton::ascii_key('4');
1699  case XK_5:
1700  case XK_KP_5:
1701  return KeyboardButton::ascii_key('5');
1702  case XK_6:
1703  case XK_KP_6:
1704  return KeyboardButton::ascii_key('6');
1705  case XK_7:
1706  case XK_KP_7:
1707  return KeyboardButton::ascii_key('7');
1708  case XK_8:
1709  case XK_KP_8:
1710  return KeyboardButton::ascii_key('8');
1711  case XK_9:
1712  case XK_KP_9:
1713  return KeyboardButton::ascii_key('9');
1714  case XK_colon:
1715  return KeyboardButton::ascii_key(':');
1716  case XK_semicolon:
1717  return KeyboardButton::ascii_key(';');
1718  case XK_less:
1719  return KeyboardButton::ascii_key('<');
1720  case XK_equal:
1721  case XK_KP_Equal:
1722  return KeyboardButton::ascii_key('=');
1723  case XK_greater:
1724  return KeyboardButton::ascii_key('>');
1725  case XK_question:
1726  return KeyboardButton::ascii_key('?');
1727  case XK_at:
1728  return KeyboardButton::ascii_key('@');
1729  case XK_A:
1730  return KeyboardButton::ascii_key('A');
1731  case XK_B:
1732  return KeyboardButton::ascii_key('B');
1733  case XK_C:
1734  return KeyboardButton::ascii_key('C');
1735  case XK_D:
1736  return KeyboardButton::ascii_key('D');
1737  case XK_E:
1738  return KeyboardButton::ascii_key('E');
1739  case XK_F:
1740  return KeyboardButton::ascii_key('F');
1741  case XK_G:
1742  return KeyboardButton::ascii_key('G');
1743  case XK_H:
1744  return KeyboardButton::ascii_key('H');
1745  case XK_I:
1746  return KeyboardButton::ascii_key('I');
1747  case XK_J:
1748  return KeyboardButton::ascii_key('J');
1749  case XK_K:
1750  return KeyboardButton::ascii_key('K');
1751  case XK_L:
1752  return KeyboardButton::ascii_key('L');
1753  case XK_M:
1754  return KeyboardButton::ascii_key('M');
1755  case XK_N:
1756  return KeyboardButton::ascii_key('N');
1757  case XK_O:
1758  return KeyboardButton::ascii_key('O');
1759  case XK_P:
1760  return KeyboardButton::ascii_key('P');
1761  case XK_Q:
1762  return KeyboardButton::ascii_key('Q');
1763  case XK_R:
1764  return KeyboardButton::ascii_key('R');
1765  case XK_S:
1766  return KeyboardButton::ascii_key('S');
1767  case XK_T:
1768  return KeyboardButton::ascii_key('T');
1769  case XK_U:
1770  return KeyboardButton::ascii_key('U');
1771  case XK_V:
1772  return KeyboardButton::ascii_key('V');
1773  case XK_W:
1774  return KeyboardButton::ascii_key('W');
1775  case XK_X:
1776  return KeyboardButton::ascii_key('X');
1777  case XK_Y:
1778  return KeyboardButton::ascii_key('Y');
1779  case XK_Z:
1780  return KeyboardButton::ascii_key('Z');
1781  case XK_bracketleft:
1782  return KeyboardButton::ascii_key('[');
1783  case XK_backslash:
1784  return KeyboardButton::ascii_key('\\');
1785  case XK_bracketright:
1786  return KeyboardButton::ascii_key(']');
1787  case XK_asciicircum:
1788  return KeyboardButton::ascii_key('^');
1789  case XK_underscore:
1790  return KeyboardButton::ascii_key('_');
1791  case XK_grave: // == XK_quoteleft
1792  case XK_dead_grave: // on int'l keyboards
1793  return KeyboardButton::ascii_key('`');
1794  case XK_a:
1795  return KeyboardButton::ascii_key('a');
1796  case XK_b:
1797  return KeyboardButton::ascii_key('b');
1798  case XK_c:
1799  return KeyboardButton::ascii_key('c');
1800  case XK_d:
1801  return KeyboardButton::ascii_key('d');
1802  case XK_e:
1803  return KeyboardButton::ascii_key('e');
1804  case XK_f:
1805  return KeyboardButton::ascii_key('f');
1806  case XK_g:
1807  return KeyboardButton::ascii_key('g');
1808  case XK_h:
1809  return KeyboardButton::ascii_key('h');
1810  case XK_i:
1811  return KeyboardButton::ascii_key('i');
1812  case XK_j:
1813  return KeyboardButton::ascii_key('j');
1814  case XK_k:
1815  return KeyboardButton::ascii_key('k');
1816  case XK_l:
1817  return KeyboardButton::ascii_key('l');
1818  case XK_m:
1819  return KeyboardButton::ascii_key('m');
1820  case XK_n:
1821  return KeyboardButton::ascii_key('n');
1822  case XK_o:
1823  return KeyboardButton::ascii_key('o');
1824  case XK_p:
1825  return KeyboardButton::ascii_key('p');
1826  case XK_q:
1827  return KeyboardButton::ascii_key('q');
1828  case XK_r:
1829  return KeyboardButton::ascii_key('r');
1830  case XK_s:
1831  return KeyboardButton::ascii_key('s');
1832  case XK_t:
1833  return KeyboardButton::ascii_key('t');
1834  case XK_u:
1835  return KeyboardButton::ascii_key('u');
1836  case XK_v:
1837  return KeyboardButton::ascii_key('v');
1838  case XK_w:
1839  return KeyboardButton::ascii_key('w');
1840  case XK_x:
1841  return KeyboardButton::ascii_key('x');
1842  case XK_y:
1843  return KeyboardButton::ascii_key('y');
1844  case XK_z:
1845  return KeyboardButton::ascii_key('z');
1846  case XK_braceleft:
1847  return KeyboardButton::ascii_key('{');
1848  case XK_bar:
1849  return KeyboardButton::ascii_key('|');
1850  case XK_braceright:
1851  return KeyboardButton::ascii_key('}');
1852  case XK_asciitilde:
1853  return KeyboardButton::ascii_key('~');
1854  case XK_F1:
1855  case XK_KP_F1:
1856  return KeyboardButton::f1();
1857  case XK_F2:
1858  case XK_KP_F2:
1859  return KeyboardButton::f2();
1860  case XK_F3:
1861  case XK_KP_F3:
1862  return KeyboardButton::f3();
1863  case XK_F4:
1864  case XK_KP_F4:
1865  return KeyboardButton::f4();
1866  case XK_F5:
1867  return KeyboardButton::f5();
1868  case XK_F6:
1869  return KeyboardButton::f6();
1870  case XK_F7:
1871  return KeyboardButton::f7();
1872  case XK_F8:
1873  return KeyboardButton::f8();
1874  case XK_F9:
1875  return KeyboardButton::f9();
1876  case XK_F10:
1877  return KeyboardButton::f10();
1878  case XK_F11:
1879  return KeyboardButton::f11();
1880  case XK_F12:
1881  return KeyboardButton::f12();
1882  case XK_KP_Left:
1883  case XK_Left:
1884  return KeyboardButton::left();
1885  case XK_KP_Up:
1886  case XK_Up:
1887  return KeyboardButton::up();
1888  case XK_KP_Right:
1889  case XK_Right:
1890  return KeyboardButton::right();
1891  case XK_KP_Down:
1892  case XK_Down:
1893  return KeyboardButton::down();
1894  case XK_KP_Prior:
1895  case XK_Prior:
1896  return KeyboardButton::page_up();
1897  case XK_KP_Next:
1898  case XK_Next:
1899  return KeyboardButton::page_down();
1900  case XK_KP_Home:
1901  case XK_Home:
1902  return KeyboardButton::home();
1903  case XK_KP_End:
1904  case XK_End:
1905  return KeyboardButton::end();
1906  case XK_KP_Insert:
1907  case XK_Insert:
1908  return KeyboardButton::insert();
1909  case XK_KP_Delete:
1910  case XK_Delete:
1911  return KeyboardButton::del();
1912  case XK_Num_Lock:
1913  return KeyboardButton::num_lock();
1914  case XK_Scroll_Lock:
1915  return KeyboardButton::scroll_lock();
1916  case XK_Print:
1917  return KeyboardButton::print_screen();
1918  case XK_Pause:
1919  return KeyboardButton::pause();
1920  case XK_Menu:
1921  return KeyboardButton::menu();
1922  case XK_Shift_L:
1923  return KeyboardButton::lshift();
1924  case XK_Shift_R:
1925  return KeyboardButton::rshift();
1926  case XK_Control_L:
1927  return KeyboardButton::lcontrol();
1928  case XK_Control_R:
1929  return KeyboardButton::rcontrol();
1930  case XK_Alt_L:
1931  return KeyboardButton::lalt();
1932  case XK_Alt_R:
1933  return KeyboardButton::ralt();
1934  case XK_Meta_L:
1935  case XK_Super_L:
1936  return KeyboardButton::lmeta();
1937  case XK_Meta_R:
1938  case XK_Super_R:
1939  return KeyboardButton::rmeta();
1940  case XK_Caps_Lock:
1941  return KeyboardButton::caps_lock();
1942  case XK_Shift_Lock:
1943  return KeyboardButton::shift_lock();
1944  }
1945  if (x11display_cat.is_debug()) {
1946  x11display_cat.debug()
1947  << "Unrecognized keysym 0x" << std::hex << key << std::dec << "\n";
1948  }
1949  return ButtonHandle::none();
1950 }
1951 
1952 /**
1953  * Maps from a single X keycode to Panda's ButtonHandle.
1954  */
1955 ButtonHandle x11GraphicsWindow::
1956 map_raw_button(KeyCode key) const {
1957 #ifdef PHAVE_LINUX_INPUT_H
1958  // Most X11 servers are configured to use the evdev driver, which
1959  // adds 8 to the underlying evdev keycodes (not sure why).
1960  // In any case, this means we can use the same mapping as our raw
1961  // input code, which uses evdev directly.
1962  int index = key - 8;
1963  if (index >= 0) {
1964  return EvdevInputDevice::map_button(index);
1965  }
1966 #endif
1967  return ButtonHandle::none();
1968 }
1969 
1970 /**
1971  * Returns the Panda ButtonHandle corresponding to the mouse button indicated
1972  * by the given button event.
1973  */
1974 ButtonHandle x11GraphicsWindow::
1975 get_mouse_button(XButtonEvent &button_event) {
1976  int index = button_event.button;
1977  if (index == x_wheel_up_button) {
1978  return MouseButton::wheel_up();
1979  } else if (index == x_wheel_down_button) {
1980  return MouseButton::wheel_down();
1981  } else if (index == x_wheel_left_button) {
1982  return MouseButton::wheel_left();
1983  } else if (index == x_wheel_right_button) {
1984  return MouseButton::wheel_right();
1985  } else {
1986  return MouseButton::button(index - 1);
1987  }
1988 }
1989 
1990 /**
1991  * Returns a ButtonMap containing the association between raw buttons and
1992  * virtual buttons.
1993  */
1994 ButtonMap *x11GraphicsWindow::
1995 get_keyboard_map() const {
1996  // NB. This could be improved by using the Xkb API. XkbDescPtr desc =
1997  // XkbGetMap(_display, XkbAllMapComponentsMask, XkbUseCoreKbd);
1998  ButtonMap *map = new ButtonMap;
1999 
2000  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
2001 
2002  for (int k = 9; k <= 135; ++k) {
2003  ButtonHandle raw_button = map_raw_button(k);
2004  if (raw_button == ButtonHandle::none()) {
2005  continue;
2006  }
2007 
2008  KeySym sym = XkbKeycodeToKeysym(_display, k, 0, 0);
2009  ButtonHandle button = map_button(sym);
2010  if (button == ButtonHandle::none()) {
2011  continue;
2012  }
2013 
2014  map->map_button(raw_button, button);
2015  }
2016 
2017  return map;
2018 }
2019 
2020 /**
2021  * This function is used as a predicate to XCheckIfEvent() to determine if the
2022  * indicated queued X event is relevant and should be returned to this window.
2023  */
2024 Bool x11GraphicsWindow::
2025 check_event(X11_Display *display, XEvent *event, char *arg) {
2026  const x11GraphicsWindow *self = (x11GraphicsWindow *)arg;
2027 
2028  // We accept any event that is sent to our window.
2029  return (event->xany.window == self->_xwindow);
2030 }
2031 
2032 /**
2033  * Loads and returns a Cursor corresponding to the indicated filename. If the
2034  * file cannot be loaded, returns None.
2035  */
2036 X11_Cursor x11GraphicsWindow::
2037 get_cursor(const Filename &filename) {
2038  x11GraphicsPipe *x11_pipe;
2039  DCAST_INTO_R(x11_pipe, _pipe, None);
2040 
2041  if (x11_pipe->_xcursor_size == -1) {
2042  x11display_cat.info()
2043  << "libXcursor.so.1 not available; cannot change mouse cursor.\n";
2044  return None;
2045  }
2046 
2047  // First, look for the unresolved filename in our index.
2048  pmap<Filename, X11_Cursor>::iterator fi = _cursor_filenames.find(filename);
2049  if (fi != _cursor_filenames.end()) {
2050  return fi->second;
2051  }
2052 
2053  // If it wasn't found, resolve the filename and search for that.
2055  Filename resolved (filename);
2056  if (!vfs->resolve_filename(resolved, get_model_path())) {
2057  // The filename doesn't exist.
2058  x11display_cat.warning()
2059  << "Could not find cursor filename " << filename << "\n";
2060  return None;
2061  }
2062  fi = _cursor_filenames.find(resolved);
2063  if (fi != _cursor_filenames.end()) {
2064  return fi->second;
2065  }
2066 
2067  // Open the file through the virtual file system.
2068  istream *str = vfs->open_read_file(resolved, true);
2069  if (str == nullptr) {
2070  x11display_cat.warning()
2071  << "Could not open cursor file " << filename << "\n";
2072  return None;
2073  }
2074 
2075  // Check the first four bytes to see what kind of file it is.
2076  char magic[4];
2077  str->read(magic, 4);
2078  if (!str->good()) {
2079  x11display_cat.warning()
2080  << "Could not read from cursor file " << filename << "\n";
2081  return None;
2082  }
2083 
2084  // Put back the read bytes. Do not use seekg, because this will
2085  // corrupt the stream if it points to encrypted/compressed file
2086  str->putback(magic[3]);
2087  str->putback(magic[2]);
2088  str->putback(magic[1]);
2089  str->putback(magic[0]);
2090 
2091  X11_Cursor h = None;
2092  if (memcmp(magic, "Xcur", 4) == 0) {
2093  // X11 cursor.
2094  x11display_cat.debug()
2095  << "Loading X11 cursor " << filename << "\n";
2096  XcursorFile xcfile;
2097  xcfile.closure = str;
2098  xcfile.read = &xcursor_read;
2099  xcfile.write = &xcursor_write;
2100  xcfile.seek = &xcursor_seek;
2101 
2102  XcursorImages *images = x11_pipe->_XcursorXcFileLoadImages(&xcfile, x11_pipe->_xcursor_size);
2103  if (images != nullptr) {
2104  h = x11_pipe->_XcursorImagesLoadCursor(_display, images);
2105  x11_pipe->_XcursorImagesDestroy(images);
2106  }
2107 
2108  } else if (memcmp(magic, "\0\0\1\0", 4) == 0
2109  || memcmp(magic, "\0\0\2\0", 4) == 0) {
2110  // Windows .ico or .cur file.
2111  x11display_cat.debug()
2112  << "Loading Windows cursor " << filename << "\n";
2113  h = read_ico(*str);
2114  }
2115 
2116  // Delete the istream.
2117  vfs->close_read_file(str);
2118 
2119  if (h == None) {
2120  x11display_cat.warning()
2121  << "X11 cursor filename '" << resolved << "' could not be loaded!\n";
2122  }
2123 
2124  _cursor_filenames[resolved] = h;
2125  return h;
2126 }
2127 
2128 /**
2129  * Reads a Windows .ico or .cur file from the indicated stream and returns it
2130  * as an X11 Cursor. If the file cannot be loaded, returns None.
2131  */
2132 X11_Cursor x11GraphicsWindow::
2133 read_ico(istream &ico) {
2134  x11GraphicsPipe *x11_pipe;
2135  DCAST_INTO_R(x11_pipe, _pipe, None);
2136 
2137  // Local structs, this is just POD, make input easier
2138  typedef struct {
2139  uint16_t reserved, type, count;
2140  } IcoHeader;
2141 
2142  typedef struct {
2143  uint8_t width, height, colorCount, reserved;
2144  uint16_t xhot, yhot;
2145  uint32_t bitmapSize, offset;
2146  } IcoEntry;
2147 
2148  typedef struct {
2149  uint32_t headerSize, width, height;
2150  uint16_t planes, bitsPerPixel;
2151  uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
2152  } IcoInfoHeader;
2153 
2154  typedef struct {
2155  uint8_t blue, green, red, reserved;
2156  } IcoColor;
2157 
2158  int i, entry = 0;
2159  unsigned int j, k, mask, shift;
2160  size_t colorCount, bitsPerPixel;
2161  IcoHeader header;
2162  IcoInfoHeader infoHeader;
2163  IcoEntry *entries = nullptr;
2164  IcoColor color, *palette = nullptr;
2165 
2166  size_t xorBmpSize, andBmpSize;
2167  char *curXor, *curAnd;
2168  char *xorBmp = nullptr, *andBmp = nullptr;
2169  XcursorImage *image = nullptr;
2170  X11_Cursor ret = None;
2171 
2172  int def_size = x11_pipe->_xcursor_size;
2173 
2174  // Get our header, note that ICO = type 1 and CUR = type 2.
2175  ico.read(reinterpret_cast<char *>(&header), sizeof(IcoHeader));
2176  if (!ico.good()) goto cleanup;
2177  if (header.type != 1 && header.type != 2) goto cleanup;
2178  if (header.count < 1) goto cleanup;
2179 
2180  // Read the entry table into memory, select the largest entry.
2181  entries = new IcoEntry[header.count];
2182  ico.read(reinterpret_cast<char *>(entries), header.count * sizeof(IcoEntry));
2183  if (!ico.good()) goto cleanup;
2184  for (i = 1; i < header.count; i++) {
2185  if (entries[i].width == def_size && entries[i].height == def_size) {
2186  // Wait, this is the default cursor size. This is perfect.
2187  entry = i;
2188  break;
2189  }
2190  if (entries[i].width > entries[entry].width ||
2191  entries[i].height > entries[entry].height)
2192  entry = i;
2193  }
2194 
2195  // Seek to the image in the ICO.
2196  ico.seekg(entries[entry].offset);
2197  if (!ico.good()) goto cleanup;
2198 
2199  if (ico.peek() == 0x89) {
2200  // Hang on, this is actually a PNG header.
2201  PNMImage img;
2203  if (!img.read(ico, "", reg->get_type_from_extension("png"))) {
2204  goto cleanup;
2205  }
2206  img.set_maxval(255);
2207 
2208  image = x11_pipe->_XcursorImageCreate(img.get_x_size(), img.get_y_size());
2209 
2210  xel *ptr = img.get_array();
2211  xelval *alpha = img.get_alpha_array();
2212  size_t num_pixels = (size_t)img.get_x_size() * (size_t)img.get_y_size();
2213  unsigned int *dest = image->pixels;
2214 
2215  if (alpha != nullptr) {
2216  for (size_t p = 0; p < num_pixels; ++p) {
2217  *dest++ = (*alpha << 24U) | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2218  ++ptr;
2219  ++alpha;
2220  }
2221  } else {
2222  for (size_t p = 0; p < num_pixels; ++p) {
2223  *dest++ = 0xff000000U | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2224  ++ptr;
2225  }
2226  }
2227 
2228  } else {
2229  ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
2230  if (!ico.good()) goto cleanup;
2231  bitsPerPixel = infoHeader.bitsPerPixel;
2232 
2233  if (infoHeader.compression != 0) goto cleanup;
2234 
2235  // Load the color palette, if one exists.
2236  if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2237  colorCount = 1 << bitsPerPixel;
2238  palette = new IcoColor[colorCount];
2239  ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
2240  if (!ico.good()) goto cleanup;
2241  }
2242 
2243  int and_stride = ((infoHeader.width >> 3) + 3) & ~0x03;
2244 
2245  // Read in the pixel data.
2246  xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2247  andBmpSize = and_stride * (infoHeader.height / 2);
2248  curXor = xorBmp = new char[xorBmpSize];
2249  curAnd = andBmp = new char[andBmpSize];
2250  ico.read(xorBmp, xorBmpSize);
2251  if (!ico.good()) goto cleanup;
2252  ico.read(andBmp, andBmpSize);
2253  if (!ico.good()) goto cleanup;
2254 
2255  image = x11_pipe->_XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2256 
2257  // Support all the formats that GIMP supports.
2258  switch (bitsPerPixel) {
2259  case 1:
2260  case 4:
2261  case 8:
2262  // For colors less that a byte wide, shift and mask the palette indices
2263  // off each element of the xorBmp and append them to the image.
2264  mask = ((1 << bitsPerPixel) - 1);
2265  for (i = image->height - 1; i >= 0; i--) {
2266  for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2267  for (k = 0; k < 8 / bitsPerPixel; k++) {
2268  shift = 8 - ((k + 1) * bitsPerPixel);
2269  color = palette[(*curXor & (mask << shift)) >> shift];
2270  image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2271  (color.green << 8) +
2272  (color.blue);
2273  }
2274 
2275  curXor++;
2276  }
2277 
2278  // Set the alpha byte properly according to the andBmp.
2279  for (j = 0; j < image->width; j += 8) {
2280  for (k = 0; k < 8; k++) {
2281  shift = 7 - k;
2282  image->pixels[(i * image->width) + j + k] |=
2283  ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2284  }
2285 
2286  curAnd++;
2287  }
2288  }
2289  break;
2290 
2291  case 24:
2292  // Pack each of the three bytes into a single color, BGR -> 0RGB
2293  for (i = image->height - 1; i >= 0; i--) {
2294  for (j = 0; j < image->width; j++) {
2295  shift = 7 - (j & 0x7);
2296  uint32_t alpha = (curAnd[j >> 3] & (1 << shift)) ? 0 : 0xff000000U;
2297  image->pixels[(i * image->width) + j] = (uint8_t)curXor[0]
2298  | ((uint8_t)curXor[1] << 8u)
2299  | ((uint8_t)curXor[2] << 16u)
2300  | alpha;
2301  curXor += 3;
2302  }
2303  curAnd += and_stride;
2304  }
2305  break;
2306 
2307  case 32:
2308  // Pack each of the four bytes into a single color, BGRA -> ARGB
2309  for (i = image->height - 1; i >= 0; i--) {
2310  for (j = 0; j < image->width; j++) {
2311  image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2312  (*(curXor + 2) << 16) +
2313  (*(curXor + 1) << 8) +
2314  (*curXor);
2315  curXor += 4;
2316  }
2317  }
2318  break;
2319 
2320  default:
2321  goto cleanup;
2322  }
2323  }
2324 
2325  // If this is an actual CUR not an ICO set up the hotspot properly.
2326  if (header.type == 2) {
2327  image->xhot = entries[entry].xhot;
2328  image->yhot = entries[entry].yhot;
2329  } else {
2330  image->xhot = 0;
2331  image->yhot = 0;
2332  }
2333 
2334  ret = x11_pipe->_XcursorImageLoadCursor(_display, image);
2335 
2336 cleanup:
2337  x11_pipe->_XcursorImageDestroy(image);
2338  delete[] entries;
2339  delete[] palette;
2340  delete[] xorBmp;
2341  delete[] andBmp;
2342 
2343  return ret;
2344 }
LightMutexHolder
Similar to MutexHolder, but for a light mutex.
Definition: lightMutexHolder.h:25
PNMFileTypeRegistry
This class maintains the set of all known PNMFileTypes in the universe.
Definition: pnmFileTypeRegistry.h:28
ButtonMap::map_button
void map_button(ButtonHandle raw_button, ButtonHandle button, const std::string &label="")
Registers a new button mapping.
Definition: buttonMap.cxx:23
x11GraphicsWindow::end_frame
virtual void end_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread after rendering is completed for a given frame.
Definition: x11GraphicsWindow.cxx:267
FrameBufferProperties
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
Definition: frameBufferProperties.h:26
LightReMutexDirect::release
void release() const
Releases the lightReMutex.
Definition: lightReMutexDirect.I:107
GraphicsWindow::set_properties_now
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
Definition: graphicsWindow.cxx:495
x11GraphicsWindow::begin_frame
virtual bool begin_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread before beginning rendering for a given frame.
Definition: x11GraphicsWindow.cxx:234
PNMImage::set_maxval
void set_maxval(xelval maxval)
Rescales the image to the indicated maxval.
Definition: pnmImage.cxx:794
PNMImage::get_alpha_array
xelval * get_alpha_array()
Directly access the underlying PNMImage array of alpha values.
Definition: pnmImage.I:1115
throw_event.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MouseButton::wheel_right
static ButtonHandle wheel_right()
Returns the ButtonHandle generated when the mouse is scrolled to the right.
Definition: mouseButton.cxx:111
x11GraphicsWindow::clear
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
Definition: x11GraphicsWindow.cxx:218
NativeWindowHandle::IntHandle
Definition: nativeWindowHandle.h:55
PointerData
Holds the data that might be generated by a 2-d pointer input device, such as the mouse in the Graphi...
Definition: pointerData.h:38
WindowProperties::get_undecorated
get_undecorated
Returns true if the window has no border.
Definition: windowProperties.h:105
GraphicsWindowInputDevice
This is a virtual input device that represents the keyboard and mouse pair that is associated with a ...
Definition: graphicsWindowInputDevice.h:28
PointerData::get_in_window
get_in_window
If this returns false, the pointer is not currently present in the window and the values returned by ...
Definition: pointerData.h:56
WindowProperties::get_size
get_size
Returns size in pixels of the useful part of the window, not including decorations.
Definition: windowProperties.h:85
ButtonHandle::get_ascii_equivalent
get_ascii_equivalent
Returns the character code associated with the button, or '\0' if no ASCII code was associated.
Definition: buttonHandle.h:63
pnmImage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::has_origin
has_origin
Returns true if the window origin has been specified, false otherwise.
Definition: windowProperties.h:76
WindowProperties::clear_fullscreen
clear_fullscreen
Removes the fullscreen specification from the properties.
Definition: windowProperties.h:119
WindowProperties::has_title
has_title
Returns true if the window title has been specified, false otherwise.
Definition: windowProperties.h:98
WindowProperties::set_origin
set_origin
Specifies the origin on the screen (in pixels, relative to the top-left corner) at which the window s...
Definition: windowProperties.h:76
LightReMutexHolder
Similar to MutexHolder, but for a light reentrant mutex.
Definition: lightReMutexHolder.h:25
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
GraphicsWindowInputDevice::focus_lost
void focus_lost(double time=ClockObject::get_global_clock() ->get_frame_time())
This should be called when the window focus is lost, so that we may miss upcoming button events (espe...
Definition: graphicsWindowInputDevice.cxx:134
GraphicsPipe::get_display_width
get_display_width
Returns the width of the entire display, if it is known.
Definition: graphicsPipe.h:94
mouseButton.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::has_cursor_hidden
has_cursor_hidden
Returns true if set_cursor_hidden() has been specified.
Definition: windowProperties.h:151
GraphicsWindowInputDevice::button_down
void button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
Definition: graphicsWindowInputDevice.cxx:75
GraphicsWindowInputDevice::button_up
void button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
Definition: graphicsWindowInputDevice.cxx:97
x11GraphicsPipe::get_im
XIM get_im() const
Returns the input method opened for the pipe, or NULL if the input method could not be opened for som...
Definition: x11GraphicsPipe.I:44
WindowProperties::get_y_size
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
Definition: windowProperties.I:159
MouseData
BEGIN_PUBLISH typedef PointerData MouseData
Deprecated alias for PointerData.
Definition: mouseData.h:23
clockObject.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ButtonHandle
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
x11GraphicsPipe::get_root
X11_Window get_root() const
Returns the handle to the root window on the pipe's display.
Definition: x11GraphicsPipe.I:35
PNMImage::get_array
xel * get_array()
Directly access the underlying PNMImage array.
Definition: pnmImage.I:1098
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsWindowInputDevice::raw_button_down
void raw_button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
Definition: graphicsWindowInputDevice.cxx:147
ClockObject::get_global_clock
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
GraphicsWindowInputDevice::get_pointer
PointerData get_pointer() const
Returns the PointerData associated with the input device's pointer.
Definition: graphicsWindowInputDevice.I:19
x11GraphicsPipe::supports_relative_mouse
bool supports_relative_mouse() const
Returns true if relative mouse mode is supported on this display.
Definition: x11GraphicsPipe.I:64
WindowProperties::set_open
set_open
Specifies whether the window should be open.
Definition: windowProperties.h:144
WindowProperties::has_foreground
has_foreground
Returns true if set_foreground() has been specified.
Definition: windowProperties.h:126
WindowProperties
A container for the various kinds of properties we might ask to have on a graphics window before we o...
Definition: windowProperties.h:29
WindowProperties::get_x_size
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
Definition: windowProperties.I:149
DrawableRegion::is_any_clear_active
virtual bool is_any_clear_active() const
Returns true if any of the clear types (so far there are just color or depth) have been set active,...
Definition: drawableRegion.cxx:83
WindowProperties::get_minimized
get_minimized
Returns true if the window is minimized.
Definition: windowProperties.h:133
MouseButton::wheel_left
static ButtonHandle wheel_left()
Returns the ButtonHandle generated when the mouse is scrolled to the left.
Definition: mouseButton.cxx:102
PNMImage::read
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
keyboardButton.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowHandle
This object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:34
WindowProperties::set_foreground
set_foreground
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
Definition: windowProperties.h:126
GraphicsWindowInputDevice::raw_button_up
void raw_button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
Definition: graphicsWindowInputDevice.cxx:156
KeyboardButton::ascii_key
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one,...
Definition: keyboardButton.cxx:24
LightReMutexDirect::try_lock
bool try_lock()
Alias for try_acquire() to match C++11 semantics.
Definition: lightReMutexDirect.I:29
graphicsPipe.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMImage
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
PNMImageHeader::get_x_size
int get_x_size() const
Returns the number of pixels in the X direction.
Definition: pnmImageHeader.I:144
WindowProperties::clear_size
clear_size
Removes the size specification from the properties.
Definition: windowProperties.h:85
MouseButton::button
static ButtonHandle button(int button_number)
Returns the ButtonHandle associated with the particular numbered mouse button (zero-based),...
Definition: mouseButton.cxx:32
GraphicsEngine
This class is the main interface to controlling the render process.
Definition: graphicsEngine.h:53
WindowProperties::get_mouse_mode
get_mouse_mode
See set_mouse_mode().
Definition: windowProperties.h:92
WindowHandle::OSHandle
Definition: windowHandle.h:63
WindowProperties::clear_z_order
clear_z_order
Removes the z_order specification from the properties.
Definition: windowProperties.h:171
ClockObject::get_real_time
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
Definition: clockObject.h:92
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
GraphicsOutput
This is a base class for the various different classes that represent the result of a frame of render...
Definition: graphicsOutput.h:63
GraphicsOutput::clear
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
Definition: graphicsOutput.cxx:1140
config_x11display.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::clear_title
clear_title
Removes the title specification from the properties.
Definition: windowProperties.h:98
WindowProperties::set_size
set_size
Specifies the requested size of the window, in pixels.
Definition: windowProperties.h:85
WindowProperties::get_x_origin
int get_x_origin() const
Returns the x coordinate of the window's top-left corner, not including decorations.
Definition: windowProperties.I:82
GraphicsWindowInputDevice::keystroke
void keystroke(int keycode, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated keystroke has been generated.
Definition: graphicsWindowInputDevice.cxx:107
get_x11.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMImageHeader::get_y_size
int get_y_size() const
Returns the number of pixels in the Y direction.
Definition: pnmImageHeader.I:153
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
WindowProperties::get_fullscreen
get_fullscreen
Returns true if the window is in fullscreen mode.
Definition: windowProperties.h:119
PNMFileTypeRegistry::get_global_ptr
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
Definition: pnmFileTypeRegistry.cxx:277
WindowProperties::clear_foreground
clear_foreground
Removes the foreground specification from the properties.
Definition: windowProperties.h:126
WindowProperties::set_mouse_mode
set_mouse_mode
Specifies the mode in which the window is to operate its mouse pointer.
Definition: windowProperties.h:92
WindowProperties::get_z_order
get_z_order
Returns the window's z_order.
Definition: windowProperties.h:171
WindowProperties::set_minimized
set_minimized
Specifies whether the window should be created minimized (true), or normal (false).
Definition: windowProperties.h:133
XRRScreenSize
Definition: x11GraphicsPipe.h:41
WindowProperties::has_size
has_size
Returns true if the window size has been specified, false otherwise.
Definition: windowProperties.h:85
WindowProperties::is_any_specified
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
Definition: windowProperties.I:41
GraphicsPipe
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
x11GraphicsWindow::get_pointer
virtual MouseData get_pointer(int device) const
Returns the MouseData associated with the nth input device's pointer.
Definition: x11GraphicsWindow.cxx:147
WindowProperties::get_foreground
get_foreground
Returns true if the window is in the foreground.
Definition: windowProperties.h:126
x11GraphicsWindow
Interfaces to the X11 window system.
Definition: x11GraphicsWindow.h:26
x11GraphicsWindow::set_properties_now
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
Definition: x11GraphicsWindow.cxx:565
pnmFileTypeRegistry.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
x11GraphicsPipe::get_hidden_cursor
X11_Cursor get_hidden_cursor()
Returns an invisible Cursor suitable for assigning to windows that have the cursor_hidden property se...
Definition: x11GraphicsPipe.I:53
WindowProperties::has_cursor_filename
has_cursor_filename
Returns true if set_cursor_filename() has been specified.
Definition: windowProperties.h:165
WindowProperties::get_cursor_hidden
get_cursor_hidden
Returns true if the mouse cursor is invisible.
Definition: windowProperties.h:151
ButtonHandle::has_ascii_equivalent
has_ascii_equivalent
Returns true if the button was created with an ASCII equivalent code (e.g.
Definition: buttonHandle.h:63
GraphicsOutput::get_fb_properties
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
Definition: graphicsOutput.I:413
GraphicsWindow::get_close_request_event
get_close_request_event
Returns the name of the event set via set_close_request_event().
Definition: graphicsWindow.h:74
WindowProperties::clear_origin
clear_origin
Removes the origin specification from the properties.
Definition: windowProperties.h:76
WindowProperties::has_minimized
has_minimized
Returns true if set_minimized() has been specified.
Definition: windowProperties.h:133
GraphicsWindowInputDevice::set_pointer_in_window
void set_pointer_in_window(double x, double y, double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is within the window...
Definition: graphicsWindowInputDevice.cxx:166
x11GraphicsPipe::disable_relative_mouse
void disable_relative_mouse()
Disables relative mouse mode for this display.
Definition: x11GraphicsPipe.I:85
pixel
Definition: pnmimage_base.h:41
WindowProperties::clear_cursor_filename
clear_cursor_filename
Removes the cursor_filename specification from the properties.
Definition: windowProperties.h:165
WindowProperties::get_fixed_size
get_fixed_size
Returns true if the window cannot be resized by the user, false otherwise.
Definition: windowProperties.h:112
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:742
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::has_fullscreen
has_fullscreen
Returns true if set_fullscreen() has been specified.
Definition: windowProperties.h:119
VirtualFileSystem::close_read_file
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
Definition: virtualFileSystem.cxx:867
x11GraphicsPipe::enable_relative_mouse
bool enable_relative_mouse()
Enables relative mouse mode for this display.
Definition: x11GraphicsPipe.I:72
x11GraphicsPipe::get_screen
int get_screen() const
Returns the X screen number associated with the pipe.
Definition: x11GraphicsPipe.I:27
x11GraphicsWindow::process_events
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
Definition: x11GraphicsWindow.cxx:292
PNMFileTypeRegistry::get_type_from_extension
PNMFileType * get_type_from_extension(const std::string &filename) const
Tries to determine what the PNMFileType is likely to be for a particular image file based on its exte...
Definition: pnmFileTypeRegistry.cxx:147
x11GraphicsPipe::find_fullscreen_crtc
RRCrtc find_fullscreen_crtc(const LPoint2i &point, int &x, int &y, int &width, int &height)
Finds a CRTC for going fullscreen to, at the given origin.
Definition: x11GraphicsPipe.cxx:363
MouseButton::wheel_up
static ButtonHandle wheel_up()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch upwards.
Definition: mouseButton.cxx:84
WindowProperties::get_cursor_filename
get_cursor_filename
Returns the icon filename associated with the mouse cursor.
Definition: windowProperties.h:165
GraphicsWindowInputDevice::set_pointer_out_of_window
void set_pointer_out_of_window(double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is no longer within ...
Definition: graphicsWindowInputDevice.cxx:182
buttonMap.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
evdevInputDevice.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MouseButton::wheel_down
static ButtonHandle wheel_down()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch downwards.
Definition: mouseButton.cxx:93
WindowProperties::has_z_order
has_z_order
Returns true if the window z_order has been specified, false otherwise.
Definition: windowProperties.h:171
nativeWindowHandle.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::get_title
get_title
Returns the window's title.
Definition: windowProperties.h:98
GraphicsStateGuardian
Encapsulates all the communication with a particular instance of a given rendering backend.
Definition: graphicsStateGuardian.h:65
GraphicsPipe::get_display_height
get_display_height
Returns the height of the entire display, if it is known.
Definition: graphicsPipe.h:95
x11GraphicsPipe::get_display
X11_Display * get_display() const
Returns a pointer to the X display associated with the pipe: the display on which to create the windo...
Definition: x11GraphicsPipe.I:19
x11GraphicsWindow.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::resolve_filename
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
Definition: virtualFileSystem.cxx:641
VirtualFileSystem::open_read_file
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
Definition: virtualFileSystem.cxx:847
GraphicsWindow::process_events
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
Definition: graphicsWindow.cxx:459
x11GraphicsPipe
This graphics pipe represents the interface for creating graphics windows on an X-based client.
Definition: x11GraphicsPipe.h:96
WindowProperties::clear_fixed_size
clear_fixed_size
Removes the fixed_size specification from the properties.
Definition: windowProperties.h:112
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
GraphicsWindow::is_fullscreen
bool is_fullscreen() const
Returns true if the window has been opened as a fullscreen window, false otherwise.
Definition: graphicsWindow.I:31
WindowProperties::clear_mouse_mode
clear_mouse_mode
Removes the mouse_mode specification from the properties.
Definition: windowProperties.h:92
GraphicsWindow
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
Definition: graphicsWindow.h:40
WindowProperties::has_fixed_size
has_fixed_size
Returns true if set_fixed_size() has been specified.
Definition: windowProperties.h:112
x11GraphicsPipe.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
textEncoder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
WindowHandle::get_os_handle
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.h:44
ButtonMap
This class represents a map containing all of the buttons of a (keyboard) device, though it can also ...
Definition: buttonMap.h:30
WindowProperties::clear_cursor_hidden
clear_cursor_hidden
Removes the cursor_hidden specification from the properties.
Definition: windowProperties.h:151
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
WindowProperties::get_y_origin
int get_y_origin() const
Returns the y coordinate of the window's top-left corner, not including decorations.
Definition: windowProperties.I:92
lightReMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
x11GraphicsWindow::move_pointer
virtual bool move_pointer(int device, int x, int y)
Forces the pointer to the indicated position within the window, if possible.
Definition: x11GraphicsWindow.cxx:184