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