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_exclam && sym <= XK_asciitilde) {
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 > 0x1000000 && sym < 0x1110000) {
2033  // Unicode code point. Encode as UTF-8.
2034  char32_t ch = sym & 0x0ffffff;
2035  if ((ch & ~0x7f) == 0) {
2036  label = string(1, (char)ch);
2037  }
2038  else if ((ch & ~0x7ff) == 0) {
2039  label =
2040  string(1, (char)((ch >> 6) | 0xc0)) +
2041  string(1, (char)((ch & 0x3f) | 0x80));
2042  }
2043  else if ((ch & ~0xffff) == 0) {
2044  label =
2045  string(1, (char)((ch >> 12) | 0xe0)) +
2046  string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
2047  string(1, (char)((ch & 0x3f) | 0x80));
2048  }
2049  else {
2050  label =
2051  string(1, (char)((ch >> 18) | 0xf0)) +
2052  string(1, (char)(((ch >> 12) & 0x3f) | 0x80)) +
2053  string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
2054  string(1, (char)((ch & 0x3f) | 0x80));
2055  }
2056  }
2057  else if ((sym >= XK_exclamdown && sym <= XK_umacron)
2058  || (sym >= XK_OE && sym <= XK_Ydiaeresis)
2059  || (sym >= XK_Serbian_dje && sym <= XK_Cyrillic_HARDSIGN)
2060  || (sym >= XK_kana_fullstop && sym <= XK_semivoicedsound)
2061  || (sym >= XK_Arabic_comma && sym <= XK_Arabic_sukun)
2062  || (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_omega)
2063  || (sym >= XK_hebrew_doublelowline && sym <= XK_hebrew_taw)
2064  || (sym >= XK_Thai_kokai && sym <= XK_Thai_lekkao)
2065  || (sym >= XK_Hangul_Kiyeog && sym <= XK_Hangul_J_YeorinHieuh)
2066  || sym == XK_EuroSign
2067  || sym == XK_Korean_Won) {
2068  // A non-unicode-based keysym. Translate this to the label.
2069  char buffer[255];
2070  int nbytes = XkbTranslateKeySym(_display, &sym, 0, buffer, 255, 0);
2071  if (nbytes > 0) {
2072  label.assign(buffer, nbytes);
2073  }
2074  }
2075 
2076  if (button == ButtonHandle::none() && label.empty()) {
2077  // No label and no mapping; this is useless.
2078  continue;
2079  }
2080 
2081  map->map_button(raw_button, button, label);
2082  }
2083 
2084  return map;
2085 }
2086 
2087 /**
2088  * This function is used as a predicate to XCheckIfEvent() to determine if the
2089  * indicated queued X event is relevant and should be returned to this window.
2090  */
2091 Bool x11GraphicsWindow::
2092 check_event(X11_Display *display, XEvent *event, char *arg) {
2093  const x11GraphicsWindow *self = (x11GraphicsWindow *)arg;
2094 
2095  // We accept any event that is sent to our window.
2096  return (event->xany.window == self->_xwindow);
2097 }
2098 
2099 /**
2100  * Loads and returns a Cursor corresponding to the indicated filename. If the
2101  * file cannot be loaded, returns None.
2102  */
2103 X11_Cursor x11GraphicsWindow::
2104 get_cursor(const Filename &filename) {
2105  x11GraphicsPipe *x11_pipe;
2106  DCAST_INTO_R(x11_pipe, _pipe, None);
2107 
2108  if (x11_pipe->_xcursor_size == -1) {
2109  x11display_cat.info()
2110  << "libXcursor.so.1 not available; cannot change mouse cursor.\n";
2111  return None;
2112  }
2113 
2114  // First, look for the unresolved filename in our index.
2115  pmap<Filename, X11_Cursor>::iterator fi = _cursor_filenames.find(filename);
2116  if (fi != _cursor_filenames.end()) {
2117  return fi->second;
2118  }
2119 
2120  // If it wasn't found, resolve the filename and search for that.
2122  Filename resolved (filename);
2123  if (!vfs->resolve_filename(resolved, get_model_path())) {
2124  // The filename doesn't exist.
2125  x11display_cat.warning()
2126  << "Could not find cursor filename " << filename << "\n";
2127  return None;
2128  }
2129  fi = _cursor_filenames.find(resolved);
2130  if (fi != _cursor_filenames.end()) {
2131  return fi->second;
2132  }
2133 
2134  // Open the file through the virtual file system.
2135  istream *str = vfs->open_read_file(resolved, true);
2136  if (str == nullptr) {
2137  x11display_cat.warning()
2138  << "Could not open cursor file " << filename << "\n";
2139  return None;
2140  }
2141 
2142  // Check the first four bytes to see what kind of file it is.
2143  char magic[4];
2144  str->read(magic, 4);
2145  if (!str->good()) {
2146  x11display_cat.warning()
2147  << "Could not read from cursor file " << filename << "\n";
2148  return None;
2149  }
2150 
2151  // Put back the read bytes. Do not use seekg, because this will
2152  // corrupt the stream if it points to encrypted/compressed file
2153  str->putback(magic[3]);
2154  str->putback(magic[2]);
2155  str->putback(magic[1]);
2156  str->putback(magic[0]);
2157 
2158  X11_Cursor h = None;
2159  if (memcmp(magic, "Xcur", 4) == 0) {
2160  // X11 cursor.
2161  x11display_cat.debug()
2162  << "Loading X11 cursor " << filename << "\n";
2163  XcursorFile xcfile;
2164  xcfile.closure = str;
2165  xcfile.read = &xcursor_read;
2166  xcfile.write = &xcursor_write;
2167  xcfile.seek = &xcursor_seek;
2168 
2169  XcursorImages *images = x11_pipe->_XcursorXcFileLoadImages(&xcfile, x11_pipe->_xcursor_size);
2170  if (images != nullptr) {
2171  h = x11_pipe->_XcursorImagesLoadCursor(_display, images);
2172  x11_pipe->_XcursorImagesDestroy(images);
2173  }
2174 
2175  } else if (memcmp(magic, "\0\0\1\0", 4) == 0
2176  || memcmp(magic, "\0\0\2\0", 4) == 0) {
2177  // Windows .ico or .cur file.
2178  x11display_cat.debug()
2179  << "Loading Windows cursor " << filename << "\n";
2180  h = read_ico(*str);
2181  }
2182 
2183  // Delete the istream.
2184  vfs->close_read_file(str);
2185 
2186  if (h == None) {
2187  x11display_cat.warning()
2188  << "X11 cursor filename '" << resolved << "' could not be loaded!\n";
2189  }
2190 
2191  _cursor_filenames[resolved] = h;
2192  return h;
2193 }
2194 
2195 /**
2196  * Reads a Windows .ico or .cur file from the indicated stream and returns it
2197  * as an X11 Cursor. If the file cannot be loaded, returns None.
2198  */
2199 X11_Cursor x11GraphicsWindow::
2200 read_ico(istream &ico) {
2201  x11GraphicsPipe *x11_pipe;
2202  DCAST_INTO_R(x11_pipe, _pipe, None);
2203 
2204  // Local structs, this is just POD, make input easier
2205  typedef struct {
2206  uint16_t reserved, type, count;
2207  } IcoHeader;
2208 
2209  typedef struct {
2210  uint8_t width, height, colorCount, reserved;
2211  uint16_t xhot, yhot;
2212  uint32_t bitmapSize, offset;
2213  } IcoEntry;
2214 
2215  typedef struct {
2216  uint32_t headerSize, width, height;
2217  uint16_t planes, bitsPerPixel;
2218  uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
2219  } IcoInfoHeader;
2220 
2221  typedef struct {
2222  uint8_t blue, green, red, reserved;
2223  } IcoColor;
2224 
2225  int i, entry = 0;
2226  unsigned int j, k, mask, shift;
2227  size_t colorCount, bitsPerPixel;
2228  IcoHeader header;
2229  IcoInfoHeader infoHeader;
2230  IcoEntry *entries = nullptr;
2231  IcoColor color, *palette = nullptr;
2232 
2233  size_t xorBmpSize, andBmpSize;
2234  char *curXor, *curAnd;
2235  char *xorBmp = nullptr, *andBmp = nullptr;
2236  XcursorImage *image = nullptr;
2237  X11_Cursor ret = None;
2238 
2239  int def_size = x11_pipe->_xcursor_size;
2240 
2241  // Get our header, note that ICO = type 1 and CUR = type 2.
2242  ico.read(reinterpret_cast<char *>(&header), sizeof(IcoHeader));
2243  if (!ico.good()) goto cleanup;
2244  if (header.type != 1 && header.type != 2) goto cleanup;
2245  if (header.count < 1) goto cleanup;
2246 
2247  // Read the entry table into memory, select the largest entry.
2248  entries = new IcoEntry[header.count];
2249  ico.read(reinterpret_cast<char *>(entries), header.count * sizeof(IcoEntry));
2250  if (!ico.good()) goto cleanup;
2251  for (i = 1; i < header.count; i++) {
2252  if (entries[i].width == def_size && entries[i].height == def_size) {
2253  // Wait, this is the default cursor size. This is perfect.
2254  entry = i;
2255  break;
2256  }
2257  if (entries[i].width > entries[entry].width ||
2258  entries[i].height > entries[entry].height)
2259  entry = i;
2260  }
2261 
2262  // Seek to the image in the ICO.
2263  ico.seekg(entries[entry].offset);
2264  if (!ico.good()) goto cleanup;
2265 
2266  if (ico.peek() == 0x89) {
2267  // Hang on, this is actually a PNG header.
2268  PNMImage img;
2270  if (!img.read(ico, "", reg->get_type_from_extension("png"))) {
2271  goto cleanup;
2272  }
2273  img.set_maxval(255);
2274 
2275  image = x11_pipe->_XcursorImageCreate(img.get_x_size(), img.get_y_size());
2276 
2277  xel *ptr = img.get_array();
2278  xelval *alpha = img.get_alpha_array();
2279  size_t num_pixels = (size_t)img.get_x_size() * (size_t)img.get_y_size();
2280  unsigned int *dest = image->pixels;
2281 
2282  if (alpha != nullptr) {
2283  for (size_t p = 0; p < num_pixels; ++p) {
2284  *dest++ = (*alpha << 24U) | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2285  ++ptr;
2286  ++alpha;
2287  }
2288  } else {
2289  for (size_t p = 0; p < num_pixels; ++p) {
2290  *dest++ = 0xff000000U | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2291  ++ptr;
2292  }
2293  }
2294 
2295  } else {
2296  ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
2297  if (!ico.good()) goto cleanup;
2298  bitsPerPixel = infoHeader.bitsPerPixel;
2299 
2300  if (infoHeader.compression != 0) goto cleanup;
2301 
2302  // Load the color palette, if one exists.
2303  if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2304  colorCount = 1 << bitsPerPixel;
2305  palette = new IcoColor[colorCount];
2306  ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
2307  if (!ico.good()) goto cleanup;
2308  }
2309 
2310  int and_stride = ((infoHeader.width >> 3) + 3) & ~0x03;
2311 
2312  // Read in the pixel data.
2313  xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2314  andBmpSize = and_stride * (infoHeader.height / 2);
2315  curXor = xorBmp = new char[xorBmpSize];
2316  curAnd = andBmp = new char[andBmpSize];
2317  ico.read(xorBmp, xorBmpSize);
2318  if (!ico.good()) goto cleanup;
2319  ico.read(andBmp, andBmpSize);
2320  if (!ico.good()) goto cleanup;
2321 
2322  image = x11_pipe->_XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2323 
2324  // Support all the formats that GIMP supports.
2325  switch (bitsPerPixel) {
2326  case 1:
2327  case 4:
2328  case 8:
2329  // For colors less that a byte wide, shift and mask the palette indices
2330  // off each element of the xorBmp and append them to the image.
2331  mask = ((1 << bitsPerPixel) - 1);
2332  for (i = image->height - 1; i >= 0; i--) {
2333  for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2334  for (k = 0; k < 8 / bitsPerPixel; k++) {
2335  shift = 8 - ((k + 1) * bitsPerPixel);
2336  color = palette[(*curXor & (mask << shift)) >> shift];
2337  image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2338  (color.green << 8) +
2339  (color.blue);
2340  }
2341 
2342  curXor++;
2343  }
2344 
2345  // Set the alpha byte properly according to the andBmp.
2346  for (j = 0; j < image->width; j += 8) {
2347  for (k = 0; k < 8; k++) {
2348  shift = 7 - k;
2349  image->pixels[(i * image->width) + j + k] |=
2350  ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2351  }
2352 
2353  curAnd++;
2354  }
2355  }
2356  break;
2357 
2358  case 24:
2359  // Pack each of the three bytes into a single color, BGR -> 0RGB
2360  for (i = image->height - 1; i >= 0; i--) {
2361  for (j = 0; j < image->width; j++) {
2362  shift = 7 - (j & 0x7);
2363  uint32_t alpha = (curAnd[j >> 3] & (1 << shift)) ? 0 : 0xff000000U;
2364  image->pixels[(i * image->width) + j] = (uint8_t)curXor[0]
2365  | ((uint8_t)curXor[1] << 8u)
2366  | ((uint8_t)curXor[2] << 16u)
2367  | alpha;
2368  curXor += 3;
2369  }
2370  curAnd += and_stride;
2371  }
2372  break;
2373 
2374  case 32:
2375  // Pack each of the four bytes into a single color, BGRA -> ARGB
2376  for (i = image->height - 1; i >= 0; i--) {
2377  for (j = 0; j < image->width; j++) {
2378  image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2379  (*(curXor + 2) << 16) +
2380  (*(curXor + 1) << 8) +
2381  (*curXor);
2382  curXor += 4;
2383  }
2384  }
2385  break;
2386 
2387  default:
2388  goto cleanup;
2389  }
2390  }
2391 
2392  // If this is an actual CUR not an ICO set up the hotspot properly.
2393  if (header.type == 2) {
2394  image->xhot = entries[entry].xhot;
2395  image->yhot = entries[entry].yhot;
2396  } else {
2397  image->xhot = 0;
2398  image->yhot = 0;
2399  }
2400 
2401  ret = x11_pipe->_XcursorImageLoadCursor(_display, image);
2402 
2403 cleanup:
2404  x11_pipe->_XcursorImageDestroy(image);
2405  delete[] entries;
2406  delete[] palette;
2407  delete[] xorBmp;
2408  delete[] andBmp;
2409 
2410  return ret;
2411 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
get_ascii_equivalent
Returns the character code associated with the button, or '\0' if no ASCII code was associated.
Definition: buttonHandle.h:63
has_ascii_equivalent
Returns true if the button was created with an ASCII equivalent code (e.g.
Definition: buttonHandle.h:63
This class represents a map containing all of the buttons of a (keyboard) device, though it can also ...
Definition: buttonMap.h:30
void map_button(ButtonHandle raw_button, ButtonHandle button, const std::string &label="")
Registers a new button mapping.
Definition: buttonMap.cxx:23
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
Definition: clockObject.h:92
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
virtual bool is_any_clear_active() const
Returns true if any of the clear types (so far there are just color or depth) have been set active,...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
This class is the main interface to controlling the render process.
This is a base class for the various different classes that represent the result of a frame of render...
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
get_display_width
Returns the width of the entire display, if it is known.
Definition: graphicsPipe.h:95
get_display_height
Returns the height of the entire display, if it is known.
Definition: graphicsPipe.h:96
Encapsulates all the communication with a particular instance of a given rendering backend.
This is a virtual input device that represents the keyboard and mouse pair that is associated with a ...
void focus_lost(double time=ClockObject::get_global_clock() ->get_frame_time())
This should be called when the window focus is lost, so that we may miss upcoming button events (espe...
void button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
void set_pointer_out_of_window(double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is no longer within ...
void keystroke(int keycode, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated keystroke has been generated.
void raw_button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
PointerData get_pointer() const
Returns the PointerData associated with the input device's pointer.
void raw_button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
void set_pointer_in_window(double x, double y, double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is within the window...
void button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
bool is_fullscreen() const
Returns true if the window has been opened as a fullscreen window, false otherwise.
get_close_request_event
Returns the name of the event set via set_close_request_event().
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one,...
Similar to MutexHolder, but for a light mutex.
bool try_lock()
Alias for try_acquire() to match C++11 semantics.
void release() const
Releases the lightReMutex.
Similar to MutexHolder, but for a light reentrant mutex.
static ButtonHandle button(int button_number)
Returns the ButtonHandle associated with the particular numbered mouse button (zero-based),...
Definition: mouseButton.cxx:32
static ButtonHandle wheel_left()
Returns the ButtonHandle generated when the mouse is scrolled to the left.
static ButtonHandle wheel_up()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch upwards.
Definition: mouseButton.cxx:84
static ButtonHandle wheel_right()
Returns the ButtonHandle generated when the mouse is scrolled to the right.
static ButtonHandle wheel_down()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch downwards.
Definition: mouseButton.cxx:93
This class maintains the set of all known PNMFileTypes in the universe.
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
PNMFileType * get_type_from_extension(const std::string &filename) const
Tries to determine what the PNMFileType is likely to be for a particular image file based on its exte...
int get_x_size() const
Returns the number of pixels in the X direction.
int get_y_size() const
Returns the number of pixels in the Y direction.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
void set_maxval(xelval maxval)
Rescales the image to the indicated maxval.
Definition: pnmImage.cxx:794
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
xel * get_array()
Directly access the underlying PNMImage array.
Definition: pnmImage.I:1098
xelval * get_alpha_array()
Directly access the underlying PNMImage array of alpha values.
Definition: pnmImage.I:1115
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
Holds the data that might be generated by a 2-d pointer input device, such as the mouse in the Graphi...
Definition: pointerData.h:38
get_in_window
If this returns false, the pointer is not currently present in the window and the values returned by ...
Definition: pointerData.h:56
A thread; that is, a lightweight process.
Definition: thread.h:46
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
A hierarchy of directories and files that appears to be one continuous file system,...
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
This object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:34
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.h:44
A container for the various kinds of properties we might ask to have on a graphics window before we o...
clear_cursor_filename
Removes the cursor_filename specification from the properties.
has_cursor_filename
Returns true if set_cursor_filename() has been specified.
get_title
Returns the window's title.
get_undecorated
Returns true if the window has no border.
clear_cursor_hidden
Removes the cursor_hidden specification from the properties.
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
set_mouse_mode
Specifies the mode in which the window is to operate its mouse pointer.
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
int get_y_origin() const
Returns the y coordinate of the window's top-left corner, not including decorations.
clear_z_order
Removes the z_order specification from the properties.
get_minimized
Returns true if the window is minimized.
clear_mouse_mode
Removes the mouse_mode specification from the properties.
has_fullscreen
Returns true if set_fullscreen() has been specified.
has_size
Returns true if the window size has been specified, false otherwise.
clear_title
Removes the title specification from the properties.
get_size
Returns size in pixels of the useful part of the window, not including decorations.
clear_origin
Removes the origin specification from the properties.
has_minimized
Returns true if set_minimized() has been specified.
set_size
Specifies the requested size of the window, in pixels.
get_foreground
Returns true if the window is in the foreground.
has_fixed_size
Returns true if set_fixed_size() has been specified.
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
clear_fixed_size
Removes the fixed_size specification from the properties.
clear_fullscreen
Removes the fullscreen specification from the properties.
set_foreground
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
clear_size
Removes the size specification from the properties.
set_open
Specifies whether the window should be open.
get_z_order
Returns the window's z_order.
get_fullscreen
Returns true if the window is in fullscreen mode.
has_title
Returns true if the window title has been specified, false otherwise.
get_mouse_mode
See set_mouse_mode().
has_origin
Returns true if the window origin has been specified, false otherwise.
has_foreground
Returns true if set_foreground() has been specified.
clear_foreground
Removes the foreground specification from the properties.
get_cursor_filename
Returns the icon filename associated with the mouse cursor.
get_cursor_hidden
Returns true if the mouse cursor is invisible.
has_cursor_hidden
Returns true if set_cursor_hidden() has been specified.
set_minimized
Specifies whether the window should be created minimized (true), or normal (false).
has_z_order
Returns true if the window z_order has been specified, false otherwise.
get_fixed_size
Returns true if the window cannot be resized by the user, false otherwise.
int get_x_origin() const
Returns the x coordinate of the window's top-left corner, not including decorations.
set_origin
Specifies the origin on the screen (in pixels, relative to the top-left corner) at which the window s...
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
This graphics pipe represents the interface for creating graphics windows on an X-based client.
X11_Window get_root() const
Returns the handle to the root window on the pipe's display.
X11_Display * get_display() const
Returns a pointer to the X display associated with the pipe: the display on which to create the windo...
RRCrtc find_fullscreen_crtc(const LPoint2i &point, int &x, int &y, int &width, int &height)
Finds a CRTC for going fullscreen to, at the given origin.
int get_screen() const
Returns the X screen number associated with the pipe.
XIM get_im() const
Returns the input method opened for the pipe, or NULL if the input method could not be opened for som...
bool supports_relative_mouse() const
Returns true if relative mouse mode is supported on this display.
bool enable_relative_mouse()
Enables relative mouse mode for this display.
X11_Cursor get_hidden_cursor()
Returns an invisible Cursor suitable for assigning to windows that have the cursor_hidden property se...
void disable_relative_mouse()
Disables relative mouse mode for this display.
Interfaces to the X11 window system.
virtual bool begin_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread before beginning rendering for a given frame.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
virtual bool move_pointer(int device, int x, int y)
Forces the pointer to the indicated position within the window, if possible.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
virtual MouseData get_pointer(int device) const
Returns the MouseData associated with the nth input device's pointer.
virtual void end_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread after rendering is completed for a given frame.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BEGIN_PUBLISH typedef PointerData MouseData
Deprecated alias for PointerData.
Definition: mouseData.h:23
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.