Panda3D
|
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