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