Panda3D
 All Classes Functions Variables Enumerations
eglGraphicsWindow.cxx
1 // Filename: eglGraphicsWindow.cxx
2 // Created by: pro-rsoft (21May09)
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 "eglGraphicsWindow.h"
16 #include "eglGraphicsStateGuardian.h"
17 #include "config_egldisplay.h"
18 #include "eglGraphicsPipe.h"
19 
20 #include "graphicsPipe.h"
21 #include "keyboardButton.h"
22 #include "mouseButton.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 "get_x11.h"
30 
31 #include <errno.h>
32 #include <sys/time.h>
33 
34 #ifdef HAVE_LINUX_INPUT_H
35 #include <linux/input.h>
36 #endif
37 
38 TypeHandle eglGraphicsWindow::_type_handle;
39 
40 #define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
41 
42 ////////////////////////////////////////////////////////////////////
43 // Function: eglGraphicsWindow::Constructor
44 // Access: Public
45 // Description:
46 ////////////////////////////////////////////////////////////////////
47 eglGraphicsWindow::
48 eglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
49  const string &name,
50  const FrameBufferProperties &fb_prop,
51  const WindowProperties &win_prop,
52  int flags,
54  GraphicsOutput *host) :
55  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
56 {
57  eglGraphicsPipe *egl_pipe;
58  DCAST_INTO_V(egl_pipe, _pipe);
59  _display = egl_pipe->get_display();
60  _screen = egl_pipe->get_screen();
61  _xwindow = (X11_Window)NULL;
62  _ic = (XIC)NULL;
63  _egl_display = egl_pipe->_egl_display;
64  _egl_surface = 0;
65  _awaiting_configure = false;
66  _wm_delete_window = egl_pipe->_wm_delete_window;
67  _net_wm_window_type = egl_pipe->_net_wm_window_type;
68  _net_wm_window_type_splash = egl_pipe->_net_wm_window_type_splash;
69  _net_wm_window_type_fullscreen = egl_pipe->_net_wm_window_type_fullscreen;
70  _net_wm_state = egl_pipe->_net_wm_state;
71  _net_wm_state_fullscreen = egl_pipe->_net_wm_state_fullscreen;
72  _net_wm_state_above = egl_pipe->_net_wm_state_above;
73  _net_wm_state_below = egl_pipe->_net_wm_state_below;
74  _net_wm_state_add = egl_pipe->_net_wm_state_add;
75  _net_wm_state_remove = egl_pipe->_net_wm_state_remove;
76 
79  add_input_device(device);
80 }
81 
82 ////////////////////////////////////////////////////////////////////
83 // Function: eglGraphicsWindow::Destructor
84 // Access: Public, Virtual
85 // Description:
86 ////////////////////////////////////////////////////////////////////
87 eglGraphicsWindow::
88 ~eglGraphicsWindow() {
89 }
90 
91 ////////////////////////////////////////////////////////////////////
92 // Function: eglGraphicsWindow::move_pointer
93 // Access: Published, Virtual
94 // Description: Forces the pointer to the indicated position within
95 // the window, if possible.
96 //
97 // Returns true if successful, false on failure. This
98 // may fail if the mouse is not currently within the
99 // window, or if the API doesn't support this operation.
100 ////////////////////////////////////////////////////////////////////
102 move_pointer(int device, int x, int y) {
103  // Note: this is not thread-safe; it should be called only from App.
104  // Probably not an issue.
105  if (device == 0) {
106  // Move the system mouse pointer.
107  if (!_properties.get_foreground() ||
108  !_input_devices[0].get_pointer().get_in_window()) {
109  // If the window doesn't have input focus, or the mouse isn't
110  // currently within the window, forget it.
111  return false;
112  }
113 
114  const MouseData &md = _input_devices[0].get_pointer();
115  if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) {
116  XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y);
117  _input_devices[0].set_pointer_in_window(x, y);
118  }
119  return true;
120  } else {
121  // Move a raw mouse.
122  if ((device < 1)||(device >= _input_devices.size())) {
123  return false;
124  }
125  _input_devices[device].set_pointer_in_window(x, y);
126  return true;
127  }
128 }
129 
130 
131 ////////////////////////////////////////////////////////////////////
132 // Function: eglGraphicsWindow::begin_frame
133 // Access: Public, Virtual
134 // Description: This function will be called within the draw thread
135 // before beginning rendering for a given frame. It
136 // should do whatever setup is required, and return true
137 // if the frame should be rendered, or false if it
138 // should be skipped.
139 ////////////////////////////////////////////////////////////////////
141 begin_frame(FrameMode mode, Thread *current_thread) {
142  PStatTimer timer(_make_current_pcollector, current_thread);
143 
144  begin_frame_spam(mode);
145  if (_gsg == (GraphicsStateGuardian *)NULL) {
146  return false;
147  }
148  if (_awaiting_configure) {
149  // Don't attempt to draw while we have just reconfigured the
150  // window and we haven't got the notification back yet.
151  return false;
152  }
153 
154  eglGraphicsStateGuardian *eglgsg;
155  DCAST_INTO_R(eglgsg, _gsg, false);
156  {
157  LightReMutexHolder holder(eglGraphicsPipe::_x_mutex);
158 
159  if (eglGetCurrentDisplay() == _egl_display &&
160  eglGetCurrentSurface(EGL_READ) == _egl_surface &&
161  eglGetCurrentSurface(EGL_DRAW) == _egl_surface &&
162  eglGetCurrentContext() == eglgsg->_context) {
163  // No need to make the context current again. Short-circuit
164  // this possibly-expensive call.
165  } else {
166  // Need to set the context.
167  if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) {
168  egldisplay_cat.error() << "Failed to call eglMakeCurrent: "
169  << get_egl_error_string(eglGetError()) << "\n";
170  }
171  }
172  }
173 
174  // Now that we have made the context current to a window, we can
175  // reset the GSG state if this is the first time it has been used.
176  // (We can't just call reset() when we construct the GSG, because
177  // reset() requires having a current context.)
178  eglgsg->reset_if_new();
179 
180  if (mode == FM_render) {
181  // begin_render_texture();
182  clear_cube_map_selection();
183  }
184 
185  _gsg->set_current_properties(&get_fb_properties());
186  return _gsg->begin_frame(current_thread);
187 }
188 
189 ////////////////////////////////////////////////////////////////////
190 // Function: eglGraphicsWindow::end_frame
191 // Access: Public, Virtual
192 // Description: This function will be called within the draw thread
193 // after rendering is completed for a given frame. It
194 // should do whatever finalization is required.
195 ////////////////////////////////////////////////////////////////////
197 end_frame(FrameMode mode, Thread *current_thread) {
198  end_frame_spam(mode);
199  nassertv(_gsg != (GraphicsStateGuardian *)NULL);
200 
201  if (mode == FM_render) {
202  // end_render_texture();
203  copy_to_textures();
204  }
205 
206  _gsg->end_frame(current_thread);
207 
208  if (mode == FM_render) {
209  trigger_flip();
210  clear_cube_map_selection();
211  }
212 }
213 
214 ////////////////////////////////////////////////////////////////////
215 // Function: eglGraphicsWindow::end_flip
216 // Access: Public, Virtual
217 // Description: This function will be called within the draw thread
218 // after begin_flip() has been called on all windows, to
219 // finish the exchange of the front and back buffers.
220 //
221 // This should cause the window to wait for the flip, if
222 // necessary.
223 ////////////////////////////////////////////////////////////////////
226  if (_gsg != (GraphicsStateGuardian *)NULL && _flip_ready) {
227 
228  // It doesn't appear to be necessary to ensure the graphics
229  // context is current before flipping the windows, and insisting
230  // on doing so can be a significant performance hit.
231 
232  //make_current();
233 
234  LightReMutexHolder holder(eglGraphicsPipe::_x_mutex);
235  eglSwapBuffers(_egl_display, _egl_surface);
236  }
238 }
239 
240 ////////////////////////////////////////////////////////////////////
241 // Function: eglGraphicsWindow::process_events
242 // Access: Public, Virtual
243 // Description: Do whatever processing is necessary to ensure that
244 // the window responds to user events. Also, honor any
245 // requests recently made via request_properties()
246 //
247 // This function is called only within the window
248 // thread.
249 ////////////////////////////////////////////////////////////////////
252  LightReMutexHolder holder(eglGraphicsPipe::_x_mutex);
253 
255 
256  if (_xwindow == (X11_Window)0) {
257  return;
258  }
259 
260  poll_raw_mice();
261 
262  XEvent event;
263  XKeyEvent keyrelease_event;
264  bool got_keyrelease_event = false;
265 
266  while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
267  if (XFilterEvent(&event, None)) {
268  continue;
269  }
270 
271  if (got_keyrelease_event) {
272  // If a keyrelease event is immediately followed by a matching
273  // keypress event, that's just key repeat and we should treat
274  // the two events accordingly. It would be nice if X provided a
275  // way to differentiate between keyrepeat and explicit
276  // keypresses more generally.
277  got_keyrelease_event = false;
278 
279  if (event.type == KeyPress &&
280  event.xkey.keycode == keyrelease_event.keycode &&
281  (event.xkey.time - keyrelease_event.time <= 1)) {
282  // In particular, we only generate down messages for the
283  // repeated keys, not down-and-up messages.
284  handle_keystroke(event.xkey);
285 
286  // We thought about not generating the keypress event, but we
287  // need that repeat for backspace. Rethink later.
288  handle_keypress(event.xkey);
289  continue;
290 
291  } else {
292  // This keyrelease event is not immediately followed by a
293  // matching keypress event, so it's a genuine release.
294  handle_keyrelease(keyrelease_event);
295  }
296  }
297 
298  WindowProperties properties;
299  ButtonHandle button;
300 
301  switch (event.type) {
302  case ReparentNotify:
303  break;
304 
305  case ConfigureNotify:
306  _awaiting_configure = false;
307  if (_properties.get_fixed_size()) {
308  // If the window properties indicate a fixed size only, undo
309  // any attempt by the user to change them. In X, there
310  // doesn't appear to be a way to universally disallow this
311  // directly (although we do set the min_size and max_size to
312  // the same value, which seems to work for most window
313  // managers.)
314  WindowProperties current_props = get_properties();
315  if (event.xconfigure.width != current_props.get_x_size() ||
316  event.xconfigure.height != current_props.get_y_size()) {
317  XWindowChanges changes;
318  changes.width = current_props.get_x_size();
319  changes.height = current_props.get_y_size();
320  int value_mask = (CWWidth | CWHeight);
321  XConfigureWindow(_display, _xwindow, value_mask, &changes);
322  }
323 
324  } else {
325  // A normal window may be resized by the user at will.
326  properties.set_size(event.xconfigure.width, event.xconfigure.height);
327  system_changed_properties(properties);
328  }
329  break;
330 
331  case ButtonPress:
332  // This refers to the mouse buttons.
333  button = get_mouse_button(event.xbutton);
334  _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
335  _input_devices[0].button_down(button);
336  break;
337 
338  case ButtonRelease:
339  button = get_mouse_button(event.xbutton);
340  _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
341  _input_devices[0].button_up(button);
342  break;
343 
344  case MotionNotify:
345  _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
346  break;
347 
348  case KeyPress:
349  handle_keystroke(event.xkey);
350  handle_keypress(event.xkey);
351  break;
352 
353  case KeyRelease:
354  // The KeyRelease can't be processed immediately, because we
355  // have to check first if it's immediately followed by a
356  // matching KeyPress event.
357  keyrelease_event = event.xkey;
358  got_keyrelease_event = true;
359  break;
360 
361  case EnterNotify:
362  _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
363  break;
364 
365  case LeaveNotify:
366  _input_devices[0].set_pointer_out_of_window();
367  break;
368 
369  case FocusIn:
370  properties.set_foreground(true);
371  system_changed_properties(properties);
372  break;
373 
374  case FocusOut:
375  properties.set_foreground(false);
376  system_changed_properties(properties);
377  break;
378 
379  case UnmapNotify:
380  properties.set_minimized(true);
381  system_changed_properties(properties);
382  break;
383 
384  case MapNotify:
385  properties.set_minimized(false);
386  system_changed_properties(properties);
387 
388  // Auto-focus the window when it is mapped.
389  XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
390  break;
391 
392  case ClientMessage:
393  if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
394  // This is a message from the window manager indicating that
395  // the user has requested to close the window.
396  string close_request_event = get_close_request_event();
397  if (!close_request_event.empty()) {
398  // In this case, the app has indicated a desire to intercept
399  // the request and process it directly.
400  throw_event(close_request_event);
401 
402  } else {
403  // In this case, the default case, the app does not intend
404  // to service the request, so we do by closing the window.
405 
406  // TODO: don't release the gsg in the window thread.
407  close_window();
408  properties.set_open(false);
409  system_changed_properties(properties);
410  }
411  }
412  break;
413 
414  case DestroyNotify:
415  // Apparently, we never get a DestroyNotify on a toplevel
416  // window. Instead, we rely on hints from the window manager
417  // (see above).
418  egldisplay_cat.info()
419  << "DestroyNotify\n";
420  break;
421 
422  default:
423  egldisplay_cat.error()
424  << "unhandled X event type " << event.type << "\n";
425  }
426  }
427 
428  if (got_keyrelease_event) {
429  // This keyrelease event is not immediately followed by a
430  // matching keypress event, so it's a genuine release.
431  handle_keyrelease(keyrelease_event);
432  }
433 }
434 
435 ////////////////////////////////////////////////////////////////////
436 // Function: eglGraphicsWindow::set_properties_now
437 // Access: Public, Virtual
438 // Description: Applies the requested set of properties to the
439 // window, if possible, for instance to request a change
440 // in size or minimization status.
441 //
442 // The window properties are applied immediately, rather
443 // than waiting until the next frame. This implies that
444 // this method may *only* be called from within the
445 // window thread.
446 //
447 // The return value is true if the properties are set,
448 // false if they are ignored. This is mainly useful for
449 // derived classes to implement extensions to this
450 // function.
451 ////////////////////////////////////////////////////////////////////
454  if (_pipe == (GraphicsPipe *)NULL) {
455  // If the pipe is null, we're probably closing down.
457  return;
458  }
459 
460  eglGraphicsPipe *egl_pipe;
461  DCAST_INTO_V(egl_pipe, _pipe);
462 
463  // Fullscreen mode is implemented with a hint to the window manager.
464  // However, we also implicitly set the origin to (0, 0) and the size
465  // to the desktop size, and request undecorated mode, in case the
466  // user has a less-capable window manager (or no window manager at
467  // all).
468  if (properties.get_fullscreen()) {
469  properties.set_undecorated(true);
470  properties.set_origin(0, 0);
471  properties.set_size(egl_pipe->get_display_width(),
472  egl_pipe->get_display_height());
473  }
474 
476  if (!properties.is_any_specified()) {
477  // The base class has already handled this case.
478  return;
479  }
480 
481  // The window is already open; we are limited to what we can change
482  // on the fly.
483 
484  // We'll pass some property requests on as a window manager hint.
485  WindowProperties wm_properties = _properties;
486  wm_properties.add_properties(properties);
487 
488  // The window title may be changed by issuing another hint request.
489  // Assume this will be honored.
490  if (properties.has_title()) {
491  _properties.set_title(properties.get_title());
492  properties.clear_title();
493  }
494 
495  // Ditto for fullscreen mode.
496  if (properties.has_fullscreen()) {
497  _properties.set_fullscreen(properties.get_fullscreen());
498  properties.clear_fullscreen();
499  }
500 
501  // The size and position of an already-open window are changed via
502  // explicit X calls. These may still get intercepted by the window
503  // manager. Rather than changing _properties immediately, we'll
504  // wait for the ConfigureNotify message to come back.
505  XWindowChanges changes;
506  int value_mask = 0;
507 
508  if (properties.has_origin()) {
509  changes.x = properties.get_x_origin();
510  changes.y = properties.get_y_origin();
511  value_mask |= (CWX | CWY);
512  properties.clear_origin();
513  }
514  if (properties.has_size()) {
515  changes.width = properties.get_x_size();
516  changes.height = properties.get_y_size();
517  value_mask |= (CWWidth | CWHeight);
518  properties.clear_size();
519  }
520  if (properties.has_z_order()) {
521  // We'll send the classic stacking request through the standard
522  // interface, for users of primitive window managers; but we'll
523  // also send it as a window manager hint, for users of modern
524  // window managers.
525  _properties.set_z_order(properties.get_z_order());
526  switch (properties.get_z_order()) {
527  case WindowProperties::Z_bottom:
528  changes.stack_mode = Below;
529  break;
530 
531  case WindowProperties::Z_normal:
532  changes.stack_mode = TopIf;
533  break;
534 
535  case WindowProperties::Z_top:
536  changes.stack_mode = Above;
537  break;
538  }
539 
540  value_mask |= (CWStackMode);
541  properties.clear_z_order();
542  }
543 
544  if (value_mask != 0) {
545  XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
546 
547  // Don't draw anything until this is done reconfiguring.
548  _awaiting_configure = true;
549  }
550 
551  // We hide the cursor by setting it to an invisible pixmap.
552  if (properties.has_cursor_hidden()) {
553  _properties.set_cursor_hidden(properties.get_cursor_hidden());
554  if (properties.get_cursor_hidden()) {
555  XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor());
556  } else {
557  XDefineCursor(_display, _xwindow, None);
558  }
559  properties.clear_cursor_hidden();
560  }
561 
562  if (properties.has_foreground()) {
563  if (properties.get_foreground()) {
564  XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
565  } else {
566  XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
567  }
568  properties.clear_foreground();
569  }
570 
571  set_wm_properties(wm_properties, true);
572 }
573 
574 ////////////////////////////////////////////////////////////////////
575 // Function: eglGraphicsWindow::close_window
576 // Access: Protected, Virtual
577 // Description: Closes the window right now. Called from the window
578 // thread.
579 ////////////////////////////////////////////////////////////////////
580 void eglGraphicsWindow::
581 close_window() {
582  if (_gsg != (GraphicsStateGuardian *)NULL) {
583  if (!eglMakeCurrent(_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
584  egldisplay_cat.error() << "Failed to call eglMakeCurrent: "
585  << get_egl_error_string(eglGetError()) << "\n";
586  }
587  _gsg.clear();
588  }
589 
590  if (_ic != (XIC)NULL) {
591  XDestroyIC(_ic);
592  _ic = (XIC)NULL;
593  }
594 
595  if (_egl_surface != 0) {
596  if (!eglDestroySurface(_egl_display, _egl_surface)) {
597  egldisplay_cat.error() << "Failed to destroy surface: "
598  << get_egl_error_string(eglGetError()) << "\n";
599  }
600  }
601 
602  if (_xwindow != (X11_Window)NULL) {
603  XDestroyWindow(_display, _xwindow);
604  _xwindow = (X11_Window)NULL;
605 
606  // This may be necessary if we just closed the last X window in an
607  // application, so the server hears the close request.
608  XFlush(_display);
609  }
610  GraphicsWindow::close_window();
611 }
612 
613 ////////////////////////////////////////////////////////////////////
614 // Function: eglGraphicsWindow::open_window
615 // Access: Protected, Virtual
616 // Description: Opens the window right now. Called from the window
617 // thread. Returns true if the window is successfully
618 // opened, or false if there was a problem.
619 ////////////////////////////////////////////////////////////////////
620 bool eglGraphicsWindow::
621 open_window() {
622  eglGraphicsPipe *egl_pipe;
623  DCAST_INTO_R(egl_pipe, _pipe, false);
624 
625  // GSG Creation/Initialization
626  eglGraphicsStateGuardian *eglgsg;
627  if (_gsg == 0) {
628  // There is no old gsg. Create a new one.
629  eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, NULL);
630  eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false);
631  _gsg = eglgsg;
632  } else {
633  // If the old gsg has the wrong pixel format, create a
634  // new one that shares with the old gsg.
635  DCAST_INTO_R(eglgsg, _gsg, false);
636  if (!eglgsg->get_fb_properties().subsumes(_fb_properties)) {
637  eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, eglgsg);
638  eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false);
639  _gsg = eglgsg;
640  }
641  }
642 
643  XVisualInfo *visual_info = eglgsg->_visual;
644  if (visual_info == NULL) {
645  // No X visual for this fbconfig; how can we open the window?
646  egldisplay_cat.error()
647  << "No X visual: cannot open window.\n";
648  return false;
649  }
650  Visual *visual = visual_info->visual;
651  int depth = visual_info->depth;
652 
653  if (!_properties.has_origin()) {
654  _properties.set_origin(0, 0);
655  }
656  if (!_properties.has_size()) {
657  _properties.set_size(100, 100);
658  }
659 
660  X11_Window parent_window = egl_pipe->get_root();
661  WindowHandle *window_handle = _properties.get_parent_window();
662  if (window_handle != NULL) {
663  egldisplay_cat.info()
664  << "Got parent_window " << *window_handle << "\n";
665  WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
666  if (os_handle != NULL) {
667  egldisplay_cat.info()
668  << "os_handle type " << os_handle->get_type() << "\n";
669 
670  if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
671  NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
672  parent_window = x11_handle->get_handle();
673  } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
674  NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
675  parent_window = (X11_Window)int_handle->get_handle();
676  }
677  }
678  }
679  _parent_window_handle = window_handle;
680 
681  setup_colormap(visual_info);
682 
683  _event_mask =
684  ButtonPressMask | ButtonReleaseMask |
685  KeyPressMask | KeyReleaseMask |
686  EnterWindowMask | LeaveWindowMask |
687  PointerMotionMask |
688  FocusChangeMask |
689  StructureNotifyMask;
690 
691  // Initialize window attributes
692  XSetWindowAttributes wa;
693  wa.background_pixel = XBlackPixel(_display, _screen);
694  wa.border_pixel = 0;
695  wa.colormap = _colormap;
696  wa.event_mask = _event_mask;
697 
698  unsigned long attrib_mask =
699  CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
700 
701  _xwindow = XCreateWindow
702  (_display, parent_window,
703  _properties.get_x_origin(), _properties.get_y_origin(),
704  _properties.get_x_size(), _properties.get_y_size(),
705  0, depth, InputOutput, visual, attrib_mask, &wa);
706 
707  if (_xwindow == (X11_Window)0) {
708  egldisplay_cat.error()
709  << "failed to create X window.\n";
710  return false;
711  }
712  set_wm_properties(_properties, false);
713 
714  // We don't specify any fancy properties of the XIC. It would be
715  // nicer if we could support fancy IM's that want preedit callbacks,
716  // etc., but that can wait until we have an X server that actually
717  // supports these to test it on.
718  XIM im = egl_pipe->get_im();
719  _ic = NULL;
720  if (im) {
721  _ic = XCreateIC
722  (im,
723  XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
724  (void*)NULL);
725  if (_ic == (XIC)NULL) {
726  egldisplay_cat.warning()
727  << "Couldn't create input context.\n";
728  }
729  }
730 
731  if (_properties.get_cursor_hidden()) {
732  XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor());
733  }
734 
735  _egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, NULL);
736  if (eglGetError() != EGL_SUCCESS) {
737  egldisplay_cat.error()
738  << "Failed to create window surface.\n";
739  return false;
740  }
741 
742  if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) {
743  egldisplay_cat.error() << "Failed to call eglMakeCurrent: "
744  << get_egl_error_string(eglGetError()) << "\n";
745  }
746  eglgsg->reset_if_new();
747  if (!eglgsg->is_valid()) {
748  close_window();
749  return false;
750  }
752  (_fb_properties, eglgsg->get_gl_renderer())) {
753  close_window();
754  return false;
755  }
756  _fb_properties = eglgsg->get_fb_properties();
757 
758  XMapWindow(_display, _xwindow);
759 
760  if (_properties.get_raw_mice()) {
761  open_raw_mice();
762  } else {
763  if (egldisplay_cat.is_debug()) {
764  egldisplay_cat.debug()
765  << "Raw mice not requested.\n";
766  }
767  }
768 
769  // Create a WindowHandle for ourselves
770  _window_handle = NativeWindowHandle::make_x11(_xwindow);
771 
772  // And tell our parent window that we're now its child.
773  if (_parent_window_handle != (WindowHandle *)NULL) {
774  _parent_window_handle->attach_child(_window_handle);
775  }
776 
777  return true;
778 }
779 
780 ////////////////////////////////////////////////////////////////////
781 // Function: eglGraphicsWindow::set_wm_properties
782 // Access: Private
783 // Description: Asks the window manager to set the appropriate
784 // properties. In X, these properties cannot be
785 // specified directly by the application; they must be
786 // requested via the window manager, which may or may
787 // not choose to honor the request.
788 //
789 // If already_mapped is true, the window has already
790 // been mapped (manifested) on the display. This means
791 // we may need to use a different action in some cases.
792 ////////////////////////////////////////////////////////////////////
793 void eglGraphicsWindow::
794 set_wm_properties(const WindowProperties &properties, bool already_mapped) {
795  // Name the window if there is a name
796  XTextProperty window_name;
797  XTextProperty *window_name_p = (XTextProperty *)NULL;
798  if (properties.has_title()) {
799  char *name = (char *)properties.get_title().c_str();
800  if (XStringListToTextProperty(&name, 1, &window_name) != 0) {
801  window_name_p = &window_name;
802  }
803  }
804 
805  // The size hints request a window of a particular size and/or a
806  // particular placement onscreen.
807  XSizeHints *size_hints_p = NULL;
808  if (properties.has_origin() || properties.has_size()) {
809  size_hints_p = XAllocSizeHints();
810  if (size_hints_p != (XSizeHints *)NULL) {
811  if (properties.has_origin()) {
812  size_hints_p->x = properties.get_x_origin();
813  size_hints_p->y = properties.get_y_origin();
814  size_hints_p->flags |= USPosition;
815  }
816  if (properties.has_size()) {
817  size_hints_p->width = properties.get_x_size();
818  size_hints_p->height = properties.get_y_size();
819  size_hints_p->flags |= USSize;
820 
821  if (properties.get_fixed_size()) {
822  size_hints_p->min_width = properties.get_x_size();
823  size_hints_p->min_height = properties.get_y_size();
824  size_hints_p->max_width = properties.get_x_size();
825  size_hints_p->max_height = properties.get_y_size();
826  size_hints_p->flags |= (PMinSize | PMaxSize);
827  }
828  }
829  }
830  }
831 
832  // The window manager hints include requests to the window manager
833  // other than those specific to window geometry.
834  XWMHints *wm_hints_p = NULL;
835  wm_hints_p = XAllocWMHints();
836  if (wm_hints_p != (XWMHints *)NULL) {
837  if (properties.has_minimized() && properties.get_minimized()) {
838  wm_hints_p->initial_state = IconicState;
839  } else {
840  wm_hints_p->initial_state = NormalState;
841  }
842  wm_hints_p->flags = StateHint;
843  }
844 
845  // Two competing window manager interfaces have evolved. One of
846  // them allows to set certain properties as a "type"; the other one
847  // as a "state". We'll try to honor both.
848  static const int max_type_data = 32;
849  PN_int32 type_data[max_type_data];
850  int next_type_data = 0;
851 
852  static const int max_state_data = 32;
853  PN_int32 state_data[max_state_data];
854  int next_state_data = 0;
855 
856  static const int max_set_data = 32;
857  class SetAction {
858  public:
859  inline SetAction() { }
860  inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
861  Atom _state;
862  Atom _action;
863  };
864  SetAction set_data[max_set_data];
865  int next_set_data = 0;
866 
867  if (properties.get_fullscreen()) {
868  // For a "fullscreen" request, we pass this through, hoping the
869  // window manager will support EWMH.
870  type_data[next_type_data++] = _net_wm_window_type_fullscreen;
871 
872  // We also request it as a state.
873  state_data[next_state_data++] = _net_wm_state_fullscreen;
874  set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_add);
875  } else {
876  set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_remove);
877  }
878 
879  // If we asked for a window without a border, there's no excellent
880  // way to arrange that. For users whose window managers follow the
881  // EWMH specification, we can ask for a "splash" screen, which is
882  // usually undecorated. It's not exactly right, but the spec
883  // doesn't give us an exactly-right option.
884 
885  // For other users, we'll totally punt and just set the window's
886  // Class to "Undecorated", and let the user configure his/her window
887  // manager not to put a border around windows of this class.
888  XClassHint *class_hints_p = NULL;
889  if (properties.get_undecorated()) {
890  class_hints_p = XAllocClassHint();
891  class_hints_p->res_class = (char*) "Undecorated";
892 
893  if (!properties.get_fullscreen()) {
894  type_data[next_type_data++] = _net_wm_window_type_splash;
895  }
896  }
897 
898  if (properties.has_z_order()) {
899  switch (properties.get_z_order()) {
900  case WindowProperties::Z_bottom:
901  state_data[next_state_data++] = _net_wm_state_below;
902  set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_add);
903  set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
904  break;
905 
906  case WindowProperties::Z_normal:
907  set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
908  set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
909  break;
910 
911  case WindowProperties::Z_top:
912  state_data[next_state_data++] = _net_wm_state_above;
913  set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
914  set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_add);
915  break;
916  }
917  }
918 
919  nassertv(next_type_data < max_type_data);
920  nassertv(next_state_data < max_state_data);
921  nassertv(next_set_data < max_set_data);
922 
923  XChangeProperty(_display, _xwindow, _net_wm_window_type,
924  XA_ATOM, 32, PropModeReplace,
925  (unsigned char *)type_data, next_type_data);
926 
927  // Request the state properties all at once.
928  XChangeProperty(_display, _xwindow, _net_wm_state,
929  XA_ATOM, 32, PropModeReplace,
930  (unsigned char *)state_data, next_state_data);
931 
932  if (already_mapped) {
933  // We have to request state changes differently when the window
934  // has been mapped. To do this, we need to send a client message
935  // to the root window for each change.
936 
937  eglGraphicsPipe *egl_pipe;
938  DCAST_INTO_V(egl_pipe, _pipe);
939 
940  for (int i = 0; i < next_set_data; ++i) {
941  XClientMessageEvent event;
942  memset(&event, 0, sizeof(event));
943 
944  event.type = ClientMessage;
945  event.send_event = True;
946  event.display = _display;
947  event.window = _xwindow;
948  event.message_type = _net_wm_state;
949  event.format = 32;
950  event.data.l[0] = set_data[i]._action;
951  event.data.l[1] = set_data[i]._state;
952  event.data.l[2] = 0;
953  event.data.l[3] = 1;
954 
955  XSendEvent(_display, egl_pipe->get_root(), True, 0, (XEvent *)&event);
956  }
957  }
958 
959  XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
960  NULL, 0, size_hints_p, wm_hints_p, class_hints_p);
961 
962  if (size_hints_p != (XSizeHints *)NULL) {
963  XFree(size_hints_p);
964  }
965  if (wm_hints_p != (XWMHints *)NULL) {
966  XFree(wm_hints_p);
967  }
968  if (class_hints_p != (XClassHint *)NULL) {
969  XFree(class_hints_p);
970  }
971 
972  // Also, indicate to the window manager that we'd like to get a
973  // chance to close our windows cleanly, rather than being rudely
974  // disconnected from the X server if the user requests a window
975  // close.
976  Atom protocols[] = {
977  _wm_delete_window,
978  };
979 
980  XSetWMProtocols(_display, _xwindow, protocols,
981  sizeof(protocols) / sizeof(Atom));
982 }
983 
984 ////////////////////////////////////////////////////////////////////
985 // Function: eglGraphicsWindow::setup_colormap
986 // Access: Private
987 // Description: Allocates a colormap appropriate to the visual and
988 // stores in in the _colormap method.
989 ////////////////////////////////////////////////////////////////////
990 void eglGraphicsWindow::
991 setup_colormap(XVisualInfo *visual) {
992  eglGraphicsPipe *egl_pipe;
993  DCAST_INTO_V(egl_pipe, _pipe);
994  X11_Window root_window = egl_pipe->get_root();
995 
996  int visual_class = visual->c_class;
997  int rc, is_rgb;
998 
999  switch (visual_class) {
1000  case PseudoColor:
1001  _colormap = XCreateColormap(_display, root_window,
1002  visual->visual, AllocAll);
1003  break;
1004  case TrueColor:
1005  case DirectColor:
1006  _colormap = XCreateColormap(_display, root_window,
1007  visual->visual, AllocNone);
1008  break;
1009  case StaticColor:
1010  case StaticGray:
1011  case GrayScale:
1012  _colormap = XCreateColormap(_display, root_window,
1013  visual->visual, AllocNone);
1014  break;
1015  default:
1016  egldisplay_cat.error()
1017  << "Could not allocate a colormap for visual class "
1018  << visual_class << ".\n";
1019  break;
1020  }
1021 }
1022 
1023 ////////////////////////////////////////////////////////////////////
1024 // Function: eglGraphicsWindow::open_raw_mice
1025 // Access: Private
1026 // Description: Adds raw mice to the _input_devices list.
1027 ////////////////////////////////////////////////////////////////////
1028 void eglGraphicsWindow::
1029 open_raw_mice()
1030 {
1031 #ifdef HAVE_LINUX_INPUT_H
1032  bool any_present = false;
1033  bool any_mice = false;
1034 
1035  for (int i=0; i<64; i++) {
1036  uint8_t evtypes[EV_MAX/8 + 1];
1037  ostringstream fnb;
1038  fnb << "/dev/input/event" << i;
1039  string fn = fnb.str();
1040  int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
1041  if (fd >= 0) {
1042  any_present = true;
1043  char name[256];
1044  char phys[256];
1045  char uniq[256];
1046  if ((ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)||
1047  (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0)||
1048  (ioctl(fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0)||
1049  (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) {
1050  close(fd);
1051  egldisplay_cat.error() <<
1052  "Opening raw mice: ioctl failed on " << fn << "\n";
1053  } else {
1054  if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) {
1055  for (char *p=name; *p; p++) {
1056  if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
1057  *p = '_';
1058  }
1059  }
1060  for (char *p=uniq; *p; p++) {
1061  if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
1062  *p = '_';
1063  }
1064  }
1065  string full_id = ((string)name) + "." + uniq;
1066  MouseDeviceInfo inf;
1067  inf._fd = fd;
1068  inf._input_device_index = _input_devices.size();
1069  inf._io_buffer = "";
1070  _mouse_device_info.push_back(inf);
1071  GraphicsWindowInputDevice device =
1073  add_input_device(device);
1074  egldisplay_cat.info() << "Raw mouse " <<
1075  inf._input_device_index << " detected: " << full_id << "\n";
1076  any_mice = true;
1077  } else {
1078  close(fd);
1079  }
1080  }
1081  } else {
1082  if ((errno == ENOENT)||(errno == ENOTDIR)) {
1083  break;
1084  } else {
1085  any_present = true;
1086  egldisplay_cat.error() <<
1087  "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
1088  }
1089  }
1090  }
1091 
1092  if (!any_present) {
1093  egldisplay_cat.error() <<
1094  "Opening raw mice: files not found: /dev/input/event*\n";
1095  } else if (!any_mice) {
1096  egldisplay_cat.error() <<
1097  "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
1098  }
1099 #else
1100  egldisplay_cat.error() <<
1101  "Opening raw mice: panda not compiled with raw mouse support.\n";
1102 #endif
1103 }
1104 
1105 ////////////////////////////////////////////////////////////////////
1106 // Function: eglGraphicsWindow::poll_raw_mice
1107 // Access: Private
1108 // Description: Reads events from the raw mouse device files.
1109 ////////////////////////////////////////////////////////////////////
1110 void eglGraphicsWindow::
1111 poll_raw_mice()
1112 {
1113 #ifdef HAVE_LINUX_INPUT_H
1114  for (int dev=0; dev<_mouse_device_info.size(); dev++) {
1115  MouseDeviceInfo &inf = _mouse_device_info[dev];
1116 
1117  // Read all bytes into buffer.
1118  if (inf._fd >= 0) {
1119  while (1) {
1120  char tbuf[1024];
1121  int nread = read(inf._fd, tbuf, sizeof(tbuf));
1122  if (nread > 0) {
1123  inf._io_buffer += string(tbuf, nread);
1124  } else {
1125  if ((nread < 0)&&((errno == EWOULDBLOCK) || (errno==EAGAIN))) {
1126  break;
1127  }
1128  close(inf._fd);
1129  inf._fd = -1;
1130  break;
1131  }
1132  }
1133  }
1134 
1135  // Process events.
1136  int nevents = inf._io_buffer.size() / sizeof(struct input_event);
1137  if (nevents == 0) {
1138  continue;
1139  }
1140  const input_event *events = (const input_event *)(inf._io_buffer.c_str());
1141  GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index];
1142  int x = dev.get_raw_pointer().get_x();
1143  int y = dev.get_raw_pointer().get_y();
1144  for (int i=0; i<nevents; i++) {
1145  if (events[i].type == EV_REL) {
1146  if (events[i].code == REL_X) x += events[i].value;
1147  if (events[i].code == REL_Y) y += events[i].value;
1148  } else if (events[i].type == EV_ABS) {
1149  if (events[i].code == ABS_X) x = events[i].value;
1150  if (events[i].code == ABS_Y) y = events[i].value;
1151  } else if (events[i].type == EV_KEY) {
1152  if ((events[i].code >= BTN_MOUSE)&&(events[i].code < BTN_MOUSE+8)) {
1153  int btn = events[i].code - BTN_MOUSE;
1154  dev.set_pointer_in_window(x,y);
1155  if (events[i].value) {
1156  dev.button_down(MouseButton::button(btn));
1157  } else {
1158  dev.button_up(MouseButton::button(btn));
1159  }
1160  }
1161  }
1162  }
1163  inf._io_buffer.erase(0,nevents*sizeof(struct input_event));
1164  dev.set_pointer_in_window(x,y);
1165  }
1166 #endif
1167 }
1168 
1169 ////////////////////////////////////////////////////////////////////
1170 // Function: eglGraphicsWindow::handle_keystroke
1171 // Access: Private
1172 // Description: Generates a keystroke corresponding to the indicated
1173 // X KeyPress event.
1174 ////////////////////////////////////////////////////////////////////
1175 void eglGraphicsWindow::
1176 handle_keystroke(XKeyEvent &event) {
1177  _input_devices[0].set_pointer_in_window(event.x, event.y);
1178 
1179  if (_ic) {
1180  // First, get the keystroke as a wide-character sequence.
1181  static const int buffer_size = 256;
1182  wchar_t buffer[buffer_size];
1183  Status status;
1184  int len = XwcLookupString(_ic, &event, buffer, buffer_size, NULL,
1185  &status);
1186  if (status == XBufferOverflow) {
1187  egldisplay_cat.error()
1188  << "Overflowed input buffer.\n";
1189  }
1190 
1191  // Now each of the returned wide characters represents a
1192  // keystroke.
1193  for (int i = 0; i < len; i++) {
1194  _input_devices[0].keystroke(buffer[i]);
1195  }
1196 
1197  } else {
1198  // Without an input context, just get the ascii keypress.
1199  ButtonHandle button = get_button(event, true);
1200  if (button.has_ascii_equivalent()) {
1201  _input_devices[0].keystroke(button.get_ascii_equivalent());
1202  }
1203  }
1204 }
1205 
1206 ////////////////////////////////////////////////////////////////////
1207 // Function: eglGraphicsWindow::handle_keypress
1208 // Access: Private
1209 // Description: Generates a keypress corresponding to the indicated
1210 // X KeyPress event.
1211 ////////////////////////////////////////////////////////////////////
1212 void eglGraphicsWindow::
1213 handle_keypress(XKeyEvent &event) {
1214  _input_devices[0].set_pointer_in_window(event.x, event.y);
1215 
1216  // Now get the raw unshifted button.
1217  ButtonHandle button = get_button(event, false);
1218  if (button != ButtonHandle::none()) {
1219  if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1220  _input_devices[0].button_down(KeyboardButton::control());
1221  }
1222  if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1223  _input_devices[0].button_down(KeyboardButton::shift());
1224  }
1225  if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1226  _input_devices[0].button_down(KeyboardButton::alt());
1227  }
1228  if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1229  _input_devices[0].button_down(KeyboardButton::meta());
1230  }
1231  _input_devices[0].button_down(button);
1232  }
1233 }
1234 
1235 ////////////////////////////////////////////////////////////////////
1236 // Function: eglGraphicsWindow::handle_keyrelease
1237 // Access: Private
1238 // Description: Generates a keyrelease corresponding to the indicated
1239 // X KeyRelease event.
1240 ////////////////////////////////////////////////////////////////////
1241 void eglGraphicsWindow::
1242 handle_keyrelease(XKeyEvent &event) {
1243  _input_devices[0].set_pointer_in_window(event.x, event.y);
1244 
1245  // Now get the raw unshifted button.
1246  ButtonHandle button = get_button(event, false);
1247  if (button != ButtonHandle::none()) {
1248  if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1249  _input_devices[0].button_up(KeyboardButton::control());
1250  }
1251  if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1252  _input_devices[0].button_up(KeyboardButton::shift());
1253  }
1254  if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1255  _input_devices[0].button_up(KeyboardButton::alt());
1256  }
1257  if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1258  _input_devices[0].button_up(KeyboardButton::meta());
1259  }
1260  _input_devices[0].button_up(button);
1261  }
1262 }
1263 
1264 ////////////////////////////////////////////////////////////////////
1265 // Function: eglGraphicsWindow::get_button
1266 // Access: Private
1267 // Description: Returns the Panda ButtonHandle corresponding to the
1268 // keyboard button indicated by the given key event.
1269 ////////////////////////////////////////////////////////////////////
1270 ButtonHandle eglGraphicsWindow::
1271 get_button(XKeyEvent &key_event, bool allow_shift) {
1272  KeySym key = XLookupKeysym(&key_event, 0);
1273 
1274  if ((key_event.state & Mod2Mask) != 0) {
1275  // Mod2Mask corresponds to NumLock being in effect. In this case,
1276  // we want to get the alternate keysym associated with any keypad
1277  // keys. Weird system.
1278  KeySym k2;
1279  ButtonHandle button;
1280  switch (key) {
1281  case XK_KP_Space:
1282  case XK_KP_Tab:
1283  case XK_KP_Enter:
1284  case XK_KP_F1:
1285  case XK_KP_F2:
1286  case XK_KP_F3:
1287  case XK_KP_F4:
1288  case XK_KP_Equal:
1289  case XK_KP_Multiply:
1290  case XK_KP_Add:
1291  case XK_KP_Separator:
1292  case XK_KP_Subtract:
1293  case XK_KP_Divide:
1294  case XK_KP_Left:
1295  case XK_KP_Up:
1296  case XK_KP_Right:
1297  case XK_KP_Down:
1298  case XK_KP_Begin:
1299  case XK_KP_Prior:
1300  case XK_KP_Next:
1301  case XK_KP_Home:
1302  case XK_KP_End:
1303  case XK_KP_Insert:
1304  case XK_KP_Delete:
1305  case XK_KP_0:
1306  case XK_KP_1:
1307  case XK_KP_2:
1308  case XK_KP_3:
1309  case XK_KP_4:
1310  case XK_KP_5:
1311  case XK_KP_6:
1312  case XK_KP_7:
1313  case XK_KP_8:
1314  case XK_KP_9:
1315  k2 = XLookupKeysym(&key_event, 1);
1316  button = map_button(k2);
1317  if (button != ButtonHandle::none()) {
1318  return button;
1319  }
1320  // If that didn't produce a button we know, just fall through
1321  // and handle the normal, un-numlocked key.
1322  break;
1323 
1324  default:
1325  break;
1326  }
1327  }
1328 
1329  if (allow_shift) {
1330  // If shift is held down, get the shifted keysym.
1331  if ((key_event.state & ShiftMask) != 0) {
1332  KeySym k2 = XLookupKeysym(&key_event, 1);
1333  ButtonHandle button = map_button(k2);
1334  if (button != ButtonHandle::none()) {
1335  return button;
1336  }
1337  }
1338 
1339  // If caps lock is down, shift lowercase letters to uppercase. We
1340  // can do this in just the ASCII set, because we handle
1341  // international keyboards elsewhere (via an input context).
1342  if ((key_event.state & (ShiftMask | LockMask)) != 0) {
1343  if (key >= XK_a and key <= XK_z) {
1344  key += (XK_A - XK_a);
1345  }
1346  }
1347  }
1348 
1349  return map_button(key);
1350 }
1351 
1352 ////////////////////////////////////////////////////////////////////
1353 // Function: eglGraphicsWindow::map_button
1354 // Access: Private
1355 // Description: Maps from a single X keysym to Panda's ButtonHandle.
1356 // Called by get_button(), above.
1357 ////////////////////////////////////////////////////////////////////
1358 ButtonHandle eglGraphicsWindow::
1359 map_button(KeySym key) {
1360  switch (key) {
1361  case XK_BackSpace:
1362  return KeyboardButton::backspace();
1363  case XK_Tab:
1364  case XK_KP_Tab:
1365  return KeyboardButton::tab();
1366  case XK_Return:
1367  case XK_KP_Enter:
1368  return KeyboardButton::enter();
1369  case XK_Escape:
1370  return KeyboardButton::escape();
1371  case XK_KP_Space:
1372  case XK_space:
1373  return KeyboardButton::space();
1374  case XK_exclam:
1375  return KeyboardButton::ascii_key('!');
1376  case XK_quotedbl:
1377  return KeyboardButton::ascii_key('"');
1378  case XK_numbersign:
1379  return KeyboardButton::ascii_key('#');
1380  case XK_dollar:
1381  return KeyboardButton::ascii_key('$');
1382  case XK_percent:
1383  return KeyboardButton::ascii_key('%');
1384  case XK_ampersand:
1385  return KeyboardButton::ascii_key('&');
1386  case XK_apostrophe: // == XK_quoteright
1387  return KeyboardButton::ascii_key('\'');
1388  case XK_parenleft:
1389  return KeyboardButton::ascii_key('(');
1390  case XK_parenright:
1391  return KeyboardButton::ascii_key(')');
1392  case XK_asterisk:
1393  case XK_KP_Multiply:
1394  return KeyboardButton::ascii_key('*');
1395  case XK_plus:
1396  case XK_KP_Add:
1397  return KeyboardButton::ascii_key('+');
1398  case XK_comma:
1399  case XK_KP_Separator:
1400  return KeyboardButton::ascii_key(',');
1401  case XK_minus:
1402  case XK_KP_Subtract:
1403  return KeyboardButton::ascii_key('-');
1404  case XK_period:
1405  case XK_KP_Decimal:
1406  return KeyboardButton::ascii_key('.');
1407  case XK_slash:
1408  case XK_KP_Divide:
1409  return KeyboardButton::ascii_key('/');
1410  case XK_0:
1411  case XK_KP_0:
1412  return KeyboardButton::ascii_key('0');
1413  case XK_1:
1414  case XK_KP_1:
1415  return KeyboardButton::ascii_key('1');
1416  case XK_2:
1417  case XK_KP_2:
1418  return KeyboardButton::ascii_key('2');
1419  case XK_3:
1420  case XK_KP_3:
1421  return KeyboardButton::ascii_key('3');
1422  case XK_4:
1423  case XK_KP_4:
1424  return KeyboardButton::ascii_key('4');
1425  case XK_5:
1426  case XK_KP_5:
1427  return KeyboardButton::ascii_key('5');
1428  case XK_6:
1429  case XK_KP_6:
1430  return KeyboardButton::ascii_key('6');
1431  case XK_7:
1432  case XK_KP_7:
1433  return KeyboardButton::ascii_key('7');
1434  case XK_8:
1435  case XK_KP_8:
1436  return KeyboardButton::ascii_key('8');
1437  case XK_9:
1438  case XK_KP_9:
1439  return KeyboardButton::ascii_key('9');
1440  case XK_colon:
1441  return KeyboardButton::ascii_key(':');
1442  case XK_semicolon:
1443  return KeyboardButton::ascii_key(';');
1444  case XK_less:
1445  return KeyboardButton::ascii_key('<');
1446  case XK_equal:
1447  case XK_KP_Equal:
1448  return KeyboardButton::ascii_key('=');
1449  case XK_greater:
1450  return KeyboardButton::ascii_key('>');
1451  case XK_question:
1452  return KeyboardButton::ascii_key('?');
1453  case XK_at:
1454  return KeyboardButton::ascii_key('@');
1455  case XK_A:
1456  return KeyboardButton::ascii_key('A');
1457  case XK_B:
1458  return KeyboardButton::ascii_key('B');
1459  case XK_C:
1460  return KeyboardButton::ascii_key('C');
1461  case XK_D:
1462  return KeyboardButton::ascii_key('D');
1463  case XK_E:
1464  return KeyboardButton::ascii_key('E');
1465  case XK_F:
1466  return KeyboardButton::ascii_key('F');
1467  case XK_G:
1468  return KeyboardButton::ascii_key('G');
1469  case XK_H:
1470  return KeyboardButton::ascii_key('H');
1471  case XK_I:
1472  return KeyboardButton::ascii_key('I');
1473  case XK_J:
1474  return KeyboardButton::ascii_key('J');
1475  case XK_K:
1476  return KeyboardButton::ascii_key('K');
1477  case XK_L:
1478  return KeyboardButton::ascii_key('L');
1479  case XK_M:
1480  return KeyboardButton::ascii_key('M');
1481  case XK_N:
1482  return KeyboardButton::ascii_key('N');
1483  case XK_O:
1484  return KeyboardButton::ascii_key('O');
1485  case XK_P:
1486  return KeyboardButton::ascii_key('P');
1487  case XK_Q:
1488  return KeyboardButton::ascii_key('Q');
1489  case XK_R:
1490  return KeyboardButton::ascii_key('R');
1491  case XK_S:
1492  return KeyboardButton::ascii_key('S');
1493  case XK_T:
1494  return KeyboardButton::ascii_key('T');
1495  case XK_U:
1496  return KeyboardButton::ascii_key('U');
1497  case XK_V:
1498  return KeyboardButton::ascii_key('V');
1499  case XK_W:
1500  return KeyboardButton::ascii_key('W');
1501  case XK_X:
1502  return KeyboardButton::ascii_key('X');
1503  case XK_Y:
1504  return KeyboardButton::ascii_key('Y');
1505  case XK_Z:
1506  return KeyboardButton::ascii_key('Z');
1507  case XK_bracketleft:
1508  return KeyboardButton::ascii_key('[');
1509  case XK_backslash:
1510  return KeyboardButton::ascii_key('\\');
1511  case XK_bracketright:
1512  return KeyboardButton::ascii_key(']');
1513  case XK_asciicircum:
1514  return KeyboardButton::ascii_key('^');
1515  case XK_underscore:
1516  return KeyboardButton::ascii_key('_');
1517  case XK_grave: // == XK_quoteleft
1518  return KeyboardButton::ascii_key('`');
1519  case XK_a:
1520  return KeyboardButton::ascii_key('a');
1521  case XK_b:
1522  return KeyboardButton::ascii_key('b');
1523  case XK_c:
1524  return KeyboardButton::ascii_key('c');
1525  case XK_d:
1526  return KeyboardButton::ascii_key('d');
1527  case XK_e:
1528  return KeyboardButton::ascii_key('e');
1529  case XK_f:
1530  return KeyboardButton::ascii_key('f');
1531  case XK_g:
1532  return KeyboardButton::ascii_key('g');
1533  case XK_h:
1534  return KeyboardButton::ascii_key('h');
1535  case XK_i:
1536  return KeyboardButton::ascii_key('i');
1537  case XK_j:
1538  return KeyboardButton::ascii_key('j');
1539  case XK_k:
1540  return KeyboardButton::ascii_key('k');
1541  case XK_l:
1542  return KeyboardButton::ascii_key('l');
1543  case XK_m:
1544  return KeyboardButton::ascii_key('m');
1545  case XK_n:
1546  return KeyboardButton::ascii_key('n');
1547  case XK_o:
1548  return KeyboardButton::ascii_key('o');
1549  case XK_p:
1550  return KeyboardButton::ascii_key('p');
1551  case XK_q:
1552  return KeyboardButton::ascii_key('q');
1553  case XK_r:
1554  return KeyboardButton::ascii_key('r');
1555  case XK_s:
1556  return KeyboardButton::ascii_key('s');
1557  case XK_t:
1558  return KeyboardButton::ascii_key('t');
1559  case XK_u:
1560  return KeyboardButton::ascii_key('u');
1561  case XK_v:
1562  return KeyboardButton::ascii_key('v');
1563  case XK_w:
1564  return KeyboardButton::ascii_key('w');
1565  case XK_x:
1566  return KeyboardButton::ascii_key('x');
1567  case XK_y:
1568  return KeyboardButton::ascii_key('y');
1569  case XK_z:
1570  return KeyboardButton::ascii_key('z');
1571  case XK_braceleft:
1572  return KeyboardButton::ascii_key('{');
1573  case XK_bar:
1574  return KeyboardButton::ascii_key('|');
1575  case XK_braceright:
1576  return KeyboardButton::ascii_key('}');
1577  case XK_asciitilde:
1578  return KeyboardButton::ascii_key('~');
1579  case XK_F1:
1580  case XK_KP_F1:
1581  return KeyboardButton::f1();
1582  case XK_F2:
1583  case XK_KP_F2:
1584  return KeyboardButton::f2();
1585  case XK_F3:
1586  case XK_KP_F3:
1587  return KeyboardButton::f3();
1588  case XK_F4:
1589  case XK_KP_F4:
1590  return KeyboardButton::f4();
1591  case XK_F5:
1592  return KeyboardButton::f5();
1593  case XK_F6:
1594  return KeyboardButton::f6();
1595  case XK_F7:
1596  return KeyboardButton::f7();
1597  case XK_F8:
1598  return KeyboardButton::f8();
1599  case XK_F9:
1600  return KeyboardButton::f9();
1601  case XK_F10:
1602  return KeyboardButton::f10();
1603  case XK_F11:
1604  return KeyboardButton::f11();
1605  case XK_F12:
1606  return KeyboardButton::f12();
1607  case XK_KP_Left:
1608  case XK_Left:
1609  return KeyboardButton::left();
1610  case XK_KP_Up:
1611  case XK_Up:
1612  return KeyboardButton::up();
1613  case XK_KP_Right:
1614  case XK_Right:
1615  return KeyboardButton::right();
1616  case XK_KP_Down:
1617  case XK_Down:
1618  return KeyboardButton::down();
1619  case XK_KP_Prior:
1620  case XK_Prior:
1621  return KeyboardButton::page_up();
1622  case XK_KP_Next:
1623  case XK_Next:
1624  return KeyboardButton::page_down();
1625  case XK_KP_Home:
1626  case XK_Home:
1627  return KeyboardButton::home();
1628  case XK_KP_End:
1629  case XK_End:
1630  return KeyboardButton::end();
1631  case XK_KP_Insert:
1632  case XK_Insert:
1633  return KeyboardButton::insert();
1634  case XK_KP_Delete:
1635  case XK_Delete:
1636  return KeyboardButton::del();
1637  case XK_Num_Lock:
1638  return KeyboardButton::num_lock();
1639  case XK_Scroll_Lock:
1640  return KeyboardButton::scroll_lock();
1641  case XK_Print:
1642  return KeyboardButton::print_screen();
1643  case XK_Pause:
1644  return KeyboardButton::pause();
1645  case XK_Menu:
1646  return KeyboardButton::menu();
1647  case XK_Shift_L:
1648  return KeyboardButton::lshift();
1649  case XK_Shift_R:
1650  return KeyboardButton::rshift();
1651  case XK_Control_L:
1652  return KeyboardButton::lcontrol();
1653  case XK_Control_R:
1654  return KeyboardButton::rcontrol();
1655  case XK_Alt_L:
1656  return KeyboardButton::lalt();
1657  case XK_Alt_R:
1658  return KeyboardButton::ralt();
1659  case XK_Meta_L:
1660  return KeyboardButton::lmeta();
1661  case XK_Meta_R:
1662  return KeyboardButton::rmeta();
1663  case XK_Caps_Lock:
1664  return KeyboardButton::caps_lock();
1665  case XK_Shift_Lock:
1666  return KeyboardButton::shift_lock();
1667  }
1668 
1669  return ButtonHandle::none();
1670 }
1671 
1672 ////////////////////////////////////////////////////////////////////
1673 // Function: eglGraphicsWindow::get_mouse_button
1674 // Access: Private
1675 // Description: Returns the Panda ButtonHandle corresponding to the
1676 // mouse button indicated by the given button event.
1677 ////////////////////////////////////////////////////////////////////
1678 ButtonHandle eglGraphicsWindow::
1679 get_mouse_button(XButtonEvent &button_event) {
1680  int index = button_event.button;
1681  if (index == x_wheel_up_button) {
1682  return MouseButton::wheel_up();
1683  } else if (index == x_wheel_down_button) {
1684  return MouseButton::wheel_down();
1685  } else if (index == x_wheel_left_button) {
1686  return MouseButton::wheel_left();
1687  } else if (index == x_wheel_right_button) {
1688  return MouseButton::wheel_right();
1689  } else {
1690  return MouseButton::button(index - 1);
1691  }
1692 }
1693 ////////////////////////////////////////////////////////////////////
1694 // Function: eglGraphicsWindow::check_event
1695 // Access: Private, Static
1696 // Description: This function is used as a predicate to
1697 // XCheckIfEvent() to determine if the indicated queued
1698 // X event is relevant and should be returned to this
1699 // window.
1700 ////////////////////////////////////////////////////////////////////
1701 Bool eglGraphicsWindow::
1702 check_event(X11_Display *display, XEvent *event, char *arg) {
1703  const eglGraphicsWindow *self = (eglGraphicsWindow *)arg;
1704 
1705  // We accept any event that is sent to our window.
1706  return (event->xany.window == self->_xwindow);
1707 }
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.
X11_Window get_root() const
Returns the handle to the root window on the pipe&#39;s display.
void set_pointer_in_window(double x, double y)
To be called by a particular kind of GraphicsWindow to indicate that the pointer is within the window...
int get_x_origin() const
Returns the x coordinate of the window&#39;s top-left corner, not including decorations.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
char get_ascii_equivalent() const
Returns the character code associated with the button, or &#39;\0&#39; if no ASCII code was associated...
Definition: buttonHandle.I:151
static ButtonHandle none()
Returns a special zero-valued ButtonHandle that is used to indicate no button.
Definition: buttonHandle.I:205
This object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:40
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
void clear_foreground()
Removes the foreground specification from the properties.
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...
bool has_cursor_hidden() const
Returns true if set_cursor_hidden() has been specified.
const WindowProperties get_properties() const
Returns the current properties of the window.
const string & get_title() const
Returns the window&#39;s title.
XIM get_im() const
Returns the input method opened for the pipe, or NULL if the input method could not be opened for som...
void clear_origin()
Removes the origin specification from the properties.
bool get_minimized() const
Returns true if the window is minimized.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
void button_down(ButtonHandle button)
Records that the indicated button has been depressed.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
An interface to the egl system for managing GLES windows under X.
void set_size(const LVector2i &size)
Specifies the requested size of the window, in pixels.
int get_y_origin() const
Returns the y coordinate of the window&#39;s top-left corner, not including decorations.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
void clear_title()
Removes the title specification from the properties.
bool has_z_order() const
Returns true if the window z_order has been specified, false otherwise.
bool subsumes(const FrameBufferProperties &other) const
Returns true if this set of properties makes strictly greater or equal demands of the framebuffer tha...
This graphics pipe represents the interface for creating OpenGL ES graphics windows on an X-based (e...
bool get_fullscreen() const
Returns true if the window is in fullscreen mode.
bool verify_hardware_software(const FrameBufferProperties &props, const string &renderer) const
Validates that the properties represent the desired kind of renderer (hardware or software)...
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:28
int get_display_height() const
Returns the height of the entire display, if it is known.
Definition: graphicsPipe.I:92
void set_undecorated(bool undecorated)
Specifies whether the window should be created with a visible title and border (false, the default) or not (true).
A container for the various kinds of properties we might ask to have on a graphics window before we o...
string get_close_request_event() const
Returns the name of the event set via set_close_request_event().
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.
bool has_title() const
Returns true if the window title has been specified, false otherwise.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
static ButtonHandle wheel_left()
Returns the ButtonHandle generated when the mouse is scrolled to the left.
bool get_foreground() const
Returns true if the window is in the foreground.
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...
This is a structure representing a single input device that may be associated with a window...
bool has_foreground() const
Returns true if set_foreground() has been specified.
X11_Cursor get_hidden_cursor()
Returns an invisible Cursor suitable for assigning to windows that have the cursor_hidden property se...
This is a base class for the various different classes that represent the result of a frame of render...
Similar to MutexHolder, but for a light reentrant mutex.
bool has_minimized() const
Returns true if set_minimized() has been specified.
void choose_pixel_format(const FrameBufferProperties &properties, X11_Display *_display, int _screen, bool need_pbuffer, bool need_pixmap)
Selects a visual or fbconfig for all the windows and buffers that use this gsg.
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
bool has_origin() const
Returns true if the window origin has been specified, false otherwise.
bool get_fixed_size() const
Returns true if the window cannot be resized by the user, false otherwise.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
MouseData get_raw_pointer() const
Returns the MouseData associated with the input device&#39;s pointer, in raw form (ie, prior to any pointer_mode interpretation).
bool has_size() const
Returns true if the window size has been specified, false otherwise.
int get_display_width() const
Returns the width of the entire display, if it is known.
Definition: graphicsPipe.I:80
Holds the data that might be generated by a 2-d pointer input device, such as the mouse in the Graphi...
Definition: mouseData.h:28
bool get_cursor_hidden() const
Returns true if the mouse cursor is invisible.
void set_foreground(bool foreground)
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
void clear_z_order()
Removes the z_order specification from the properties.
void add_properties(const WindowProperties &other)
Sets any properties that are explicitly specified in other on this object.
A thread; that is, a lightweight process.
Definition: thread.h:51
void clear_cursor_hidden()
Removes the cursor_hidden specification from the properties.
int get_screen() const
Returns the X screen number associated with the pipe.
void set_minimized(bool minimized)
Specifies whether the window should be created minimized (true), or normal (false).
Encapsulates all the communication with a particular instance of a given rendering backend...
X11_Display * get_display() const
Returns a pointer to the X display associated with the pipe: the display on which to create the windo...
static ButtonHandle wheel_right()
Returns the ButtonHandle generated when the mouse is scrolled to the right.
virtual bool move_pointer(int device, int x, int y)
Forces the pointer to the indicated position within the window, if possible.
A tiny specialization on GLESGraphicsStateGuardian to add some egl-specific information.
This class is the main interface to controlling the render process.
ZOrder get_z_order() const
Returns the window&#39;s z_order.
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
OSHandle * get_os_handle() const
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.I:41
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
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 get_undecorated() const
Returns true if the window has no border.
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one...
void set_open(bool open)
Specifies whether the window should be open.
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.
const FrameBufferProperties & get_fb_properties() const
Gets the FrameBufferProperties for all windows and buffers that use this GSG.
bool has_fullscreen() const
Returns true if set_fullscreen() has been specified.
bool has_ascii_equivalent() const
Returns true if the button was created with an ASCII equivalent code (e.g.
Definition: buttonHandle.I:140
static int size()
Returns 3: the number of components of a LVecBase3.
Definition: lvecBase3.h:452
static GraphicsWindowInputDevice pointer_only(GraphicsWindow *host, const string &name)
This named constructor returns an input device that only has a pointing device, no keyboard...