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