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