Panda3D
|
00001 // Filename: eglGraphicsWindow.cxx 00002 // Created by: pro-rsoft (21May09) 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 "eglGraphicsWindow.h" 00016 #include "eglGraphicsStateGuardian.h" 00017 #include "config_egldisplay.h" 00018 #include "eglGraphicsPipe.h" 00019 00020 #include "graphicsPipe.h" 00021 #include "keyboardButton.h" 00022 #include "mouseButton.h" 00023 #include "clockObject.h" 00024 #include "pStatTimer.h" 00025 #include "textEncoder.h" 00026 #include "throw_event.h" 00027 #include "lightReMutexHolder.h" 00028 #include "nativeWindowHandle.h" 00029 00030 #include <errno.h> 00031 #include <sys/time.h> 00032 #include <X11/keysym.h> 00033 #include <X11/Xutil.h> 00034 #include <X11/Xatom.h> 00035 00036 #ifdef HAVE_LINUX_INPUT_H 00037 #include <linux/input.h> 00038 #endif 00039 00040 TypeHandle eglGraphicsWindow::_type_handle; 00041 00042 #define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7))) 00043 00044 //////////////////////////////////////////////////////////////////// 00045 // Function: eglGraphicsWindow::Constructor 00046 // Access: Public 00047 // Description: 00048 //////////////////////////////////////////////////////////////////// 00049 eglGraphicsWindow:: 00050 eglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, 00051 const string &name, 00052 const FrameBufferProperties &fb_prop, 00053 const WindowProperties &win_prop, 00054 int flags, 00055 GraphicsStateGuardian *gsg, 00056 GraphicsOutput *host) : 00057 GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) 00058 { 00059 eglGraphicsPipe *egl_pipe; 00060 DCAST_INTO_V(egl_pipe, _pipe); 00061 _display = egl_pipe->get_display(); 00062 _screen = egl_pipe->get_screen(); 00063 _xwindow = (Window)NULL; 00064 _ic = (XIC)NULL; 00065 _egl_display = egl_pipe->_egl_display; 00066 _egl_surface = 0; 00067 _awaiting_configure = false; 00068 _wm_delete_window = egl_pipe->_wm_delete_window; 00069 _net_wm_window_type = egl_pipe->_net_wm_window_type; 00070 _net_wm_window_type_splash = egl_pipe->_net_wm_window_type_splash; 00071 _net_wm_window_type_fullscreen = egl_pipe->_net_wm_window_type_fullscreen; 00072 _net_wm_state = egl_pipe->_net_wm_state; 00073 _net_wm_state_fullscreen = egl_pipe->_net_wm_state_fullscreen; 00074 _net_wm_state_above = egl_pipe->_net_wm_state_above; 00075 _net_wm_state_below = egl_pipe->_net_wm_state_below; 00076 _net_wm_state_add = egl_pipe->_net_wm_state_add; 00077 _net_wm_state_remove = egl_pipe->_net_wm_state_remove; 00078 00079 GraphicsWindowInputDevice device = 00080 GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); 00081 add_input_device(device); 00082 } 00083 00084 //////////////////////////////////////////////////////////////////// 00085 // Function: eglGraphicsWindow::Destructor 00086 // Access: Public, Virtual 00087 // Description: 00088 //////////////////////////////////////////////////////////////////// 00089 eglGraphicsWindow:: 00090 ~eglGraphicsWindow() { 00091 } 00092 00093 //////////////////////////////////////////////////////////////////// 00094 // Function: eglGraphicsWindow::move_pointer 00095 // Access: Published, Virtual 00096 // Description: Forces the pointer to the indicated position within 00097 // the window, if possible. 00098 // 00099 // Returns true if successful, false on failure. This 00100 // may fail if the mouse is not currently within the 00101 // window, or if the API doesn't support this operation. 00102 //////////////////////////////////////////////////////////////////// 00103 bool eglGraphicsWindow:: 00104 move_pointer(int device, int x, int y) { 00105 // Note: this is not thread-safe; it should be called only from App. 00106 // Probably not an issue. 00107 if (device == 0) { 00108 // Move the system mouse pointer. 00109 if (!_properties.get_foreground() || 00110 !_input_devices[0].get_pointer().get_in_window()) { 00111 // If the window doesn't have input focus, or the mouse isn't 00112 // currently within the window, forget it. 00113 return false; 00114 } 00115 00116 const MouseData &md = _input_devices[0].get_pointer(); 00117 if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) { 00118 XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y); 00119 _input_devices[0].set_pointer_in_window(x, y); 00120 } 00121 return true; 00122 } else { 00123 // Move a raw mouse. 00124 if ((device < 1)||(device >= _input_devices.size())) { 00125 return false; 00126 } 00127 _input_devices[device].set_pointer_in_window(x, y); 00128 return true; 00129 } 00130 } 00131 00132 00133 //////////////////////////////////////////////////////////////////// 00134 // Function: eglGraphicsWindow::begin_frame 00135 // Access: Public, Virtual 00136 // Description: This function will be called within the draw thread 00137 // before beginning rendering for a given frame. It 00138 // should do whatever setup is required, and return true 00139 // if the frame should be rendered, or false if it 00140 // should be skipped. 00141 //////////////////////////////////////////////////////////////////// 00142 bool eglGraphicsWindow:: 00143 begin_frame(FrameMode mode, Thread *current_thread) { 00144 PStatTimer timer(_make_current_pcollector, current_thread); 00145 00146 begin_frame_spam(mode); 00147 if (_gsg == (GraphicsStateGuardian *)NULL) { 00148 return false; 00149 } 00150 if (_awaiting_configure) { 00151 // Don't attempt to draw while we have just reconfigured the 00152 // window and we haven't got the notification back yet. 00153 return false; 00154 } 00155 00156 eglGraphicsStateGuardian *eglgsg; 00157 DCAST_INTO_R(eglgsg, _gsg, false); 00158 { 00159 LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); 00160 00161 if (eglGetCurrentDisplay() == _egl_display && 00162 eglGetCurrentSurface(EGL_READ) == _egl_surface && 00163 eglGetCurrentSurface(EGL_DRAW) == _egl_surface && 00164 eglGetCurrentContext() == eglgsg->_context) { 00165 // No need to make the context current again. Short-circuit 00166 // this possibly-expensive call. 00167 } else { 00168 // Need to set the context. 00169 if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) { 00170 egldisplay_cat.error() << "Failed to call eglMakeCurrent: " 00171 << get_egl_error_string(eglGetError()) << "\n"; 00172 } 00173 } 00174 } 00175 00176 // Now that we have made the context current to a window, we can 00177 // reset the GSG state if this is the first time it has been used. 00178 // (We can't just call reset() when we construct the GSG, because 00179 // reset() requires having a current context.) 00180 eglgsg->reset_if_new(); 00181 00182 if (mode == FM_render) { 00183 // begin_render_texture(); 00184 clear_cube_map_selection(); 00185 } 00186 00187 _gsg->set_current_properties(&get_fb_properties()); 00188 return _gsg->begin_frame(current_thread); 00189 } 00190 00191 //////////////////////////////////////////////////////////////////// 00192 // Function: eglGraphicsWindow::end_frame 00193 // Access: Public, Virtual 00194 // Description: This function will be called within the draw thread 00195 // after rendering is completed for a given frame. It 00196 // should do whatever finalization is required. 00197 //////////////////////////////////////////////////////////////////// 00198 void eglGraphicsWindow:: 00199 end_frame(FrameMode mode, Thread *current_thread) { 00200 end_frame_spam(mode); 00201 nassertv(_gsg != (GraphicsStateGuardian *)NULL); 00202 00203 if (mode == FM_render) { 00204 // end_render_texture(); 00205 copy_to_textures(); 00206 } 00207 00208 _gsg->end_frame(current_thread); 00209 00210 if (mode == FM_render) { 00211 trigger_flip(); 00212 if (_one_shot) { 00213 prepare_for_deletion(); 00214 } 00215 clear_cube_map_selection(); 00216 } 00217 } 00218 00219 //////////////////////////////////////////////////////////////////// 00220 // Function: eglGraphicsWindow::begin_flip 00221 // Access: Public, Virtual 00222 // Description: This function will be called within the draw thread 00223 // after end_frame() has been called on all windows, to 00224 // initiate the exchange of the front and back buffers. 00225 // 00226 // This should instruct the window to prepare for the 00227 // flip at the next video sync, but it should not wait. 00228 // 00229 // We have the two separate functions, begin_flip() and 00230 // end_flip(), to make it easier to flip all of the 00231 // windows at the same time. 00232 //////////////////////////////////////////////////////////////////// 00233 void eglGraphicsWindow:: 00234 begin_flip() { 00235 if (_gsg != (GraphicsStateGuardian *)NULL) { 00236 00237 // It doesn't appear to be necessary to ensure the graphics 00238 // context is current before flipping the windows, and insisting 00239 // on doing so can be a significant performance hit. 00240 00241 //make_current(); 00242 00243 LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); 00244 eglSwapBuffers(_egl_display, _egl_surface); 00245 } 00246 } 00247 00248 //////////////////////////////////////////////////////////////////// 00249 // Function: eglGraphicsWindow::process_events 00250 // Access: Public, Virtual 00251 // Description: Do whatever processing is necessary to ensure that 00252 // the window responds to user events. Also, honor any 00253 // requests recently made via request_properties() 00254 // 00255 // This function is called only within the window 00256 // thread. 00257 //////////////////////////////////////////////////////////////////// 00258 void eglGraphicsWindow:: 00259 process_events() { 00260 LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); 00261 00262 GraphicsWindow::process_events(); 00263 00264 if (_xwindow == (Window)0) { 00265 return; 00266 } 00267 00268 poll_raw_mice(); 00269 00270 XEvent event; 00271 XKeyEvent keyrelease_event; 00272 bool got_keyrelease_event = false; 00273 00274 while (XCheckIfEvent(_display, &event, check_event, (char *)this)) { 00275 if (XFilterEvent(&event, None)) { 00276 continue; 00277 } 00278 00279 if (got_keyrelease_event) { 00280 // If a keyrelease event is immediately followed by a matching 00281 // keypress event, that's just key repeat and we should treat 00282 // the two events accordingly. It would be nice if X provided a 00283 // way to differentiate between keyrepeat and explicit 00284 // keypresses more generally. 00285 got_keyrelease_event = false; 00286 00287 if (event.type == KeyPress && 00288 event.xkey.keycode == keyrelease_event.keycode && 00289 (event.xkey.time - keyrelease_event.time <= 1)) { 00290 // In particular, we only generate down messages for the 00291 // repeated keys, not down-and-up messages. 00292 handle_keystroke(event.xkey); 00293 00294 // We thought about not generating the keypress event, but we 00295 // need that repeat for backspace. Rethink later. 00296 handle_keypress(event.xkey); 00297 continue; 00298 00299 } else { 00300 // This keyrelease event is not immediately followed by a 00301 // matching keypress event, so it's a genuine release. 00302 handle_keyrelease(keyrelease_event); 00303 } 00304 } 00305 00306 WindowProperties properties; 00307 ButtonHandle button; 00308 00309 switch (event.type) { 00310 case ReparentNotify: 00311 break; 00312 00313 case ConfigureNotify: 00314 _awaiting_configure = false; 00315 if (_properties.get_fixed_size()) { 00316 // If the window properties indicate a fixed size only, undo 00317 // any attempt by the user to change them. In X, there 00318 // doesn't appear to be a way to universally disallow this 00319 // directly (although we do set the min_size and max_size to 00320 // the same value, which seems to work for most window 00321 // managers.) 00322 WindowProperties current_props = get_properties(); 00323 if (event.xconfigure.width != current_props.get_x_size() || 00324 event.xconfigure.height != current_props.get_y_size()) { 00325 XWindowChanges changes; 00326 changes.width = current_props.get_x_size(); 00327 changes.height = current_props.get_y_size(); 00328 int value_mask = (CWWidth | CWHeight); 00329 XConfigureWindow(_display, _xwindow, value_mask, &changes); 00330 } 00331 00332 } else { 00333 // A normal window may be resized by the user at will. 00334 properties.set_size(event.xconfigure.width, event.xconfigure.height); 00335 system_changed_properties(properties); 00336 } 00337 break; 00338 00339 case ButtonPress: 00340 // This refers to the mouse buttons. 00341 button = get_mouse_button(event.xbutton); 00342 _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); 00343 _input_devices[0].button_down(button); 00344 break; 00345 00346 case ButtonRelease: 00347 button = get_mouse_button(event.xbutton); 00348 _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); 00349 _input_devices[0].button_up(button); 00350 break; 00351 00352 case MotionNotify: 00353 _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y); 00354 break; 00355 00356 case KeyPress: 00357 handle_keystroke(event.xkey); 00358 handle_keypress(event.xkey); 00359 break; 00360 00361 case KeyRelease: 00362 // The KeyRelease can't be processed immediately, because we 00363 // have to check first if it's immediately followed by a 00364 // matching KeyPress event. 00365 keyrelease_event = event.xkey; 00366 got_keyrelease_event = true; 00367 break; 00368 00369 case EnterNotify: 00370 _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y); 00371 break; 00372 00373 case LeaveNotify: 00374 _input_devices[0].set_pointer_out_of_window(); 00375 break; 00376 00377 case FocusIn: 00378 properties.set_foreground(true); 00379 system_changed_properties(properties); 00380 break; 00381 00382 case FocusOut: 00383 properties.set_foreground(false); 00384 system_changed_properties(properties); 00385 break; 00386 00387 case UnmapNotify: 00388 properties.set_minimized(true); 00389 system_changed_properties(properties); 00390 break; 00391 00392 case MapNotify: 00393 properties.set_minimized(false); 00394 system_changed_properties(properties); 00395 00396 // Auto-focus the window when it is mapped. 00397 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime); 00398 break; 00399 00400 case ClientMessage: 00401 if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) { 00402 // This is a message from the window manager indicating that 00403 // the user has requested to close the window. 00404 string close_request_event = get_close_request_event(); 00405 if (!close_request_event.empty()) { 00406 // In this case, the app has indicated a desire to intercept 00407 // the request and process it directly. 00408 throw_event(close_request_event); 00409 00410 } else { 00411 // In this case, the default case, the app does not intend 00412 // to service the request, so we do by closing the window. 00413 00414 // TODO: don't release the gsg in the window thread. 00415 close_window(); 00416 properties.set_open(false); 00417 system_changed_properties(properties); 00418 } 00419 } 00420 break; 00421 00422 case DestroyNotify: 00423 // Apparently, we never get a DestroyNotify on a toplevel 00424 // window. Instead, we rely on hints from the window manager 00425 // (see above). 00426 egldisplay_cat.info() 00427 << "DestroyNotify\n"; 00428 break; 00429 00430 default: 00431 egldisplay_cat.error() 00432 << "unhandled X event type " << event.type << "\n"; 00433 } 00434 } 00435 00436 if (got_keyrelease_event) { 00437 // This keyrelease event is not immediately followed by a 00438 // matching keypress event, so it's a genuine release. 00439 handle_keyrelease(keyrelease_event); 00440 } 00441 } 00442 00443 //////////////////////////////////////////////////////////////////// 00444 // Function: eglGraphicsWindow::set_properties_now 00445 // Access: Public, Virtual 00446 // Description: Applies the requested set of properties to the 00447 // window, if possible, for instance to request a change 00448 // in size or minimization status. 00449 // 00450 // The window properties are applied immediately, rather 00451 // than waiting until the next frame. This implies that 00452 // this method may *only* be called from within the 00453 // window thread. 00454 // 00455 // The return value is true if the properties are set, 00456 // false if they are ignored. This is mainly useful for 00457 // derived classes to implement extensions to this 00458 // function. 00459 //////////////////////////////////////////////////////////////////// 00460 void eglGraphicsWindow:: 00461 set_properties_now(WindowProperties &properties) { 00462 if (_pipe == (GraphicsPipe *)NULL) { 00463 // If the pipe is null, we're probably closing down. 00464 GraphicsWindow::set_properties_now(properties); 00465 return; 00466 } 00467 00468 eglGraphicsPipe *egl_pipe; 00469 DCAST_INTO_V(egl_pipe, _pipe); 00470 00471 // Fullscreen mode is implemented with a hint to the window manager. 00472 // However, we also implicitly set the origin to (0, 0) and the size 00473 // to the desktop size, and request undecorated mode, in case the 00474 // user has a less-capable window manager (or no window manager at 00475 // all). 00476 if (properties.get_fullscreen()) { 00477 properties.set_undecorated(true); 00478 properties.set_origin(0, 0); 00479 properties.set_size(egl_pipe->get_display_width(), 00480 egl_pipe->get_display_height()); 00481 } 00482 00483 GraphicsWindow::set_properties_now(properties); 00484 if (!properties.is_any_specified()) { 00485 // The base class has already handled this case. 00486 return; 00487 } 00488 00489 // The window is already open; we are limited to what we can change 00490 // on the fly. 00491 00492 // We'll pass some property requests on as a window manager hint. 00493 WindowProperties wm_properties = _properties; 00494 wm_properties.add_properties(properties); 00495 00496 // The window title may be changed by issuing another hint request. 00497 // Assume this will be honored. 00498 if (properties.has_title()) { 00499 _properties.set_title(properties.get_title()); 00500 properties.clear_title(); 00501 } 00502 00503 // Ditto for fullscreen mode. 00504 if (properties.has_fullscreen()) { 00505 _properties.set_fullscreen(properties.get_fullscreen()); 00506 properties.clear_fullscreen(); 00507 } 00508 00509 // The size and position of an already-open window are changed via 00510 // explicit X calls. These may still get intercepted by the window 00511 // manager. Rather than changing _properties immediately, we'll 00512 // wait for the ConfigureNotify message to come back. 00513 XWindowChanges changes; 00514 int value_mask = 0; 00515 00516 if (properties.has_origin()) { 00517 changes.x = properties.get_x_origin(); 00518 changes.y = properties.get_y_origin(); 00519 value_mask |= (CWX | CWY); 00520 properties.clear_origin(); 00521 } 00522 if (properties.has_size()) { 00523 changes.width = properties.get_x_size(); 00524 changes.height = properties.get_y_size(); 00525 value_mask |= (CWWidth | CWHeight); 00526 properties.clear_size(); 00527 } 00528 if (properties.has_z_order()) { 00529 // We'll send the classic stacking request through the standard 00530 // interface, for users of primitive window managers; but we'll 00531 // also send it as a window manager hint, for users of modern 00532 // window managers. 00533 _properties.set_z_order(properties.get_z_order()); 00534 switch (properties.get_z_order()) { 00535 case WindowProperties::Z_bottom: 00536 changes.stack_mode = Below; 00537 break; 00538 00539 case WindowProperties::Z_normal: 00540 changes.stack_mode = TopIf; 00541 break; 00542 00543 case WindowProperties::Z_top: 00544 changes.stack_mode = Above; 00545 break; 00546 } 00547 00548 value_mask |= (CWStackMode); 00549 properties.clear_z_order(); 00550 } 00551 00552 if (value_mask != 0) { 00553 XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes); 00554 00555 // Don't draw anything until this is done reconfiguring. 00556 _awaiting_configure = true; 00557 } 00558 00559 // We hide the cursor by setting it to an invisible pixmap. 00560 if (properties.has_cursor_hidden()) { 00561 _properties.set_cursor_hidden(properties.get_cursor_hidden()); 00562 if (properties.get_cursor_hidden()) { 00563 XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor()); 00564 } else { 00565 XDefineCursor(_display, _xwindow, None); 00566 } 00567 properties.clear_cursor_hidden(); 00568 } 00569 00570 if (properties.has_foreground()) { 00571 if (properties.get_foreground()) { 00572 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime); 00573 } else { 00574 XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime); 00575 } 00576 properties.clear_foreground(); 00577 } 00578 00579 set_wm_properties(wm_properties, true); 00580 } 00581 00582 //////////////////////////////////////////////////////////////////// 00583 // Function: eglGraphicsWindow::close_window 00584 // Access: Protected, Virtual 00585 // Description: Closes the window right now. Called from the window 00586 // thread. 00587 //////////////////////////////////////////////////////////////////// 00588 void eglGraphicsWindow:: 00589 close_window() { 00590 if (_gsg != (GraphicsStateGuardian *)NULL) { 00591 if (!eglMakeCurrent(_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { 00592 egldisplay_cat.error() << "Failed to call eglMakeCurrent: " 00593 << get_egl_error_string(eglGetError()) << "\n"; 00594 } 00595 _gsg.clear(); 00596 _active = false; 00597 } 00598 00599 if (_ic != (XIC)NULL) { 00600 XDestroyIC(_ic); 00601 _ic = (XIC)NULL; 00602 } 00603 00604 if (_egl_surface != 0) { 00605 if (!eglDestroySurface(_egl_display, _egl_surface)) { 00606 egldisplay_cat.error() << "Failed to destroy surface: " 00607 << get_egl_error_string(eglGetError()) << "\n"; 00608 } 00609 } 00610 00611 if (_xwindow != (Window)NULL) { 00612 XDestroyWindow(_display, _xwindow); 00613 _xwindow = (Window)NULL; 00614 00615 // This may be necessary if we just closed the last X window in an 00616 // application, so the server hears the close request. 00617 XFlush(_display); 00618 } 00619 GraphicsWindow::close_window(); 00620 } 00621 00622 //////////////////////////////////////////////////////////////////// 00623 // Function: eglGraphicsWindow::open_window 00624 // Access: Protected, Virtual 00625 // Description: Opens the window right now. Called from the window 00626 // thread. Returns true if the window is successfully 00627 // opened, or false if there was a problem. 00628 //////////////////////////////////////////////////////////////////// 00629 bool eglGraphicsWindow:: 00630 open_window() { 00631 eglGraphicsPipe *egl_pipe; 00632 DCAST_INTO_R(egl_pipe, _pipe, false); 00633 00634 // GSG Creation/Initialization 00635 eglGraphicsStateGuardian *eglgsg; 00636 if (_gsg == 0) { 00637 // There is no old gsg. Create a new one. 00638 eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, NULL); 00639 eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false); 00640 _gsg = eglgsg; 00641 } else { 00642 // If the old gsg has the wrong pixel format, create a 00643 // new one that shares with the old gsg. 00644 DCAST_INTO_R(eglgsg, _gsg, false); 00645 if (!eglgsg->get_fb_properties().subsumes(_fb_properties)) { 00646 eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, eglgsg); 00647 eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false); 00648 _gsg = eglgsg; 00649 } 00650 } 00651 00652 XVisualInfo *visual_info = eglgsg->_visual; 00653 if (visual_info == NULL) { 00654 // No X visual for this fbconfig; how can we open the window? 00655 egldisplay_cat.error() 00656 << "No X visual: cannot open window.\n"; 00657 return false; 00658 } 00659 Visual *visual = visual_info->visual; 00660 int depth = visual_info->depth; 00661 00662 if (!_properties.has_origin()) { 00663 _properties.set_origin(0, 0); 00664 } 00665 if (!_properties.has_size()) { 00666 _properties.set_size(100, 100); 00667 } 00668 00669 Window parent_window = egl_pipe->get_root(); 00670 WindowHandle *window_handle = _properties.get_parent_window(); 00671 if (window_handle != NULL) { 00672 egldisplay_cat.info() 00673 << "Got parent_window " << *window_handle << "\n"; 00674 WindowHandle::OSHandle *os_handle = window_handle->get_os_handle(); 00675 if (os_handle != NULL) { 00676 egldisplay_cat.info() 00677 << "os_handle type " << os_handle->get_type() << "\n"; 00678 00679 if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) { 00680 NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle); 00681 parent_window = x11_handle->get_handle(); 00682 } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) { 00683 NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle); 00684 parent_window = (Window)int_handle->get_handle(); 00685 } 00686 } 00687 } 00688 _parent_window_handle = window_handle; 00689 00690 setup_colormap(visual_info); 00691 00692 _event_mask = 00693 ButtonPressMask | ButtonReleaseMask | 00694 KeyPressMask | KeyReleaseMask | 00695 EnterWindowMask | LeaveWindowMask | 00696 PointerMotionMask | 00697 FocusChangeMask | 00698 StructureNotifyMask; 00699 00700 // Initialize window attributes 00701 XSetWindowAttributes wa; 00702 wa.background_pixel = XBlackPixel(_display, _screen); 00703 wa.border_pixel = 0; 00704 wa.colormap = _colormap; 00705 wa.event_mask = _event_mask; 00706 00707 unsigned long attrib_mask = 00708 CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; 00709 00710 _xwindow = XCreateWindow 00711 (_display, parent_window, 00712 _properties.get_x_origin(), _properties.get_y_origin(), 00713 _properties.get_x_size(), _properties.get_y_size(), 00714 0, depth, InputOutput, visual, attrib_mask, &wa); 00715 00716 if (_xwindow == (Window)0) { 00717 egldisplay_cat.error() 00718 << "failed to create X window.\n"; 00719 return false; 00720 } 00721 set_wm_properties(_properties, false); 00722 00723 // We don't specify any fancy properties of the XIC. It would be 00724 // nicer if we could support fancy IM's that want preedit callbacks, 00725 // etc., but that can wait until we have an X server that actually 00726 // supports these to test it on. 00727 XIM im = egl_pipe->get_im(); 00728 _ic = NULL; 00729 if (im) { 00730 _ic = XCreateIC 00731 (im, 00732 XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 00733 (void*)NULL); 00734 if (_ic == (XIC)NULL) { 00735 egldisplay_cat.warning() 00736 << "Couldn't create input context.\n"; 00737 } 00738 } 00739 00740 if (_properties.get_cursor_hidden()) { 00741 XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor()); 00742 } 00743 00744 _egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, NULL); 00745 if (eglGetError() != EGL_SUCCESS) { 00746 egldisplay_cat.error() 00747 << "Failed to create window surface.\n"; 00748 return false; 00749 } 00750 00751 if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) { 00752 egldisplay_cat.error() << "Failed to call eglMakeCurrent: " 00753 << get_egl_error_string(eglGetError()) << "\n"; 00754 } 00755 eglgsg->reset_if_new(); 00756 if (!eglgsg->is_valid()) { 00757 close_window(); 00758 return false; 00759 } 00760 if (!eglgsg->get_fb_properties().verify_hardware_software 00761 (_fb_properties, eglgsg->get_gl_renderer())) { 00762 close_window(); 00763 return false; 00764 } 00765 _fb_properties = eglgsg->get_fb_properties(); 00766 00767 XMapWindow(_display, _xwindow); 00768 00769 if (_properties.get_raw_mice()) { 00770 open_raw_mice(); 00771 } else { 00772 if (egldisplay_cat.is_debug()) { 00773 egldisplay_cat.debug() 00774 << "Raw mice not requested.\n"; 00775 } 00776 } 00777 00778 // Create a WindowHandle for ourselves 00779 _window_handle = NativeWindowHandle::make_x11(_xwindow); 00780 00781 // And tell our parent window that we're now its child. 00782 if (_parent_window_handle != (WindowHandle *)NULL) { 00783 _parent_window_handle->attach_child(_window_handle); 00784 } 00785 00786 return true; 00787 } 00788 00789 //////////////////////////////////////////////////////////////////// 00790 // Function: eglGraphicsWindow::set_wm_properties 00791 // Access: Private 00792 // Description: Asks the window manager to set the appropriate 00793 // properties. In X, these properties cannot be 00794 // specified directly by the application; they must be 00795 // requested via the window manager, which may or may 00796 // not choose to honor the request. 00797 // 00798 // If already_mapped is true, the window has already 00799 // been mapped (manifested) on the display. This means 00800 // we may need to use a different action in some cases. 00801 //////////////////////////////////////////////////////////////////// 00802 void eglGraphicsWindow:: 00803 set_wm_properties(const WindowProperties &properties, bool already_mapped) { 00804 // Name the window if there is a name 00805 XTextProperty window_name; 00806 XTextProperty *window_name_p = (XTextProperty *)NULL; 00807 if (properties.has_title()) { 00808 char *name = (char *)properties.get_title().c_str(); 00809 if (XStringListToTextProperty(&name, 1, &window_name) != 0) { 00810 window_name_p = &window_name; 00811 } 00812 } 00813 00814 // The size hints request a window of a particular size and/or a 00815 // particular placement onscreen. 00816 XSizeHints *size_hints_p = NULL; 00817 if (properties.has_origin() || properties.has_size()) { 00818 size_hints_p = XAllocSizeHints(); 00819 if (size_hints_p != (XSizeHints *)NULL) { 00820 if (properties.has_origin()) { 00821 size_hints_p->x = properties.get_x_origin(); 00822 size_hints_p->y = properties.get_y_origin(); 00823 size_hints_p->flags |= USPosition; 00824 } 00825 if (properties.has_size()) { 00826 size_hints_p->width = properties.get_x_size(); 00827 size_hints_p->height = properties.get_y_size(); 00828 size_hints_p->flags |= USSize; 00829 00830 if (properties.get_fixed_size()) { 00831 size_hints_p->min_width = properties.get_x_size(); 00832 size_hints_p->min_height = properties.get_y_size(); 00833 size_hints_p->max_width = properties.get_x_size(); 00834 size_hints_p->max_height = properties.get_y_size(); 00835 size_hints_p->flags |= (PMinSize | PMaxSize); 00836 } 00837 } 00838 } 00839 } 00840 00841 // The window manager hints include requests to the window manager 00842 // other than those specific to window geometry. 00843 XWMHints *wm_hints_p = NULL; 00844 wm_hints_p = XAllocWMHints(); 00845 if (wm_hints_p != (XWMHints *)NULL) { 00846 if (properties.has_minimized() && properties.get_minimized()) { 00847 wm_hints_p->initial_state = IconicState; 00848 } else { 00849 wm_hints_p->initial_state = NormalState; 00850 } 00851 wm_hints_p->flags = StateHint; 00852 } 00853 00854 // Two competing window manager interfaces have evolved. One of 00855 // them allows to set certain properties as a "type"; the other one 00856 // as a "state". We'll try to honor both. 00857 static const int max_type_data = 32; 00858 PN_int32 type_data[max_type_data]; 00859 int next_type_data = 0; 00860 00861 static const int max_state_data = 32; 00862 PN_int32 state_data[max_state_data]; 00863 int next_state_data = 0; 00864 00865 static const int max_set_data = 32; 00866 class SetAction { 00867 public: 00868 inline SetAction() { } 00869 inline SetAction(Atom state, Atom action) : _state(state), _action(action) { } 00870 Atom _state; 00871 Atom _action; 00872 }; 00873 SetAction set_data[max_set_data]; 00874 int next_set_data = 0; 00875 00876 if (properties.get_fullscreen()) { 00877 // For a "fullscreen" request, we pass this through, hoping the 00878 // window manager will support EWMH. 00879 type_data[next_type_data++] = _net_wm_window_type_fullscreen; 00880 00881 // We also request it as a state. 00882 state_data[next_state_data++] = _net_wm_state_fullscreen; 00883 set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_add); 00884 } else { 00885 set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_remove); 00886 } 00887 00888 // If we asked for a window without a border, there's no excellent 00889 // way to arrange that. For users whose window managers follow the 00890 // EWMH specification, we can ask for a "splash" screen, which is 00891 // usually undecorated. It's not exactly right, but the spec 00892 // doesn't give us an exactly-right option. 00893 00894 // For other users, we'll totally punt and just set the window's 00895 // Class to "Undecorated", and let the user configure his/her window 00896 // manager not to put a border around windows of this class. 00897 XClassHint *class_hints_p = NULL; 00898 if (properties.get_undecorated()) { 00899 class_hints_p = XAllocClassHint(); 00900 class_hints_p->res_class = (char*) "Undecorated"; 00901 00902 if (!properties.get_fullscreen()) { 00903 type_data[next_type_data++] = _net_wm_window_type_splash; 00904 } 00905 } 00906 00907 if (properties.has_z_order()) { 00908 switch (properties.get_z_order()) { 00909 case WindowProperties::Z_bottom: 00910 state_data[next_state_data++] = _net_wm_state_below; 00911 set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_add); 00912 set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove); 00913 break; 00914 00915 case WindowProperties::Z_normal: 00916 set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove); 00917 set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove); 00918 break; 00919 00920 case WindowProperties::Z_top: 00921 state_data[next_state_data++] = _net_wm_state_above; 00922 set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove); 00923 set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_add); 00924 break; 00925 } 00926 } 00927 00928 nassertv(next_type_data < max_type_data); 00929 nassertv(next_state_data < max_state_data); 00930 nassertv(next_set_data < max_set_data); 00931 00932 XChangeProperty(_display, _xwindow, _net_wm_window_type, 00933 XA_ATOM, 32, PropModeReplace, 00934 (unsigned char *)type_data, next_type_data); 00935 00936 // Request the state properties all at once. 00937 XChangeProperty(_display, _xwindow, _net_wm_state, 00938 XA_ATOM, 32, PropModeReplace, 00939 (unsigned char *)state_data, next_state_data); 00940 00941 if (already_mapped) { 00942 // We have to request state changes differently when the window 00943 // has been mapped. To do this, we need to send a client message 00944 // to the root window for each change. 00945 00946 eglGraphicsPipe *egl_pipe; 00947 DCAST_INTO_V(egl_pipe, _pipe); 00948 00949 for (int i = 0; i < next_set_data; ++i) { 00950 XClientMessageEvent event; 00951 memset(&event, 0, sizeof(event)); 00952 00953 event.type = ClientMessage; 00954 event.send_event = True; 00955 event.display = _display; 00956 event.window = _xwindow; 00957 event.message_type = _net_wm_state; 00958 event.format = 32; 00959 event.data.l[0] = set_data[i]._action; 00960 event.data.l[1] = set_data[i]._state; 00961 event.data.l[2] = 0; 00962 event.data.l[3] = 1; 00963 00964 XSendEvent(_display, egl_pipe->get_root(), True, 0, (XEvent *)&event); 00965 } 00966 } 00967 00968 XSetWMProperties(_display, _xwindow, window_name_p, window_name_p, 00969 NULL, 0, size_hints_p, wm_hints_p, class_hints_p); 00970 00971 if (size_hints_p != (XSizeHints *)NULL) { 00972 XFree(size_hints_p); 00973 } 00974 if (wm_hints_p != (XWMHints *)NULL) { 00975 XFree(wm_hints_p); 00976 } 00977 if (class_hints_p != (XClassHint *)NULL) { 00978 XFree(class_hints_p); 00979 } 00980 00981 // Also, indicate to the window manager that we'd like to get a 00982 // chance to close our windows cleanly, rather than being rudely 00983 // disconnected from the X server if the user requests a window 00984 // close. 00985 Atom protocols[] = { 00986 _wm_delete_window, 00987 }; 00988 00989 XSetWMProtocols(_display, _xwindow, protocols, 00990 sizeof(protocols) / sizeof(Atom)); 00991 } 00992 00993 //////////////////////////////////////////////////////////////////// 00994 // Function: eglGraphicsWindow::setup_colormap 00995 // Access: Private 00996 // Description: Allocates a colormap appropriate to the visual and 00997 // stores in in the _colormap method. 00998 //////////////////////////////////////////////////////////////////// 00999 void eglGraphicsWindow:: 01000 setup_colormap(XVisualInfo *visual) { 01001 eglGraphicsPipe *egl_pipe; 01002 DCAST_INTO_V(egl_pipe, _pipe); 01003 Window root_window = egl_pipe->get_root(); 01004 01005 int visual_class = visual->c_class; 01006 int rc, is_rgb; 01007 01008 switch (visual_class) { 01009 case PseudoColor: 01010 _colormap = XCreateColormap(_display, root_window, 01011 visual->visual, AllocAll); 01012 break; 01013 case TrueColor: 01014 case DirectColor: 01015 _colormap = XCreateColormap(_display, root_window, 01016 visual->visual, AllocNone); 01017 break; 01018 case StaticColor: 01019 case StaticGray: 01020 case GrayScale: 01021 _colormap = XCreateColormap(_display, root_window, 01022 visual->visual, AllocNone); 01023 break; 01024 default: 01025 egldisplay_cat.error() 01026 << "Could not allocate a colormap for visual class " 01027 << visual_class << ".\n"; 01028 break; 01029 } 01030 } 01031 01032 //////////////////////////////////////////////////////////////////// 01033 // Function: eglGraphicsWindow::open_raw_mice 01034 // Access: Private 01035 // Description: Adds raw mice to the _input_devices list. 01036 //////////////////////////////////////////////////////////////////// 01037 void eglGraphicsWindow:: 01038 open_raw_mice() 01039 { 01040 #ifdef HAVE_LINUX_INPUT_H 01041 bool any_present = false; 01042 bool any_mice = false; 01043 01044 for (int i=0; i<64; i++) { 01045 uint8_t evtypes[EV_MAX/8 + 1]; 01046 ostringstream fnb; 01047 fnb << "/dev/input/event" << i; 01048 string fn = fnb.str(); 01049 int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0); 01050 if (fd >= 0) { 01051 any_present = true; 01052 char name[256]; 01053 char phys[256]; 01054 char uniq[256]; 01055 if ((ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)|| 01056 (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0)|| 01057 (ioctl(fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0)|| 01058 (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) { 01059 close(fd); 01060 egldisplay_cat.error() << 01061 "Opening raw mice: ioctl failed on " << fn << "\n"; 01062 } else { 01063 if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) { 01064 for (char *p=name; *p; p++) { 01065 if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) { 01066 *p = '_'; 01067 } 01068 } 01069 for (char *p=uniq; *p; p++) { 01070 if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) { 01071 *p = '_'; 01072 } 01073 } 01074 string full_id = ((string)name) + "." + uniq; 01075 MouseDeviceInfo inf; 01076 inf._fd = fd; 01077 inf._input_device_index = _input_devices.size(); 01078 inf._io_buffer = ""; 01079 _mouse_device_info.push_back(inf); 01080 GraphicsWindowInputDevice device = 01081 GraphicsWindowInputDevice::pointer_only(this, full_id); 01082 add_input_device(device); 01083 egldisplay_cat.info() << "Raw mouse " << 01084 inf._input_device_index << " detected: " << full_id << "\n"; 01085 any_mice = true; 01086 } else { 01087 close(fd); 01088 } 01089 } 01090 } else { 01091 if ((errno == ENOENT)||(errno == ENOTDIR)) { 01092 break; 01093 } else { 01094 any_present = true; 01095 egldisplay_cat.error() << 01096 "Opening raw mice: " << strerror(errno) << " " << fn << "\n"; 01097 } 01098 } 01099 } 01100 01101 if (!any_present) { 01102 egldisplay_cat.error() << 01103 "Opening raw mice: files not found: /dev/input/event*\n"; 01104 } else if (!any_mice) { 01105 egldisplay_cat.error() << 01106 "Opening raw mice: no mouse devices detected in /dev/input/event*\n"; 01107 } 01108 #else 01109 egldisplay_cat.error() << 01110 "Opening raw mice: panda not compiled with raw mouse support.\n"; 01111 #endif 01112 } 01113 01114 //////////////////////////////////////////////////////////////////// 01115 // Function: eglGraphicsWindow::poll_raw_mice 01116 // Access: Private 01117 // Description: Reads events from the raw mouse device files. 01118 //////////////////////////////////////////////////////////////////// 01119 void eglGraphicsWindow:: 01120 poll_raw_mice() 01121 { 01122 #ifdef HAVE_LINUX_INPUT_H 01123 for (int dev=0; dev<_mouse_device_info.size(); dev++) { 01124 MouseDeviceInfo &inf = _mouse_device_info[dev]; 01125 01126 // Read all bytes into buffer. 01127 if (inf._fd >= 0) { 01128 while (1) { 01129 char tbuf[1024]; 01130 int nread = read(inf._fd, tbuf, sizeof(tbuf)); 01131 if (nread > 0) { 01132 inf._io_buffer += string(tbuf, nread); 01133 } else { 01134 if ((nread < 0)&&((errno == EWOULDBLOCK) || (errno==EAGAIN))) { 01135 break; 01136 } 01137 close(inf._fd); 01138 inf._fd = -1; 01139 break; 01140 } 01141 } 01142 } 01143 01144 // Process events. 01145 int nevents = inf._io_buffer.size() / sizeof(struct input_event); 01146 if (nevents == 0) { 01147 continue; 01148 } 01149 const input_event *events = (const input_event *)(inf._io_buffer.c_str()); 01150 GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index]; 01151 int x = dev.get_raw_pointer().get_x(); 01152 int y = dev.get_raw_pointer().get_y(); 01153 for (int i=0; i<nevents; i++) { 01154 if (events[i].type == EV_REL) { 01155 if (events[i].code == REL_X) x += events[i].value; 01156 if (events[i].code == REL_Y) y += events[i].value; 01157 } else if (events[i].type == EV_ABS) { 01158 if (events[i].code == ABS_X) x = events[i].value; 01159 if (events[i].code == ABS_Y) y = events[i].value; 01160 } else if (events[i].type == EV_KEY) { 01161 if ((events[i].code >= BTN_MOUSE)&&(events[i].code < BTN_MOUSE+8)) { 01162 int btn = events[i].code - BTN_MOUSE; 01163 dev.set_pointer_in_window(x,y); 01164 if (events[i].value) { 01165 dev.button_down(MouseButton::button(btn)); 01166 } else { 01167 dev.button_up(MouseButton::button(btn)); 01168 } 01169 } 01170 } 01171 } 01172 inf._io_buffer.erase(0,nevents*sizeof(struct input_event)); 01173 dev.set_pointer_in_window(x,y); 01174 } 01175 #endif 01176 } 01177 01178 //////////////////////////////////////////////////////////////////// 01179 // Function: eglGraphicsWindow::handle_keystroke 01180 // Access: Private 01181 // Description: Generates a keystroke corresponding to the indicated 01182 // X KeyPress event. 01183 //////////////////////////////////////////////////////////////////// 01184 void eglGraphicsWindow:: 01185 handle_keystroke(XKeyEvent &event) { 01186 _input_devices[0].set_pointer_in_window(event.x, event.y); 01187 01188 if (_ic) { 01189 // First, get the keystroke as a wide-character sequence. 01190 static const int buffer_size = 256; 01191 wchar_t buffer[buffer_size]; 01192 Status status; 01193 int len = XwcLookupString(_ic, &event, buffer, buffer_size, NULL, 01194 &status); 01195 if (status == XBufferOverflow) { 01196 egldisplay_cat.error() 01197 << "Overflowed input buffer.\n"; 01198 } 01199 01200 // Now each of the returned wide characters represents a 01201 // keystroke. 01202 for (int i = 0; i < len; i++) { 01203 _input_devices[0].keystroke(buffer[i]); 01204 } 01205 01206 } else { 01207 // Without an input context, just get the ascii keypress. 01208 ButtonHandle button = get_button(event, true); 01209 if (button.has_ascii_equivalent()) { 01210 _input_devices[0].keystroke(button.get_ascii_equivalent()); 01211 } 01212 } 01213 } 01214 01215 //////////////////////////////////////////////////////////////////// 01216 // Function: eglGraphicsWindow::handle_keypress 01217 // Access: Private 01218 // Description: Generates a keypress corresponding to the indicated 01219 // X KeyPress event. 01220 //////////////////////////////////////////////////////////////////// 01221 void eglGraphicsWindow:: 01222 handle_keypress(XKeyEvent &event) { 01223 _input_devices[0].set_pointer_in_window(event.x, event.y); 01224 01225 // Now get the raw unshifted button. 01226 ButtonHandle button = get_button(event, false); 01227 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { 01228 _input_devices[0].button_down(KeyboardButton::control()); 01229 } 01230 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { 01231 _input_devices[0].button_down(KeyboardButton::shift()); 01232 } 01233 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { 01234 _input_devices[0].button_down(KeyboardButton::alt()); 01235 } 01236 if (button != ButtonHandle::none()) { 01237 _input_devices[0].button_down(button); 01238 } 01239 } 01240 01241 //////////////////////////////////////////////////////////////////// 01242 // Function: eglGraphicsWindow::handle_keyrelease 01243 // Access: Private 01244 // Description: Generates a keyrelease corresponding to the indicated 01245 // X KeyRelease event. 01246 //////////////////////////////////////////////////////////////////// 01247 void eglGraphicsWindow:: 01248 handle_keyrelease(XKeyEvent &event) { 01249 _input_devices[0].set_pointer_in_window(event.x, event.y); 01250 01251 // Now get the raw unshifted button. 01252 ButtonHandle button = get_button(event, false); 01253 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) { 01254 _input_devices[0].button_up(KeyboardButton::control()); 01255 } 01256 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) { 01257 _input_devices[0].button_up(KeyboardButton::shift()); 01258 } 01259 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) { 01260 _input_devices[0].button_up(KeyboardButton::alt()); 01261 } 01262 if (button != ButtonHandle::none()) { 01263 _input_devices[0].button_up(button); 01264 } 01265 } 01266 01267 //////////////////////////////////////////////////////////////////// 01268 // Function: eglGraphicsWindow::get_button 01269 // Access: Private 01270 // Description: Returns the Panda ButtonHandle corresponding to the 01271 // keyboard button indicated by the given key event. 01272 //////////////////////////////////////////////////////////////////// 01273 ButtonHandle eglGraphicsWindow:: 01274 get_button(XKeyEvent &key_event, bool allow_shift) { 01275 KeySym key = XLookupKeysym(&key_event, 0); 01276 01277 if ((key_event.state & Mod2Mask) != 0) { 01278 // Mod2Mask corresponds to NumLock being in effect. In this case, 01279 // we want to get the alternate keysym associated with any keypad 01280 // keys. Weird system. 01281 KeySym k2; 01282 ButtonHandle button; 01283 switch (key) { 01284 case XK_KP_Space: 01285 case XK_KP_Tab: 01286 case XK_KP_Enter: 01287 case XK_KP_F1: 01288 case XK_KP_F2: 01289 case XK_KP_F3: 01290 case XK_KP_F4: 01291 case XK_KP_Equal: 01292 case XK_KP_Multiply: 01293 case XK_KP_Add: 01294 case XK_KP_Separator: 01295 case XK_KP_Subtract: 01296 case XK_KP_Divide: 01297 case XK_KP_Left: 01298 case XK_KP_Up: 01299 case XK_KP_Right: 01300 case XK_KP_Down: 01301 case XK_KP_Begin: 01302 case XK_KP_Prior: 01303 case XK_KP_Next: 01304 case XK_KP_Home: 01305 case XK_KP_End: 01306 case XK_KP_Insert: 01307 case XK_KP_Delete: 01308 case XK_KP_0: 01309 case XK_KP_1: 01310 case XK_KP_2: 01311 case XK_KP_3: 01312 case XK_KP_4: 01313 case XK_KP_5: 01314 case XK_KP_6: 01315 case XK_KP_7: 01316 case XK_KP_8: 01317 case XK_KP_9: 01318 k2 = XLookupKeysym(&key_event, 1); 01319 button = map_button(k2); 01320 if (button != ButtonHandle::none()) { 01321 return button; 01322 } 01323 // If that didn't produce a button we know, just fall through 01324 // and handle the normal, un-numlocked key. 01325 break; 01326 01327 default: 01328 break; 01329 } 01330 } 01331 01332 if (allow_shift) { 01333 // If shift is held down, get the shifted keysym. 01334 if ((key_event.state & ShiftMask) != 0) { 01335 KeySym k2 = XLookupKeysym(&key_event, 1); 01336 ButtonHandle button = map_button(k2); 01337 if (button != ButtonHandle::none()) { 01338 return button; 01339 } 01340 } 01341 01342 // If caps lock is down, shift lowercase letters to uppercase. We 01343 // can do this in just the ASCII set, because we handle 01344 // international keyboards elsewhere (via an input context). 01345 if ((key_event.state & (ShiftMask | LockMask)) != 0) { 01346 if (key >= XK_a and key <= XK_z) { 01347 key += (XK_A - XK_a); 01348 } 01349 } 01350 } 01351 01352 return map_button(key); 01353 } 01354 01355 //////////////////////////////////////////////////////////////////// 01356 // Function: eglGraphicsWindow::map_button 01357 // Access: Private 01358 // Description: Maps from a single X keysym to Panda's ButtonHandle. 01359 // Called by get_button(), above. 01360 //////////////////////////////////////////////////////////////////// 01361 ButtonHandle eglGraphicsWindow:: 01362 map_button(KeySym key) { 01363 switch (key) { 01364 case XK_BackSpace: 01365 return KeyboardButton::backspace(); 01366 case XK_Tab: 01367 case XK_KP_Tab: 01368 return KeyboardButton::tab(); 01369 case XK_Return: 01370 case XK_KP_Enter: 01371 return KeyboardButton::enter(); 01372 case XK_Escape: 01373 return KeyboardButton::escape(); 01374 case XK_KP_Space: 01375 case XK_space: 01376 return KeyboardButton::space(); 01377 case XK_exclam: 01378 return KeyboardButton::ascii_key('!'); 01379 case XK_quotedbl: 01380 return KeyboardButton::ascii_key('"'); 01381 case XK_numbersign: 01382 return KeyboardButton::ascii_key('#'); 01383 case XK_dollar: 01384 return KeyboardButton::ascii_key('$'); 01385 case XK_percent: 01386 return KeyboardButton::ascii_key('%'); 01387 case XK_ampersand: 01388 return KeyboardButton::ascii_key('&'); 01389 case XK_apostrophe: // == XK_quoteright 01390 return KeyboardButton::ascii_key('\''); 01391 case XK_parenleft: 01392 return KeyboardButton::ascii_key('('); 01393 case XK_parenright: 01394 return KeyboardButton::ascii_key(')'); 01395 case XK_asterisk: 01396 case XK_KP_Multiply: 01397 return KeyboardButton::ascii_key('*'); 01398 case XK_plus: 01399 case XK_KP_Add: 01400 return KeyboardButton::ascii_key('+'); 01401 case XK_comma: 01402 case XK_KP_Separator: 01403 return KeyboardButton::ascii_key(','); 01404 case XK_minus: 01405 case XK_KP_Subtract: 01406 return KeyboardButton::ascii_key('-'); 01407 case XK_period: 01408 case XK_KP_Decimal: 01409 return KeyboardButton::ascii_key('.'); 01410 case XK_slash: 01411 case XK_KP_Divide: 01412 return KeyboardButton::ascii_key('/'); 01413 case XK_0: 01414 case XK_KP_0: 01415 return KeyboardButton::ascii_key('0'); 01416 case XK_1: 01417 case XK_KP_1: 01418 return KeyboardButton::ascii_key('1'); 01419 case XK_2: 01420 case XK_KP_2: 01421 return KeyboardButton::ascii_key('2'); 01422 case XK_3: 01423 case XK_KP_3: 01424 return KeyboardButton::ascii_key('3'); 01425 case XK_4: 01426 case XK_KP_4: 01427 return KeyboardButton::ascii_key('4'); 01428 case XK_5: 01429 case XK_KP_5: 01430 return KeyboardButton::ascii_key('5'); 01431 case XK_6: 01432 case XK_KP_6: 01433 return KeyboardButton::ascii_key('6'); 01434 case XK_7: 01435 case XK_KP_7: 01436 return KeyboardButton::ascii_key('7'); 01437 case XK_8: 01438 case XK_KP_8: 01439 return KeyboardButton::ascii_key('8'); 01440 case XK_9: 01441 case XK_KP_9: 01442 return KeyboardButton::ascii_key('9'); 01443 case XK_colon: 01444 return KeyboardButton::ascii_key(':'); 01445 case XK_semicolon: 01446 return KeyboardButton::ascii_key(';'); 01447 case XK_less: 01448 return KeyboardButton::ascii_key('<'); 01449 case XK_equal: 01450 case XK_KP_Equal: 01451 return KeyboardButton::ascii_key('='); 01452 case XK_greater: 01453 return KeyboardButton::ascii_key('>'); 01454 case XK_question: 01455 return KeyboardButton::ascii_key('?'); 01456 case XK_at: 01457 return KeyboardButton::ascii_key('@'); 01458 case XK_A: 01459 return KeyboardButton::ascii_key('A'); 01460 case XK_B: 01461 return KeyboardButton::ascii_key('B'); 01462 case XK_C: 01463 return KeyboardButton::ascii_key('C'); 01464 case XK_D: 01465 return KeyboardButton::ascii_key('D'); 01466 case XK_E: 01467 return KeyboardButton::ascii_key('E'); 01468 case XK_F: 01469 return KeyboardButton::ascii_key('F'); 01470 case XK_G: 01471 return KeyboardButton::ascii_key('G'); 01472 case XK_H: 01473 return KeyboardButton::ascii_key('H'); 01474 case XK_I: 01475 return KeyboardButton::ascii_key('I'); 01476 case XK_J: 01477 return KeyboardButton::ascii_key('J'); 01478 case XK_K: 01479 return KeyboardButton::ascii_key('K'); 01480 case XK_L: 01481 return KeyboardButton::ascii_key('L'); 01482 case XK_M: 01483 return KeyboardButton::ascii_key('M'); 01484 case XK_N: 01485 return KeyboardButton::ascii_key('N'); 01486 case XK_O: 01487 return KeyboardButton::ascii_key('O'); 01488 case XK_P: 01489 return KeyboardButton::ascii_key('P'); 01490 case XK_Q: 01491 return KeyboardButton::ascii_key('Q'); 01492 case XK_R: 01493 return KeyboardButton::ascii_key('R'); 01494 case XK_S: 01495 return KeyboardButton::ascii_key('S'); 01496 case XK_T: 01497 return KeyboardButton::ascii_key('T'); 01498 case XK_U: 01499 return KeyboardButton::ascii_key('U'); 01500 case XK_V: 01501 return KeyboardButton::ascii_key('V'); 01502 case XK_W: 01503 return KeyboardButton::ascii_key('W'); 01504 case XK_X: 01505 return KeyboardButton::ascii_key('X'); 01506 case XK_Y: 01507 return KeyboardButton::ascii_key('Y'); 01508 case XK_Z: 01509 return KeyboardButton::ascii_key('Z'); 01510 case XK_bracketleft: 01511 return KeyboardButton::ascii_key('['); 01512 case XK_backslash: 01513 return KeyboardButton::ascii_key('\\'); 01514 case XK_bracketright: 01515 return KeyboardButton::ascii_key(']'); 01516 case XK_asciicircum: 01517 return KeyboardButton::ascii_key('^'); 01518 case XK_underscore: 01519 return KeyboardButton::ascii_key('_'); 01520 case XK_grave: // == XK_quoteleft 01521 return KeyboardButton::ascii_key('`'); 01522 case XK_a: 01523 return KeyboardButton::ascii_key('a'); 01524 case XK_b: 01525 return KeyboardButton::ascii_key('b'); 01526 case XK_c: 01527 return KeyboardButton::ascii_key('c'); 01528 case XK_d: 01529 return KeyboardButton::ascii_key('d'); 01530 case XK_e: 01531 return KeyboardButton::ascii_key('e'); 01532 case XK_f: 01533 return KeyboardButton::ascii_key('f'); 01534 case XK_g: 01535 return KeyboardButton::ascii_key('g'); 01536 case XK_h: 01537 return KeyboardButton::ascii_key('h'); 01538 case XK_i: 01539 return KeyboardButton::ascii_key('i'); 01540 case XK_j: 01541 return KeyboardButton::ascii_key('j'); 01542 case XK_k: 01543 return KeyboardButton::ascii_key('k'); 01544 case XK_l: 01545 return KeyboardButton::ascii_key('l'); 01546 case XK_m: 01547 return KeyboardButton::ascii_key('m'); 01548 case XK_n: 01549 return KeyboardButton::ascii_key('n'); 01550 case XK_o: 01551 return KeyboardButton::ascii_key('o'); 01552 case XK_p: 01553 return KeyboardButton::ascii_key('p'); 01554 case XK_q: 01555 return KeyboardButton::ascii_key('q'); 01556 case XK_r: 01557 return KeyboardButton::ascii_key('r'); 01558 case XK_s: 01559 return KeyboardButton::ascii_key('s'); 01560 case XK_t: 01561 return KeyboardButton::ascii_key('t'); 01562 case XK_u: 01563 return KeyboardButton::ascii_key('u'); 01564 case XK_v: 01565 return KeyboardButton::ascii_key('v'); 01566 case XK_w: 01567 return KeyboardButton::ascii_key('w'); 01568 case XK_x: 01569 return KeyboardButton::ascii_key('x'); 01570 case XK_y: 01571 return KeyboardButton::ascii_key('y'); 01572 case XK_z: 01573 return KeyboardButton::ascii_key('z'); 01574 case XK_braceleft: 01575 return KeyboardButton::ascii_key('{'); 01576 case XK_bar: 01577 return KeyboardButton::ascii_key('|'); 01578 case XK_braceright: 01579 return KeyboardButton::ascii_key('}'); 01580 case XK_asciitilde: 01581 return KeyboardButton::ascii_key('~'); 01582 case XK_F1: 01583 case XK_KP_F1: 01584 return KeyboardButton::f1(); 01585 case XK_F2: 01586 case XK_KP_F2: 01587 return KeyboardButton::f2(); 01588 case XK_F3: 01589 case XK_KP_F3: 01590 return KeyboardButton::f3(); 01591 case XK_F4: 01592 case XK_KP_F4: 01593 return KeyboardButton::f4(); 01594 case XK_F5: 01595 return KeyboardButton::f5(); 01596 case XK_F6: 01597 return KeyboardButton::f6(); 01598 case XK_F7: 01599 return KeyboardButton::f7(); 01600 case XK_F8: 01601 return KeyboardButton::f8(); 01602 case XK_F9: 01603 return KeyboardButton::f9(); 01604 case XK_F10: 01605 return KeyboardButton::f10(); 01606 case XK_F11: 01607 return KeyboardButton::f11(); 01608 case XK_F12: 01609 return KeyboardButton::f12(); 01610 case XK_KP_Left: 01611 case XK_Left: 01612 return KeyboardButton::left(); 01613 case XK_KP_Up: 01614 case XK_Up: 01615 return KeyboardButton::up(); 01616 case XK_KP_Right: 01617 case XK_Right: 01618 return KeyboardButton::right(); 01619 case XK_KP_Down: 01620 case XK_Down: 01621 return KeyboardButton::down(); 01622 case XK_KP_Prior: 01623 case XK_Prior: 01624 return KeyboardButton::page_up(); 01625 case XK_KP_Next: 01626 case XK_Next: 01627 return KeyboardButton::page_down(); 01628 case XK_KP_Home: 01629 case XK_Home: 01630 return KeyboardButton::home(); 01631 case XK_KP_End: 01632 case XK_End: 01633 return KeyboardButton::end(); 01634 case XK_KP_Insert: 01635 case XK_Insert: 01636 return KeyboardButton::insert(); 01637 case XK_KP_Delete: 01638 case XK_Delete: 01639 return KeyboardButton::del(); 01640 case XK_Num_Lock: 01641 return KeyboardButton::num_lock(); 01642 case XK_Scroll_Lock: 01643 return KeyboardButton::scroll_lock(); 01644 case XK_Print: 01645 return KeyboardButton::print_screen(); 01646 case XK_Pause: 01647 return KeyboardButton::pause(); 01648 case XK_Shift_L: 01649 return KeyboardButton::lshift(); 01650 case XK_Shift_R: 01651 return KeyboardButton::rshift(); 01652 case XK_Control_L: 01653 return KeyboardButton::lcontrol(); 01654 case XK_Control_R: 01655 return KeyboardButton::rcontrol(); 01656 case XK_Alt_L: 01657 return KeyboardButton::lalt(); 01658 case XK_Alt_R: 01659 return KeyboardButton::ralt(); 01660 case XK_Meta_L: 01661 case XK_Meta_R: 01662 return KeyboardButton::meta(); 01663 case XK_Caps_Lock: 01664 return KeyboardButton::caps_lock(); 01665 case XK_Shift_Lock: 01666 return KeyboardButton::shift_lock(); 01667 } 01668 01669 return ButtonHandle::none(); 01670 } 01671 01672 //////////////////////////////////////////////////////////////////// 01673 // Function: eglGraphicsWindow::get_mouse_button 01674 // Access: Private 01675 // Description: Returns the Panda ButtonHandle corresponding to the 01676 // mouse button indicated by the given button event. 01677 //////////////////////////////////////////////////////////////////// 01678 ButtonHandle eglGraphicsWindow:: 01679 get_mouse_button(XButtonEvent &button_event) { 01680 int index = button_event.button; 01681 if (index == x_wheel_up_button) { 01682 return MouseButton::wheel_up(); 01683 } else if (index == x_wheel_down_button) { 01684 return MouseButton::wheel_down(); 01685 } else if (index == x_wheel_left_button) { 01686 return MouseButton::wheel_left(); 01687 } else if (index == x_wheel_right_button) { 01688 return MouseButton::wheel_right(); 01689 } else { 01690 return MouseButton::button(index - 1); 01691 } 01692 } 01693 //////////////////////////////////////////////////////////////////// 01694 // Function: eglGraphicsWindow::check_event 01695 // Access: Private, Static 01696 // Description: This function is used as a predicate to 01697 // XCheckIfEvent() to determine if the indicated queued 01698 // X event is relevant and should be returned to this 01699 // window. 01700 //////////////////////////////////////////////////////////////////// 01701 Bool eglGraphicsWindow:: 01702 check_event(Display *display, XEvent *event, char *arg) { 01703 const eglGraphicsWindow *self = (eglGraphicsWindow *)arg; 01704 01705 // We accept any event that is sent to our window. 01706 return (event->xany.window == self->_xwindow); 01707 }