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