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  changes.x = 0;
772  changes.y = 0;
773  value_mask |= CWX | CWY;
774  properties.clear_origin();
775 
776  } else if (properties.has_origin()) {
777  changes.x = properties.get_x_origin();
778  changes.y = properties.get_y_origin();
779  if (changes.x != -1) value_mask |= CWX;
780  if (changes.y != -1) value_mask |= CWY;
781  properties.clear_origin();
782  }
783 
784  // This, too. But we can't currently change out of fixed_size mode.
785  if (properties.has_fixed_size() && properties.get_fixed_size()) {
786  _properties.set_fixed_size(properties.get_fixed_size());
787  properties.clear_fixed_size();
788  _fixed_size = _properties.get_size();
789  }
790 
791  if (properties.has_size()) {
792  changes.width = properties.get_x_size();
793  changes.height = properties.get_y_size();
794  value_mask |= (CWWidth | CWHeight);
795 
796  if (_properties.get_fixed_size()) {
797  _fixed_size = properties.get_size();
798  }
799  properties.clear_size();
800  }
801 
802  if (properties.has_z_order()) {
803  // We'll send the classic stacking request through the standard interface,
804  // for users of primitive window managers; but we'll also send it as a
805  // window manager hint, for users of modern window managers.
806  _properties.set_z_order(properties.get_z_order());
807  switch (properties.get_z_order()) {
808  case WindowProperties::Z_bottom:
809  changes.stack_mode = Below;
810  break;
811 
812  case WindowProperties::Z_normal:
813  changes.stack_mode = TopIf;
814  break;
815 
816  case WindowProperties::Z_top:
817  changes.stack_mode = Above;
818  break;
819  }
820 
821  value_mask |= (CWStackMode);
822  properties.clear_z_order();
823  }
824 
825  // We hide the cursor by setting it to an invisible pixmap. We can also
826  // load a custom cursor from a file.
827  if (properties.has_cursor_hidden() || properties.has_cursor_filename()) {
828  if (properties.has_cursor_hidden()) {
829  _properties.set_cursor_hidden(properties.get_cursor_hidden());
830  properties.clear_cursor_hidden();
831  }
832  Filename cursor_filename;
833  if (properties.has_cursor_filename()) {
834  cursor_filename = properties.get_cursor_filename();
835  _properties.set_cursor_filename(cursor_filename);
836  properties.clear_cursor_filename();
837  }
838  Filename filename = properties.get_cursor_filename();
839  _properties.set_cursor_filename(filename);
840 
841  if (_properties.get_cursor_hidden()) {
842  XDefineCursor(_display, _xwindow, x11_pipe->get_hidden_cursor());
843 
844  } else if (!cursor_filename.empty()) {
845  // Note that if the cursor fails to load, cursor will be None
846  X11_Cursor cursor = get_cursor(cursor_filename);
847  XDefineCursor(_display, _xwindow, cursor);
848 
849  } else {
850  XDefineCursor(_display, _xwindow, None);
851  }
852 
853  // Regrab the mouse if we changed the cursor, otherwise it won't update.
854  if (!properties.has_mouse_mode() &&
855  _properties.get_mouse_mode() != WindowProperties::M_absolute) {
856  properties.set_mouse_mode(_properties.get_mouse_mode());
857  }
858  }
859 
860  if (properties.has_foreground()) {
861  if (properties.get_foreground()) {
862  XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
863  } else {
864  XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
865  }
866  properties.clear_foreground();
867  }
868 
869  if (properties.has_mouse_mode()) {
870  switch (properties.get_mouse_mode()) {
871  case WindowProperties::M_absolute:
872  XUngrabPointer(_display, CurrentTime);
873  if (_dga_mouse_enabled) {
874  x11_pipe->disable_relative_mouse();
875  _dga_mouse_enabled = false;
876  }
877  _properties.set_mouse_mode(WindowProperties::M_absolute);
878  properties.clear_mouse_mode();
879  break;
880 
881  case WindowProperties::M_relative:
882  if (!_dga_mouse_enabled) {
883  if (x11_pipe->supports_relative_mouse()) {
884  X11_Cursor cursor = None;
885  if (_properties.get_cursor_hidden()) {
886  x11GraphicsPipe *x11_pipe;
887  DCAST_INTO_V(x11_pipe, _pipe);
888  cursor = x11_pipe->get_hidden_cursor();
889  }
890 
891  if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
892  GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
893  x11display_cat.error() << "Failed to grab pointer!\n";
894  } else {
895  x11_pipe->enable_relative_mouse();
896 
897  _properties.set_mouse_mode(WindowProperties::M_relative);
898  properties.clear_mouse_mode();
899  _dga_mouse_enabled = true;
900 
901  // Get the real mouse position, so we can addsubtract our relative
902  // coordinates later.
903  XEvent event;
904  XQueryPointer(_display, _xwindow, &event.xbutton.root,
905  &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
906  &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
907  _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
908  }
909  } else {
910  x11display_cat.warning()
911  << "XF86DGA extension not available, cannot enable relative mouse mode\n";
912  _dga_mouse_enabled = false;
913  }
914  }
915  break;
916 
917  case WindowProperties::M_confined:
918  {
919  x11GraphicsPipe *x11_pipe;
920  DCAST_INTO_V(x11_pipe, _pipe);
921 
922  if (_dga_mouse_enabled) {
923  x11_pipe->disable_relative_mouse();
924  _dga_mouse_enabled = false;
925  }
926  X11_Cursor cursor = None;
927  if (_properties.get_cursor_hidden()) {
928  cursor = x11_pipe->get_hidden_cursor();
929  }
930 
931  if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
932  GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
933  x11display_cat.error() << "Failed to grab pointer!\n";
934  } else {
935  _properties.set_mouse_mode(WindowProperties::M_confined);
936  properties.clear_mouse_mode();
937  }
938  }
939  break;
940  }
941  }
942 
943  if (value_mask != 0) {
944  // We must call this after changing the WM properties, otherwise we may
945  // get misleading ConfigureNotify events in the wrong order.
946  XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
947 
948  // Don't draw anything until this is done reconfiguring.
949  _awaiting_configure = true;
950  }
951 }
952 
953 /**
954  * Overridden from GraphicsWindow.
955  */
956 void x11GraphicsWindow::
957 mouse_mode_absolute() {
958  // unused: remove in 1.10!
959 }
960 
961 /**
962  * Overridden from GraphicsWindow.
963  */
964 void x11GraphicsWindow::
965 mouse_mode_relative() {
966  // unused: remove in 1.10!
967 }
968 
969 /**
970  * Closes the window right now. Called from the window thread.
971  */
972 void x11GraphicsWindow::
973 close_window() {
974  if (_gsg != nullptr) {
975  _gsg.clear();
976  }
977 
978  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
979  if (_ic != (XIC)nullptr) {
980  XDestroyIC(_ic);
981  _ic = (XIC)nullptr;
982  }
983 
984  if (_xwindow != (X11_Window)nullptr) {
985  XDestroyWindow(_display, _xwindow);
986  _xwindow = (X11_Window)nullptr;
987 
988  // This may be necessary if we just closed the last X window in an
989  // application, so the server hears the close request.
990  XFlush(_display);
991  }
992 
993  // Change the resolution back to what it was. Don't remove the SizeID
994  // typecast!
995  if (_orig_size_id != (SizeID) -1) {
996  X11_Window root;
997  if (_pipe != nullptr) {
998  x11GraphicsPipe *x11_pipe;
999  DCAST_INTO_V(x11_pipe, _pipe);
1000  root = x11_pipe->get_root();
1001  } else {
1002  // Oops. Looks like the pipe was destroyed before the window gets
1003  // closed. Oh well, let's get the root window by ourselves.
1004  root = RootWindow(_display, _screen);
1005  }
1006  XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, root);
1007  _XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime);
1008  _orig_size_id = -1;
1009  }
1010 
1011  GraphicsWindow::close_window();
1012 }
1013 
1014 /**
1015  * Opens the window right now. Called from the window thread. Returns true
1016  * if the window is successfully opened, or false if there was a problem.
1017  */
1018 bool x11GraphicsWindow::
1019 open_window() {
1020  if (_visual_info == nullptr) {
1021  // No X visual for this fbconfig; how can we open the window?
1022  x11display_cat.error()
1023  << "No X visual: cannot open window.\n";
1024  return false;
1025  }
1026 
1027  x11GraphicsPipe *x11_pipe;
1028  DCAST_INTO_R(x11_pipe, _pipe, false);
1029 
1030  if (!_properties.has_origin()) {
1031  _properties.set_origin(0, 0);
1032  }
1033  if (!_properties.has_size()) {
1034  _properties.set_size(100, 100);
1035  }
1036 
1037  // Make sure we are not making X11 calls from other threads.
1038  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
1039 
1040  X11_Window parent_window = x11_pipe->get_root();
1041  WindowHandle *window_handle = _properties.get_parent_window();
1042  if (window_handle != nullptr) {
1043  x11display_cat.info()
1044  << "Got parent_window " << *window_handle << "\n";
1045  WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
1046  if (os_handle != nullptr) {
1047  x11display_cat.info()
1048  << "os_handle type " << os_handle->get_type() << "\n";
1049 
1050  if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
1051  NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
1052  parent_window = x11_handle->get_handle();
1053  } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
1054  NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
1055  parent_window = (X11_Window)int_handle->get_handle();
1056  }
1057  }
1058  }
1059  _parent_window_handle = window_handle;
1060 
1061  _event_mask =
1062  ButtonPressMask | ButtonReleaseMask |
1063  KeyPressMask | KeyReleaseMask |
1064  EnterWindowMask | LeaveWindowMask |
1065  PointerMotionMask |
1066  FocusChangeMask | StructureNotifyMask;
1067 
1068  // Initialize window attributes
1069  XSetWindowAttributes wa;
1070  wa.background_pixel = XBlackPixel(_display, _screen);
1071  wa.border_pixel = 0;
1072  wa.colormap = _colormap;
1073  wa.event_mask = _event_mask;
1074  wa.override_redirect = _override_redirect;
1075 
1076  unsigned long attrib_mask =
1077  CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
1078 
1079  _xwindow = XCreateWindow
1080  (_display, parent_window,
1081  _properties.get_x_origin(), _properties.get_y_origin(),
1082  _properties.get_x_size(), _properties.get_y_size(),
1083  0, _visual_info->depth, InputOutput,
1084  _visual_info->visual, attrib_mask, &wa);
1085 
1086  if (_xwindow == (X11_Window)0) {
1087  x11display_cat.error()
1088  << "failed to create X window.\n";
1089  return false;
1090  }
1091 
1092  if (_properties.get_fixed_size()) {
1093  _fixed_size = _properties.get_size();
1094  }
1095 
1096  set_wm_properties(_properties, false);
1097 
1098  // We don't specify any fancy properties of the XIC. It would be nicer if
1099  // we could support fancy IM's that want preedit callbacks, etc., but that
1100  // can wait until we have an X server that actually supports these to test
1101  // it on.
1102  XIM im = x11_pipe->get_im();
1103  _ic = nullptr;
1104  if (im) {
1105  _ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
1106  XNClientWindow, _xwindow, nullptr);
1107  if (_ic == (XIC)nullptr) {
1108  x11display_cat.warning()
1109  << "Couldn't create input context.\n";
1110  }
1111  }
1112 
1113  if (_properties.get_cursor_hidden()) {
1114  XDefineCursor(_display, _xwindow, x11_pipe->get_hidden_cursor());
1115 
1116  } else if (_properties.has_cursor_filename() && !_properties.get_cursor_filename().empty()) {
1117  // Note that if the cursor fails to load, cursor will be None
1118  X11_Cursor cursor = get_cursor(_properties.get_cursor_filename());
1119  XDefineCursor(_display, _xwindow, cursor);
1120  }
1121 
1122  XMapWindow(_display, _xwindow);
1123 
1124  if (_properties.get_raw_mice()) {
1125  open_raw_mice();
1126  } else {
1127  if (x11display_cat.is_debug()) {
1128  x11display_cat.debug()
1129  << "Raw mice not requested.\n";
1130  }
1131  }
1132 
1133  // Create a WindowHandle for ourselves
1134  _window_handle = NativeWindowHandle::make_x11(_xwindow);
1135 
1136  // And tell our parent window that we're now its child.
1137  if (_parent_window_handle != nullptr) {
1138  _parent_window_handle->attach_child(_window_handle);
1139  }
1140 
1141  return true;
1142 }
1143 
1144 /**
1145  * Asks the window manager to set the appropriate properties. In X, these
1146  * properties cannot be specified directly by the application; they must be
1147  * requested via the window manager, which may or may not choose to honor the
1148  * request.
1149  *
1150  * If already_mapped is true, the window has already been mapped (manifested)
1151  * on the display. This means we may need to use a different action in some
1152  * cases.
1153  *
1154  * Assumes the X11 lock is held.
1155  */
1156 void x11GraphicsWindow::
1157 set_wm_properties(const WindowProperties &properties, bool already_mapped) {
1158  x11GraphicsPipe *x11_pipe;
1159  DCAST_INTO_V(x11_pipe, _pipe);
1160 
1161  // Name the window if there is a name
1162  XTextProperty window_name;
1163  XTextProperty *window_name_p = nullptr;
1164  if (properties.has_title()) {
1165  const char *name = properties.get_title().c_str();
1166  if (XStringListToTextProperty((char **)&name, 1, &window_name) != 0) {
1167  window_name_p = &window_name;
1168  }
1169  }
1170 
1171  // The size hints request a window of a particular size andor a particular
1172  // placement onscreen.
1173  XSizeHints *size_hints_p = nullptr;
1174  if (properties.has_origin() || properties.has_size()) {
1175  size_hints_p = XAllocSizeHints();
1176  if (size_hints_p != nullptr) {
1177  if (properties.has_origin()) {
1178  size_hints_p->x = properties.get_x_origin();
1179  size_hints_p->y = properties.get_y_origin();
1180  size_hints_p->flags |= USPosition;
1181  }
1182  LVecBase2i size = _properties.get_size();
1183  if (properties.has_size()) {
1184  size = properties.get_size();
1185  size_hints_p->width = size.get_x();
1186  size_hints_p->height = size.get_y();
1187  size_hints_p->flags |= USSize;
1188  }
1189  if (properties.get_fixed_size()) {
1190  size_hints_p->min_width = size.get_x();
1191  size_hints_p->min_height = size.get_y();
1192  size_hints_p->max_width = size.get_x();
1193  size_hints_p->max_height = size.get_y();
1194  size_hints_p->flags |= (PMinSize | PMaxSize);
1195  }
1196  }
1197  }
1198 
1199  // The window manager hints include requests to the window manager other
1200  // than those specific to window geometry.
1201  XWMHints *wm_hints_p = nullptr;
1202  wm_hints_p = XAllocWMHints();
1203  if (wm_hints_p != nullptr) {
1204  if (properties.has_minimized() && properties.get_minimized()) {
1205  wm_hints_p->initial_state = IconicState;
1206  } else {
1207  wm_hints_p->initial_state = NormalState;
1208  }
1209  wm_hints_p->flags = StateHint;
1210  }
1211 
1212  // Two competing window manager interfaces have evolved. One of them allows
1213  // to set certain properties as a "type"; the other one as a "state". We'll
1214  // try to honor both.
1215  static const int max_type_data = 32;
1216  int32_t type_data[max_type_data];
1217  int next_type_data = 0;
1218 
1219  static const int max_state_data = 32;
1220  int32_t state_data[max_state_data];
1221  int next_state_data = 0;
1222 
1223  static const int max_set_data = 32;
1224  class SetAction {
1225  public:
1226  inline SetAction() { }
1227  inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
1228  Atom _state;
1229  Atom _action;
1230  };
1231  SetAction set_data[max_set_data];
1232  int next_set_data = 0;
1233 
1234  if (properties.has_fullscreen()) {
1235  if (properties.get_fullscreen()) {
1236  // For a "fullscreen" request, we pass this through, hoping the window
1237  // manager will support EWMH.
1238  type_data[next_type_data++] = x11_pipe->_net_wm_window_type_fullscreen;
1239 
1240  // We also request it as a state.
1241  state_data[next_state_data++] = x11_pipe->_net_wm_state_fullscreen;
1242  // Don't ask me why this has to be 10 and not _net_wm_state_add. It
1243  // doesn't seem to work otherwise.
1244  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 1);
1245 
1246  } else {
1247  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 0);
1248  }
1249  }
1250 
1251  // If we asked for a window without a border, there's no excellent way to
1252  // arrange that. For users whose window managers follow the EWMH
1253  // specification, we can ask for a "splash" screen, which is usually
1254  // undecorated. It's not exactly right, but the spec doesn't give us an
1255  // exactly-right option.
1256 
1257  // For other users, we'll totally punt and just set the window's Class to
1258  // "Undecorated", and let the user configure hisher window manager not to
1259  // put a border around windows of this class.
1260  XClassHint *class_hints_p = nullptr;
1261  if (!x_wm_class.empty()) {
1262  // Unless the user wanted to use his own WM_CLASS, of course.
1263  class_hints_p = XAllocClassHint();
1264  class_hints_p->res_class = (char*) x_wm_class.c_str();
1265  if (!x_wm_class_name.empty()) {
1266  class_hints_p->res_name = (char*) x_wm_class_name.c_str();
1267  }
1268 
1269  } else if (properties.get_undecorated() || properties.get_fullscreen()) {
1270  class_hints_p = XAllocClassHint();
1271  class_hints_p->res_class = (char*) "Undecorated";
1272  }
1273 
1274  if (properties.get_undecorated() && !properties.get_fullscreen()) {
1275  type_data[next_type_data++] = x11_pipe->_net_wm_window_type_splash;
1276  }
1277 
1278  if (properties.has_z_order()) {
1279  switch (properties.get_z_order()) {
1280  case WindowProperties::Z_bottom:
1281  state_data[next_state_data++] = x11_pipe->_net_wm_state_below;
1282  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1283  x11_pipe->_net_wm_state_add);
1284  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1285  x11_pipe->_net_wm_state_remove);
1286  break;
1287 
1288  case WindowProperties::Z_normal:
1289  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1290  x11_pipe->_net_wm_state_remove);
1291  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1292  x11_pipe->_net_wm_state_remove);
1293  break;
1294 
1295  case WindowProperties::Z_top:
1296  state_data[next_state_data++] = x11_pipe->_net_wm_state_above;
1297  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1298  x11_pipe->_net_wm_state_remove);
1299  set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1300  x11_pipe->_net_wm_state_add);
1301  break;
1302  }
1303  }
1304 
1305  nassertv(next_type_data < max_type_data);
1306  nassertv(next_state_data < max_state_data);
1307  nassertv(next_set_data < max_set_data);
1308 
1309  // Add the process ID as a convenience for other applications.
1310  int32_t pid = getpid();
1311  XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_pid,
1312  XA_CARDINAL, 32, PropModeReplace,
1313  (unsigned char *)&pid, 1);
1314 
1315  // Disable compositing effects in fullscreen mode.
1316  if (properties.has_fullscreen()) {
1317  int32_t compositor = properties.get_fullscreen() ? 1 : 0;
1318  XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_bypass_compositor,
1319  XA_CARDINAL, 32, PropModeReplace,
1320  (unsigned char *)&compositor, 1);
1321  }
1322 
1323  XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type,
1324  XA_ATOM, 32, PropModeReplace,
1325  (unsigned char *)type_data, next_type_data);
1326 
1327  // Request the state properties all at once.
1328  XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_state,
1329  XA_ATOM, 32, PropModeReplace,
1330  (unsigned char *)state_data, next_state_data);
1331 
1332  if (already_mapped) {
1333  // We have to request state changes differently when the window has been
1334  // mapped. To do this, we need to send a client message to the root
1335  // window for each change.
1336 
1337  x11GraphicsPipe *x11_pipe;
1338  DCAST_INTO_V(x11_pipe, _pipe);
1339 
1340  for (int i = 0; i < next_set_data; ++i) {
1341  XClientMessageEvent event;
1342  memset(&event, 0, sizeof(event));
1343  event.type = ClientMessage;
1344  event.send_event = True;
1345  event.display = _display;
1346  event.window = _xwindow;
1347  event.message_type = x11_pipe->_net_wm_state;
1348  event.format = 32;
1349  event.data.l[0] = set_data[i]._action;
1350  event.data.l[1] = set_data[i]._state;
1351  event.data.l[2] = 0;
1352  event.data.l[3] = 1;
1353 
1354  XSendEvent(_display, x11_pipe->get_root(), True, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&event);
1355  }
1356  }
1357 
1358  XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
1359  nullptr, 0, size_hints_p, wm_hints_p, class_hints_p);
1360 
1361  if (size_hints_p != nullptr) {
1362  XFree(size_hints_p);
1363  }
1364  if (wm_hints_p != nullptr) {
1365  XFree(wm_hints_p);
1366  }
1367  if (class_hints_p != nullptr) {
1368  XFree(class_hints_p);
1369  }
1370 
1371  // Also, indicate to the window manager that we'd like to get a chance to
1372  // close our windows cleanly, rather than being rudely disconnected from the
1373  // X server if the user requests a window close.
1374  Atom protocols[] = {
1375  _wm_delete_window,
1376  };
1377 
1378  XSetWMProtocols(_display, _xwindow, protocols,
1379  sizeof(protocols) / sizeof(Atom));
1380 }
1381 
1382 /**
1383  * Allocates a colormap appropriate to the visual and stores in in the
1384  * _colormap method.
1385  */
1386 void x11GraphicsWindow::
1387 setup_colormap(XVisualInfo *visual) {
1388  x11GraphicsPipe *x11_pipe;
1389  DCAST_INTO_V(x11_pipe, _pipe);
1390  X11_Window root_window = x11_pipe->get_root();
1391 
1392  _colormap = XCreateColormap(_display, root_window,
1393  visual->visual, AllocNone);
1394 }
1395 
1396 /**
1397  * Adds raw mice to the _input_devices list.
1398  * @deprecated obtain raw devices via the device manager instead.
1399  */
1400 void x11GraphicsWindow::
1401 open_raw_mice() {
1402 #ifdef PHAVE_LINUX_INPUT_H
1403  bool any_present = false;
1404  bool any_mice = false;
1405 
1406  for (int i=0; i<64; i++) {
1407  ostringstream fnb;
1408  fnb << "/dev/input/event" << i;
1409  string fn = fnb.str();
1410  int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
1411  if (fd >= 0) {
1412  EvdevInputDevice *device = new EvdevInputDevice(nullptr, fd);
1413  nassertd(device != NULL) continue;
1414 
1415  if (device->has_pointer()) {
1416  add_input_device(device);
1417 
1418  x11display_cat.info()
1419  << "Raw mouse " << _input_devices.size()
1420  << " detected: " << device->get_name() << "\n";
1421 
1422  any_mice = true;
1423  any_present = true;
1424  }
1425  } else {
1426  if (errno == ENOENT || errno == ENOTDIR) {
1427  break;
1428  } else {
1429  any_present = true;
1430  x11display_cat.error()
1431  << "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
1432  }
1433  }
1434  }
1435 
1436  if (any_mice) {
1437  _properties.set_raw_mice(true);
1438 
1439  } else if (!any_present) {
1440  x11display_cat.error() <<
1441  "Opening raw mice: files not found: /dev/input/event*\n";
1442 
1443  } else {
1444  x11display_cat.error() <<
1445  "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
1446  }
1447 #else
1448  x11display_cat.error() <<
1449  "Opening raw mice: panda not compiled with raw mouse support.\n";
1450 #endif
1451 }
1452 
1453 /**
1454  * Generates a keystroke corresponding to the indicated X KeyPress event.
1455  */
1456 void x11GraphicsWindow::
1457 handle_keystroke(XKeyEvent &event) {
1458  if (!_dga_mouse_enabled) {
1459  _input->set_pointer_in_window(event.x, event.y);
1460  }
1461 
1462  if (_ic) {
1463  // First, get the keystroke as a wide-character sequence.
1464  static const int buffer_size = 256;
1465  wchar_t buffer[buffer_size];
1466  Status status;
1467  int len = XwcLookupString(_ic, &event, buffer, buffer_size, nullptr,
1468  &status);
1469  if (status == XBufferOverflow) {
1470  x11display_cat.error()
1471  << "Overflowed input buffer.\n";
1472  }
1473 
1474  // Now each of the returned wide characters represents a keystroke.
1475  for (int i = 0; i < len; i++) {
1476  _input->keystroke(buffer[i]);
1477  }
1478 
1479  } else {
1480  // Without an input context, just get the ascii keypress.
1481  ButtonHandle button = get_button(event, true);
1482  if (button.has_ascii_equivalent()) {
1483  _input->keystroke(button.get_ascii_equivalent());
1484  }
1485  }
1486 }
1487 
1488 /**
1489  * Generates a keypress corresponding to the indicated X KeyPress event.
1490  */
1491 void x11GraphicsWindow::
1492 handle_keypress(XKeyEvent &event) {
1493  if (!_dga_mouse_enabled) {
1494  _input->set_pointer_in_window(event.x, event.y);
1495  }
1496 
1497  // Now get the raw unshifted button.
1498  ButtonHandle button = get_button(event, false);
1499  if (button != ButtonHandle::none()) {
1500  if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1501  _input->button_down(KeyboardButton::control());
1502  }
1503  if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1504  _input->button_down(KeyboardButton::shift());
1505  }
1506  if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1507  _input->button_down(KeyboardButton::alt());
1508  }
1509  if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1510  _input->button_down(KeyboardButton::meta());
1511  }
1512  _input->button_down(button);
1513  }
1514 }
1515 
1516 /**
1517  * Generates a keyrelease corresponding to the indicated X KeyRelease event.
1518  */
1519 void x11GraphicsWindow::
1520 handle_keyrelease(XKeyEvent &event) {
1521  if (!_dga_mouse_enabled) {
1522  _input->set_pointer_in_window(event.x, event.y);
1523  }
1524 
1525  // Now get the raw unshifted button.
1526  ButtonHandle button = get_button(event, false);
1527  if (button != ButtonHandle::none()) {
1528  if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1529  _input->button_up(KeyboardButton::control());
1530  }
1531  if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1532  _input->button_up(KeyboardButton::shift());
1533  }
1534  if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1535  _input->button_up(KeyboardButton::alt());
1536  }
1537  if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1538  _input->button_up(KeyboardButton::meta());
1539  }
1540  _input->button_up(button);
1541  }
1542 }
1543 
1544 /**
1545  * Returns the Panda ButtonHandle corresponding to the keyboard button
1546  * indicated by the given key event.
1547  */
1548 ButtonHandle x11GraphicsWindow::
1549 get_button(XKeyEvent &key_event, bool allow_shift) {
1550  KeySym key = XLookupKeysym(&key_event, 0);
1551 
1552  if ((key_event.state & Mod2Mask) != 0) {
1553  // Mod2Mask corresponds to NumLock being in effect. In this case, we want
1554  // to get the alternate keysym associated with any keypad keys. Weird
1555  // system.
1556  KeySym k2;
1557  ButtonHandle button;
1558  switch (key) {
1559  case XK_KP_Space:
1560  case XK_KP_Tab:
1561  case XK_KP_Enter:
1562  case XK_KP_F1:
1563  case XK_KP_F2:
1564  case XK_KP_F3:
1565  case XK_KP_F4:
1566  case XK_KP_Equal:
1567  case XK_KP_Multiply:
1568  case XK_KP_Add:
1569  case XK_KP_Separator:
1570  case XK_KP_Subtract:
1571  case XK_KP_Divide:
1572  case XK_KP_Left:
1573  case XK_KP_Up:
1574  case XK_KP_Right:
1575  case XK_KP_Down:
1576  case XK_KP_Begin:
1577  case XK_KP_Prior:
1578  case XK_KP_Next:
1579  case XK_KP_Home:
1580  case XK_KP_End:
1581  case XK_KP_Insert:
1582  case XK_KP_Delete:
1583  case XK_KP_0:
1584  case XK_KP_1:
1585  case XK_KP_2:
1586  case XK_KP_3:
1587  case XK_KP_4:
1588  case XK_KP_5:
1589  case XK_KP_6:
1590  case XK_KP_7:
1591  case XK_KP_8:
1592  case XK_KP_9:
1593  k2 = XLookupKeysym(&key_event, 1);
1594  button = map_button(k2);
1595  if (button != ButtonHandle::none()) {
1596  return button;
1597  }
1598  // If that didn't produce a button we know, just fall through and handle
1599  // the normal, un-numlocked key.
1600  break;
1601 
1602  default:
1603  break;
1604  }
1605  }
1606 
1607  if (allow_shift) {
1608  // If shift is held down, get the shifted keysym.
1609  if ((key_event.state & ShiftMask) != 0) {
1610  KeySym k2 = XLookupKeysym(&key_event, 1);
1611  ButtonHandle button = map_button(k2);
1612  if (button != ButtonHandle::none()) {
1613  return button;
1614  }
1615  }
1616 
1617  // If caps lock is down, shift lowercase letters to uppercase. We can do
1618  // this in just the ASCII set, because we handle international keyboards
1619  // elsewhere (via an input context).
1620  if ((key_event.state & (ShiftMask | LockMask)) != 0) {
1621  if (key >= XK_a && key <= XK_z) {
1622  key += (XK_A - XK_a);
1623  }
1624  }
1625  }
1626 
1627  return map_button(key);
1628 }
1629 
1630 /**
1631  * Maps from a single X keysym to Panda's ButtonHandle. Called by
1632  * get_button(), above.
1633  */
1634 ButtonHandle x11GraphicsWindow::
1635 map_button(KeySym key) const {
1636  switch (key) {
1637  case NoSymbol:
1638  return ButtonHandle::none();
1639  case XK_BackSpace:
1640  return KeyboardButton::backspace();
1641  case XK_Tab:
1642  case XK_KP_Tab:
1643  return KeyboardButton::tab();
1644  case XK_Return:
1645  case XK_KP_Enter:
1646  return KeyboardButton::enter();
1647  case XK_Escape:
1648  return KeyboardButton::escape();
1649  case XK_KP_Space:
1650  case XK_space:
1651  return KeyboardButton::space();
1652  case XK_exclam:
1653  return KeyboardButton::ascii_key('!');
1654  case XK_quotedbl:
1655  return KeyboardButton::ascii_key('"');
1656  case XK_numbersign:
1657  return KeyboardButton::ascii_key('#');
1658  case XK_dollar:
1659  return KeyboardButton::ascii_key('$');
1660  case XK_percent:
1661  return KeyboardButton::ascii_key('%');
1662  case XK_ampersand:
1663  return KeyboardButton::ascii_key('&');
1664  case XK_apostrophe: // == XK_quoteright
1665  case XK_dead_acute: // on int'l keyboards
1666  return KeyboardButton::ascii_key('\'');
1667  case XK_parenleft:
1668  return KeyboardButton::ascii_key('(');
1669  case XK_parenright:
1670  return KeyboardButton::ascii_key(')');
1671  case XK_asterisk:
1672  case XK_KP_Multiply:
1673  return KeyboardButton::ascii_key('*');
1674  case XK_plus:
1675  case XK_KP_Add:
1676  return KeyboardButton::ascii_key('+');
1677  case XK_comma:
1678  case XK_KP_Separator:
1679  return KeyboardButton::ascii_key(',');
1680  case XK_minus:
1681  case XK_KP_Subtract:
1682  return KeyboardButton::ascii_key('-');
1683  case XK_period:
1684  case XK_KP_Decimal:
1685  return KeyboardButton::ascii_key('.');
1686  case XK_slash:
1687  case XK_KP_Divide:
1688  return KeyboardButton::ascii_key('/');
1689  case XK_0:
1690  case XK_KP_0:
1691  return KeyboardButton::ascii_key('0');
1692  case XK_1:
1693  case XK_KP_1:
1694  return KeyboardButton::ascii_key('1');
1695  case XK_2:
1696  case XK_KP_2:
1697  return KeyboardButton::ascii_key('2');
1698  case XK_3:
1699  case XK_KP_3:
1700  return KeyboardButton::ascii_key('3');
1701  case XK_4:
1702  case XK_KP_4:
1703  return KeyboardButton::ascii_key('4');
1704  case XK_5:
1705  case XK_KP_5:
1706  return KeyboardButton::ascii_key('5');
1707  case XK_6:
1708  case XK_KP_6:
1709  return KeyboardButton::ascii_key('6');
1710  case XK_7:
1711  case XK_KP_7:
1712  return KeyboardButton::ascii_key('7');
1713  case XK_8:
1714  case XK_KP_8:
1715  return KeyboardButton::ascii_key('8');
1716  case XK_9:
1717  case XK_KP_9:
1718  return KeyboardButton::ascii_key('9');
1719  case XK_colon:
1720  return KeyboardButton::ascii_key(':');
1721  case XK_semicolon:
1722  return KeyboardButton::ascii_key(';');
1723  case XK_less:
1724  return KeyboardButton::ascii_key('<');
1725  case XK_equal:
1726  case XK_KP_Equal:
1727  return KeyboardButton::ascii_key('=');
1728  case XK_greater:
1729  return KeyboardButton::ascii_key('>');
1730  case XK_question:
1731  return KeyboardButton::ascii_key('?');
1732  case XK_at:
1733  return KeyboardButton::ascii_key('@');
1734  case XK_A:
1735  return KeyboardButton::ascii_key('A');
1736  case XK_B:
1737  return KeyboardButton::ascii_key('B');
1738  case XK_C:
1739  return KeyboardButton::ascii_key('C');
1740  case XK_D:
1741  return KeyboardButton::ascii_key('D');
1742  case XK_E:
1743  return KeyboardButton::ascii_key('E');
1744  case XK_F:
1745  return KeyboardButton::ascii_key('F');
1746  case XK_G:
1747  return KeyboardButton::ascii_key('G');
1748  case XK_H:
1749  return KeyboardButton::ascii_key('H');
1750  case XK_I:
1751  return KeyboardButton::ascii_key('I');
1752  case XK_J:
1753  return KeyboardButton::ascii_key('J');
1754  case XK_K:
1755  return KeyboardButton::ascii_key('K');
1756  case XK_L:
1757  return KeyboardButton::ascii_key('L');
1758  case XK_M:
1759  return KeyboardButton::ascii_key('M');
1760  case XK_N:
1761  return KeyboardButton::ascii_key('N');
1762  case XK_O:
1763  return KeyboardButton::ascii_key('O');
1764  case XK_P:
1765  return KeyboardButton::ascii_key('P');
1766  case XK_Q:
1767  return KeyboardButton::ascii_key('Q');
1768  case XK_R:
1769  return KeyboardButton::ascii_key('R');
1770  case XK_S:
1771  return KeyboardButton::ascii_key('S');
1772  case XK_T:
1773  return KeyboardButton::ascii_key('T');
1774  case XK_U:
1775  return KeyboardButton::ascii_key('U');
1776  case XK_V:
1777  return KeyboardButton::ascii_key('V');
1778  case XK_W:
1779  return KeyboardButton::ascii_key('W');
1780  case XK_X:
1781  return KeyboardButton::ascii_key('X');
1782  case XK_Y:
1783  return KeyboardButton::ascii_key('Y');
1784  case XK_Z:
1785  return KeyboardButton::ascii_key('Z');
1786  case XK_bracketleft:
1787  return KeyboardButton::ascii_key('[');
1788  case XK_backslash:
1789  return KeyboardButton::ascii_key('\\');
1790  case XK_bracketright:
1791  return KeyboardButton::ascii_key(']');
1792  case XK_asciicircum:
1793  return KeyboardButton::ascii_key('^');
1794  case XK_underscore:
1795  return KeyboardButton::ascii_key('_');
1796  case XK_grave: // == XK_quoteleft
1797  case XK_dead_grave: // on int'l keyboards
1798  return KeyboardButton::ascii_key('`');
1799  case XK_a:
1800  return KeyboardButton::ascii_key('a');
1801  case XK_b:
1802  return KeyboardButton::ascii_key('b');
1803  case XK_c:
1804  return KeyboardButton::ascii_key('c');
1805  case XK_d:
1806  return KeyboardButton::ascii_key('d');
1807  case XK_e:
1808  return KeyboardButton::ascii_key('e');
1809  case XK_f:
1810  return KeyboardButton::ascii_key('f');
1811  case XK_g:
1812  return KeyboardButton::ascii_key('g');
1813  case XK_h:
1814  return KeyboardButton::ascii_key('h');
1815  case XK_i:
1816  return KeyboardButton::ascii_key('i');
1817  case XK_j:
1818  return KeyboardButton::ascii_key('j');
1819  case XK_k:
1820  return KeyboardButton::ascii_key('k');
1821  case XK_l:
1822  return KeyboardButton::ascii_key('l');
1823  case XK_m:
1824  return KeyboardButton::ascii_key('m');
1825  case XK_n:
1826  return KeyboardButton::ascii_key('n');
1827  case XK_o:
1828  return KeyboardButton::ascii_key('o');
1829  case XK_p:
1830  return KeyboardButton::ascii_key('p');
1831  case XK_q:
1832  return KeyboardButton::ascii_key('q');
1833  case XK_r:
1834  return KeyboardButton::ascii_key('r');
1835  case XK_s:
1836  return KeyboardButton::ascii_key('s');
1837  case XK_t:
1838  return KeyboardButton::ascii_key('t');
1839  case XK_u:
1840  return KeyboardButton::ascii_key('u');
1841  case XK_v:
1842  return KeyboardButton::ascii_key('v');
1843  case XK_w:
1844  return KeyboardButton::ascii_key('w');
1845  case XK_x:
1846  return KeyboardButton::ascii_key('x');
1847  case XK_y:
1848  return KeyboardButton::ascii_key('y');
1849  case XK_z:
1850  return KeyboardButton::ascii_key('z');
1851  case XK_braceleft:
1852  return KeyboardButton::ascii_key('{');
1853  case XK_bar:
1854  return KeyboardButton::ascii_key('|');
1855  case XK_braceright:
1856  return KeyboardButton::ascii_key('}');
1857  case XK_asciitilde:
1858  return KeyboardButton::ascii_key('~');
1859  case XK_F1:
1860  case XK_KP_F1:
1861  return KeyboardButton::f1();
1862  case XK_F2:
1863  case XK_KP_F2:
1864  return KeyboardButton::f2();
1865  case XK_F3:
1866  case XK_KP_F3:
1867  return KeyboardButton::f3();
1868  case XK_F4:
1869  case XK_KP_F4:
1870  return KeyboardButton::f4();
1871  case XK_F5:
1872  return KeyboardButton::f5();
1873  case XK_F6:
1874  return KeyboardButton::f6();
1875  case XK_F7:
1876  return KeyboardButton::f7();
1877  case XK_F8:
1878  return KeyboardButton::f8();
1879  case XK_F9:
1880  return KeyboardButton::f9();
1881  case XK_F10:
1882  return KeyboardButton::f10();
1883  case XK_F11:
1884  return KeyboardButton::f11();
1885  case XK_F12:
1886  return KeyboardButton::f12();
1887  case XK_KP_Left:
1888  case XK_Left:
1889  return KeyboardButton::left();
1890  case XK_KP_Up:
1891  case XK_Up:
1892  return KeyboardButton::up();
1893  case XK_KP_Right:
1894  case XK_Right:
1895  return KeyboardButton::right();
1896  case XK_KP_Down:
1897  case XK_Down:
1898  return KeyboardButton::down();
1899  case XK_KP_Prior:
1900  case XK_Prior:
1901  return KeyboardButton::page_up();
1902  case XK_KP_Next:
1903  case XK_Next:
1904  return KeyboardButton::page_down();
1905  case XK_KP_Home:
1906  case XK_Home:
1907  return KeyboardButton::home();
1908  case XK_KP_End:
1909  case XK_End:
1910  return KeyboardButton::end();
1911  case XK_KP_Insert:
1912  case XK_Insert:
1913  return KeyboardButton::insert();
1914  case XK_KP_Delete:
1915  case XK_Delete:
1916  return KeyboardButton::del();
1917  case XK_Num_Lock:
1918  return KeyboardButton::num_lock();
1919  case XK_Scroll_Lock:
1920  return KeyboardButton::scroll_lock();
1921  case XK_Print:
1922  return KeyboardButton::print_screen();
1923  case XK_Pause:
1924  return KeyboardButton::pause();
1925  case XK_Menu:
1926  return KeyboardButton::menu();
1927  case XK_Shift_L:
1928  return KeyboardButton::lshift();
1929  case XK_Shift_R:
1930  return KeyboardButton::rshift();
1931  case XK_Control_L:
1932  return KeyboardButton::lcontrol();
1933  case XK_Control_R:
1934  return KeyboardButton::rcontrol();
1935  case XK_Alt_L:
1936  return KeyboardButton::lalt();
1937  case XK_Alt_R:
1938  return KeyboardButton::ralt();
1939  case XK_Meta_L:
1940  case XK_Super_L:
1941  return KeyboardButton::lmeta();
1942  case XK_Meta_R:
1943  case XK_Super_R:
1944  return KeyboardButton::rmeta();
1945  case XK_Caps_Lock:
1946  return KeyboardButton::caps_lock();
1947  case XK_Shift_Lock:
1948  return KeyboardButton::shift_lock();
1949  }
1950  if (x11display_cat.is_debug()) {
1951  x11display_cat.debug()
1952  << "Unrecognized keysym 0x" << std::hex << key << std::dec << "\n";
1953  }
1954  return ButtonHandle::none();
1955 }
1956 
1957 /**
1958  * Maps from a single X keycode to Panda's ButtonHandle.
1959  */
1960 ButtonHandle x11GraphicsWindow::
1961 map_raw_button(KeyCode key) const {
1962 #ifdef PHAVE_LINUX_INPUT_H
1963  // Most X11 servers are configured to use the evdev driver, which
1964  // adds 8 to the underlying evdev keycodes (not sure why).
1965  // In any case, this means we can use the same mapping as our raw
1966  // input code, which uses evdev directly.
1967  int index = key - 8;
1968  if (index > 0 && index < 128) {
1969  return EvdevInputDevice::map_button(index);
1970  }
1971 #endif
1972  return ButtonHandle::none();
1973 }
1974 
1975 /**
1976  * Returns the Panda ButtonHandle corresponding to the mouse button indicated
1977  * by the given button event.
1978  */
1979 ButtonHandle x11GraphicsWindow::
1980 get_mouse_button(XButtonEvent &button_event) {
1981  int index = button_event.button;
1982  if (index == x_wheel_up_button) {
1983  return MouseButton::wheel_up();
1984  } else if (index == x_wheel_down_button) {
1985  return MouseButton::wheel_down();
1986  } else if (index == x_wheel_left_button) {
1987  return MouseButton::wheel_left();
1988  } else if (index == x_wheel_right_button) {
1989  return MouseButton::wheel_right();
1990  } else {
1991  return MouseButton::button(index - 1);
1992  }
1993 }
1994 
1995 /**
1996  * Returns a ButtonMap containing the association between raw buttons and
1997  * virtual buttons.
1998  */
1999 ButtonMap *x11GraphicsWindow::
2000 get_keyboard_map() const {
2001  // NB. This could be improved by using the Xkb API. XkbDescPtr desc =
2002  // XkbGetMap(_display, XkbAllMapComponentsMask, XkbUseCoreKbd);
2003  ButtonMap *map = new ButtonMap;
2004 
2005  LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
2006 
2007  for (int k = 9; k <= 135; ++k) {
2008  if (k >= 78 && k <= 91) {
2009  // Ignore numpad keys for now. These are not mapped to separate button
2010  // handles in Panda, so we don't want their mappings to conflict with
2011  // the regular numeric keys.
2012  continue;
2013  }
2014 
2015  ButtonHandle raw_button = map_raw_button(k);
2016  if (raw_button == ButtonHandle::none()) {
2017  continue;
2018  }
2019 
2020  KeySym sym = XkbKeycodeToKeysym(_display, k, 0, 0);
2021  ButtonHandle button = map_button(sym);
2022  std::string label;
2023 
2024  // Compose a label for some keys; I have not yet been able to find an API
2025  // that does this effectively.
2026  if (sym >= XK_a && sym <= XK_z) {
2027  label = toupper((char)sym);
2028  }
2029  else if (sym >= XK_F1 && sym <= XK_F35) {
2030  label = "F" + format_string(sym - XK_F1 + 1);
2031  }
2032  else if (sym >= XK_exclamdown && sym <= XK_ydiaeresis) {
2033  // A latin-1 symbol. Translate this to the label.
2034  char buffer[255];
2035  int nbytes = XkbTranslateKeySym(_display, &sym, 0, buffer, 255, 0);
2036  if (nbytes > 0) {
2037  label.assign(buffer, nbytes);
2038  }
2039  }
2040 
2041  if (button == ButtonHandle::none() && label.empty()) {
2042  // No label and no mapping; this is useless.
2043  continue;
2044  }
2045 
2046  map->map_button(raw_button, button, label);
2047  }
2048 
2049  return map;
2050 }
2051 
2052 /**
2053  * This function is used as a predicate to XCheckIfEvent() to determine if the
2054  * indicated queued X event is relevant and should be returned to this window.
2055  */
2056 Bool x11GraphicsWindow::
2057 check_event(X11_Display *display, XEvent *event, char *arg) {
2058  const x11GraphicsWindow *self = (x11GraphicsWindow *)arg;
2059 
2060  // We accept any event that is sent to our window.
2061  return (event->xany.window == self->_xwindow);
2062 }
2063 
2064 /**
2065  * Loads and returns a Cursor corresponding to the indicated filename. If the
2066  * file cannot be loaded, returns None.
2067  */
2068 X11_Cursor x11GraphicsWindow::
2069 get_cursor(const Filename &filename) {
2070  x11GraphicsPipe *x11_pipe;
2071  DCAST_INTO_R(x11_pipe, _pipe, None);
2072 
2073  if (x11_pipe->_xcursor_size == -1) {
2074  x11display_cat.info()
2075  << "libXcursor.so.1 not available; cannot change mouse cursor.\n";
2076  return None;
2077  }
2078 
2079  // First, look for the unresolved filename in our index.
2080  pmap<Filename, X11_Cursor>::iterator fi = _cursor_filenames.find(filename);
2081  if (fi != _cursor_filenames.end()) {
2082  return fi->second;
2083  }
2084 
2085  // If it wasn't found, resolve the filename and search for that.
2087  Filename resolved (filename);
2088  if (!vfs->resolve_filename(resolved, get_model_path())) {
2089  // The filename doesn't exist.
2090  x11display_cat.warning()
2091  << "Could not find cursor filename " << filename << "\n";
2092  return None;
2093  }
2094  fi = _cursor_filenames.find(resolved);
2095  if (fi != _cursor_filenames.end()) {
2096  return fi->second;
2097  }
2098 
2099  // Open the file through the virtual file system.
2100  istream *str = vfs->open_read_file(resolved, true);
2101  if (str == nullptr) {
2102  x11display_cat.warning()
2103  << "Could not open cursor file " << filename << "\n";
2104  return None;
2105  }
2106 
2107  // Check the first four bytes to see what kind of file it is.
2108  char magic[4];
2109  str->read(magic, 4);
2110  if (!str->good()) {
2111  x11display_cat.warning()
2112  << "Could not read from cursor file " << filename << "\n";
2113  return None;
2114  }
2115 
2116  // Put back the read bytes. Do not use seekg, because this will
2117  // corrupt the stream if it points to encrypted/compressed file
2118  str->putback(magic[3]);
2119  str->putback(magic[2]);
2120  str->putback(magic[1]);
2121  str->putback(magic[0]);
2122 
2123  X11_Cursor h = None;
2124  if (memcmp(magic, "Xcur", 4) == 0) {
2125  // X11 cursor.
2126  x11display_cat.debug()
2127  << "Loading X11 cursor " << filename << "\n";
2128  XcursorFile xcfile;
2129  xcfile.closure = str;
2130  xcfile.read = &xcursor_read;
2131  xcfile.write = &xcursor_write;
2132  xcfile.seek = &xcursor_seek;
2133 
2134  XcursorImages *images = x11_pipe->_XcursorXcFileLoadImages(&xcfile, x11_pipe->_xcursor_size);
2135  if (images != nullptr) {
2136  h = x11_pipe->_XcursorImagesLoadCursor(_display, images);
2137  x11_pipe->_XcursorImagesDestroy(images);
2138  }
2139 
2140  } else if (memcmp(magic, "\0\0\1\0", 4) == 0
2141  || memcmp(magic, "\0\0\2\0", 4) == 0) {
2142  // Windows .ico or .cur file.
2143  x11display_cat.debug()
2144  << "Loading Windows cursor " << filename << "\n";
2145  h = read_ico(*str);
2146  }
2147 
2148  // Delete the istream.
2149  vfs->close_read_file(str);
2150 
2151  if (h == None) {
2152  x11display_cat.warning()
2153  << "X11 cursor filename '" << resolved << "' could not be loaded!\n";
2154  }
2155 
2156  _cursor_filenames[resolved] = h;
2157  return h;
2158 }
2159 
2160 /**
2161  * Reads a Windows .ico or .cur file from the indicated stream and returns it
2162  * as an X11 Cursor. If the file cannot be loaded, returns None.
2163  */
2164 X11_Cursor x11GraphicsWindow::
2165 read_ico(istream &ico) {
2166  x11GraphicsPipe *x11_pipe;
2167  DCAST_INTO_R(x11_pipe, _pipe, None);
2168 
2169  // Local structs, this is just POD, make input easier
2170  typedef struct {
2171  uint16_t reserved, type, count;
2172  } IcoHeader;
2173 
2174  typedef struct {
2175  uint8_t width, height, colorCount, reserved;
2176  uint16_t xhot, yhot;
2177  uint32_t bitmapSize, offset;
2178  } IcoEntry;
2179 
2180  typedef struct {
2181  uint32_t headerSize, width, height;
2182  uint16_t planes, bitsPerPixel;
2183  uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
2184  } IcoInfoHeader;
2185 
2186  typedef struct {
2187  uint8_t blue, green, red, reserved;
2188  } IcoColor;
2189 
2190  int i, entry = 0;
2191  unsigned int j, k, mask, shift;
2192  size_t colorCount, bitsPerPixel;
2193  IcoHeader header;
2194  IcoInfoHeader infoHeader;
2195  IcoEntry *entries = nullptr;
2196  IcoColor color, *palette = nullptr;
2197 
2198  size_t xorBmpSize, andBmpSize;
2199  char *curXor, *curAnd;
2200  char *xorBmp = nullptr, *andBmp = nullptr;
2201  XcursorImage *image = nullptr;
2202  X11_Cursor ret = None;
2203 
2204  int def_size = x11_pipe->_xcursor_size;
2205 
2206  // Get our header, note that ICO = type 1 and CUR = type 2.
2207  ico.read(reinterpret_cast<char *>(&header), sizeof(IcoHeader));
2208  if (!ico.good()) goto cleanup;
2209  if (header.type != 1 && header.type != 2) goto cleanup;
2210  if (header.count < 1) goto cleanup;
2211 
2212  // Read the entry table into memory, select the largest entry.
2213  entries = new IcoEntry[header.count];
2214  ico.read(reinterpret_cast<char *>(entries), header.count * sizeof(IcoEntry));
2215  if (!ico.good()) goto cleanup;
2216  for (i = 1; i < header.count; i++) {
2217  if (entries[i].width == def_size && entries[i].height == def_size) {
2218  // Wait, this is the default cursor size. This is perfect.
2219  entry = i;
2220  break;
2221  }
2222  if (entries[i].width > entries[entry].width ||
2223  entries[i].height > entries[entry].height)
2224  entry = i;
2225  }
2226 
2227  // Seek to the image in the ICO.
2228  ico.seekg(entries[entry].offset);
2229  if (!ico.good()) goto cleanup;
2230 
2231  if (ico.peek() == 0x89) {
2232  // Hang on, this is actually a PNG header.
2233  PNMImage img;
2235  if (!img.read(ico, "", reg->get_type_from_extension("png"))) {
2236  goto cleanup;
2237  }
2238  img.set_maxval(255);
2239 
2240  image = x11_pipe->_XcursorImageCreate(img.get_x_size(), img.get_y_size());
2241 
2242  xel *ptr = img.get_array();
2243  xelval *alpha = img.get_alpha_array();
2244  size_t num_pixels = (size_t)img.get_x_size() * (size_t)img.get_y_size();
2245  unsigned int *dest = image->pixels;
2246 
2247  if (alpha != nullptr) {
2248  for (size_t p = 0; p < num_pixels; ++p) {
2249  *dest++ = (*alpha << 24U) | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2250  ++ptr;
2251  ++alpha;
2252  }
2253  } else {
2254  for (size_t p = 0; p < num_pixels; ++p) {
2255  *dest++ = 0xff000000U | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2256  ++ptr;
2257  }
2258  }
2259 
2260  } else {
2261  ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
2262  if (!ico.good()) goto cleanup;
2263  bitsPerPixel = infoHeader.bitsPerPixel;
2264 
2265  if (infoHeader.compression != 0) goto cleanup;
2266 
2267  // Load the color palette, if one exists.
2268  if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2269  colorCount = 1 << bitsPerPixel;
2270  palette = new IcoColor[colorCount];
2271  ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
2272  if (!ico.good()) goto cleanup;
2273  }
2274 
2275  int and_stride = ((infoHeader.width >> 3) + 3) & ~0x03;
2276 
2277  // Read in the pixel data.
2278  xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2279  andBmpSize = and_stride * (infoHeader.height / 2);
2280  curXor = xorBmp = new char[xorBmpSize];
2281  curAnd = andBmp = new char[andBmpSize];
2282  ico.read(xorBmp, xorBmpSize);
2283  if (!ico.good()) goto cleanup;
2284  ico.read(andBmp, andBmpSize);
2285  if (!ico.good()) goto cleanup;
2286 
2287  image = x11_pipe->_XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2288 
2289  // Support all the formats that GIMP supports.
2290  switch (bitsPerPixel) {
2291  case 1:
2292  case 4:
2293  case 8:
2294  // For colors less that a byte wide, shift and mask the palette indices
2295  // off each element of the xorBmp and append them to the image.
2296  mask = ((1 << bitsPerPixel) - 1);
2297  for (i = image->height - 1; i >= 0; i--) {
2298  for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2299  for (k = 0; k < 8 / bitsPerPixel; k++) {
2300  shift = 8 - ((k + 1) * bitsPerPixel);
2301  color = palette[(*curXor & (mask << shift)) >> shift];
2302  image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2303  (color.green << 8) +
2304  (color.blue);
2305  }
2306 
2307  curXor++;
2308  }
2309 
2310  // Set the alpha byte properly according to the andBmp.
2311  for (j = 0; j < image->width; j += 8) {
2312  for (k = 0; k < 8; k++) {
2313  shift = 7 - k;
2314  image->pixels[(i * image->width) + j + k] |=
2315  ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2316  }
2317 
2318  curAnd++;
2319  }
2320  }
2321  break;
2322 
2323  case 24:
2324  // Pack each of the three bytes into a single color, BGR -> 0RGB
2325  for (i = image->height - 1; i >= 0; i--) {
2326  for (j = 0; j < image->width; j++) {
2327  shift = 7 - (j & 0x7);
2328  uint32_t alpha = (curAnd[j >> 3] & (1 << shift)) ? 0 : 0xff000000U;
2329  image->pixels[(i * image->width) + j] = (uint8_t)curXor[0]
2330  | ((uint8_t)curXor[1] << 8u)
2331  | ((uint8_t)curXor[2] << 16u)
2332  | alpha;
2333  curXor += 3;
2334  }
2335  curAnd += and_stride;
2336  }
2337  break;
2338 
2339  case 32:
2340  // Pack each of the four bytes into a single color, BGRA -> ARGB
2341  for (i = image->height - 1; i >= 0; i--) {
2342  for (j = 0; j < image->width; j++) {
2343  image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2344  (*(curXor + 2) << 16) +
2345  (*(curXor + 1) << 8) +
2346  (*curXor);
2347  curXor += 4;
2348  }
2349  }
2350  break;
2351 
2352  default:
2353  goto cleanup;
2354  }
2355  }
2356 
2357  // If this is an actual CUR not an ICO set up the hotspot properly.
2358  if (header.type == 2) {
2359  image->xhot = entries[entry].xhot;
2360  image->yhot = entries[entry].yhot;
2361  } else {
2362  image->xhot = 0;
2363  image->yhot = 0;
2364  }
2365 
2366  ret = x11_pipe->_XcursorImageLoadCursor(_display, image);
2367 
2368 cleanup:
2369  x11_pipe->_XcursorImageDestroy(image);
2370  delete[] entries;
2371  delete[] palette;
2372  delete[] xorBmp;
2373  delete[] andBmp;
2374 
2375  return ret;
2376 }
LightMutexHolder
Similar to MutexHolder, but for a light mutex.
Definition: lightMutexHolder.h:25
PNMFileTypeRegistry
This class maintains the set of all known PNMFileTypes in the universe.
Definition: pnmFileTypeRegistry.h:28
ButtonMap::map_button
void map_button(ButtonHandle raw_button, ButtonHandle button, const std::string &label="")
Registers a new button mapping.
Definition: buttonMap.cxx:23
x11GraphicsWindow::end_frame
virtual void end_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread after rendering is completed for a given frame.
Definition: x11GraphicsWindow.cxx:267
FrameBufferProperties
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
Definition: frameBufferProperties.h:26
LightReMutexDirect::release
void release() const
Releases the lightReMutex.
Definition: lightReMutexDirect.I:107
GraphicsWindow::set_properties_now
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
Definition: graphicsWindow.cxx:495
x11GraphicsWindow::begin_frame
virtual bool begin_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread before beginning rendering for a given frame.
Definition: x11GraphicsWindow.cxx:234
PNMImage::set_maxval
void set_maxval(xelval maxval)
Rescales the image to the indicated maxval.
Definition: pnmImage.cxx:794
throw_event.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MouseButton::wheel_right
static ButtonHandle wheel_right()
Returns the ButtonHandle generated when the mouse is scrolled to the right.
Definition: mouseButton.cxx:111
x11GraphicsWindow::clear
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
Definition: x11GraphicsWindow.cxx:218
NativeWindowHandle::IntHandle
Definition: nativeWindowHandle.h:55
PointerData
Holds the data that might be generated by a 2-d pointer input device, such as the mouse in the Graphi...
Definition: pointerData.h:38
WindowProperties::get_undecorated
get_undecorated
Returns true if the window has no border.
Definition: windowProperties.h:105
GraphicsWindowInputDevice
This is a virtual input device that represents the keyboard and mouse pair that is associated with a ...
Definition: graphicsWindowInputDevice.h:28
PointerData::get_in_window
get_in_window
If this returns false, the pointer is not currently present in the window and the values returned by ...
Definition: pointerData.h:56
WindowProperties::get_size
get_size
Returns size in pixels of the useful part of the window, not including decorations.
Definition: windowProperties.h:85
PNMImage::get_alpha_array
xelval * get_alpha_array()
Directly access the underlying PNMImage array of alpha values.
Definition: pnmImage.I:1115
ButtonHandle::get_ascii_equivalent
get_ascii_equivalent
Returns the character code associated with the button, or '\0' if no ASCII code was associated.
Definition: buttonHandle.h:63
pnmImage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::has_origin
has_origin
Returns true if the window origin has been specified, false otherwise.
Definition: windowProperties.h:76
WindowProperties::clear_fullscreen
clear_fullscreen
Removes the fullscreen specification from the properties.
Definition: windowProperties.h:119
WindowProperties::has_title
has_title
Returns true if the window title has been specified, false otherwise.
Definition: windowProperties.h:98
WindowProperties::set_origin
set_origin
Specifies the origin on the screen (in pixels, relative to the top-left corner) at which the window s...
Definition: windowProperties.h:76
LightReMutexHolder
Similar to MutexHolder, but for a light reentrant mutex.
Definition: lightReMutexHolder.h:25
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
GraphicsWindowInputDevice::focus_lost
void focus_lost(double time=ClockObject::get_global_clock() ->get_frame_time())
This should be called when the window focus is lost, so that we may miss upcoming button events (espe...
Definition: graphicsWindowInputDevice.cxx:134
GraphicsPipe::get_display_width
get_display_width
Returns the width of the entire display, if it is known.
Definition: graphicsPipe.h:95
mouseButton.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::has_cursor_hidden
has_cursor_hidden
Returns true if set_cursor_hidden() has been specified.
Definition: windowProperties.h:151
GraphicsWindowInputDevice::button_down
void button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
Definition: graphicsWindowInputDevice.cxx:75
GraphicsWindowInputDevice::button_up
void button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
Definition: graphicsWindowInputDevice.cxx:97
x11GraphicsPipe::get_im
XIM get_im() const
Returns the input method opened for the pipe, or NULL if the input method could not be opened for som...
Definition: x11GraphicsPipe.I:44
WindowProperties::get_y_size
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
Definition: windowProperties.I:159
MouseData
BEGIN_PUBLISH typedef PointerData MouseData
Deprecated alias for PointerData.
Definition: mouseData.h:23
clockObject.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ButtonHandle
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
x11GraphicsPipe::get_root
X11_Window get_root() const
Returns the handle to the root window on the pipe's display.
Definition: x11GraphicsPipe.I:35
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsWindowInputDevice::raw_button_down
void raw_button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
Definition: graphicsWindowInputDevice.cxx:147
ClockObject::get_global_clock
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
GraphicsWindowInputDevice::get_pointer
PointerData get_pointer() const
Returns the PointerData associated with the input device's pointer.
Definition: graphicsWindowInputDevice.I:19
x11GraphicsPipe::supports_relative_mouse
bool supports_relative_mouse() const
Returns true if relative mouse mode is supported on this display.
Definition: x11GraphicsPipe.I:64
WindowProperties::set_open
set_open
Specifies whether the window should be open.
Definition: windowProperties.h:144
WindowProperties::has_foreground
has_foreground
Returns true if set_foreground() has been specified.
Definition: windowProperties.h:126
WindowProperties
A container for the various kinds of properties we might ask to have on a graphics window before we o...
Definition: windowProperties.h:29
WindowProperties::get_x_size
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
Definition: windowProperties.I:149
DrawableRegion::is_any_clear_active
virtual bool is_any_clear_active() const
Returns true if any of the clear types (so far there are just color or depth) have been set active,...
Definition: drawableRegion.cxx:83
WindowProperties::get_minimized
get_minimized
Returns true if the window is minimized.
Definition: windowProperties.h:133
MouseButton::wheel_left
static ButtonHandle wheel_left()
Returns the ButtonHandle generated when the mouse is scrolled to the left.
Definition: mouseButton.cxx:102
PNMImage::read
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
keyboardButton.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowHandle
This object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:34
WindowProperties::set_foreground
set_foreground
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
Definition: windowProperties.h:126
GraphicsWindowInputDevice::raw_button_up
void raw_button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
Definition: graphicsWindowInputDevice.cxx:156
KeyboardButton::ascii_key
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one,...
Definition: keyboardButton.cxx:24
LightReMutexDirect::try_lock
bool try_lock()
Alias for try_acquire() to match C++11 semantics.
Definition: lightReMutexDirect.I:29
graphicsPipe.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMImage
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
PNMImageHeader::get_x_size
int get_x_size() const
Returns the number of pixels in the X direction.
Definition: pnmImageHeader.I:144
WindowProperties::clear_size
clear_size
Removes the size specification from the properties.
Definition: windowProperties.h:85
MouseButton::button
static ButtonHandle button(int button_number)
Returns the ButtonHandle associated with the particular numbered mouse button (zero-based),...
Definition: mouseButton.cxx:32
GraphicsEngine
This class is the main interface to controlling the render process.
Definition: graphicsEngine.h:53
WindowProperties::get_mouse_mode
get_mouse_mode
See set_mouse_mode().
Definition: windowProperties.h:92
WindowHandle::OSHandle
Definition: windowHandle.h:63
WindowProperties::clear_z_order
clear_z_order
Removes the z_order specification from the properties.
Definition: windowProperties.h:171
ClockObject::get_real_time
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
Definition: clockObject.h:92
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
GraphicsOutput
This is a base class for the various different classes that represent the result of a frame of render...
Definition: graphicsOutput.h:63
GraphicsOutput::clear
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
Definition: graphicsOutput.cxx:1140
config_x11display.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::clear_title
clear_title
Removes the title specification from the properties.
Definition: windowProperties.h:98
WindowProperties::set_size
set_size
Specifies the requested size of the window, in pixels.
Definition: windowProperties.h:85
WindowProperties::get_x_origin
int get_x_origin() const
Returns the x coordinate of the window's top-left corner, not including decorations.
Definition: windowProperties.I:82
GraphicsWindowInputDevice::keystroke
void keystroke(int keycode, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated keystroke has been generated.
Definition: graphicsWindowInputDevice.cxx:107
get_x11.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMImageHeader::get_y_size
int get_y_size() const
Returns the number of pixels in the Y direction.
Definition: pnmImageHeader.I:153
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
WindowProperties::get_fullscreen
get_fullscreen
Returns true if the window is in fullscreen mode.
Definition: windowProperties.h:119
PNMFileTypeRegistry::get_global_ptr
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
Definition: pnmFileTypeRegistry.cxx:277
PNMImage::get_array
xel * get_array()
Directly access the underlying PNMImage array.
Definition: pnmImage.I:1098
WindowProperties::clear_foreground
clear_foreground
Removes the foreground specification from the properties.
Definition: windowProperties.h:126
WindowProperties::set_mouse_mode
set_mouse_mode
Specifies the mode in which the window is to operate its mouse pointer.
Definition: windowProperties.h:92
WindowProperties::get_z_order
get_z_order
Returns the window's z_order.
Definition: windowProperties.h:171
WindowProperties::set_minimized
set_minimized
Specifies whether the window should be created minimized (true), or normal (false).
Definition: windowProperties.h:133
XRRScreenSize
Definition: x11GraphicsPipe.h:41
WindowProperties::has_size
has_size
Returns true if the window size has been specified, false otherwise.
Definition: windowProperties.h:85
WindowProperties::is_any_specified
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
Definition: windowProperties.I:41
GraphicsPipe
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
x11GraphicsWindow::get_pointer
virtual MouseData get_pointer(int device) const
Returns the MouseData associated with the nth input device's pointer.
Definition: x11GraphicsWindow.cxx:147
WindowProperties::get_foreground
get_foreground
Returns true if the window is in the foreground.
Definition: windowProperties.h:126
x11GraphicsWindow
Interfaces to the X11 window system.
Definition: x11GraphicsWindow.h:26
x11GraphicsWindow::set_properties_now
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
Definition: x11GraphicsWindow.cxx:586
pnmFileTypeRegistry.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
x11GraphicsPipe::get_hidden_cursor
X11_Cursor get_hidden_cursor()
Returns an invisible Cursor suitable for assigning to windows that have the cursor_hidden property se...
Definition: x11GraphicsPipe.I:53
WindowProperties::has_cursor_filename
has_cursor_filename
Returns true if set_cursor_filename() has been specified.
Definition: windowProperties.h:165
WindowProperties::get_cursor_hidden
get_cursor_hidden
Returns true if the mouse cursor is invisible.
Definition: windowProperties.h:151
ButtonHandle::has_ascii_equivalent
has_ascii_equivalent
Returns true if the button was created with an ASCII equivalent code (e.g.
Definition: buttonHandle.h:63
GraphicsOutput::get_fb_properties
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
Definition: graphicsOutput.I:413
GraphicsWindow::get_close_request_event
get_close_request_event
Returns the name of the event set via set_close_request_event().
Definition: graphicsWindow.h:74
WindowProperties::clear_origin
clear_origin
Removes the origin specification from the properties.
Definition: windowProperties.h:76
WindowProperties::has_minimized
has_minimized
Returns true if set_minimized() has been specified.
Definition: windowProperties.h:133
GraphicsWindowInputDevice::set_pointer_in_window
void set_pointer_in_window(double x, double y, double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is within the window...
Definition: graphicsWindowInputDevice.cxx:166
x11GraphicsPipe::disable_relative_mouse
void disable_relative_mouse()
Disables relative mouse mode for this display.
Definition: x11GraphicsPipe.I:85
pixel
Definition: pnmimage_base.h:41
WindowProperties::clear_cursor_filename
clear_cursor_filename
Removes the cursor_filename specification from the properties.
Definition: windowProperties.h:165
WindowProperties::get_fixed_size
get_fixed_size
Returns true if the window cannot be resized by the user, false otherwise.
Definition: windowProperties.h:112
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:741
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::has_fullscreen
has_fullscreen
Returns true if set_fullscreen() has been specified.
Definition: windowProperties.h:119
VirtualFileSystem::close_read_file
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
Definition: virtualFileSystem.cxx:866
x11GraphicsPipe::enable_relative_mouse
bool enable_relative_mouse()
Enables relative mouse mode for this display.
Definition: x11GraphicsPipe.I:72
x11GraphicsPipe::get_screen
int get_screen() const
Returns the X screen number associated with the pipe.
Definition: x11GraphicsPipe.I:27
x11GraphicsWindow::process_events
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
Definition: x11GraphicsWindow.cxx:292
PNMFileTypeRegistry::get_type_from_extension
PNMFileType * get_type_from_extension(const std::string &filename) const
Tries to determine what the PNMFileType is likely to be for a particular image file based on its exte...
Definition: pnmFileTypeRegistry.cxx:147
x11GraphicsPipe::find_fullscreen_crtc
RRCrtc find_fullscreen_crtc(const LPoint2i &point, int &x, int &y, int &width, int &height)
Finds a CRTC for going fullscreen to, at the given origin.
Definition: x11GraphicsPipe.cxx:392
MouseButton::wheel_up
static ButtonHandle wheel_up()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch upwards.
Definition: mouseButton.cxx:84
WindowProperties::get_cursor_filename
get_cursor_filename
Returns the icon filename associated with the mouse cursor.
Definition: windowProperties.h:165
GraphicsWindowInputDevice::set_pointer_out_of_window
void set_pointer_out_of_window(double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is no longer within ...
Definition: graphicsWindowInputDevice.cxx:182
buttonMap.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
evdevInputDevice.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MouseButton::wheel_down
static ButtonHandle wheel_down()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch downwards.
Definition: mouseButton.cxx:93
WindowProperties::has_z_order
has_z_order
Returns true if the window z_order has been specified, false otherwise.
Definition: windowProperties.h:171
nativeWindowHandle.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::get_title
get_title
Returns the window's title.
Definition: windowProperties.h:98
GraphicsStateGuardian
Encapsulates all the communication with a particular instance of a given rendering backend.
Definition: graphicsStateGuardian.h:65
GraphicsPipe::get_display_height
get_display_height
Returns the height of the entire display, if it is known.
Definition: graphicsPipe.h:96
x11GraphicsPipe::get_display
X11_Display * get_display() const
Returns a pointer to the X display associated with the pipe: the display on which to create the windo...
Definition: x11GraphicsPipe.I:19
x11GraphicsWindow.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::resolve_filename
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
Definition: virtualFileSystem.cxx:640
VirtualFileSystem::open_read_file
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
Definition: virtualFileSystem.cxx:846
GraphicsWindow::process_events
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
Definition: graphicsWindow.cxx:459
x11GraphicsPipe
This graphics pipe represents the interface for creating graphics windows on an X-based client.
Definition: x11GraphicsPipe.h:96
WindowProperties::clear_fixed_size
clear_fixed_size
Removes the fixed_size specification from the properties.
Definition: windowProperties.h:112
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
GraphicsWindow::is_fullscreen
bool is_fullscreen() const
Returns true if the window has been opened as a fullscreen window, false otherwise.
Definition: graphicsWindow.I:31
WindowProperties::clear_mouse_mode
clear_mouse_mode
Removes the mouse_mode specification from the properties.
Definition: windowProperties.h:92
GraphicsWindow
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
Definition: graphicsWindow.h:40
WindowProperties::has_fixed_size
has_fixed_size
Returns true if set_fixed_size() has been specified.
Definition: windowProperties.h:112
x11GraphicsPipe.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
textEncoder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
WindowHandle::get_os_handle
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.h:44
ButtonMap
This class represents a map containing all of the buttons of a (keyboard) device, though it can also ...
Definition: buttonMap.h:30
WindowProperties::clear_cursor_hidden
clear_cursor_hidden
Removes the cursor_hidden specification from the properties.
Definition: windowProperties.h:151
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
WindowProperties::get_y_origin
int get_y_origin() const
Returns the y coordinate of the window's top-left corner, not including decorations.
Definition: windowProperties.I:92
lightReMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
x11GraphicsWindow::move_pointer
virtual bool move_pointer(int device, int x, int y)
Forces the pointer to the indicated position within the window, if possible.
Definition: x11GraphicsWindow.cxx:184