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