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