Panda3D

x11GraphicsWindow.cxx

00001 // Filename: x11GraphicsWindow.cxx
00002 // Created by:  rdb (07Jul09)
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 "x11GraphicsWindow.h"
00016 #include "config_x11display.h"
00017 #include "x11GraphicsPipe.h"
00018 
00019 #include "graphicsPipe.h"
00020 #include "keyboardButton.h"
00021 #include "mouseButton.h"
00022 #include "clockObject.h"
00023 #include "pStatTimer.h"
00024 #include "textEncoder.h"
00025 #include "throw_event.h"
00026 #include "lightReMutexHolder.h"
00027 #include "nativeWindowHandle.h"
00028 #include "virtualFileSystem.h"
00029 #include "get_x11.h"
00030 
00031 #include <errno.h>
00032 #include <fcntl.h>
00033 #include <sys/time.h>
00034 
00035 #ifdef HAVE_LINUX_INPUT_H
00036 #include <linux/input.h>
00037 #endif
00038 
00039 #ifdef HAVE_XCURSOR
00040 static int xcursor_read(XcursorFile *file, unsigned char *buf, int len) {
00041   istream* str = (istream*) file->closure;
00042   str->read((char*) buf, len);
00043   return str->gcount();
00044 }
00045 
00046 static int xcursor_write(XcursorFile *file, unsigned char *buf, int len) {
00047   // Not implemented, we don't need it.
00048   nassertr_always(false, 0);
00049 }
00050 
00051 static int xcursor_seek(XcursorFile *file, long offset, int whence) {
00052   istream* str = (istream*) file->closure;
00053   switch (whence) {
00054   case SEEK_SET:
00055     str->seekg(offset, istream::beg);
00056     break;
00057   case SEEK_CUR:
00058     str->seekg(offset, istream::cur);
00059     break;
00060   case SEEK_END:
00061     str->seekg(offset, istream::end);
00062   }
00063   
00064   return str->tellg();
00065 }
00066 #endif
00067 
00068 TypeHandle x11GraphicsWindow::_type_handle;
00069 
00070 #define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
00071 
00072 ////////////////////////////////////////////////////////////////////
00073 //     Function: x11GraphicsWindow::Constructor
00074 //       Access: Public
00075 //  Description:
00076 ////////////////////////////////////////////////////////////////////
00077 x11GraphicsWindow::
00078 x11GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, 
00079                   const string &name,
00080                   const FrameBufferProperties &fb_prop,
00081                   const WindowProperties &win_prop,
00082                   int flags,
00083                   GraphicsStateGuardian *gsg,
00084                   GraphicsOutput *host) :
00085   GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
00086 {
00087   x11GraphicsPipe *x11_pipe;
00088   DCAST_INTO_V(x11_pipe, _pipe);
00089   _display = x11_pipe->get_display();
00090   _screen = x11_pipe->get_screen();
00091   _xwindow = (X11_Window)NULL;
00092   _ic = (XIC)NULL;
00093   _visual_info = NULL;
00094 #ifdef HAVE_XRANDR
00095   _orig_size_id = -1;
00096 #endif
00097   _awaiting_configure = false;
00098   _dga_mouse_enabled = false;
00099   _wm_delete_window = x11_pipe->_wm_delete_window;
00100   _net_wm_window_type = x11_pipe->_net_wm_window_type;
00101   _net_wm_window_type_splash = x11_pipe->_net_wm_window_type_splash;
00102   _net_wm_window_type_fullscreen = x11_pipe->_net_wm_window_type_fullscreen;
00103   _net_wm_state = x11_pipe->_net_wm_state;
00104   _net_wm_state_fullscreen = x11_pipe->_net_wm_state_fullscreen;
00105   _net_wm_state_above = x11_pipe->_net_wm_state_above;
00106   _net_wm_state_below = x11_pipe->_net_wm_state_below;
00107   _net_wm_state_add = x11_pipe->_net_wm_state_add;
00108   _net_wm_state_remove = x11_pipe->_net_wm_state_remove;
00109 
00110   GraphicsWindowInputDevice device =
00111     GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
00112   add_input_device(device);
00113 }
00114 
00115 ////////////////////////////////////////////////////////////////////
00116 //     Function: x11GraphicsWindow::Destructor
00117 //       Access: Public, Virtual
00118 //  Description:
00119 ////////////////////////////////////////////////////////////////////
00120 x11GraphicsWindow::
00121 ~x11GraphicsWindow() {
00122   pmap<Filename, X11_Cursor>::iterator it;
00123 
00124   for (it = _cursor_filenames.begin(); it != _cursor_filenames.end(); it++) {
00125     XFreeCursor(_display, it->second);
00126   }
00127 }
00128 
00129 ////////////////////////////////////////////////////////////////////
00130 //     Function: x11GraphicsWindow::move_pointer
00131 //       Access: Published, Virtual
00132 //  Description: Forces the pointer to the indicated position within
00133 //               the window, if possible.  
00134 //
00135 //               Returns true if successful, false on failure.  This
00136 //               may fail if the mouse is not currently within the
00137 //               window, or if the API doesn't support this operation.
00138 ////////////////////////////////////////////////////////////////////
00139 bool x11GraphicsWindow::
00140 move_pointer(int device, int x, int y) {
00141   // Note: this is not thread-safe; it should be called only from App.
00142   // Probably not an issue.
00143   if (device == 0) {
00144     // Move the system mouse pointer.
00145     if (!_properties.get_foreground() ||
00146         !_input_devices[0].get_pointer().get_in_window()) {
00147       // If the window doesn't have input focus, or the mouse isn't
00148       // currently within the window, forget it.
00149       return false;
00150     }
00151 
00152     const MouseData &md = _input_devices[0].get_pointer();
00153     if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) {
00154       if (!_dga_mouse_enabled) {
00155         XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y);
00156       }
00157       _input_devices[0].set_pointer_in_window(x, y);
00158     }
00159     return true;
00160   } else {
00161     // Move a raw mouse.
00162     if ((device < 1)||(device >= _input_devices.size())) {
00163       return false;
00164     }
00165     _input_devices[device].set_pointer_in_window(x, y);
00166     return true;
00167   }
00168 }
00169 
00170 
00171 ////////////////////////////////////////////////////////////////////
00172 //     Function: x11GraphicsWindow::begin_frame
00173 //       Access: Public, Virtual
00174 //  Description: This function will be called within the draw thread
00175 //               before beginning rendering for a given frame.  It
00176 //               should do whatever setup is required, and return true
00177 //               if the frame should be rendered, or false if it
00178 //               should be skipped.
00179 ////////////////////////////////////////////////////////////////////
00180 bool x11GraphicsWindow::
00181 begin_frame(FrameMode mode, Thread *current_thread) {
00182   PStatTimer timer(_make_current_pcollector, current_thread);
00183 
00184   begin_frame_spam(mode);
00185   if (_gsg == (GraphicsStateGuardian *)NULL) {
00186     return false;
00187   }
00188   if (_awaiting_configure) {
00189     // Don't attempt to draw while we have just reconfigured the
00190     // window and we haven't got the notification back yet.
00191     return false;
00192   }
00193   
00194   // Reset the GSG state if this is the first time it has been used.
00195   // (We can't just call reset() when we construct the GSG, because
00196   // reset() requires having a current context.)
00197   _gsg->reset_if_new();
00198   
00199   if (mode == FM_render) {
00200     // begin_render_texture();
00201     clear_cube_map_selection();
00202   }
00203   
00204   _gsg->set_current_properties(&get_fb_properties());
00205   return _gsg->begin_frame(current_thread);
00206 }
00207 
00208 ////////////////////////////////////////////////////////////////////
00209 //     Function: x11GraphicsWindow::end_frame
00210 //       Access: Public, Virtual
00211 //  Description: This function will be called within the draw thread
00212 //               after rendering is completed for a given frame.  It
00213 //               should do whatever finalization is required.
00214 ////////////////////////////////////////////////////////////////////
00215 void x11GraphicsWindow::
00216 end_frame(FrameMode mode, Thread *current_thread) {
00217   end_frame_spam(mode);
00218   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
00219 
00220   if (mode == FM_render) {
00221     // end_render_texture();
00222     copy_to_textures();
00223   }
00224 
00225   _gsg->end_frame(current_thread);
00226 
00227   if (mode == FM_render) {
00228     trigger_flip();
00229     clear_cube_map_selection();
00230   }
00231 }
00232 
00233 ////////////////////////////////////////////////////////////////////
00234 //     Function: x11GraphicsWindow::process_events
00235 //       Access: Public, Virtual
00236 //  Description: Do whatever processing is necessary to ensure that
00237 //               the window responds to user events.  Also, honor any
00238 //               requests recently made via request_properties()
00239 //
00240 //               This function is called only within the window
00241 //               thread.
00242 ////////////////////////////////////////////////////////////////////
00243 void x11GraphicsWindow::
00244 process_events() {
00245   LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
00246 
00247   GraphicsWindow::process_events();
00248 
00249   if (_xwindow == (X11_Window)0) {
00250     return;
00251   }
00252   
00253   poll_raw_mice();
00254   
00255   XEvent event;
00256   XKeyEvent keyrelease_event;
00257   bool got_keyrelease_event = false;
00258 
00259   while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
00260     if (XFilterEvent(&event, None)) {
00261       continue;
00262     }
00263 
00264     if (got_keyrelease_event) {
00265       // If a keyrelease event is immediately followed by a matching
00266       // keypress event, that's just key repeat and we should treat
00267       // the two events accordingly.  It would be nice if X provided a
00268       // way to differentiate between keyrepeat and explicit
00269       // keypresses more generally.
00270       got_keyrelease_event = false;
00271 
00272       if (event.type == KeyPress &&
00273           event.xkey.keycode == keyrelease_event.keycode &&
00274           (event.xkey.time - keyrelease_event.time <= 1)) {
00275         // In particular, we only generate down messages for the
00276         // repeated keys, not down-and-up messages.
00277         handle_keystroke(event.xkey);
00278 
00279         // We thought about not generating the keypress event, but we
00280         // need that repeat for backspace.  Rethink later.
00281         handle_keypress(event.xkey);
00282         continue;
00283 
00284       } else {
00285         // This keyrelease event is not immediately followed by a
00286         // matching keypress event, so it's a genuine release.
00287         handle_keyrelease(keyrelease_event);
00288       }
00289     }
00290 
00291     WindowProperties properties;
00292     ButtonHandle button;
00293 
00294     switch (event.type) {
00295     case ReparentNotify:
00296       break;
00297 
00298     case ConfigureNotify:
00299       _awaiting_configure = false;
00300 
00301       // Is this the inner corner or the outer corner?  The Xlib docs
00302       // say it should be the outer corner, but it appears to be the
00303       // inner corner on my own implementation, which is inconsistent
00304       // with XConfigureWindow.  (Panda really wants to work with the
00305       // inner corner, anyway, but that means we need to fix
00306       // XConfigureWindow too.)
00307       properties.set_origin(event.xconfigure.x, event.xconfigure.y);
00308 
00309       if (_properties.get_fixed_size()) {
00310         // If the window properties indicate a fixed size only, undo
00311         // any attempt by the user to change them.  In X, there
00312         // doesn't appear to be a way to universally disallow this
00313         // directly (although we do set the min_size and max_size to
00314         // the same value, which seems to work for most window
00315         // managers.)
00316         WindowProperties current_props = get_properties();
00317         if (event.xconfigure.width != current_props.get_x_size() ||
00318             event.xconfigure.height != current_props.get_y_size()) {
00319           XWindowChanges changes;
00320           changes.width = current_props.get_x_size();
00321           changes.height = current_props.get_y_size();
00322           int value_mask = (CWWidth | CWHeight);
00323           XConfigureWindow(_display, _xwindow, value_mask, &changes);
00324         }
00325 
00326       } else {
00327         // A normal window may be resized by the user at will.
00328         properties.set_size(event.xconfigure.width, event.xconfigure.height);
00329       }
00330       system_changed_properties(properties);
00331       break;
00332 
00333     case ButtonPress:
00334       // This refers to the mouse buttons.
00335       button = get_mouse_button(event.xbutton);
00336       if (!_dga_mouse_enabled) {
00337         _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
00338       }
00339       _input_devices[0].button_down(button);
00340       break;
00341       
00342     case ButtonRelease:
00343       button = get_mouse_button(event.xbutton);
00344       if (!_dga_mouse_enabled) {
00345         _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
00346       }
00347       _input_devices[0].button_up(button);
00348       break;
00349 
00350     case MotionNotify:
00351       if (_dga_mouse_enabled) {
00352         const MouseData &md = _input_devices[0].get_raw_pointer();
00353         _input_devices[0].set_pointer_in_window(md.get_x() + event.xmotion.x_root, md.get_y() + event.xmotion.y_root);
00354       } else {
00355         _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
00356       }
00357       break;
00358 
00359     case KeyPress:
00360       handle_keystroke(event.xkey);
00361       handle_keypress(event.xkey);
00362       break;
00363 
00364     case KeyRelease:
00365       // The KeyRelease can't be processed immediately, because we
00366       // have to check first if it's immediately followed by a
00367       // matching KeyPress event.
00368       keyrelease_event = event.xkey;
00369       got_keyrelease_event = true;
00370       break;
00371 
00372     case EnterNotify:
00373       if (_dga_mouse_enabled) {
00374         const MouseData &md = _input_devices[0].get_raw_pointer();
00375         _input_devices[0].set_pointer_in_window(md.get_x(), md.get_y());
00376       } else {
00377         _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
00378       }
00379       break;
00380 
00381     case LeaveNotify:
00382       _input_devices[0].set_pointer_out_of_window();
00383       break;
00384 
00385     case FocusIn:
00386       properties.set_foreground(true);
00387       system_changed_properties(properties);
00388       break;
00389 
00390     case FocusOut:
00391       _input_devices[0].focus_lost();
00392       properties.set_foreground(false);
00393       system_changed_properties(properties);
00394       break;
00395 
00396     case UnmapNotify:
00397       properties.set_minimized(true);
00398       system_changed_properties(properties);
00399       break;
00400 
00401     case MapNotify:
00402       properties.set_minimized(false);
00403       system_changed_properties(properties);
00404 
00405       // Auto-focus the window when it is mapped.
00406       XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
00407       break;
00408 
00409     case ClientMessage:
00410       if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
00411         // This is a message from the window manager indicating that
00412         // the user has requested to close the window.
00413         string close_request_event = get_close_request_event();
00414         if (!close_request_event.empty()) {
00415           // In this case, the app has indicated a desire to intercept
00416           // the request and process it directly.
00417           throw_event(close_request_event);
00418 
00419         } else {
00420           // In this case, the default case, the app does not intend
00421           // to service the request, so we do by closing the window.
00422 
00423           // TODO: don't release the gsg in the window thread.
00424           close_window();
00425           properties.set_open(false);
00426           system_changed_properties(properties);
00427         }
00428       }
00429       break;
00430 
00431     case DestroyNotify:
00432       // Apparently, we never get a DestroyNotify on a toplevel
00433       // window.  Instead, we rely on hints from the window manager
00434       // (see above).
00435       x11display_cat.info()
00436         << "DestroyNotify\n";
00437       break;
00438 
00439     default:
00440       x11display_cat.warning()
00441         << "unhandled X event type " << event.type << "\n";
00442     }
00443   }
00444 
00445   if (got_keyrelease_event) {
00446     // This keyrelease event is not immediately followed by a
00447     // matching keypress event, so it's a genuine release.
00448     handle_keyrelease(keyrelease_event);
00449   }
00450 }
00451 
00452 ////////////////////////////////////////////////////////////////////
00453 //     Function: x11GraphicsWindow::set_properties_now
00454 //       Access: Public, Virtual
00455 //  Description: Applies the requested set of properties to the
00456 //               window, if possible, for instance to request a change
00457 //               in size or minimization status.
00458 //
00459 //               The window properties are applied immediately, rather
00460 //               than waiting until the next frame.  This implies that
00461 //               this method may *only* be called from within the
00462 //               window thread.
00463 //
00464 //               The return value is true if the properties are set,
00465 //               false if they are ignored.  This is mainly useful for
00466 //               derived classes to implement extensions to this
00467 //               function.
00468 ////////////////////////////////////////////////////////////////////
00469 void x11GraphicsWindow::
00470 set_properties_now(WindowProperties &properties) {
00471   if (_pipe == (GraphicsPipe *)NULL) {
00472     // If the pipe is null, we're probably closing down.
00473     GraphicsWindow::set_properties_now(properties);
00474     return;
00475   }
00476 
00477   x11GraphicsPipe *x11_pipe;
00478   DCAST_INTO_V(x11_pipe, _pipe);
00479   
00480   // Handle fullscreen mode.
00481   if (properties.has_fullscreen()) {
00482     if (properties.get_fullscreen()) {
00483 #ifdef HAVE_XRANDR
00484       XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, x11_pipe->get_root());
00485       if (_orig_size_id == (SizeID) -1) {
00486         _orig_size_id = XRRConfigCurrentConfiguration(conf, &_orig_rotation);
00487       }
00488       int num_sizes, reqsizex, reqsizey, new_size_id = -1;
00489       if (properties.has_size()) {
00490         reqsizex = properties.get_x_size();
00491         reqsizey = properties.get_y_size();
00492       } else {
00493         reqsizex = _properties.get_x_size();
00494         reqsizey = _properties.get_y_size();
00495       }
00496       XRRScreenSize *xrrs;
00497       xrrs = XRRSizes(_display, 0, &num_sizes);
00498       for (int i = 0; i < num_sizes; ++i) {
00499         if (xrrs[i].width == properties.get_x_size() && 
00500             xrrs[i].height == properties.get_y_size()) {
00501           new_size_id = i;
00502         }
00503       }
00504       if (new_size_id == -1) {
00505         x11display_cat.error() 
00506           << "Videocard has no supported display resolutions at specified res ("
00507           << reqsizex << " x " << reqsizey <<")\n";
00508         _orig_size_id = -1;
00509       } else {
00510         if (new_size_id != _orig_size_id) {
00511           XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime);
00512         } else {
00513           _orig_size_id = -1;
00514         }
00515       }
00516 #else
00517       // If we don't have Xrandr support, we fake the fullscreen
00518       // support by setting the window size to the desktop size.
00519       properties.set_size(x11_pipe->get_display_width(),
00520                           x11_pipe->get_display_height());
00521 #endif
00522     } else {
00523 #ifdef HAVE_XRANDR
00524       // Change the resolution back to what it was.
00525       // Don't remove the SizeID typecast!
00526       if (_orig_size_id != (SizeID) -1) {
00527         XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, x11_pipe->get_root());
00528         XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), _orig_size_id, _orig_rotation, CurrentTime);
00529         _orig_size_id = -1;
00530       }
00531 #endif
00532       // Set the origin back to what it was
00533       if (!properties.has_origin() && _properties.has_origin()) {
00534         properties.set_origin(_properties.get_x_origin(), _properties.get_y_origin());
00535       }
00536     }
00537   }
00538   
00539   if (properties.has_origin()) {
00540     // A coordinate of -2 means to center the window on screen.
00541     if (properties.get_x_origin() == -2 || properties.get_y_origin() == -2) {
00542       int x_origin = properties.get_x_origin();
00543       int y_origin = properties.get_y_origin();
00544       if (properties.has_size()) {
00545         if (x_origin == -2) {
00546           x_origin = 0.5 * (x11_pipe->get_display_width() - properties.get_x_size());
00547         }
00548         if (y_origin == -2) {
00549           y_origin = 0.5 * (x11_pipe->get_display_height() - properties.get_y_size());
00550         }
00551       } else {
00552         if (x_origin == -2) {
00553           x_origin = 0.5 * (x11_pipe->get_display_width() - _properties.get_x_size());
00554         }
00555         if (y_origin == -2) {
00556           y_origin = 0.5 * (x11_pipe->get_display_height() - _properties.get_y_size());
00557         }
00558       }
00559       properties.set_origin(x_origin, y_origin);
00560     }
00561   }
00562   
00563   GraphicsWindow::set_properties_now(properties);
00564   if (!properties.is_any_specified()) {
00565     // The base class has already handled this case.
00566     return;
00567   }
00568 
00569   // The window is already open; we are limited to what we can change
00570   // on the fly.
00571 
00572   // We'll pass some property requests on as a window manager hint.
00573   WindowProperties wm_properties = _properties;
00574   wm_properties.add_properties(properties);
00575 
00576   // The window title may be changed by issuing another hint request.
00577   // Assume this will be honored.
00578   if (properties.has_title()) {
00579     _properties.set_title(properties.get_title());
00580     properties.clear_title();
00581   }
00582   
00583   // Same for fullscreen.
00584   if (properties.has_fullscreen()) {
00585     _properties.set_fullscreen(properties.get_fullscreen());
00586     properties.clear_fullscreen();
00587   }
00588   
00589   // The size and position of an already-open window are changed via
00590   // explicit X calls.  These may still get intercepted by the window
00591   // manager.  Rather than changing _properties immediately, we'll
00592   // wait for the ConfigureNotify message to come back.
00593   XWindowChanges changes;
00594   int value_mask = 0;
00595   
00596   if (_properties.get_fullscreen()) {
00597     changes.x = 0;
00598     changes.y = 0;
00599     value_mask |= CWX | CWY;
00600     properties.clear_origin();
00601   } else if (properties.has_origin()) {
00602     changes.x = properties.get_x_origin();
00603     changes.y = properties.get_y_origin();
00604     if (changes.x != -1) value_mask |= CWX;
00605     if (changes.y != -1) value_mask |= CWY;
00606     properties.clear_origin();
00607   }
00608   
00609   if (properties.has_size()) {
00610     changes.width = properties.get_x_size();
00611     changes.height = properties.get_y_size();
00612     value_mask |= (CWWidth | CWHeight);
00613     properties.clear_size();
00614   }
00615   
00616   if (properties.has_z_order()) {
00617     // We'll send the classic stacking request through the standard
00618     // interface, for users of primitive window managers; but we'll
00619     // also send it as a window manager hint, for users of modern
00620     // window managers.
00621     _properties.set_z_order(properties.get_z_order());
00622     switch (properties.get_z_order()) {
00623     case WindowProperties::Z_bottom:
00624       changes.stack_mode = Below;
00625       break;
00626 
00627     case WindowProperties::Z_normal:
00628       changes.stack_mode = TopIf;
00629       break;
00630 
00631     case WindowProperties::Z_top:
00632       changes.stack_mode = Above;
00633       break;
00634     }
00635 
00636     value_mask |= (CWStackMode);
00637     properties.clear_z_order();
00638   }
00639 
00640   if (value_mask != 0) {
00641     XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
00642 
00643     // Don't draw anything until this is done reconfiguring.
00644     _awaiting_configure = true;
00645   }
00646 
00647   // We hide the cursor by setting it to an invisible pixmap.
00648   // We can also load a custom cursor from a file.
00649   if (properties.has_cursor_hidden() || properties.has_cursor_filename()) {
00650     if (properties.has_cursor_hidden()) {
00651       _properties.set_cursor_hidden(properties.get_cursor_hidden());
00652       properties.clear_cursor_hidden();
00653     }
00654     Filename cursor_filename;
00655     if (properties.has_cursor_filename()) {
00656       cursor_filename = properties.get_cursor_filename();
00657       _properties.set_cursor_filename(cursor_filename);
00658       properties.clear_cursor_filename();
00659     }
00660     Filename filename = properties.get_cursor_filename();
00661     _properties.set_cursor_filename(filename);
00662 
00663     if (_properties.get_cursor_hidden()) {
00664       XDefineCursor(_display, _xwindow, x11_pipe->get_hidden_cursor());
00665 
00666     } else if (!cursor_filename.empty()) {
00667       // Note that if the cursor fails to load, cursor will be None
00668       X11_Cursor cursor = get_cursor(cursor_filename);
00669       XDefineCursor(_display, _xwindow, cursor);
00670 
00671     } else {
00672       XDefineCursor(_display, _xwindow, None);
00673     }
00674   }
00675 
00676   if (properties.has_foreground()) {
00677     if (properties.get_foreground()) {
00678       XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
00679     } else {
00680       XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
00681     }
00682     properties.clear_foreground();
00683   }
00684 
00685   set_wm_properties(wm_properties, true);
00686 }
00687 
00688 ////////////////////////////////////////////////////////////////////
00689 //     Function: x11GraphicsWindow::mouse_mode_absolute
00690 //       Access: Private, Virtual
00691 //  Description: Overridden from GraphicsWindow.
00692 ////////////////////////////////////////////////////////////////////
00693 void x11GraphicsWindow::
00694 mouse_mode_absolute() {
00695 #ifdef HAVE_XF86DGA
00696   if (!_dga_mouse_enabled) return;
00697 
00698   XUngrabPointer(_display, CurrentTime);
00699   x11display_cat.info() << "Disabling relative mouse using XF86DGA extension\n";
00700   XF86DGADirectVideo(_display, _screen, 0);
00701   _dga_mouse_enabled = false;
00702 #endif
00703 }
00704 
00705 ////////////////////////////////////////////////////////////////////
00706 //     Function: x11GraphicsWindow::mouse_mode_relative
00707 //       Access: Private, Virtual
00708 //  Description: Overridden from GraphicsWindow.
00709 ////////////////////////////////////////////////////////////////////
00710 void x11GraphicsWindow::
00711 mouse_mode_relative() {
00712 #ifdef HAVE_XF86DGA
00713   if (_dga_mouse_enabled) return;
00714 
00715   int major_ver, minor_ver;
00716   if (XF86DGAQueryVersion(_display, &major_ver, &minor_ver)) {
00717 
00718     X11_Cursor cursor = None;
00719     if (_properties.get_cursor_hidden()) {
00720       x11GraphicsPipe *x11_pipe;
00721       DCAST_INTO_V(x11_pipe, _pipe);
00722       cursor = x11_pipe->get_hidden_cursor();
00723     }
00724 
00725     if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
00726         GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
00727       x11display_cat.error() << "Failed to grab pointer!\n";
00728       return;
00729     }
00730 
00731     x11display_cat.info() << "Enabling relative mouse using XF86DGA extension\n";
00732     XF86DGADirectVideo(_display, _screen, XF86DGADirectMouse);
00733 
00734     _dga_mouse_enabled = true;
00735   } else {
00736     x11display_cat.info() << "XF86DGA extension not available\n";
00737     _dga_mouse_enabled = false;
00738   }
00739 
00740   // Get the real mouse position, so we can add/subtract
00741   // our relative coordinates later.
00742   XEvent event;
00743   XQueryPointer(_display, _xwindow, &event.xbutton.root,
00744     &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
00745     &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
00746   _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
00747 #endif
00748 }
00749 
00750 ////////////////////////////////////////////////////////////////////
00751 //     Function: x11GraphicsWindow::close_window
00752 //       Access: Protected, Virtual
00753 //  Description: Closes the window right now.  Called from the window
00754 //               thread.
00755 ////////////////////////////////////////////////////////////////////
00756 void x11GraphicsWindow::
00757 close_window() {
00758   if (_gsg != (GraphicsStateGuardian *)NULL) {
00759     _gsg.clear();
00760   }
00761   
00762   if (_ic != (XIC)NULL) {
00763     XDestroyIC(_ic);
00764     _ic = (XIC)NULL;
00765   }
00766 
00767   if (_xwindow != (X11_Window)NULL) {
00768     XDestroyWindow(_display, _xwindow);
00769     _xwindow = (X11_Window)NULL;
00770 
00771     // This may be necessary if we just closed the last X window in an
00772     // application, so the server hears the close request.
00773     XFlush(_display);
00774   }
00775 
00776 #ifdef HAVE_XRANDR
00777   // Change the resolution back to what it was.
00778   // Don't remove the SizeID typecast!
00779   if (_orig_size_id != (SizeID) -1) {
00780     X11_Window root;
00781     if (_pipe != NULL) {
00782       x11GraphicsPipe *x11_pipe;
00783       DCAST_INTO_V(x11_pipe, _pipe);
00784       root = x11_pipe->get_root();
00785     } else {
00786       // Oops. Looks like the pipe was destroyed
00787       // before the window gets closed. Oh well,
00788       // let's get the root window by ourselves.
00789       root = RootWindow(_display, _screen);
00790     }
00791     XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, root);
00792     XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime);
00793     _orig_size_id = -1;
00794   }
00795 #endif
00796 
00797   GraphicsWindow::close_window();
00798 }
00799 
00800 ////////////////////////////////////////////////////////////////////
00801 //     Function: x11GraphicsWindow::open_window
00802 //       Access: Protected, Virtual
00803 //  Description: Opens the window right now.  Called from the window
00804 //               thread.  Returns true if the window is successfully
00805 //               opened, or false if there was a problem.
00806 ////////////////////////////////////////////////////////////////////
00807 bool x11GraphicsWindow::
00808 open_window() {
00809   if (_visual_info == NULL) {
00810     // No X visual for this fbconfig; how can we open the window?
00811     x11display_cat.error()
00812       << "No X visual: cannot open window.\n";
00813     return false;
00814   }
00815   
00816   x11GraphicsPipe *x11_pipe;
00817   DCAST_INTO_R(x11_pipe, _pipe, false);
00818   
00819   if (!_properties.has_origin()) {
00820     _properties.set_origin(0, 0);
00821   }
00822   if (!_properties.has_size()) {
00823     _properties.set_size(100, 100);
00824   }
00825   
00826 #ifdef HAVE_XRANDR
00827   if (_properties.get_fullscreen()) {
00828     XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, x11_pipe->get_root());
00829     if (_orig_size_id == (SizeID) -1) {
00830       _orig_size_id = XRRConfigCurrentConfiguration(conf, &_orig_rotation);
00831     }
00832     int num_sizes, new_size_id = -1;
00833     XRRScreenSize *xrrs;
00834     xrrs = XRRSizes(_display, 0, &num_sizes);
00835     for (int i = 0; i < num_sizes; ++i) {
00836       if (xrrs[i].width == _properties.get_x_size() && 
00837           xrrs[i].height == _properties.get_y_size()) {
00838         new_size_id = i;
00839       }
00840     }
00841     if (new_size_id == -1) {
00842       x11display_cat.error() 
00843         << "Videocard has no supported display resolutions at specified res ("
00844         << _properties.get_x_size() << " x " << _properties.get_y_size() <<")\n";
00845       _orig_size_id = -1;
00846       return false;
00847     }
00848     if (new_size_id != _orig_size_id) {
00849       XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime);
00850     } else {
00851       _orig_size_id = -1;
00852     }
00853   }
00854 #endif
00855   
00856   X11_Window parent_window = x11_pipe->get_root();
00857   WindowHandle *window_handle = _properties.get_parent_window();
00858   if (window_handle != NULL) {
00859     x11display_cat.info()
00860       << "Got parent_window " << *window_handle << "\n";
00861     WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
00862     if (os_handle != NULL) {
00863       x11display_cat.info()
00864         << "os_handle type " << os_handle->get_type() << "\n";
00865       
00866       if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
00867         NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
00868         parent_window = x11_handle->get_handle();
00869       } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
00870         NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
00871         parent_window = (X11_Window)int_handle->get_handle();
00872       }
00873     }
00874   }
00875   _parent_window_handle = window_handle;
00876   
00877   _event_mask =
00878     ButtonPressMask | ButtonReleaseMask |
00879     KeyPressMask | KeyReleaseMask |
00880     EnterWindowMask | LeaveWindowMask |
00881     PointerMotionMask |
00882     FocusChangeMask | StructureNotifyMask;
00883 
00884   // Initialize window attributes
00885   XSetWindowAttributes wa;
00886   wa.background_pixel = XBlackPixel(_display, _screen);
00887   wa.border_pixel = 0;
00888   wa.colormap = _colormap;
00889   wa.event_mask = _event_mask;
00890 
00891   unsigned long attrib_mask = 
00892     CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
00893 
00894   _xwindow = XCreateWindow
00895     (_display, parent_window,
00896      _properties.get_x_origin(), _properties.get_y_origin(),
00897      _properties.get_x_size(), _properties.get_y_size(),
00898      0, _visual_info->depth, InputOutput,
00899      _visual_info->visual, attrib_mask, &wa);
00900 
00901   if (_xwindow == (X11_Window)0) {
00902     x11display_cat.error()
00903       << "failed to create X window.\n";
00904     return false;
00905   }
00906   set_wm_properties(_properties, false);
00907 
00908   // We don't specify any fancy properties of the XIC.  It would be
00909   // nicer if we could support fancy IM's that want preedit callbacks,
00910   // etc., but that can wait until we have an X server that actually
00911   // supports these to test it on.
00912   XIM im = x11_pipe->get_im();
00913   _ic = NULL;
00914   if (im) {
00915     _ic = XCreateIC
00916       (im,
00917        XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
00918        (void*)NULL);
00919     if (_ic == (XIC)NULL) {
00920       x11display_cat.warning()
00921         << "Couldn't create input context.\n";
00922     }
00923   }
00924 
00925   if (_properties.get_cursor_hidden()) {
00926     XDefineCursor(_display, _xwindow, x11_pipe->get_hidden_cursor());
00927 
00928   } else if (_properties.has_cursor_filename() && !_properties.get_cursor_filename().empty()) {
00929     // Note that if the cursor fails to load, cursor will be None
00930     X11_Cursor cursor = get_cursor(_properties.get_cursor_filename());
00931     XDefineCursor(_display, _xwindow, cursor);
00932   }
00933   
00934   XMapWindow(_display, _xwindow);
00935 
00936   if (_properties.get_raw_mice()) {
00937     open_raw_mice();
00938   } else {
00939     if (x11display_cat.is_debug()) {
00940       x11display_cat.debug()
00941         << "Raw mice not requested.\n";
00942     }
00943   }
00944 
00945   // Create a WindowHandle for ourselves
00946   _window_handle = NativeWindowHandle::make_x11(_xwindow);
00947 
00948   // And tell our parent window that we're now its child.
00949   if (_parent_window_handle != (WindowHandle *)NULL) {
00950     _parent_window_handle->attach_child(_window_handle);
00951   }
00952   
00953   return true;
00954 }
00955 
00956 ////////////////////////////////////////////////////////////////////
00957 //     Function: x11GraphicsWindow::set_wm_properties
00958 //       Access: Private
00959 //  Description: Asks the window manager to set the appropriate
00960 //               properties.  In X, these properties cannot be
00961 //               specified directly by the application; they must be
00962 //               requested via the window manager, which may or may
00963 //               not choose to honor the request.
00964 //
00965 //               If already_mapped is true, the window has already
00966 //               been mapped (manifested) on the display.  This means
00967 //               we may need to use a different action in some cases.
00968 ////////////////////////////////////////////////////////////////////
00969 void x11GraphicsWindow::
00970 set_wm_properties(const WindowProperties &properties, bool already_mapped) {
00971   // Name the window if there is a name
00972   XTextProperty window_name;
00973   XTextProperty *window_name_p = (XTextProperty *)NULL;
00974   if (properties.has_title()) {
00975     char *name = (char *)properties.get_title().c_str();
00976     if (XStringListToTextProperty(&name, 1, &window_name) != 0) {
00977       window_name_p = &window_name;
00978     }
00979   }
00980 
00981   // The size hints request a window of a particular size and/or a
00982   // particular placement onscreen.
00983   XSizeHints *size_hints_p = NULL;
00984   if (properties.has_origin() || properties.has_size()) {
00985     size_hints_p = XAllocSizeHints();
00986     if (size_hints_p != (XSizeHints *)NULL) {
00987       if (properties.has_origin()) {
00988         if (_properties.get_fullscreen()) {
00989           size_hints_p->x = 0;
00990           size_hints_p->y = 0;
00991         } else {
00992           size_hints_p->x = properties.get_x_origin();
00993           size_hints_p->y = properties.get_y_origin();
00994         }
00995         size_hints_p->flags |= USPosition;
00996       }
00997       if (properties.has_size()) {
00998         size_hints_p->width = properties.get_x_size();
00999         size_hints_p->height = properties.get_y_size();
01000         size_hints_p->flags |= USSize;
01001 
01002         if (properties.get_fixed_size()) {
01003           size_hints_p->min_width = properties.get_x_size();
01004           size_hints_p->min_height = properties.get_y_size();
01005           size_hints_p->max_width = properties.get_x_size();
01006           size_hints_p->max_height = properties.get_y_size();
01007           size_hints_p->flags |= (PMinSize | PMaxSize);
01008         }
01009       }
01010     }
01011   }
01012 
01013   // The window manager hints include requests to the window manager
01014   // other than those specific to window geometry.
01015   XWMHints *wm_hints_p = NULL;
01016   wm_hints_p = XAllocWMHints();
01017   if (wm_hints_p != (XWMHints *)NULL) {
01018     if (properties.has_minimized() && properties.get_minimized()) {
01019       wm_hints_p->initial_state = IconicState;
01020     } else {
01021       wm_hints_p->initial_state = NormalState;
01022     }
01023     wm_hints_p->flags = StateHint;
01024   }
01025 
01026   // Two competing window manager interfaces have evolved.  One of
01027   // them allows to set certain properties as a "type"; the other one
01028   // as a "state".  We'll try to honor both.
01029   static const int max_type_data = 32;
01030   PN_int32 type_data[max_type_data];
01031   int next_type_data = 0;
01032 
01033   static const int max_state_data = 32;
01034   PN_int32 state_data[max_state_data];
01035   int next_state_data = 0;
01036 
01037   static const int max_set_data = 32;
01038   class SetAction {
01039   public:
01040     inline SetAction() { }
01041     inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
01042     Atom _state;
01043     Atom _action;
01044   };
01045   SetAction set_data[max_set_data];
01046   int next_set_data = 0;
01047 
01048   if (properties.get_fullscreen()) {
01049     // For a "fullscreen" request, we pass this through, hoping the
01050     // window manager will support EWMH.
01051     type_data[next_type_data++] = _net_wm_window_type_fullscreen;
01052 
01053     // We also request it as a state.
01054     state_data[next_state_data++] = _net_wm_state_fullscreen;
01055     // Don't ask me why this has to be 1/0 and not _net_wm_state_add.
01056     // It doesn't seem to work otherwise.
01057     set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, 1);
01058   } else {
01059     set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, 0);
01060   }
01061 
01062   // If we asked for a window without a border, there's no excellent
01063   // way to arrange that.  For users whose window managers follow the
01064   // EWMH specification, we can ask for a "splash" screen, which is
01065   // usually undecorated.  It's not exactly right, but the spec
01066   // doesn't give us an exactly-right option.
01067 
01068   // For other users, we'll totally punt and just set the window's
01069   // Class to "Undecorated", and let the user configure his/her window
01070   // manager not to put a border around windows of this class.
01071   XClassHint *class_hints_p = NULL;
01072   if (properties.get_undecorated() || properties.get_fullscreen()) {
01073     class_hints_p = XAllocClassHint();
01074     class_hints_p->res_class = (char*) "Undecorated";
01075 
01076     if (!properties.get_fullscreen()) {
01077       type_data[next_type_data++] = _net_wm_window_type_splash;
01078     }
01079   }
01080 
01081   if (properties.has_z_order()) {
01082     switch (properties.get_z_order()) {
01083     case WindowProperties::Z_bottom:
01084       state_data[next_state_data++] = _net_wm_state_below;
01085       set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_add);
01086       set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
01087       break;
01088 
01089     case WindowProperties::Z_normal:
01090       set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
01091       set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
01092       break;
01093 
01094     case WindowProperties::Z_top:
01095       state_data[next_state_data++] = _net_wm_state_above;
01096       set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
01097       set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_add);
01098       break;
01099     }
01100   }
01101 
01102   nassertv(next_type_data < max_type_data);
01103   nassertv(next_state_data < max_state_data);
01104   nassertv(next_set_data < max_set_data);
01105 
01106   XChangeProperty(_display, _xwindow, _net_wm_window_type,
01107                   XA_ATOM, 32, PropModeReplace,
01108                   (unsigned char *)type_data, next_type_data);
01109 
01110   // Request the state properties all at once.
01111   XChangeProperty(_display, _xwindow, _net_wm_state,
01112                   XA_ATOM, 32, PropModeReplace,
01113                   (unsigned char *)state_data, next_state_data);
01114 
01115   if (already_mapped) {
01116     // We have to request state changes differently when the window
01117     // has been mapped.  To do this, we need to send a client message
01118     // to the root window for each change.
01119 
01120     x11GraphicsPipe *x11_pipe;
01121     DCAST_INTO_V(x11_pipe, _pipe);
01122   
01123     for (int i = 0; i < next_set_data; ++i) {
01124       XClientMessageEvent event;
01125       memset(&event, 0, sizeof(event));
01126       event.type = ClientMessage;
01127       event.send_event = True;
01128       event.display = _display;
01129       event.window = _xwindow;
01130       event.message_type = _net_wm_state;
01131       event.format = 32;
01132       event.data.l[0] = set_data[i]._action;
01133       event.data.l[1] = set_data[i]._state;
01134       event.data.l[2] = 0;
01135       event.data.l[3] = 1;
01136 
01137       XSendEvent(_display, x11_pipe->get_root(), True, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&event);
01138     }
01139   }
01140 
01141   XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
01142                    NULL, 0, size_hints_p, wm_hints_p, class_hints_p);
01143 
01144   if (size_hints_p != (XSizeHints *)NULL) {
01145     XFree(size_hints_p);
01146   }
01147   if (wm_hints_p != (XWMHints *)NULL) {
01148     XFree(wm_hints_p);
01149   }
01150   if (class_hints_p != (XClassHint *)NULL) {
01151     XFree(class_hints_p);
01152   }
01153 
01154   // Also, indicate to the window manager that we'd like to get a
01155   // chance to close our windows cleanly, rather than being rudely
01156   // disconnected from the X server if the user requests a window
01157   // close.
01158   Atom protocols[] = {
01159     _wm_delete_window,
01160   };
01161 
01162   XSetWMProtocols(_display, _xwindow, protocols, 
01163                   sizeof(protocols) / sizeof(Atom));
01164 }
01165 
01166 ////////////////////////////////////////////////////////////////////
01167 //     Function: x11GraphicsWindow::setup_colormap
01168 //       Access: Private, Virtual
01169 //  Description: Allocates a colormap appropriate to the visual and
01170 //               stores in in the _colormap method.
01171 ////////////////////////////////////////////////////////////////////
01172 void x11GraphicsWindow::
01173 setup_colormap(XVisualInfo *visual) {
01174   x11GraphicsPipe *x11_pipe;
01175   DCAST_INTO_V(x11_pipe, _pipe);
01176   X11_Window root_window = x11_pipe->get_root();
01177 
01178   _colormap = XCreateColormap(_display, root_window,
01179                               visual->visual, AllocNone);
01180 }
01181 
01182 ////////////////////////////////////////////////////////////////////
01183 //     Function: x11GraphicsWindow::open_raw_mice
01184 //       Access: Private
01185 //  Description: Adds raw mice to the _input_devices list.
01186 ////////////////////////////////////////////////////////////////////
01187 void x11GraphicsWindow::
01188 open_raw_mice() {
01189 #ifdef HAVE_LINUX_INPUT_H
01190   bool any_present = false;
01191   bool any_mice = false;
01192   
01193   for (int i=0; i<64; i++) {
01194     uint8_t evtypes[EV_MAX/8 + 1];
01195     ostringstream fnb;
01196     fnb << "/dev/input/event" << i;
01197     string fn = fnb.str();
01198     int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
01199     if (fd >= 0) {
01200       any_present = true;
01201       char name[256];
01202       char phys[256];
01203       char uniq[256];
01204       if ((ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)||
01205           (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0)||
01206           (ioctl(fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0)||
01207           (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) {
01208         close(fd);
01209         x11display_cat.error() <<
01210           "Opening raw mice: ioctl failed on " << fn << "\n";
01211       } else {
01212         if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) {
01213           for (char *p=name; *p; p++) {
01214             if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
01215               *p = '_';
01216             }
01217           }
01218           for (char *p=uniq; *p; p++) {
01219             if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
01220               *p = '_';
01221             }
01222           }
01223           string full_id = ((string)name) + "." + uniq;
01224           MouseDeviceInfo inf;
01225           inf._fd = fd;
01226           inf._input_device_index = _input_devices.size();
01227           inf._io_buffer = "";
01228           _mouse_device_info.push_back(inf);
01229           GraphicsWindowInputDevice device =
01230             GraphicsWindowInputDevice::pointer_only(this, full_id);
01231           add_input_device(device);
01232           x11display_cat.info() << "Raw mouse " <<
01233             inf._input_device_index << " detected: " << full_id << "\n";
01234           any_mice = true;
01235         } else {
01236           close(fd);
01237         }
01238       }
01239     } else {
01240       if ((errno == ENOENT)||(errno == ENOTDIR)) {
01241         break;
01242       } else {
01243         any_present = true;
01244         x11display_cat.error() << 
01245           "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
01246       }
01247     }
01248   }
01249   
01250   if (!any_present) {
01251     x11display_cat.error() << 
01252       "Opening raw mice: files not found: /dev/input/event*\n";
01253   } else if (!any_mice) {
01254     x11display_cat.error() << 
01255       "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
01256   }
01257 #else
01258   x11display_cat.error() <<
01259     "Opening raw mice: panda not compiled with raw mouse support.\n";
01260 #endif
01261 }
01262 
01263 ////////////////////////////////////////////////////////////////////
01264 //     Function: x11GraphicsWindow::poll_raw_mice
01265 //       Access: Private
01266 //  Description: Reads events from the raw mouse device files.
01267 ////////////////////////////////////////////////////////////////////
01268 void x11GraphicsWindow::
01269 poll_raw_mice()
01270 {
01271 #ifdef HAVE_LINUX_INPUT_H
01272   for (int dev=0; dev<_mouse_device_info.size(); dev++) {
01273     MouseDeviceInfo &inf = _mouse_device_info[dev];
01274 
01275     // Read all bytes into buffer.
01276     if (inf._fd >= 0) {
01277       while (1) {
01278         char tbuf[1024];
01279         int nread = read(inf._fd, tbuf, sizeof(tbuf));
01280         if (nread > 0) {
01281           inf._io_buffer += string(tbuf, nread);
01282         } else {
01283           if ((nread < 0)&&((errno == EWOULDBLOCK) || (errno==EAGAIN))) {
01284             break;
01285           }
01286           close(inf._fd);
01287           inf._fd = -1;
01288           break;
01289         }
01290       }
01291     }
01292     
01293     // Process events.
01294     int nevents = inf._io_buffer.size() / sizeof(struct input_event);
01295     if (nevents == 0) {
01296       continue;
01297     }
01298     const input_event *events = (const input_event *)(inf._io_buffer.c_str());
01299     GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index];
01300     int x = dev.get_raw_pointer().get_x();
01301     int y = dev.get_raw_pointer().get_y();
01302     for (int i=0; i<nevents; i++) {
01303       if (events[i].type == EV_REL) {
01304         if (events[i].code == REL_X) x += events[i].value;
01305         if (events[i].code == REL_Y) y += events[i].value;
01306       } else if (events[i].type == EV_ABS) {
01307         if (events[i].code == ABS_X) x = events[i].value;
01308         if (events[i].code == ABS_Y) y = events[i].value;
01309       } else if (events[i].type == EV_KEY) {
01310         if ((events[i].code >= BTN_MOUSE)&&(events[i].code < BTN_MOUSE+8)) {
01311           int btn = events[i].code - BTN_MOUSE;
01312           dev.set_pointer_in_window(x,y);
01313           if (events[i].value) {
01314             dev.button_down(MouseButton::button(btn));
01315           } else {
01316             dev.button_up(MouseButton::button(btn));
01317           }
01318         }
01319       }
01320     }
01321     inf._io_buffer.erase(0,nevents*sizeof(struct input_event));
01322     dev.set_pointer_in_window(x,y);
01323   }
01324 #endif
01325 }
01326 
01327 ////////////////////////////////////////////////////////////////////
01328 //     Function: x11GraphicsWindow::handle_keystroke
01329 //       Access: Private
01330 //  Description: Generates a keystroke corresponding to the indicated
01331 //               X KeyPress event.
01332 ////////////////////////////////////////////////////////////////////
01333 void x11GraphicsWindow::
01334 handle_keystroke(XKeyEvent &event) {
01335   if (!_dga_mouse_enabled) {
01336     _input_devices[0].set_pointer_in_window(event.x, event.y);
01337   }
01338 
01339   if (_ic) {
01340     // First, get the keystroke as a wide-character sequence.
01341     static const int buffer_size = 256;
01342     wchar_t buffer[buffer_size];
01343     Status status;
01344     int len = XwcLookupString(_ic, &event, buffer, buffer_size, NULL,
01345                               &status);
01346     if (status == XBufferOverflow) {
01347       x11display_cat.error()
01348         << "Overflowed input buffer.\n";
01349     }
01350     
01351     // Now each of the returned wide characters represents a
01352     // keystroke.
01353     for (int i = 0; i < len; i++) {
01354       _input_devices[0].keystroke(buffer[i]);
01355     }
01356 
01357   } else {
01358     // Without an input context, just get the ascii keypress.
01359     ButtonHandle button = get_button(event, true);
01360     if (button.has_ascii_equivalent()) {
01361       _input_devices[0].keystroke(button.get_ascii_equivalent());
01362     }
01363   }
01364 }
01365 
01366 ////////////////////////////////////////////////////////////////////
01367 //     Function: x11GraphicsWindow::handle_keypress
01368 //       Access: Private
01369 //  Description: Generates a keypress corresponding to the indicated
01370 //               X KeyPress event.
01371 ////////////////////////////////////////////////////////////////////
01372 void x11GraphicsWindow::
01373 handle_keypress(XKeyEvent &event) {
01374   if (!_dga_mouse_enabled) {
01375     _input_devices[0].set_pointer_in_window(event.x, event.y);
01376   }
01377 
01378   // Now get the raw unshifted button.
01379   ButtonHandle button = get_button(event, false);
01380   if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
01381     _input_devices[0].button_down(KeyboardButton::control());
01382   }
01383   if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
01384     _input_devices[0].button_down(KeyboardButton::shift());
01385   }
01386   if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
01387     _input_devices[0].button_down(KeyboardButton::alt());
01388   }
01389   if (button != ButtonHandle::none()) {
01390     _input_devices[0].button_down(button);
01391   }
01392 }
01393 
01394 ////////////////////////////////////////////////////////////////////
01395 //     Function: x11GraphicsWindow::handle_keyrelease
01396 //       Access: Private
01397 //  Description: Generates a keyrelease corresponding to the indicated
01398 //               X KeyRelease event.
01399 ////////////////////////////////////////////////////////////////////
01400 void x11GraphicsWindow::
01401 handle_keyrelease(XKeyEvent &event) {
01402   if (!_dga_mouse_enabled) {
01403     _input_devices[0].set_pointer_in_window(event.x, event.y);
01404   }
01405 
01406   // Now get the raw unshifted button.
01407   ButtonHandle button = get_button(event, false);
01408   if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
01409     _input_devices[0].button_up(KeyboardButton::control());
01410   }
01411   if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
01412     _input_devices[0].button_up(KeyboardButton::shift());
01413   }
01414   if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
01415     _input_devices[0].button_up(KeyboardButton::alt());
01416   }
01417   if (button != ButtonHandle::none()) {
01418     _input_devices[0].button_up(button);
01419   }
01420 }
01421 
01422 ////////////////////////////////////////////////////////////////////
01423 //     Function: x11GraphicsWindow::get_button
01424 //       Access: Private
01425 //  Description: Returns the Panda ButtonHandle corresponding to the
01426 //               keyboard button indicated by the given key event.
01427 ////////////////////////////////////////////////////////////////////
01428 ButtonHandle x11GraphicsWindow::
01429 get_button(XKeyEvent &key_event, bool allow_shift) {
01430   KeySym key = XLookupKeysym(&key_event, 0);
01431 
01432   if ((key_event.state & Mod2Mask) != 0) {
01433     // Mod2Mask corresponds to NumLock being in effect.  In this case,
01434     // we want to get the alternate keysym associated with any keypad
01435     // keys.  Weird system.
01436     KeySym k2;
01437     ButtonHandle button;
01438     switch (key) {
01439     case XK_KP_Space:
01440     case XK_KP_Tab:
01441     case XK_KP_Enter:
01442     case XK_KP_F1:
01443     case XK_KP_F2:
01444     case XK_KP_F3:
01445     case XK_KP_F4:
01446     case XK_KP_Equal:
01447     case XK_KP_Multiply:
01448     case XK_KP_Add:
01449     case XK_KP_Separator:
01450     case XK_KP_Subtract:
01451     case XK_KP_Divide:
01452     case XK_KP_Left:
01453     case XK_KP_Up:
01454     case XK_KP_Right:
01455     case XK_KP_Down:
01456     case XK_KP_Begin:
01457     case XK_KP_Prior:
01458     case XK_KP_Next:
01459     case XK_KP_Home:
01460     case XK_KP_End:
01461     case XK_KP_Insert:
01462     case XK_KP_Delete:
01463     case XK_KP_0:
01464     case XK_KP_1:
01465     case XK_KP_2:
01466     case XK_KP_3:
01467     case XK_KP_4:
01468     case XK_KP_5:
01469     case XK_KP_6:
01470     case XK_KP_7:
01471     case XK_KP_8:
01472     case XK_KP_9:
01473       k2 = XLookupKeysym(&key_event, 1);
01474       button = map_button(k2);
01475       if (button != ButtonHandle::none()) {
01476         return button;
01477       }
01478       // If that didn't produce a button we know, just fall through
01479       // and handle the normal, un-numlocked key.
01480       break;
01481 
01482     default:
01483       break;
01484     } 
01485   }
01486 
01487   if (allow_shift) {
01488     // If shift is held down, get the shifted keysym.
01489     if ((key_event.state & ShiftMask) != 0) {
01490       KeySym k2 = XLookupKeysym(&key_event, 1);
01491       ButtonHandle button = map_button(k2);
01492       if (button != ButtonHandle::none()) {
01493         return button;
01494       }
01495     }
01496 
01497     // If caps lock is down, shift lowercase letters to uppercase.  We
01498     // can do this in just the ASCII set, because we handle
01499     // international keyboards elsewhere (via an input context).
01500     if ((key_event.state & (ShiftMask | LockMask)) != 0) {
01501       if (key >= XK_a and key <= XK_z) {
01502         key += (XK_A - XK_a);
01503       }
01504     }
01505   }
01506 
01507   return map_button(key);
01508 }
01509 
01510 ////////////////////////////////////////////////////////////////////
01511 //     Function: x11GraphicsWindow::map_button
01512 //       Access: Private
01513 //  Description: Maps from a single X keysym to Panda's ButtonHandle.
01514 //               Called by get_button(), above.
01515 ////////////////////////////////////////////////////////////////////
01516 ButtonHandle x11GraphicsWindow::
01517 map_button(KeySym key) {
01518   switch (key) {
01519   case XK_BackSpace:
01520     return KeyboardButton::backspace();
01521   case XK_Tab:
01522   case XK_KP_Tab:
01523     return KeyboardButton::tab();
01524   case XK_Return:
01525   case XK_KP_Enter:
01526     return KeyboardButton::enter();
01527   case XK_Escape:
01528     return KeyboardButton::escape();
01529   case XK_KP_Space:
01530   case XK_space:
01531     return KeyboardButton::space();
01532   case XK_exclam:
01533     return KeyboardButton::ascii_key('!');
01534   case XK_quotedbl:
01535     return KeyboardButton::ascii_key('"');
01536   case XK_numbersign:
01537     return KeyboardButton::ascii_key('#');
01538   case XK_dollar:
01539     return KeyboardButton::ascii_key('$');
01540   case XK_percent:
01541     return KeyboardButton::ascii_key('%');
01542   case XK_ampersand:
01543     return KeyboardButton::ascii_key('&');
01544   case XK_apostrophe: // == XK_quoteright
01545     return KeyboardButton::ascii_key('\'');
01546   case XK_parenleft:
01547     return KeyboardButton::ascii_key('(');
01548   case XK_parenright:
01549     return KeyboardButton::ascii_key(')');
01550   case XK_asterisk:
01551   case XK_KP_Multiply:
01552     return KeyboardButton::ascii_key('*');
01553   case XK_plus:
01554   case XK_KP_Add:
01555     return KeyboardButton::ascii_key('+');
01556   case XK_comma:
01557   case XK_KP_Separator:
01558     return KeyboardButton::ascii_key(',');
01559   case XK_minus:
01560   case XK_KP_Subtract:
01561     return KeyboardButton::ascii_key('-');
01562   case XK_period:
01563   case XK_KP_Decimal:
01564     return KeyboardButton::ascii_key('.');
01565   case XK_slash:
01566   case XK_KP_Divide:
01567     return KeyboardButton::ascii_key('/');
01568   case XK_0:
01569   case XK_KP_0:
01570     return KeyboardButton::ascii_key('0');
01571   case XK_1:
01572   case XK_KP_1:
01573     return KeyboardButton::ascii_key('1');
01574   case XK_2:
01575   case XK_KP_2:
01576     return KeyboardButton::ascii_key('2');
01577   case XK_3:
01578   case XK_KP_3:
01579     return KeyboardButton::ascii_key('3');
01580   case XK_4:
01581   case XK_KP_4:
01582     return KeyboardButton::ascii_key('4');
01583   case XK_5:
01584   case XK_KP_5:
01585     return KeyboardButton::ascii_key('5');
01586   case XK_6:
01587   case XK_KP_6:
01588     return KeyboardButton::ascii_key('6');
01589   case XK_7:
01590   case XK_KP_7:
01591     return KeyboardButton::ascii_key('7');
01592   case XK_8:
01593   case XK_KP_8:
01594     return KeyboardButton::ascii_key('8');
01595   case XK_9:
01596   case XK_KP_9:
01597     return KeyboardButton::ascii_key('9');
01598   case XK_colon:
01599     return KeyboardButton::ascii_key(':');
01600   case XK_semicolon:
01601     return KeyboardButton::ascii_key(';');
01602   case XK_less:
01603     return KeyboardButton::ascii_key('<');
01604   case XK_equal:
01605   case XK_KP_Equal:
01606     return KeyboardButton::ascii_key('=');
01607   case XK_greater:
01608     return KeyboardButton::ascii_key('>');
01609   case XK_question:
01610     return KeyboardButton::ascii_key('?');
01611   case XK_at:
01612     return KeyboardButton::ascii_key('@');
01613   case XK_A:
01614     return KeyboardButton::ascii_key('A');
01615   case XK_B:
01616     return KeyboardButton::ascii_key('B');
01617   case XK_C:
01618     return KeyboardButton::ascii_key('C');
01619   case XK_D:
01620     return KeyboardButton::ascii_key('D');
01621   case XK_E:
01622     return KeyboardButton::ascii_key('E');
01623   case XK_F:
01624     return KeyboardButton::ascii_key('F');
01625   case XK_G:
01626     return KeyboardButton::ascii_key('G');
01627   case XK_H:
01628     return KeyboardButton::ascii_key('H');
01629   case XK_I:
01630     return KeyboardButton::ascii_key('I');
01631   case XK_J:
01632     return KeyboardButton::ascii_key('J');
01633   case XK_K:
01634     return KeyboardButton::ascii_key('K');
01635   case XK_L:
01636     return KeyboardButton::ascii_key('L');
01637   case XK_M:
01638     return KeyboardButton::ascii_key('M');
01639   case XK_N:
01640     return KeyboardButton::ascii_key('N');
01641   case XK_O:
01642     return KeyboardButton::ascii_key('O');
01643   case XK_P:
01644     return KeyboardButton::ascii_key('P');
01645   case XK_Q:
01646     return KeyboardButton::ascii_key('Q');
01647   case XK_R:
01648     return KeyboardButton::ascii_key('R');
01649   case XK_S:
01650     return KeyboardButton::ascii_key('S');
01651   case XK_T:
01652     return KeyboardButton::ascii_key('T');
01653   case XK_U:
01654     return KeyboardButton::ascii_key('U');
01655   case XK_V:
01656     return KeyboardButton::ascii_key('V');
01657   case XK_W:
01658     return KeyboardButton::ascii_key('W');
01659   case XK_X:
01660     return KeyboardButton::ascii_key('X');
01661   case XK_Y:
01662     return KeyboardButton::ascii_key('Y');
01663   case XK_Z:
01664     return KeyboardButton::ascii_key('Z');
01665   case XK_bracketleft:
01666     return KeyboardButton::ascii_key('[');
01667   case XK_backslash:
01668     return KeyboardButton::ascii_key('\\');
01669   case XK_bracketright:
01670     return KeyboardButton::ascii_key(']');
01671   case XK_asciicircum:
01672     return KeyboardButton::ascii_key('^');
01673   case XK_underscore:
01674     return KeyboardButton::ascii_key('_');
01675   case XK_grave: // == XK_quoteleft
01676     return KeyboardButton::ascii_key('`');
01677   case XK_a:
01678     return KeyboardButton::ascii_key('a');
01679   case XK_b:
01680     return KeyboardButton::ascii_key('b');
01681   case XK_c:
01682     return KeyboardButton::ascii_key('c');
01683   case XK_d:
01684     return KeyboardButton::ascii_key('d');
01685   case XK_e:
01686     return KeyboardButton::ascii_key('e');
01687   case XK_f:
01688     return KeyboardButton::ascii_key('f');
01689   case XK_g:
01690     return KeyboardButton::ascii_key('g');
01691   case XK_h:
01692     return KeyboardButton::ascii_key('h');
01693   case XK_i:
01694     return KeyboardButton::ascii_key('i');
01695   case XK_j:
01696     return KeyboardButton::ascii_key('j');
01697   case XK_k:
01698     return KeyboardButton::ascii_key('k');
01699   case XK_l:
01700     return KeyboardButton::ascii_key('l');
01701   case XK_m:
01702     return KeyboardButton::ascii_key('m');
01703   case XK_n:
01704     return KeyboardButton::ascii_key('n');
01705   case XK_o:
01706     return KeyboardButton::ascii_key('o');
01707   case XK_p:
01708     return KeyboardButton::ascii_key('p');
01709   case XK_q:
01710     return KeyboardButton::ascii_key('q');
01711   case XK_r:
01712     return KeyboardButton::ascii_key('r');
01713   case XK_s:
01714     return KeyboardButton::ascii_key('s');
01715   case XK_t:
01716     return KeyboardButton::ascii_key('t');
01717   case XK_u:
01718     return KeyboardButton::ascii_key('u');
01719   case XK_v:
01720     return KeyboardButton::ascii_key('v');
01721   case XK_w:
01722     return KeyboardButton::ascii_key('w');
01723   case XK_x:
01724     return KeyboardButton::ascii_key('x');
01725   case XK_y:
01726     return KeyboardButton::ascii_key('y');
01727   case XK_z:
01728     return KeyboardButton::ascii_key('z');
01729   case XK_braceleft:
01730     return KeyboardButton::ascii_key('{');
01731   case XK_bar:
01732     return KeyboardButton::ascii_key('|');
01733   case XK_braceright:
01734     return KeyboardButton::ascii_key('}');
01735   case XK_asciitilde:
01736     return KeyboardButton::ascii_key('~');
01737   case XK_F1:
01738   case XK_KP_F1:
01739     return KeyboardButton::f1();
01740   case XK_F2:
01741   case XK_KP_F2:
01742     return KeyboardButton::f2();
01743   case XK_F3:
01744   case XK_KP_F3:
01745     return KeyboardButton::f3();
01746   case XK_F4:
01747   case XK_KP_F4:
01748     return KeyboardButton::f4();
01749   case XK_F5:
01750     return KeyboardButton::f5();
01751   case XK_F6:
01752     return KeyboardButton::f6();
01753   case XK_F7:
01754     return KeyboardButton::f7();
01755   case XK_F8:
01756     return KeyboardButton::f8();
01757   case XK_F9:
01758     return KeyboardButton::f9();
01759   case XK_F10:
01760     return KeyboardButton::f10();
01761   case XK_F11:
01762     return KeyboardButton::f11();
01763   case XK_F12:
01764     return KeyboardButton::f12();
01765   case XK_KP_Left:
01766   case XK_Left:
01767     return KeyboardButton::left();
01768   case XK_KP_Up:
01769   case XK_Up:
01770     return KeyboardButton::up();
01771   case XK_KP_Right:
01772   case XK_Right:
01773     return KeyboardButton::right();
01774   case XK_KP_Down:
01775   case XK_Down:
01776     return KeyboardButton::down();
01777   case XK_KP_Prior:
01778   case XK_Prior:
01779     return KeyboardButton::page_up();
01780   case XK_KP_Next:
01781   case XK_Next:
01782     return KeyboardButton::page_down();
01783   case XK_KP_Home:
01784   case XK_Home:
01785     return KeyboardButton::home();
01786   case XK_KP_End:
01787   case XK_End:
01788     return KeyboardButton::end();
01789   case XK_KP_Insert:
01790   case XK_Insert:
01791     return KeyboardButton::insert();
01792   case XK_KP_Delete:
01793   case XK_Delete:
01794     return KeyboardButton::del();
01795   case XK_Num_Lock:
01796     return KeyboardButton::num_lock();
01797   case XK_Scroll_Lock:
01798     return KeyboardButton::scroll_lock();
01799   case XK_Print:
01800     return KeyboardButton::print_screen();
01801   case XK_Pause:
01802     return KeyboardButton::pause();
01803   case XK_Shift_L:
01804     return KeyboardButton::lshift();
01805   case XK_Shift_R:
01806     return KeyboardButton::rshift();
01807   case XK_Control_L:
01808     return KeyboardButton::lcontrol();
01809   case XK_Control_R:
01810     return KeyboardButton::rcontrol();
01811   case XK_Alt_L:
01812     return KeyboardButton::lalt();
01813   case XK_Alt_R:
01814     return KeyboardButton::ralt();
01815   case XK_Meta_L:
01816   case XK_Meta_R:
01817     return KeyboardButton::meta();
01818   case XK_Caps_Lock:
01819     return KeyboardButton::caps_lock();
01820   case XK_Shift_Lock:
01821     return KeyboardButton::shift_lock();
01822   }
01823 
01824   return ButtonHandle::none();
01825 }
01826 
01827 ////////////////////////////////////////////////////////////////////
01828 //     Function: x11GraphicsWindow::get_mouse_button
01829 //       Access: Private
01830 //  Description: Returns the Panda ButtonHandle corresponding to the
01831 //               mouse button indicated by the given button event.
01832 ////////////////////////////////////////////////////////////////////
01833 ButtonHandle x11GraphicsWindow::
01834 get_mouse_button(XButtonEvent &button_event) {
01835   int index = button_event.button;
01836   if (index == x_wheel_up_button) {
01837     return MouseButton::wheel_up();
01838   } else if (index == x_wheel_down_button) {
01839     return MouseButton::wheel_down();
01840   } else if (index == x_wheel_left_button) {
01841     return MouseButton::wheel_left();
01842   } else if (index == x_wheel_right_button) {
01843     return MouseButton::wheel_right();
01844   } else {
01845     return MouseButton::button(index - 1);
01846   }
01847 }
01848 
01849 ////////////////////////////////////////////////////////////////////
01850 //     Function: x11GraphicsWindow::check_event
01851 //       Access: Private, Static
01852 //  Description: This function is used as a predicate to
01853 //               XCheckIfEvent() to determine if the indicated queued
01854 //               X event is relevant and should be returned to this
01855 //               window.
01856 ////////////////////////////////////////////////////////////////////
01857 Bool x11GraphicsWindow::
01858 check_event(X11_Display *display, XEvent *event, char *arg) {
01859   const x11GraphicsWindow *self = (x11GraphicsWindow *)arg;
01860 
01861   // We accept any event that is sent to our window.
01862   return (event->xany.window == self->_xwindow);
01863 }
01864 
01865 ////////////////////////////////////////////////////////////////////
01866 //     Function: x11GraphicsWindow::get_cursor
01867 //       Access: Private
01868 //  Description: Loads and returns a Cursor corresponding to the
01869 //               indicated filename.  If the file cannot be loaded,
01870 //               returns None.
01871 ////////////////////////////////////////////////////////////////////
01872 X11_Cursor x11GraphicsWindow::
01873 get_cursor(const Filename &filename) {
01874 #ifndef HAVE_XCURSOR
01875   x11display_cat.info()
01876     << "XCursor support not enabled in build; cannot change mouse cursor.\n";
01877   return None;
01878 #else  // HAVE_XCURSOR
01879   // First, look for the unresolved filename in our index.
01880   pmap<Filename, X11_Cursor>::iterator fi = _cursor_filenames.find(filename);
01881   if (fi != _cursor_filenames.end()) {
01882     return fi->second;
01883   }
01884 
01885   // If it wasn't found, resolve the filename and search for that.
01886   Filename resolved = filename;
01887   if (!resolved.resolve_filename(get_model_path())) {
01888     // The filename doesn't exist.
01889     x11display_cat.warning()
01890       << "Could not find cursor filename " << filename << "\n";
01891     return None;
01892   }
01893   fi = _cursor_filenames.find(resolved);
01894   if (fi != _cursor_filenames.end()) {
01895     _cursor_filenames[filename] = (*fi).second;
01896     return fi->second;
01897   }
01898 
01899   // Open the file through the virtual file system.
01900   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
01901   istream *str = vfs->open_read_file(resolved, true);
01902   if (str == NULL) {
01903     x11display_cat.warning()
01904       << "Could not open cursor file " << filename << "\n";
01905     return None;
01906   }
01907   
01908   // Check the first four bytes to see what kind of file it is.
01909   char magic[4];
01910   str->read(magic, 4);
01911   if (!str->good()) {
01912     x11display_cat.warning()
01913       << "Could not read from cursor file " << filename << "\n";
01914     return None;
01915   }
01916   str->seekg(0, istream::beg);
01917 
01918   X11_Cursor h = None;
01919   if (memcmp(magic, "Xcur", 4) == 0) {
01920     // X11 cursor.
01921     x11display_cat.debug()
01922       << "Loading X11 cursor " << filename << "\n";
01923     XcursorFile xcfile;
01924     xcfile.closure = str;
01925     xcfile.read = &xcursor_read;
01926     xcfile.write = &xcursor_write;
01927     xcfile.seek = &xcursor_seek;
01928 
01929     XcursorImages *images = XcursorXcFileLoadImages(&xcfile, XcursorGetDefaultSize(_display));
01930     if (images != NULL) {
01931       h = XcursorImagesLoadCursor(_display, images);
01932       XcursorImagesDestroy(images);
01933     }
01934 
01935   } else if (memcmp(magic, "\0\0\1\0", 4) == 0
01936           || memcmp(magic, "\0\0\2\0", 4) == 0) {
01937     // Windows .ico or .cur file.
01938     x11display_cat.debug()
01939       << "Loading Windows cursor " << filename << "\n";
01940     h = read_ico(*str);
01941   }
01942 
01943   // Delete the istream.
01944   vfs->close_read_file(str);
01945 
01946   if (h == None) {
01947     x11display_cat.warning()
01948       << "X11 cursor filename '" << resolved << "' could not be loaded!\n";
01949   }
01950 
01951   _cursor_filenames[resolved] = h;
01952   return h;
01953 #endif  // HAVE_XCURSOR
01954 }
01955 
01956 #ifdef HAVE_XCURSOR
01957 ////////////////////////////////////////////////////////////////////
01958 //     Function: x11GraphicsWindow::load_ico
01959 //       Access: Private
01960 //  Description: Reads a Windows .ico or .cur file from the
01961 //               indicated stream and returns it as an X11 Cursor.
01962 //               If the file cannot be loaded, returns None.
01963 ////////////////////////////////////////////////////////////////////
01964 X11_Cursor x11GraphicsWindow::
01965 read_ico(istream &ico) {
01966  // Local structs, this is just POD, make input easier
01967  typedef struct {
01968     uint16_t reserved, type, count;
01969   } IcoHeader;
01970 
01971   typedef struct {
01972     uint8_t width, height, colorCount, reserved;
01973     uint16_t xhot, yhot;
01974     uint32_t bitmapSize, offset;
01975   } IcoEntry;
01976 
01977   typedef struct {
01978     uint32_t headerSize, width, height;
01979     uint16_t planes, bitsPerPixel;
01980     uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
01981   } IcoInfoHeader;
01982 
01983   typedef struct {
01984     uint8_t blue, green, red, reserved;
01985   } IcoColor;
01986 
01987   int i, entry = 0;
01988   unsigned int j, k, mask, shift;
01989   size_t colorCount, bitsPerPixel;
01990   IcoHeader header;
01991   IcoInfoHeader infoHeader;
01992   IcoEntry *entries = NULL;
01993   IcoColor color, *palette = NULL;
01994 
01995   size_t xorBmpSize, andBmpSize;
01996   char *curXor, *curAnd;
01997   char *xorBmp = NULL, *andBmp = NULL;
01998   XcursorImage *image = NULL;
01999   X11_Cursor ret = None;
02000 
02001   int def_size = XcursorGetDefaultSize(_display);
02002 
02003   // Get our header, note that ICO = type 1 and CUR = type 2.
02004   ico.read(reinterpret_cast<char *>(&header), sizeof(IcoHeader));
02005   if (!ico.good()) goto cleanup;
02006   if (header.type != 1 && header.type != 2) goto cleanup;
02007   if (header.count < 1) goto cleanup;
02008 
02009   // Read the entry table into memory, select the largest entry.
02010   entries = new IcoEntry[header.count];
02011   ico.read(reinterpret_cast<char *>(entries), header.count * sizeof(IcoEntry));
02012   if (!ico.good()) goto cleanup;
02013   for (i = 1; i < header.count; i++) {
02014     if (entries[i].width == def_size && entries[i].height == def_size) {
02015       // Wait, this is the default cursor size.  This is perfect.
02016       entry = i;
02017       break;
02018     }
02019     if (entries[i].width > entries[entry].width ||
02020         entries[i].height > entries[entry].height)
02021         entry = i;
02022   }
02023 
02024   // Seek to the image in the ICO.
02025   ico.seekg(entries[entry].offset);
02026   if (!ico.good()) goto cleanup;
02027   ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
02028   if (!ico.good()) goto cleanup;
02029   bitsPerPixel = infoHeader.bitsPerPixel;
02030 
02031   // TODO: Support PNG compressed ICOs.
02032   if (infoHeader.compression != 0) goto cleanup;
02033 
02034   // Load the color palette, if one exists.
02035   if (bitsPerPixel != 24 && bitsPerPixel != 32) {
02036     colorCount = 1 << bitsPerPixel;
02037     palette = new IcoColor[colorCount];
02038     ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
02039     if (!ico.good()) goto cleanup;
02040   }
02041 
02042   // Read in the pixel data.
02043   xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
02044   andBmpSize = (infoHeader.width * (infoHeader.height / 2)) / 8;
02045   curXor = xorBmp = new char[xorBmpSize];
02046   curAnd = andBmp = new char[andBmpSize];
02047   ico.read(xorBmp, xorBmpSize);
02048   if (!ico.good()) goto cleanup;
02049   ico.read(andBmp, andBmpSize);
02050   if (!ico.good()) goto cleanup;
02051 
02052   // If this is an actual CUR not an ICO set up the hotspot properly.
02053   image = XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
02054   if (header.type == 2) { image->xhot = entries[entry].xhot; image->yhot = entries[entry].yhot; }
02055 
02056   // Support all the formats that GIMP supports, minus PNG compressed ICOs.
02057   // Would need to use libpng to decode the compressed ones.
02058   switch (bitsPerPixel) {
02059   case 1:
02060   case 4:
02061   case 8:
02062     // For colors less that a byte wide, shift and mask the palette indices
02063     // off each element of the xorBmp and append them to the image.
02064     mask = ((1 << bitsPerPixel) - 1);
02065     for (i = image->height - 1; i >= 0; i--) {
02066       for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
02067         for (k = 0; k < 8 / bitsPerPixel; k++) {
02068           shift = 8 - ((k + 1) * bitsPerPixel);
02069           color = palette[(*curXor & (mask << shift)) >> shift];
02070           image->pixels[(i * image->width) + j + k] = (color.red << 16) +
02071                                                       (color.green << 8) +
02072                                                       (color.blue);
02073         }
02074 
02075         curXor++;
02076       }
02077 
02078       // Set the alpha byte properly according to the andBmp.
02079       for (j = 0; j < image->width; j += 8) {
02080         for (k = 0; k < 8; k++) {
02081           shift = 7 - k;
02082           image->pixels[(i * image->width) + j + k] |=
02083             ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
02084         }
02085 
02086         curAnd++;
02087       }
02088     }
02089 
02090     break;
02091   case 24:
02092     // Pack each of the three bytes into a single color, BGR -> 0RGB
02093     for (i = image->height - 1; i >= 0; i--) {
02094       for (j = 0; j < image->width; j++) {
02095         image->pixels[(i * image->width) + j] = (*(curXor + 2) << 16) +
02096                                                 (*(curXor + 1) << 8) + (*curXor);
02097         curXor += 3;
02098       }
02099 
02100       // Set the alpha byte properly according to the andBmp.
02101       for (j = 0; j < image->width; j += 8) {
02102         for (k = 0; k < 8; k++) {
02103           shift = 7 - k;
02104           image->pixels[(i * image->width) + j + k] |=
02105             ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
02106         }
02107 
02108         curAnd++;
02109       }
02110 
02111     }
02112 
02113     break;
02114   case 32:
02115     // Pack each of the four bytes into a single color, BGRA -> ARGB
02116     for (i = image->height - 1; i >= 0; i--) {
02117       for (j = 0; j < image->width; j++) {
02118         image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
02119                                                 (*(curXor + 2) << 16) +
02120                                                 (*(curXor + 1) << 8) +
02121                                                 (*curXor);
02122         curXor += 4;
02123       }
02124     }
02125 
02126     break;
02127   default:
02128     goto cleanup;
02129     break;
02130   }
02131 
02132   ret = XcursorImageLoadCursor(_display, image);
02133 
02134 cleanup:
02135   XcursorImageDestroy(image);
02136   delete[] entries;
02137   delete[] palette;
02138   delete[] xorBmp;
02139   delete[] andBmp;
02140 
02141   return ret;
02142 }
02143 #endif  // HAVE_XCURSOR
02144 
 All Classes Functions Variables Enumerations