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