Panda3D

eglGraphicsWindow.cxx

00001 // Filename: eglGraphicsWindow.cxx
00002 // Created by:  pro-rsoft (21May09)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "eglGraphicsWindow.h"
00016 #include "eglGraphicsStateGuardian.h"
00017 #include "config_egldisplay.h"
00018 #include "eglGraphicsPipe.h"
00019 
00020 #include "graphicsPipe.h"
00021 #include "keyboardButton.h"
00022 #include "mouseButton.h"
00023 #include "clockObject.h"
00024 #include "pStatTimer.h"
00025 #include "textEncoder.h"
00026 #include "throw_event.h"
00027 #include "lightReMutexHolder.h"
00028 #include "nativeWindowHandle.h"
00029 
00030 #include <errno.h>
00031 #include <sys/time.h>
00032 #include <X11/keysym.h>
00033 #include <X11/Xutil.h>
00034 #include <X11/Xatom.h>
00035 
00036 #ifdef HAVE_LINUX_INPUT_H
00037 #include <linux/input.h>
00038 #endif
00039 
00040 TypeHandle eglGraphicsWindow::_type_handle;
00041 
00042 #define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
00043 
00044 ////////////////////////////////////////////////////////////////////
00045 //     Function: eglGraphicsWindow::Constructor
00046 //       Access: Public
00047 //  Description:
00048 ////////////////////////////////////////////////////////////////////
00049 eglGraphicsWindow::
00050 eglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
00051                   const string &name,
00052                   const FrameBufferProperties &fb_prop,
00053                   const WindowProperties &win_prop,
00054                   int flags,
00055                   GraphicsStateGuardian *gsg,
00056                   GraphicsOutput *host) :
00057   GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
00058 {
00059   eglGraphicsPipe *egl_pipe;
00060   DCAST_INTO_V(egl_pipe, _pipe);
00061   _display = egl_pipe->get_display();
00062   _screen = egl_pipe->get_screen();
00063   _xwindow = (Window)NULL;
00064   _ic = (XIC)NULL;
00065   _egl_display = egl_pipe->_egl_display;
00066   _egl_surface = 0;
00067   _awaiting_configure = false;
00068   _wm_delete_window = egl_pipe->_wm_delete_window;
00069   _net_wm_window_type = egl_pipe->_net_wm_window_type;
00070   _net_wm_window_type_splash = egl_pipe->_net_wm_window_type_splash;
00071   _net_wm_window_type_fullscreen = egl_pipe->_net_wm_window_type_fullscreen;
00072   _net_wm_state = egl_pipe->_net_wm_state;
00073   _net_wm_state_fullscreen = egl_pipe->_net_wm_state_fullscreen;
00074   _net_wm_state_above = egl_pipe->_net_wm_state_above;
00075   _net_wm_state_below = egl_pipe->_net_wm_state_below;
00076   _net_wm_state_add = egl_pipe->_net_wm_state_add;
00077   _net_wm_state_remove = egl_pipe->_net_wm_state_remove;
00078 
00079   GraphicsWindowInputDevice device =
00080     GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
00081   add_input_device(device);
00082 }
00083 
00084 ////////////////////////////////////////////////////////////////////
00085 //     Function: eglGraphicsWindow::Destructor
00086 //       Access: Public, Virtual
00087 //  Description:
00088 ////////////////////////////////////////////////////////////////////
00089 eglGraphicsWindow::
00090 ~eglGraphicsWindow() {
00091 }
00092 
00093 ////////////////////////////////////////////////////////////////////
00094 //     Function: eglGraphicsWindow::move_pointer
00095 //       Access: Published, Virtual
00096 //  Description: Forces the pointer to the indicated position within
00097 //               the window, if possible.
00098 //
00099 //               Returns true if successful, false on failure.  This
00100 //               may fail if the mouse is not currently within the
00101 //               window, or if the API doesn't support this operation.
00102 ////////////////////////////////////////////////////////////////////
00103 bool eglGraphicsWindow::
00104 move_pointer(int device, int x, int y) {
00105   // Note: this is not thread-safe; it should be called only from App.
00106   // Probably not an issue.
00107   if (device == 0) {
00108     // Move the system mouse pointer.
00109     if (!_properties.get_foreground() ||
00110         !_input_devices[0].get_pointer().get_in_window()) {
00111       // If the window doesn't have input focus, or the mouse isn't
00112       // currently within the window, forget it.
00113       return false;
00114     }
00115 
00116     const MouseData &md = _input_devices[0].get_pointer();
00117     if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) {
00118       XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y);
00119       _input_devices[0].set_pointer_in_window(x, y);
00120     }
00121     return true;
00122   } else {
00123     // Move a raw mouse.
00124     if ((device < 1)||(device >= _input_devices.size())) {
00125       return false;
00126     }
00127     _input_devices[device].set_pointer_in_window(x, y);
00128     return true;
00129   }
00130 }
00131 
00132 
00133 ////////////////////////////////////////////////////////////////////
00134 //     Function: eglGraphicsWindow::begin_frame
00135 //       Access: Public, Virtual
00136 //  Description: This function will be called within the draw thread
00137 //               before beginning rendering for a given frame.  It
00138 //               should do whatever setup is required, and return true
00139 //               if the frame should be rendered, or false if it
00140 //               should be skipped.
00141 ////////////////////////////////////////////////////////////////////
00142 bool eglGraphicsWindow::
00143 begin_frame(FrameMode mode, Thread *current_thread) {
00144   PStatTimer timer(_make_current_pcollector, current_thread);
00145 
00146   begin_frame_spam(mode);
00147   if (_gsg == (GraphicsStateGuardian *)NULL) {
00148     return false;
00149   }
00150   if (_awaiting_configure) {
00151     // Don't attempt to draw while we have just reconfigured the
00152     // window and we haven't got the notification back yet.
00153     return false;
00154   }
00155 
00156   eglGraphicsStateGuardian *eglgsg;
00157   DCAST_INTO_R(eglgsg, _gsg, false);
00158   {
00159     LightReMutexHolder holder(eglGraphicsPipe::_x_mutex);
00160 
00161     if (eglGetCurrentDisplay() == _egl_display &&
00162         eglGetCurrentSurface(EGL_READ) == _egl_surface &&
00163         eglGetCurrentSurface(EGL_DRAW) == _egl_surface &&
00164         eglGetCurrentContext() == eglgsg->_context) {
00165       // No need to make the context current again.  Short-circuit
00166       // this possibly-expensive call.
00167     } else {
00168       // Need to set the context.
00169       if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) {
00170         egldisplay_cat.error() << "Failed to call eglMakeCurrent: "
00171           << get_egl_error_string(eglGetError()) << "\n";
00172       }
00173     }
00174   }
00175 
00176   // Now that we have made the context current to a window, we can
00177   // reset the GSG state if this is the first time it has been used.
00178   // (We can't just call reset() when we construct the GSG, because
00179   // reset() requires having a current context.)
00180   eglgsg->reset_if_new();
00181 
00182   if (mode == FM_render) {
00183     // begin_render_texture();
00184     clear_cube_map_selection();
00185   }
00186 
00187   _gsg->set_current_properties(&get_fb_properties());
00188   return _gsg->begin_frame(current_thread);
00189 }
00190 
00191 ////////////////////////////////////////////////////////////////////
00192 //     Function: eglGraphicsWindow::end_frame
00193 //       Access: Public, Virtual
00194 //  Description: This function will be called within the draw thread
00195 //               after rendering is completed for a given frame.  It
00196 //               should do whatever finalization is required.
00197 ////////////////////////////////////////////////////////////////////
00198 void eglGraphicsWindow::
00199 end_frame(FrameMode mode, Thread *current_thread) {
00200   end_frame_spam(mode);
00201   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
00202 
00203   if (mode == FM_render) {
00204     // end_render_texture();
00205     copy_to_textures();
00206   }
00207 
00208   _gsg->end_frame(current_thread);
00209 
00210   if (mode == FM_render) {
00211     trigger_flip();
00212     if (_one_shot) {
00213       prepare_for_deletion();
00214     }
00215     clear_cube_map_selection();
00216   }
00217 }
00218 
00219 ////////////////////////////////////////////////////////////////////
00220 //     Function: eglGraphicsWindow::begin_flip
00221 //       Access: Public, Virtual
00222 //  Description: This function will be called within the draw thread
00223 //               after end_frame() has been called on all windows, to
00224 //               initiate the exchange of the front and back buffers.
00225 //
00226 //               This should instruct the window to prepare for the
00227 //               flip at the next video sync, but it should not wait.
00228 //
00229 //               We have the two separate functions, begin_flip() and
00230 //               end_flip(), to make it easier to flip all of the
00231 //               windows at the same time.
00232 ////////////////////////////////////////////////////////////////////
00233 void eglGraphicsWindow::
00234 begin_flip() {
00235   if (_gsg != (GraphicsStateGuardian *)NULL) {
00236 
00237     // It doesn't appear to be necessary to ensure the graphics
00238     // context is current before flipping the windows, and insisting
00239     // on doing so can be a significant performance hit.
00240 
00241     //make_current();
00242 
00243     LightReMutexHolder holder(eglGraphicsPipe::_x_mutex);
00244     eglSwapBuffers(_egl_display, _egl_surface);
00245   }
00246 }
00247 
00248 ////////////////////////////////////////////////////////////////////
00249 //     Function: eglGraphicsWindow::process_events
00250 //       Access: Public, Virtual
00251 //  Description: Do whatever processing is necessary to ensure that
00252 //               the window responds to user events.  Also, honor any
00253 //               requests recently made via request_properties()
00254 //
00255 //               This function is called only within the window
00256 //               thread.
00257 ////////////////////////////////////////////////////////////////////
00258 void eglGraphicsWindow::
00259 process_events() {
00260   LightReMutexHolder holder(eglGraphicsPipe::_x_mutex);
00261 
00262   GraphicsWindow::process_events();
00263 
00264   if (_xwindow == (Window)0) {
00265     return;
00266   }
00267 
00268   poll_raw_mice();
00269 
00270   XEvent event;
00271   XKeyEvent keyrelease_event;
00272   bool got_keyrelease_event = false;
00273 
00274   while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
00275     if (XFilterEvent(&event, None)) {
00276       continue;
00277     }
00278 
00279     if (got_keyrelease_event) {
00280       // If a keyrelease event is immediately followed by a matching
00281       // keypress event, that's just key repeat and we should treat
00282       // the two events accordingly.  It would be nice if X provided a
00283       // way to differentiate between keyrepeat and explicit
00284       // keypresses more generally.
00285       got_keyrelease_event = false;
00286 
00287       if (event.type == KeyPress &&
00288           event.xkey.keycode == keyrelease_event.keycode &&
00289           (event.xkey.time - keyrelease_event.time <= 1)) {
00290         // In particular, we only generate down messages for the
00291         // repeated keys, not down-and-up messages.
00292         handle_keystroke(event.xkey);
00293 
00294         // We thought about not generating the keypress event, but we
00295         // need that repeat for backspace.  Rethink later.
00296         handle_keypress(event.xkey);
00297         continue;
00298 
00299       } else {
00300         // This keyrelease event is not immediately followed by a
00301         // matching keypress event, so it's a genuine release.
00302         handle_keyrelease(keyrelease_event);
00303       }
00304     }
00305 
00306     WindowProperties properties;
00307     ButtonHandle button;
00308 
00309     switch (event.type) {
00310     case ReparentNotify:
00311       break;
00312 
00313     case ConfigureNotify:
00314       _awaiting_configure = false;
00315       if (_properties.get_fixed_size()) {
00316         // If the window properties indicate a fixed size only, undo
00317         // any attempt by the user to change them.  In X, there
00318         // doesn't appear to be a way to universally disallow this
00319         // directly (although we do set the min_size and max_size to
00320         // the same value, which seems to work for most window
00321         // managers.)
00322         WindowProperties current_props = get_properties();
00323         if (event.xconfigure.width != current_props.get_x_size() ||
00324             event.xconfigure.height != current_props.get_y_size()) {
00325           XWindowChanges changes;
00326           changes.width = current_props.get_x_size();
00327           changes.height = current_props.get_y_size();
00328           int value_mask = (CWWidth | CWHeight);
00329           XConfigureWindow(_display, _xwindow, value_mask, &changes);
00330         }
00331 
00332       } else {
00333         // A normal window may be resized by the user at will.
00334         properties.set_size(event.xconfigure.width, event.xconfigure.height);
00335         system_changed_properties(properties);
00336       }
00337       break;
00338 
00339     case ButtonPress:
00340       // This refers to the mouse buttons.
00341       button = get_mouse_button(event.xbutton);
00342       _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
00343       _input_devices[0].button_down(button);
00344       break;
00345 
00346     case ButtonRelease:
00347       button = get_mouse_button(event.xbutton);
00348       _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
00349       _input_devices[0].button_up(button);
00350       break;
00351 
00352     case MotionNotify:
00353       _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
00354       break;
00355 
00356     case KeyPress:
00357       handle_keystroke(event.xkey);
00358       handle_keypress(event.xkey);
00359       break;
00360 
00361     case KeyRelease:
00362       // The KeyRelease can't be processed immediately, because we
00363       // have to check first if it's immediately followed by a
00364       // matching KeyPress event.
00365       keyrelease_event = event.xkey;
00366       got_keyrelease_event = true;
00367       break;
00368 
00369     case EnterNotify:
00370       _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
00371       break;
00372 
00373     case LeaveNotify:
00374       _input_devices[0].set_pointer_out_of_window();
00375       break;
00376 
00377     case FocusIn:
00378       properties.set_foreground(true);
00379       system_changed_properties(properties);
00380       break;
00381 
00382     case FocusOut:
00383       properties.set_foreground(false);
00384       system_changed_properties(properties);
00385       break;
00386 
00387     case UnmapNotify:
00388       properties.set_minimized(true);
00389       system_changed_properties(properties);
00390       break;
00391 
00392     case MapNotify:
00393       properties.set_minimized(false);
00394       system_changed_properties(properties);
00395 
00396       // Auto-focus the window when it is mapped.
00397       XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
00398       break;
00399 
00400     case ClientMessage:
00401       if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
00402         // This is a message from the window manager indicating that
00403         // the user has requested to close the window.
00404         string close_request_event = get_close_request_event();
00405         if (!close_request_event.empty()) {
00406           // In this case, the app has indicated a desire to intercept
00407           // the request and process it directly.
00408           throw_event(close_request_event);
00409 
00410         } else {
00411           // In this case, the default case, the app does not intend
00412           // to service the request, so we do by closing the window.
00413 
00414           // TODO: don't release the gsg in the window thread.
00415           close_window();
00416           properties.set_open(false);
00417           system_changed_properties(properties);
00418         }
00419       }
00420       break;
00421 
00422     case DestroyNotify:
00423       // Apparently, we never get a DestroyNotify on a toplevel
00424       // window.  Instead, we rely on hints from the window manager
00425       // (see above).
00426       egldisplay_cat.info()
00427         << "DestroyNotify\n";
00428       break;
00429 
00430     default:
00431       egldisplay_cat.error()
00432         << "unhandled X event type " << event.type << "\n";
00433     }
00434   }
00435 
00436   if (got_keyrelease_event) {
00437     // This keyrelease event is not immediately followed by a
00438     // matching keypress event, so it's a genuine release.
00439     handle_keyrelease(keyrelease_event);
00440   }
00441 }
00442 
00443 ////////////////////////////////////////////////////////////////////
00444 //     Function: eglGraphicsWindow::set_properties_now
00445 //       Access: Public, Virtual
00446 //  Description: Applies the requested set of properties to the
00447 //               window, if possible, for instance to request a change
00448 //               in size or minimization status.
00449 //
00450 //               The window properties are applied immediately, rather
00451 //               than waiting until the next frame.  This implies that
00452 //               this method may *only* be called from within the
00453 //               window thread.
00454 //
00455 //               The return value is true if the properties are set,
00456 //               false if they are ignored.  This is mainly useful for
00457 //               derived classes to implement extensions to this
00458 //               function.
00459 ////////////////////////////////////////////////////////////////////
00460 void eglGraphicsWindow::
00461 set_properties_now(WindowProperties &properties) {
00462   if (_pipe == (GraphicsPipe *)NULL) {
00463     // If the pipe is null, we're probably closing down.
00464     GraphicsWindow::set_properties_now(properties);
00465     return;
00466   }
00467 
00468   eglGraphicsPipe *egl_pipe;
00469   DCAST_INTO_V(egl_pipe, _pipe);
00470 
00471   // Fullscreen mode is implemented with a hint to the window manager.
00472   // However, we also implicitly set the origin to (0, 0) and the size
00473   // to the desktop size, and request undecorated mode, in case the
00474   // user has a less-capable window manager (or no window manager at
00475   // all).
00476   if (properties.get_fullscreen()) {
00477     properties.set_undecorated(true);
00478     properties.set_origin(0, 0);
00479     properties.set_size(egl_pipe->get_display_width(),
00480                         egl_pipe->get_display_height());
00481   }
00482 
00483   GraphicsWindow::set_properties_now(properties);
00484   if (!properties.is_any_specified()) {
00485     // The base class has already handled this case.
00486     return;
00487   }
00488 
00489   // The window is already open; we are limited to what we can change
00490   // on the fly.
00491 
00492   // We'll pass some property requests on as a window manager hint.
00493   WindowProperties wm_properties = _properties;
00494   wm_properties.add_properties(properties);
00495 
00496   // The window title may be changed by issuing another hint request.
00497   // Assume this will be honored.
00498   if (properties.has_title()) {
00499     _properties.set_title(properties.get_title());
00500     properties.clear_title();
00501   }
00502 
00503   // Ditto for fullscreen mode.
00504   if (properties.has_fullscreen()) {
00505     _properties.set_fullscreen(properties.get_fullscreen());
00506     properties.clear_fullscreen();
00507   }
00508 
00509   // The size and position of an already-open window are changed via
00510   // explicit X calls.  These may still get intercepted by the window
00511   // manager.  Rather than changing _properties immediately, we'll
00512   // wait for the ConfigureNotify message to come back.
00513   XWindowChanges changes;
00514   int value_mask = 0;
00515 
00516   if (properties.has_origin()) {
00517     changes.x = properties.get_x_origin();
00518     changes.y = properties.get_y_origin();
00519     value_mask |= (CWX | CWY);
00520     properties.clear_origin();
00521   }
00522   if (properties.has_size()) {
00523     changes.width = properties.get_x_size();
00524     changes.height = properties.get_y_size();
00525     value_mask |= (CWWidth | CWHeight);
00526     properties.clear_size();
00527   }
00528   if (properties.has_z_order()) {
00529     // We'll send the classic stacking request through the standard
00530     // interface, for users of primitive window managers; but we'll
00531     // also send it as a window manager hint, for users of modern
00532     // window managers.
00533     _properties.set_z_order(properties.get_z_order());
00534     switch (properties.get_z_order()) {
00535     case WindowProperties::Z_bottom:
00536       changes.stack_mode = Below;
00537       break;
00538 
00539     case WindowProperties::Z_normal:
00540       changes.stack_mode = TopIf;
00541       break;
00542 
00543     case WindowProperties::Z_top:
00544       changes.stack_mode = Above;
00545       break;
00546     }
00547 
00548     value_mask |= (CWStackMode);
00549     properties.clear_z_order();
00550   }
00551 
00552   if (value_mask != 0) {
00553     XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
00554 
00555     // Don't draw anything until this is done reconfiguring.
00556     _awaiting_configure = true;
00557   }
00558 
00559   // We hide the cursor by setting it to an invisible pixmap.
00560   if (properties.has_cursor_hidden()) {
00561     _properties.set_cursor_hidden(properties.get_cursor_hidden());
00562     if (properties.get_cursor_hidden()) {
00563       XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor());
00564     } else {
00565       XDefineCursor(_display, _xwindow, None);
00566     }
00567     properties.clear_cursor_hidden();
00568   }
00569 
00570   if (properties.has_foreground()) {
00571     if (properties.get_foreground()) {
00572       XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
00573     } else {
00574       XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
00575     }
00576     properties.clear_foreground();
00577   }
00578 
00579   set_wm_properties(wm_properties, true);
00580 }
00581 
00582 ////////////////////////////////////////////////////////////////////
00583 //     Function: eglGraphicsWindow::close_window
00584 //       Access: Protected, Virtual
00585 //  Description: Closes the window right now.  Called from the window
00586 //               thread.
00587 ////////////////////////////////////////////////////////////////////
00588 void eglGraphicsWindow::
00589 close_window() {
00590   if (_gsg != (GraphicsStateGuardian *)NULL) {
00591     if (!eglMakeCurrent(_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
00592       egldisplay_cat.error() << "Failed to call eglMakeCurrent: "
00593         << get_egl_error_string(eglGetError()) << "\n";
00594     }
00595     _gsg.clear();
00596     _active = false;
00597   }
00598 
00599   if (_ic != (XIC)NULL) {
00600     XDestroyIC(_ic);
00601     _ic = (XIC)NULL;
00602   }
00603 
00604   if (_egl_surface != 0) {
00605     if (!eglDestroySurface(_egl_display, _egl_surface)) {
00606       egldisplay_cat.error() << "Failed to destroy surface: "
00607         << get_egl_error_string(eglGetError()) << "\n";
00608     }
00609   }
00610 
00611   if (_xwindow != (Window)NULL) {
00612     XDestroyWindow(_display, _xwindow);
00613     _xwindow = (Window)NULL;
00614 
00615     // This may be necessary if we just closed the last X window in an
00616     // application, so the server hears the close request.
00617     XFlush(_display);
00618   }
00619   GraphicsWindow::close_window();
00620 }
00621 
00622 ////////////////////////////////////////////////////////////////////
00623 //     Function: eglGraphicsWindow::open_window
00624 //       Access: Protected, Virtual
00625 //  Description: Opens the window right now.  Called from the window
00626 //               thread.  Returns true if the window is successfully
00627 //               opened, or false if there was a problem.
00628 ////////////////////////////////////////////////////////////////////
00629 bool eglGraphicsWindow::
00630 open_window() {
00631   eglGraphicsPipe *egl_pipe;
00632   DCAST_INTO_R(egl_pipe, _pipe, false);
00633 
00634   // GSG Creation/Initialization
00635   eglGraphicsStateGuardian *eglgsg;
00636   if (_gsg == 0) {
00637     // There is no old gsg.  Create a new one.
00638     eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, NULL);
00639     eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false);
00640     _gsg = eglgsg;
00641   } else {
00642     // If the old gsg has the wrong pixel format, create a
00643     // new one that shares with the old gsg.
00644     DCAST_INTO_R(eglgsg, _gsg, false);
00645     if (!eglgsg->get_fb_properties().subsumes(_fb_properties)) {
00646       eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, eglgsg);
00647       eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false);
00648       _gsg = eglgsg;
00649     }
00650   }
00651 
00652   XVisualInfo *visual_info = eglgsg->_visual;
00653   if (visual_info == NULL) {
00654     // No X visual for this fbconfig; how can we open the window?
00655     egldisplay_cat.error()
00656       << "No X visual: cannot open window.\n";
00657     return false;
00658   }
00659   Visual *visual = visual_info->visual;
00660   int depth = visual_info->depth;
00661 
00662   if (!_properties.has_origin()) {
00663     _properties.set_origin(0, 0);
00664   }
00665   if (!_properties.has_size()) {
00666     _properties.set_size(100, 100);
00667   }
00668   
00669   Window parent_window = egl_pipe->get_root();
00670   WindowHandle *window_handle = _properties.get_parent_window();
00671   if (window_handle != NULL) {
00672     egldisplay_cat.info()
00673       << "Got parent_window " << *window_handle << "\n";
00674     WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
00675     if (os_handle != NULL) {
00676       egldisplay_cat.info()
00677         << "os_handle type " << os_handle->get_type() << "\n";
00678       
00679       if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
00680         NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
00681         parent_window = x11_handle->get_handle();
00682       } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
00683         NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
00684         parent_window = (Window)int_handle->get_handle();
00685       }
00686     }
00687   }
00688   _parent_window_handle = window_handle;
00689 
00690   setup_colormap(visual_info);
00691 
00692   _event_mask =
00693     ButtonPressMask | ButtonReleaseMask |
00694     KeyPressMask | KeyReleaseMask |
00695     EnterWindowMask | LeaveWindowMask |
00696     PointerMotionMask |
00697     FocusChangeMask |
00698     StructureNotifyMask;
00699 
00700   // Initialize window attributes
00701   XSetWindowAttributes wa;
00702   wa.background_pixel = XBlackPixel(_display, _screen);
00703   wa.border_pixel = 0;
00704   wa.colormap = _colormap;
00705   wa.event_mask = _event_mask;
00706 
00707   unsigned long attrib_mask =
00708     CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
00709 
00710   _xwindow = XCreateWindow
00711     (_display, parent_window,
00712      _properties.get_x_origin(), _properties.get_y_origin(),
00713      _properties.get_x_size(), _properties.get_y_size(),
00714      0, depth, InputOutput, visual, attrib_mask, &wa);
00715 
00716   if (_xwindow == (Window)0) {
00717     egldisplay_cat.error()
00718       << "failed to create X window.\n";
00719     return false;
00720   }
00721   set_wm_properties(_properties, false);
00722 
00723   // We don't specify any fancy properties of the XIC.  It would be
00724   // nicer if we could support fancy IM's that want preedit callbacks,
00725   // etc., but that can wait until we have an X server that actually
00726   // supports these to test it on.
00727   XIM im = egl_pipe->get_im();
00728   _ic = NULL;
00729   if (im) {
00730     _ic = XCreateIC
00731       (im,
00732        XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
00733        (void*)NULL);
00734     if (_ic == (XIC)NULL) {
00735       egldisplay_cat.warning()
00736         << "Couldn't create input context.\n";
00737     }
00738   }
00739 
00740   if (_properties.get_cursor_hidden()) {
00741     XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor());
00742   }
00743 
00744   _egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, NULL);
00745   if (eglGetError() != EGL_SUCCESS) {
00746     egldisplay_cat.error()
00747       << "Failed to create window surface.\n";
00748     return false;
00749   }
00750 
00751   if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) {
00752     egldisplay_cat.error() << "Failed to call eglMakeCurrent: "
00753       << get_egl_error_string(eglGetError()) << "\n";
00754   }
00755   eglgsg->reset_if_new();
00756   if (!eglgsg->is_valid()) {
00757     close_window();
00758     return false;
00759   }
00760   if (!eglgsg->get_fb_properties().verify_hardware_software
00761       (_fb_properties, eglgsg->get_gl_renderer())) {
00762     close_window();
00763     return false;
00764   }
00765   _fb_properties = eglgsg->get_fb_properties();
00766 
00767   XMapWindow(_display, _xwindow);
00768 
00769   if (_properties.get_raw_mice()) {
00770     open_raw_mice();
00771   } else {
00772     if (egldisplay_cat.is_debug()) {
00773       egldisplay_cat.debug()
00774         << "Raw mice not requested.\n";
00775     }
00776   }
00777 
00778   // Create a WindowHandle for ourselves
00779   _window_handle = NativeWindowHandle::make_x11(_xwindow);
00780 
00781   // And tell our parent window that we're now its child.
00782   if (_parent_window_handle != (WindowHandle *)NULL) {
00783     _parent_window_handle->attach_child(_window_handle);
00784   }
00785 
00786   return true;
00787 }
00788 
00789 ////////////////////////////////////////////////////////////////////
00790 //     Function: eglGraphicsWindow::set_wm_properties
00791 //       Access: Private
00792 //  Description: Asks the window manager to set the appropriate
00793 //               properties.  In X, these properties cannot be
00794 //               specified directly by the application; they must be
00795 //               requested via the window manager, which may or may
00796 //               not choose to honor the request.
00797 //
00798 //               If already_mapped is true, the window has already
00799 //               been mapped (manifested) on the display.  This means
00800 //               we may need to use a different action in some cases.
00801 ////////////////////////////////////////////////////////////////////
00802 void eglGraphicsWindow::
00803 set_wm_properties(const WindowProperties &properties, bool already_mapped) {
00804   // Name the window if there is a name
00805   XTextProperty window_name;
00806   XTextProperty *window_name_p = (XTextProperty *)NULL;
00807   if (properties.has_title()) {
00808     char *name = (char *)properties.get_title().c_str();
00809     if (XStringListToTextProperty(&name, 1, &window_name) != 0) {
00810       window_name_p = &window_name;
00811     }
00812   }
00813 
00814   // The size hints request a window of a particular size and/or a
00815   // particular placement onscreen.
00816   XSizeHints *size_hints_p = NULL;
00817   if (properties.has_origin() || properties.has_size()) {
00818     size_hints_p = XAllocSizeHints();
00819     if (size_hints_p != (XSizeHints *)NULL) {
00820       if (properties.has_origin()) {
00821         size_hints_p->x = properties.get_x_origin();
00822         size_hints_p->y = properties.get_y_origin();
00823         size_hints_p->flags |= USPosition;
00824       }
00825       if (properties.has_size()) {
00826         size_hints_p->width = properties.get_x_size();
00827         size_hints_p->height = properties.get_y_size();
00828         size_hints_p->flags |= USSize;
00829 
00830         if (properties.get_fixed_size()) {
00831           size_hints_p->min_width = properties.get_x_size();
00832           size_hints_p->min_height = properties.get_y_size();
00833           size_hints_p->max_width = properties.get_x_size();
00834           size_hints_p->max_height = properties.get_y_size();
00835           size_hints_p->flags |= (PMinSize | PMaxSize);
00836         }
00837       }
00838     }
00839   }
00840 
00841   // The window manager hints include requests to the window manager
00842   // other than those specific to window geometry.
00843   XWMHints *wm_hints_p = NULL;
00844   wm_hints_p = XAllocWMHints();
00845   if (wm_hints_p != (XWMHints *)NULL) {
00846     if (properties.has_minimized() && properties.get_minimized()) {
00847       wm_hints_p->initial_state = IconicState;
00848     } else {
00849       wm_hints_p->initial_state = NormalState;
00850     }
00851     wm_hints_p->flags = StateHint;
00852   }
00853 
00854   // Two competing window manager interfaces have evolved.  One of
00855   // them allows to set certain properties as a "type"; the other one
00856   // as a "state".  We'll try to honor both.
00857   static const int max_type_data = 32;
00858   PN_int32 type_data[max_type_data];
00859   int next_type_data = 0;
00860 
00861   static const int max_state_data = 32;
00862   PN_int32 state_data[max_state_data];
00863   int next_state_data = 0;
00864 
00865   static const int max_set_data = 32;
00866   class SetAction {
00867   public:
00868     inline SetAction() { }
00869     inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
00870     Atom _state;
00871     Atom _action;
00872   };
00873   SetAction set_data[max_set_data];
00874   int next_set_data = 0;
00875 
00876   if (properties.get_fullscreen()) {
00877     // For a "fullscreen" request, we pass this through, hoping the
00878     // window manager will support EWMH.
00879     type_data[next_type_data++] = _net_wm_window_type_fullscreen;
00880 
00881     // We also request it as a state.
00882     state_data[next_state_data++] = _net_wm_state_fullscreen;
00883     set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_add);
00884   } else {
00885     set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_remove);
00886   }
00887 
00888   // If we asked for a window without a border, there's no excellent
00889   // way to arrange that.  For users whose window managers follow the
00890   // EWMH specification, we can ask for a "splash" screen, which is
00891   // usually undecorated.  It's not exactly right, but the spec
00892   // doesn't give us an exactly-right option.
00893 
00894   // For other users, we'll totally punt and just set the window's
00895   // Class to "Undecorated", and let the user configure his/her window
00896   // manager not to put a border around windows of this class.
00897   XClassHint *class_hints_p = NULL;
00898   if (properties.get_undecorated()) {
00899     class_hints_p = XAllocClassHint();
00900     class_hints_p->res_class = (char*) "Undecorated";
00901 
00902     if (!properties.get_fullscreen()) {
00903       type_data[next_type_data++] = _net_wm_window_type_splash;
00904     }
00905   }
00906 
00907   if (properties.has_z_order()) {
00908     switch (properties.get_z_order()) {
00909     case WindowProperties::Z_bottom:
00910       state_data[next_state_data++] = _net_wm_state_below;
00911       set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_add);
00912       set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
00913       break;
00914 
00915     case WindowProperties::Z_normal:
00916       set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
00917       set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
00918       break;
00919 
00920     case WindowProperties::Z_top:
00921       state_data[next_state_data++] = _net_wm_state_above;
00922       set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
00923       set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_add);
00924       break;
00925     }
00926   }
00927 
00928   nassertv(next_type_data < max_type_data);
00929   nassertv(next_state_data < max_state_data);
00930   nassertv(next_set_data < max_set_data);
00931 
00932   XChangeProperty(_display, _xwindow, _net_wm_window_type,
00933                   XA_ATOM, 32, PropModeReplace,
00934                   (unsigned char *)type_data, next_type_data);
00935 
00936   // Request the state properties all at once.
00937   XChangeProperty(_display, _xwindow, _net_wm_state,
00938                   XA_ATOM, 32, PropModeReplace,
00939                   (unsigned char *)state_data, next_state_data);
00940 
00941   if (already_mapped) {
00942     // We have to request state changes differently when the window
00943     // has been mapped.  To do this, we need to send a client message
00944     // to the root window for each change.
00945 
00946     eglGraphicsPipe *egl_pipe;
00947     DCAST_INTO_V(egl_pipe, _pipe);
00948 
00949     for (int i = 0; i < next_set_data; ++i) {
00950       XClientMessageEvent event;
00951       memset(&event, 0, sizeof(event));
00952 
00953       event.type = ClientMessage;
00954       event.send_event = True;
00955       event.display = _display;
00956       event.window = _xwindow;
00957       event.message_type = _net_wm_state;
00958       event.format = 32;
00959       event.data.l[0] = set_data[i]._action;
00960       event.data.l[1] = set_data[i]._state;
00961       event.data.l[2] = 0;
00962       event.data.l[3] = 1;
00963 
00964       XSendEvent(_display, egl_pipe->get_root(), True, 0, (XEvent *)&event);
00965     }
00966   }
00967 
00968   XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
00969                    NULL, 0, size_hints_p, wm_hints_p, class_hints_p);
00970 
00971   if (size_hints_p != (XSizeHints *)NULL) {
00972     XFree(size_hints_p);
00973   }
00974   if (wm_hints_p != (XWMHints *)NULL) {
00975     XFree(wm_hints_p);
00976   }
00977   if (class_hints_p != (XClassHint *)NULL) {
00978     XFree(class_hints_p);
00979   }
00980 
00981   // Also, indicate to the window manager that we'd like to get a
00982   // chance to close our windows cleanly, rather than being rudely
00983   // disconnected from the X server if the user requests a window
00984   // close.
00985   Atom protocols[] = {
00986     _wm_delete_window,
00987   };
00988 
00989   XSetWMProtocols(_display, _xwindow, protocols,
00990                   sizeof(protocols) / sizeof(Atom));
00991 }
00992 
00993 ////////////////////////////////////////////////////////////////////
00994 //     Function: eglGraphicsWindow::setup_colormap
00995 //       Access: Private
00996 //  Description: Allocates a colormap appropriate to the visual and
00997 //               stores in in the _colormap method.
00998 ////////////////////////////////////////////////////////////////////
00999 void eglGraphicsWindow::
01000 setup_colormap(XVisualInfo *visual) {
01001   eglGraphicsPipe *egl_pipe;
01002   DCAST_INTO_V(egl_pipe, _pipe);
01003   Window root_window = egl_pipe->get_root();
01004 
01005   int visual_class = visual->c_class;
01006   int rc, is_rgb;
01007 
01008   switch (visual_class) {
01009     case PseudoColor:
01010       _colormap = XCreateColormap(_display, root_window,
01011                                   visual->visual, AllocAll);
01012       break;
01013     case TrueColor:
01014     case DirectColor:
01015       _colormap = XCreateColormap(_display, root_window,
01016                                   visual->visual, AllocNone);
01017       break;
01018     case StaticColor:
01019     case StaticGray:
01020     case GrayScale:
01021       _colormap = XCreateColormap(_display, root_window,
01022                                   visual->visual, AllocNone);
01023       break;
01024     default:
01025       egldisplay_cat.error()
01026         << "Could not allocate a colormap for visual class "
01027         << visual_class << ".\n";
01028       break;
01029   }
01030 }
01031 
01032 ////////////////////////////////////////////////////////////////////
01033 //     Function: eglGraphicsWindow::open_raw_mice
01034 //       Access: Private
01035 //  Description: Adds raw mice to the _input_devices list.
01036 ////////////////////////////////////////////////////////////////////
01037 void eglGraphicsWindow::
01038 open_raw_mice()
01039 {
01040 #ifdef HAVE_LINUX_INPUT_H
01041   bool any_present = false;
01042   bool any_mice = false;
01043 
01044   for (int i=0; i<64; i++) {
01045     uint8_t evtypes[EV_MAX/8 + 1];
01046     ostringstream fnb;
01047     fnb << "/dev/input/event" << i;
01048     string fn = fnb.str();
01049     int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
01050     if (fd >= 0) {
01051       any_present = true;
01052       char name[256];
01053       char phys[256];
01054       char uniq[256];
01055       if ((ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)||
01056           (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0)||
01057           (ioctl(fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0)||
01058           (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) {
01059         close(fd);
01060         egldisplay_cat.error() <<
01061           "Opening raw mice: ioctl failed on " << fn << "\n";
01062       } else {
01063         if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) {
01064           for (char *p=name; *p; p++) {
01065             if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
01066               *p = '_';
01067             }
01068           }
01069           for (char *p=uniq; *p; p++) {
01070             if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
01071               *p = '_';
01072             }
01073           }
01074           string full_id = ((string)name) + "." + uniq;
01075           MouseDeviceInfo inf;
01076           inf._fd = fd;
01077           inf._input_device_index = _input_devices.size();
01078           inf._io_buffer = "";
01079           _mouse_device_info.push_back(inf);
01080           GraphicsWindowInputDevice device =
01081             GraphicsWindowInputDevice::pointer_only(this, full_id);
01082           add_input_device(device);
01083           egldisplay_cat.info() << "Raw mouse " <<
01084             inf._input_device_index << " detected: " << full_id << "\n";
01085           any_mice = true;
01086         } else {
01087           close(fd);
01088         }
01089       }
01090     } else {
01091       if ((errno == ENOENT)||(errno == ENOTDIR)) {
01092         break;
01093       } else {
01094         any_present = true;
01095         egldisplay_cat.error() <<
01096           "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
01097       }
01098     }
01099   }
01100 
01101   if (!any_present) {
01102     egldisplay_cat.error() <<
01103       "Opening raw mice: files not found: /dev/input/event*\n";
01104   } else if (!any_mice) {
01105     egldisplay_cat.error() <<
01106       "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
01107   }
01108 #else
01109   egldisplay_cat.error() <<
01110     "Opening raw mice: panda not compiled with raw mouse support.\n";
01111 #endif
01112 }
01113 
01114 ////////////////////////////////////////////////////////////////////
01115 //     Function: eglGraphicsWindow::poll_raw_mice
01116 //       Access: Private
01117 //  Description: Reads events from the raw mouse device files.
01118 ////////////////////////////////////////////////////////////////////
01119 void eglGraphicsWindow::
01120 poll_raw_mice()
01121 {
01122 #ifdef HAVE_LINUX_INPUT_H
01123   for (int dev=0; dev<_mouse_device_info.size(); dev++) {
01124     MouseDeviceInfo &inf = _mouse_device_info[dev];
01125 
01126     // Read all bytes into buffer.
01127     if (inf._fd >= 0) {
01128       while (1) {
01129         char tbuf[1024];
01130         int nread = read(inf._fd, tbuf, sizeof(tbuf));
01131         if (nread > 0) {
01132           inf._io_buffer += string(tbuf, nread);
01133         } else {
01134           if ((nread < 0)&&((errno == EWOULDBLOCK) || (errno==EAGAIN))) {
01135             break;
01136           }
01137           close(inf._fd);
01138           inf._fd = -1;
01139           break;
01140         }
01141       }
01142     }
01143 
01144     // Process events.
01145     int nevents = inf._io_buffer.size() / sizeof(struct input_event);
01146     if (nevents == 0) {
01147       continue;
01148     }
01149     const input_event *events = (const input_event *)(inf._io_buffer.c_str());
01150     GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index];
01151     int x = dev.get_raw_pointer().get_x();
01152     int y = dev.get_raw_pointer().get_y();
01153     for (int i=0; i<nevents; i++) {
01154       if (events[i].type == EV_REL) {
01155         if (events[i].code == REL_X) x += events[i].value;
01156         if (events[i].code == REL_Y) y += events[i].value;
01157       } else if (events[i].type == EV_ABS) {
01158         if (events[i].code == ABS_X) x = events[i].value;
01159         if (events[i].code == ABS_Y) y = events[i].value;
01160       } else if (events[i].type == EV_KEY) {
01161         if ((events[i].code >= BTN_MOUSE)&&(events[i].code < BTN_MOUSE+8)) {
01162           int btn = events[i].code - BTN_MOUSE;
01163           dev.set_pointer_in_window(x,y);
01164           if (events[i].value) {
01165             dev.button_down(MouseButton::button(btn));
01166           } else {
01167             dev.button_up(MouseButton::button(btn));
01168           }
01169         }
01170       }
01171     }
01172     inf._io_buffer.erase(0,nevents*sizeof(struct input_event));
01173     dev.set_pointer_in_window(x,y);
01174   }
01175 #endif
01176 }
01177 
01178 ////////////////////////////////////////////////////////////////////
01179 //     Function: eglGraphicsWindow::handle_keystroke
01180 //       Access: Private
01181 //  Description: Generates a keystroke corresponding to the indicated
01182 //               X KeyPress event.
01183 ////////////////////////////////////////////////////////////////////
01184 void eglGraphicsWindow::
01185 handle_keystroke(XKeyEvent &event) {
01186   _input_devices[0].set_pointer_in_window(event.x, event.y);
01187 
01188   if (_ic) {
01189     // First, get the keystroke as a wide-character sequence.
01190     static const int buffer_size = 256;
01191     wchar_t buffer[buffer_size];
01192     Status status;
01193     int len = XwcLookupString(_ic, &event, buffer, buffer_size, NULL,
01194                               &status);
01195     if (status == XBufferOverflow) {
01196       egldisplay_cat.error()
01197         << "Overflowed input buffer.\n";
01198     }
01199 
01200     // Now each of the returned wide characters represents a
01201     // keystroke.
01202     for (int i = 0; i < len; i++) {
01203       _input_devices[0].keystroke(buffer[i]);
01204     }
01205 
01206   } else {
01207     // Without an input context, just get the ascii keypress.
01208     ButtonHandle button = get_button(event, true);
01209     if (button.has_ascii_equivalent()) {
01210       _input_devices[0].keystroke(button.get_ascii_equivalent());
01211     }
01212   }
01213 }
01214 
01215 ////////////////////////////////////////////////////////////////////
01216 //     Function: eglGraphicsWindow::handle_keypress
01217 //       Access: Private
01218 //  Description: Generates a keypress corresponding to the indicated
01219 //               X KeyPress event.
01220 ////////////////////////////////////////////////////////////////////
01221 void eglGraphicsWindow::
01222 handle_keypress(XKeyEvent &event) {
01223   _input_devices[0].set_pointer_in_window(event.x, event.y);
01224 
01225   // Now get the raw unshifted button.
01226   ButtonHandle button = get_button(event, false);
01227   if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
01228     _input_devices[0].button_down(KeyboardButton::control());
01229   }
01230   if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
01231     _input_devices[0].button_down(KeyboardButton::shift());
01232   }
01233   if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
01234     _input_devices[0].button_down(KeyboardButton::alt());
01235   }
01236   if (button != ButtonHandle::none()) {
01237     _input_devices[0].button_down(button);
01238   }
01239 }
01240 
01241 ////////////////////////////////////////////////////////////////////
01242 //     Function: eglGraphicsWindow::handle_keyrelease
01243 //       Access: Private
01244 //  Description: Generates a keyrelease corresponding to the indicated
01245 //               X KeyRelease event.
01246 ////////////////////////////////////////////////////////////////////
01247 void eglGraphicsWindow::
01248 handle_keyrelease(XKeyEvent &event) {
01249   _input_devices[0].set_pointer_in_window(event.x, event.y);
01250 
01251   // Now get the raw unshifted button.
01252   ButtonHandle button = get_button(event, false);
01253   if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
01254     _input_devices[0].button_up(KeyboardButton::control());
01255   }
01256   if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
01257     _input_devices[0].button_up(KeyboardButton::shift());
01258   }
01259   if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
01260     _input_devices[0].button_up(KeyboardButton::alt());
01261   }
01262   if (button != ButtonHandle::none()) {
01263     _input_devices[0].button_up(button);
01264   }
01265 }
01266 
01267 ////////////////////////////////////////////////////////////////////
01268 //     Function: eglGraphicsWindow::get_button
01269 //       Access: Private
01270 //  Description: Returns the Panda ButtonHandle corresponding to the
01271 //               keyboard button indicated by the given key event.
01272 ////////////////////////////////////////////////////////////////////
01273 ButtonHandle eglGraphicsWindow::
01274 get_button(XKeyEvent &key_event, bool allow_shift) {
01275   KeySym key = XLookupKeysym(&key_event, 0);
01276 
01277   if ((key_event.state & Mod2Mask) != 0) {
01278     // Mod2Mask corresponds to NumLock being in effect.  In this case,
01279     // we want to get the alternate keysym associated with any keypad
01280     // keys.  Weird system.
01281     KeySym k2;
01282     ButtonHandle button;
01283     switch (key) {
01284     case XK_KP_Space:
01285     case XK_KP_Tab:
01286     case XK_KP_Enter:
01287     case XK_KP_F1:
01288     case XK_KP_F2:
01289     case XK_KP_F3:
01290     case XK_KP_F4:
01291     case XK_KP_Equal:
01292     case XK_KP_Multiply:
01293     case XK_KP_Add:
01294     case XK_KP_Separator:
01295     case XK_KP_Subtract:
01296     case XK_KP_Divide:
01297     case XK_KP_Left:
01298     case XK_KP_Up:
01299     case XK_KP_Right:
01300     case XK_KP_Down:
01301     case XK_KP_Begin:
01302     case XK_KP_Prior:
01303     case XK_KP_Next:
01304     case XK_KP_Home:
01305     case XK_KP_End:
01306     case XK_KP_Insert:
01307     case XK_KP_Delete:
01308     case XK_KP_0:
01309     case XK_KP_1:
01310     case XK_KP_2:
01311     case XK_KP_3:
01312     case XK_KP_4:
01313     case XK_KP_5:
01314     case XK_KP_6:
01315     case XK_KP_7:
01316     case XK_KP_8:
01317     case XK_KP_9:
01318       k2 = XLookupKeysym(&key_event, 1);
01319       button = map_button(k2);
01320       if (button != ButtonHandle::none()) {
01321         return button;
01322       }
01323       // If that didn't produce a button we know, just fall through
01324       // and handle the normal, un-numlocked key.
01325       break;
01326 
01327     default:
01328       break;
01329     }
01330   }
01331 
01332   if (allow_shift) {
01333     // If shift is held down, get the shifted keysym.
01334     if ((key_event.state & ShiftMask) != 0) {
01335       KeySym k2 = XLookupKeysym(&key_event, 1);
01336       ButtonHandle button = map_button(k2);
01337       if (button != ButtonHandle::none()) {
01338         return button;
01339       }
01340     }
01341 
01342     // If caps lock is down, shift lowercase letters to uppercase.  We
01343     // can do this in just the ASCII set, because we handle
01344     // international keyboards elsewhere (via an input context).
01345     if ((key_event.state & (ShiftMask | LockMask)) != 0) {
01346       if (key >= XK_a and key <= XK_z) {
01347         key += (XK_A - XK_a);
01348       }
01349     }
01350   }
01351 
01352   return map_button(key);
01353 }
01354 
01355 ////////////////////////////////////////////////////////////////////
01356 //     Function: eglGraphicsWindow::map_button
01357 //       Access: Private
01358 //  Description: Maps from a single X keysym to Panda's ButtonHandle.
01359 //               Called by get_button(), above.
01360 ////////////////////////////////////////////////////////////////////
01361 ButtonHandle eglGraphicsWindow::
01362 map_button(KeySym key) {
01363   switch (key) {
01364   case XK_BackSpace:
01365     return KeyboardButton::backspace();
01366   case XK_Tab:
01367   case XK_KP_Tab:
01368     return KeyboardButton::tab();
01369   case XK_Return:
01370   case XK_KP_Enter:
01371     return KeyboardButton::enter();
01372   case XK_Escape:
01373     return KeyboardButton::escape();
01374   case XK_KP_Space:
01375   case XK_space:
01376     return KeyboardButton::space();
01377   case XK_exclam:
01378     return KeyboardButton::ascii_key('!');
01379   case XK_quotedbl:
01380     return KeyboardButton::ascii_key('"');
01381   case XK_numbersign:
01382     return KeyboardButton::ascii_key('#');
01383   case XK_dollar:
01384     return KeyboardButton::ascii_key('$');
01385   case XK_percent:
01386     return KeyboardButton::ascii_key('%');
01387   case XK_ampersand:
01388     return KeyboardButton::ascii_key('&');
01389   case XK_apostrophe: // == XK_quoteright
01390     return KeyboardButton::ascii_key('\'');
01391   case XK_parenleft:
01392     return KeyboardButton::ascii_key('(');
01393   case XK_parenright:
01394     return KeyboardButton::ascii_key(')');
01395   case XK_asterisk:
01396   case XK_KP_Multiply:
01397     return KeyboardButton::ascii_key('*');
01398   case XK_plus:
01399   case XK_KP_Add:
01400     return KeyboardButton::ascii_key('+');
01401   case XK_comma:
01402   case XK_KP_Separator:
01403     return KeyboardButton::ascii_key(',');
01404   case XK_minus:
01405   case XK_KP_Subtract:
01406     return KeyboardButton::ascii_key('-');
01407   case XK_period:
01408   case XK_KP_Decimal:
01409     return KeyboardButton::ascii_key('.');
01410   case XK_slash:
01411   case XK_KP_Divide:
01412     return KeyboardButton::ascii_key('/');
01413   case XK_0:
01414   case XK_KP_0:
01415     return KeyboardButton::ascii_key('0');
01416   case XK_1:
01417   case XK_KP_1:
01418     return KeyboardButton::ascii_key('1');
01419   case XK_2:
01420   case XK_KP_2:
01421     return KeyboardButton::ascii_key('2');
01422   case XK_3:
01423   case XK_KP_3:
01424     return KeyboardButton::ascii_key('3');
01425   case XK_4:
01426   case XK_KP_4:
01427     return KeyboardButton::ascii_key('4');
01428   case XK_5:
01429   case XK_KP_5:
01430     return KeyboardButton::ascii_key('5');
01431   case XK_6:
01432   case XK_KP_6:
01433     return KeyboardButton::ascii_key('6');
01434   case XK_7:
01435   case XK_KP_7:
01436     return KeyboardButton::ascii_key('7');
01437   case XK_8:
01438   case XK_KP_8:
01439     return KeyboardButton::ascii_key('8');
01440   case XK_9:
01441   case XK_KP_9:
01442     return KeyboardButton::ascii_key('9');
01443   case XK_colon:
01444     return KeyboardButton::ascii_key(':');
01445   case XK_semicolon:
01446     return KeyboardButton::ascii_key(';');
01447   case XK_less:
01448     return KeyboardButton::ascii_key('<');
01449   case XK_equal:
01450   case XK_KP_Equal:
01451     return KeyboardButton::ascii_key('=');
01452   case XK_greater:
01453     return KeyboardButton::ascii_key('>');
01454   case XK_question:
01455     return KeyboardButton::ascii_key('?');
01456   case XK_at:
01457     return KeyboardButton::ascii_key('@');
01458   case XK_A:
01459     return KeyboardButton::ascii_key('A');
01460   case XK_B:
01461     return KeyboardButton::ascii_key('B');
01462   case XK_C:
01463     return KeyboardButton::ascii_key('C');
01464   case XK_D:
01465     return KeyboardButton::ascii_key('D');
01466   case XK_E:
01467     return KeyboardButton::ascii_key('E');
01468   case XK_F:
01469     return KeyboardButton::ascii_key('F');
01470   case XK_G:
01471     return KeyboardButton::ascii_key('G');
01472   case XK_H:
01473     return KeyboardButton::ascii_key('H');
01474   case XK_I:
01475     return KeyboardButton::ascii_key('I');
01476   case XK_J:
01477     return KeyboardButton::ascii_key('J');
01478   case XK_K:
01479     return KeyboardButton::ascii_key('K');
01480   case XK_L:
01481     return KeyboardButton::ascii_key('L');
01482   case XK_M:
01483     return KeyboardButton::ascii_key('M');
01484   case XK_N:
01485     return KeyboardButton::ascii_key('N');
01486   case XK_O:
01487     return KeyboardButton::ascii_key('O');
01488   case XK_P:
01489     return KeyboardButton::ascii_key('P');
01490   case XK_Q:
01491     return KeyboardButton::ascii_key('Q');
01492   case XK_R:
01493     return KeyboardButton::ascii_key('R');
01494   case XK_S:
01495     return KeyboardButton::ascii_key('S');
01496   case XK_T:
01497     return KeyboardButton::ascii_key('T');
01498   case XK_U:
01499     return KeyboardButton::ascii_key('U');
01500   case XK_V:
01501     return KeyboardButton::ascii_key('V');
01502   case XK_W:
01503     return KeyboardButton::ascii_key('W');
01504   case XK_X:
01505     return KeyboardButton::ascii_key('X');
01506   case XK_Y:
01507     return KeyboardButton::ascii_key('Y');
01508   case XK_Z:
01509     return KeyboardButton::ascii_key('Z');
01510   case XK_bracketleft:
01511     return KeyboardButton::ascii_key('[');
01512   case XK_backslash:
01513     return KeyboardButton::ascii_key('\\');
01514   case XK_bracketright:
01515     return KeyboardButton::ascii_key(']');
01516   case XK_asciicircum:
01517     return KeyboardButton::ascii_key('^');
01518   case XK_underscore:
01519     return KeyboardButton::ascii_key('_');
01520   case XK_grave: // == XK_quoteleft
01521     return KeyboardButton::ascii_key('`');
01522   case XK_a:
01523     return KeyboardButton::ascii_key('a');
01524   case XK_b:
01525     return KeyboardButton::ascii_key('b');
01526   case XK_c:
01527     return KeyboardButton::ascii_key('c');
01528   case XK_d:
01529     return KeyboardButton::ascii_key('d');
01530   case XK_e:
01531     return KeyboardButton::ascii_key('e');
01532   case XK_f:
01533     return KeyboardButton::ascii_key('f');
01534   case XK_g:
01535     return KeyboardButton::ascii_key('g');
01536   case XK_h:
01537     return KeyboardButton::ascii_key('h');
01538   case XK_i:
01539     return KeyboardButton::ascii_key('i');
01540   case XK_j:
01541     return KeyboardButton::ascii_key('j');
01542   case XK_k:
01543     return KeyboardButton::ascii_key('k');
01544   case XK_l:
01545     return KeyboardButton::ascii_key('l');
01546   case XK_m:
01547     return KeyboardButton::ascii_key('m');
01548   case XK_n:
01549     return KeyboardButton::ascii_key('n');
01550   case XK_o:
01551     return KeyboardButton::ascii_key('o');
01552   case XK_p:
01553     return KeyboardButton::ascii_key('p');
01554   case XK_q:
01555     return KeyboardButton::ascii_key('q');
01556   case XK_r:
01557     return KeyboardButton::ascii_key('r');
01558   case XK_s:
01559     return KeyboardButton::ascii_key('s');
01560   case XK_t:
01561     return KeyboardButton::ascii_key('t');
01562   case XK_u:
01563     return KeyboardButton::ascii_key('u');
01564   case XK_v:
01565     return KeyboardButton::ascii_key('v');
01566   case XK_w:
01567     return KeyboardButton::ascii_key('w');
01568   case XK_x:
01569     return KeyboardButton::ascii_key('x');
01570   case XK_y:
01571     return KeyboardButton::ascii_key('y');
01572   case XK_z:
01573     return KeyboardButton::ascii_key('z');
01574   case XK_braceleft:
01575     return KeyboardButton::ascii_key('{');
01576   case XK_bar:
01577     return KeyboardButton::ascii_key('|');
01578   case XK_braceright:
01579     return KeyboardButton::ascii_key('}');
01580   case XK_asciitilde:
01581     return KeyboardButton::ascii_key('~');
01582   case XK_F1:
01583   case XK_KP_F1:
01584     return KeyboardButton::f1();
01585   case XK_F2:
01586   case XK_KP_F2:
01587     return KeyboardButton::f2();
01588   case XK_F3:
01589   case XK_KP_F3:
01590     return KeyboardButton::f3();
01591   case XK_F4:
01592   case XK_KP_F4:
01593     return KeyboardButton::f4();
01594   case XK_F5:
01595     return KeyboardButton::f5();
01596   case XK_F6:
01597     return KeyboardButton::f6();
01598   case XK_F7:
01599     return KeyboardButton::f7();
01600   case XK_F8:
01601     return KeyboardButton::f8();
01602   case XK_F9:
01603     return KeyboardButton::f9();
01604   case XK_F10:
01605     return KeyboardButton::f10();
01606   case XK_F11:
01607     return KeyboardButton::f11();
01608   case XK_F12:
01609     return KeyboardButton::f12();
01610   case XK_KP_Left:
01611   case XK_Left:
01612     return KeyboardButton::left();
01613   case XK_KP_Up:
01614   case XK_Up:
01615     return KeyboardButton::up();
01616   case XK_KP_Right:
01617   case XK_Right:
01618     return KeyboardButton::right();
01619   case XK_KP_Down:
01620   case XK_Down:
01621     return KeyboardButton::down();
01622   case XK_KP_Prior:
01623   case XK_Prior:
01624     return KeyboardButton::page_up();
01625   case XK_KP_Next:
01626   case XK_Next:
01627     return KeyboardButton::page_down();
01628   case XK_KP_Home:
01629   case XK_Home:
01630     return KeyboardButton::home();
01631   case XK_KP_End:
01632   case XK_End:
01633     return KeyboardButton::end();
01634   case XK_KP_Insert:
01635   case XK_Insert:
01636     return KeyboardButton::insert();
01637   case XK_KP_Delete:
01638   case XK_Delete:
01639     return KeyboardButton::del();
01640   case XK_Num_Lock:
01641     return KeyboardButton::num_lock();
01642   case XK_Scroll_Lock:
01643     return KeyboardButton::scroll_lock();
01644   case XK_Print:
01645     return KeyboardButton::print_screen();
01646   case XK_Pause:
01647     return KeyboardButton::pause();
01648   case XK_Shift_L:
01649     return KeyboardButton::lshift();
01650   case XK_Shift_R:
01651     return KeyboardButton::rshift();
01652   case XK_Control_L:
01653     return KeyboardButton::lcontrol();
01654   case XK_Control_R:
01655     return KeyboardButton::rcontrol();
01656   case XK_Alt_L:
01657     return KeyboardButton::lalt();
01658   case XK_Alt_R:
01659     return KeyboardButton::ralt();
01660   case XK_Meta_L:
01661   case XK_Meta_R:
01662     return KeyboardButton::meta();
01663   case XK_Caps_Lock:
01664     return KeyboardButton::caps_lock();
01665   case XK_Shift_Lock:
01666     return KeyboardButton::shift_lock();
01667   }
01668 
01669   return ButtonHandle::none();
01670 }
01671 
01672 ////////////////////////////////////////////////////////////////////
01673 //     Function: eglGraphicsWindow::get_mouse_button
01674 //       Access: Private
01675 //  Description: Returns the Panda ButtonHandle corresponding to the
01676 //               mouse button indicated by the given button event.
01677 ////////////////////////////////////////////////////////////////////
01678 ButtonHandle eglGraphicsWindow::
01679 get_mouse_button(XButtonEvent &button_event) {
01680   int index = button_event.button;
01681   if (index == x_wheel_up_button) {
01682     return MouseButton::wheel_up();
01683   } else if (index == x_wheel_down_button) {
01684     return MouseButton::wheel_down();
01685   } else if (index == x_wheel_left_button) {
01686     return MouseButton::wheel_left();
01687   } else if (index == x_wheel_right_button) {
01688     return MouseButton::wheel_right();
01689   } else {
01690     return MouseButton::button(index - 1);
01691   }
01692 }
01693 ////////////////////////////////////////////////////////////////////
01694 //     Function: eglGraphicsWindow::check_event
01695 //       Access: Private, Static
01696 //  Description: This function is used as a predicate to
01697 //               XCheckIfEvent() to determine if the indicated queued
01698 //               X event is relevant and should be returned to this
01699 //               window.
01700 ////////////////////////////////////////////////////////////////////
01701 Bool eglGraphicsWindow::
01702 check_event(Display *display, XEvent *event, char *arg) {
01703   const eglGraphicsWindow *self = (eglGraphicsWindow *)arg;
01704 
01705   // We accept any event that is sent to our window.
01706   return (event->xany.window == self->_xwindow);
01707 }
 All Classes Functions Variables Enumerations