Panda3D
|
00001 // Filename: subprocessWindow.cxx 00002 // Created by: drose (11Jul09) 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 "subprocessWindow.h" 00016 00017 #ifdef SUPPORT_SUBPROCESS_WINDOW 00018 00019 #include "graphicsEngine.h" 00020 #include "config_display.h" 00021 #include "nativeWindowHandle.h" 00022 00023 TypeHandle SubprocessWindow::_type_handle; 00024 00025 //////////////////////////////////////////////////////////////////// 00026 // Function: SubprocessWindow::Constructor 00027 // Access: Protected 00028 // Description: Normally, the SubprocessWindow constructor is not 00029 // called directly; these are created instead via the 00030 // GraphicsEngine::make_window() function. 00031 //////////////////////////////////////////////////////////////////// 00032 SubprocessWindow:: 00033 SubprocessWindow(GraphicsEngine *engine, GraphicsPipe *pipe, 00034 const string &name, 00035 const FrameBufferProperties &fb_prop, 00036 const WindowProperties &win_prop, 00037 int flags, 00038 GraphicsStateGuardian *gsg, 00039 GraphicsOutput *host) : 00040 GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) 00041 { 00042 GraphicsWindowInputDevice device = 00043 GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse"); 00044 _input_devices.push_back(device); 00045 00046 // This will be an offscreen buffer that we use to render the actual 00047 // contents. 00048 _buffer = NULL; 00049 00050 // Create a texture to receive the contents of the framebuffer from 00051 // the offscreen buffer. 00052 _texture = new Texture(name); 00053 00054 _fd = -1; 00055 _mmap_size = 0; 00056 _filename = string(); 00057 _swbuffer = NULL; 00058 _last_event_flags = 0; 00059 } 00060 00061 //////////////////////////////////////////////////////////////////// 00062 // Function: SubprocessWindow::Destructor 00063 // Access: Published, Virtual 00064 // Description: 00065 //////////////////////////////////////////////////////////////////// 00066 SubprocessWindow:: 00067 ~SubprocessWindow() { 00068 nassertv(_buffer == NULL); 00069 nassertv(_swbuffer == NULL); 00070 } 00071 00072 //////////////////////////////////////////////////////////////////// 00073 // Function: SubprocessWindow::process_events 00074 // Access: Public, Virtual 00075 // Description: Do whatever processing is necessary to ensure that 00076 // the window responds to user events. Also, honor any 00077 // requests recently made via request_properties(). 00078 // 00079 // This function is called only within the window 00080 // thread. 00081 //////////////////////////////////////////////////////////////////// 00082 void SubprocessWindow:: 00083 process_events() { 00084 GraphicsWindow::process_events(); 00085 00086 if (_swbuffer != NULL) { 00087 SubprocessWindowBuffer::Event swb_event; 00088 while (_swbuffer->get_event(swb_event)) { 00089 // Deal with this event. 00090 if (swb_event._flags & SubprocessWindowBuffer::EF_mouse_position) { 00091 _input_devices[0].set_pointer_in_window(swb_event._x, swb_event._y); 00092 } else if ((swb_event._flags & SubprocessWindowBuffer::EF_has_mouse) == 0) { 00093 _input_devices[0].set_pointer_out_of_window(); 00094 } 00095 00096 unsigned int diff = swb_event._flags ^ _last_event_flags; 00097 _last_event_flags = swb_event._flags; 00098 00099 if (diff & SubprocessWindowBuffer::EF_shift_held) { 00100 transition_button(swb_event._flags & SubprocessWindowBuffer::EF_shift_held, KeyboardButton::shift()); 00101 } 00102 if (diff & SubprocessWindowBuffer::EF_control_held) { 00103 transition_button(swb_event._flags & SubprocessWindowBuffer::EF_control_held, KeyboardButton::control()); 00104 } 00105 if (diff & SubprocessWindowBuffer::EF_alt_held) { 00106 transition_button(swb_event._flags & SubprocessWindowBuffer::EF_alt_held, KeyboardButton::alt()); 00107 } 00108 if (diff & SubprocessWindowBuffer::EF_meta_held) { 00109 transition_button(swb_event._flags & SubprocessWindowBuffer::EF_meta_held, KeyboardButton::meta()); 00110 } 00111 00112 ButtonHandle button = ButtonHandle::none(); 00113 if (swb_event._source == SubprocessWindowBuffer::ES_mouse) { 00114 button = MouseButton::button(swb_event._code); 00115 00116 } else if (swb_event._source == SubprocessWindowBuffer::ES_keyboard) { 00117 int keycode; 00118 button = translate_key(keycode, swb_event._code, swb_event._flags); 00119 if (keycode != 0 && swb_event._type != SubprocessWindowBuffer::ET_button_up) { 00120 _input_devices[0].keystroke(keycode); 00121 } 00122 } 00123 00124 if (swb_event._type == SubprocessWindowBuffer::ET_button_up) { 00125 _input_devices[0].button_up(button); 00126 } else if (swb_event._type == SubprocessWindowBuffer::ET_button_down) { 00127 _input_devices[0].button_down(button); 00128 } 00129 } 00130 } 00131 } 00132 00133 //////////////////////////////////////////////////////////////////// 00134 // Function: SubprocessWindow::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 SubprocessWindow:: 00143 begin_frame(FrameMode mode, Thread *current_thread) { 00144 if (_swbuffer == NULL || _buffer == NULL) { 00145 return false; 00146 } 00147 00148 bool result = _buffer->begin_frame(mode, current_thread); 00149 return result; 00150 } 00151 00152 //////////////////////////////////////////////////////////////////// 00153 // Function: SubprocessWindow::end_frame 00154 // Access: Public, Virtual 00155 // Description: This function will be called within the draw thread 00156 // after rendering is completed for a given frame. It 00157 // should do whatever finalization is required. 00158 //////////////////////////////////////////////////////////////////// 00159 void SubprocessWindow:: 00160 end_frame(FrameMode mode, Thread *current_thread) { 00161 _buffer->end_frame(mode, current_thread); 00162 00163 if (mode == FM_render) { 00164 _flip_ready = true; 00165 } 00166 } 00167 00168 //////////////////////////////////////////////////////////////////// 00169 // Function: SubprocessWindow::begin_flip 00170 // Access: Public, Virtual 00171 // Description: This function will be called within the draw thread 00172 // after end_frame() has been called on all windows, to 00173 // initiate the exchange of the front and back buffers. 00174 // 00175 // This should instruct the window to prepare for the 00176 // flip at the next video sync, but it should not wait. 00177 // 00178 // We have the two separate functions, begin_flip() and 00179 // end_flip(), to make it easier to flip all of the 00180 // windows at the same time. 00181 //////////////////////////////////////////////////////////////////// 00182 void SubprocessWindow:: 00183 begin_flip() { 00184 nassertv(_buffer != (GraphicsBuffer *)NULL); 00185 if (_swbuffer == NULL) { 00186 return; 00187 } 00188 00189 RenderBuffer buffer(_gsg, DrawableRegion::get_renderbuffer_type(RTP_color)); 00190 buffer = _gsg->get_render_buffer(_buffer->get_draw_buffer_type(), 00191 _buffer->get_fb_properties()); 00192 00193 bool copied = 00194 _gsg->framebuffer_copy_to_ram(_texture, -1, 00195 _overlay_display_region, buffer); 00196 00197 if (copied) { 00198 CPTA_uchar image = _texture->get_ram_image(); 00199 size_t framebuffer_size = _swbuffer->get_framebuffer_size(); 00200 nassertv(image.size() == framebuffer_size); 00201 00202 if (!_swbuffer->ready_for_write()) { 00203 // We have to wait for the other end to remove the last frame we 00204 // rendered. We only wait so long before we give up, so we 00205 // don't completely starve the Python process just because the 00206 // render window is offscreen or something. 00207 00208 ClockObject *clock = ClockObject::get_global_clock(); 00209 double start = clock->get_real_time(); 00210 while (!_swbuffer->ready_for_write()) { 00211 Thread::force_yield(); 00212 double now = clock->get_real_time(); 00213 if (now - start > subprocess_window_max_wait) { 00214 // Never mind. 00215 return; 00216 } 00217 } 00218 } 00219 00220 // We're ready to go. Copy the image to our shared framebuffer. 00221 void *target = _swbuffer->open_write_framebuffer(); 00222 memcpy(target, image.p(), framebuffer_size); 00223 _swbuffer->close_write_framebuffer(); 00224 } 00225 } 00226 00227 //////////////////////////////////////////////////////////////////// 00228 // Function: SubprocessWindow::set_properties_now 00229 // Access: Public, Virtual 00230 // Description: Applies the requested set of properties to the 00231 // window, if possible, for instance to request a change 00232 // in size or minimization status. 00233 // 00234 // The window properties are applied immediately, rather 00235 // than waiting until the next frame. This implies that 00236 // this method may *only* be called from within the 00237 // window thread. 00238 // 00239 // The properties that have been applied are cleared 00240 // from the structure by this function; so on return, 00241 // whatever remains in the properties structure are 00242 // those that were unchanged for some reason (probably 00243 // because the underlying interface does not support 00244 // changing that property on an open window). 00245 //////////////////////////////////////////////////////////////////// 00246 void SubprocessWindow:: 00247 set_properties_now(WindowProperties &properties) { 00248 Filename filename; 00249 WindowHandle *window_handle = properties.get_parent_window(); 00250 if (window_handle != NULL) { 00251 WindowHandle::OSHandle *os_handle = window_handle->get_os_handle(); 00252 if (os_handle != NULL) { 00253 if (os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) { 00254 NativeWindowHandle::SubprocessHandle *subprocess_handle = DCAST(NativeWindowHandle::SubprocessHandle, os_handle); 00255 filename = subprocess_handle->get_filename(); 00256 } 00257 } 00258 } 00259 00260 if (!filename.empty() && filename != _filename) { 00261 // We're changing the subprocess buffer filename; that means we 00262 // might as well completely close and re-open the window. 00263 display_cat.info() << "Re-opening SubprocessWindow\n"; 00264 internal_close_window(); 00265 00266 _properties.add_properties(properties); 00267 properties.clear(); 00268 00269 internal_open_window(); 00270 set_size_and_recalc(_properties.get_x_size(), _properties.get_y_size()); 00271 throw_event(get_window_event(), this); 00272 return; 00273 } 00274 00275 GraphicsWindow::set_properties_now(properties); 00276 if (!properties.is_any_specified()) { 00277 // The base class has already handled this case. 00278 return; 00279 } 00280 00281 if (properties.has_parent_window()) { 00282 // Redundant parent-window specification. 00283 properties.clear_parent_window(); 00284 } 00285 } 00286 00287 //////////////////////////////////////////////////////////////////// 00288 // Function: SubprocessWindow::close_window 00289 // Access: Protected, Virtual 00290 // Description: Closes the window right now. Called from the window 00291 // thread. 00292 //////////////////////////////////////////////////////////////////// 00293 void SubprocessWindow:: 00294 close_window() { 00295 internal_close_window(); 00296 00297 WindowProperties properties; 00298 properties.set_open(false); 00299 properties.set_foreground(false); 00300 system_changed_properties(properties); 00301 } 00302 00303 //////////////////////////////////////////////////////////////////// 00304 // Function: SubprocessWindow::open_window 00305 // Access: Protected, Virtual 00306 // Description: Opens the window right now. Called from the window 00307 // thread. Returns true if the window is successfully 00308 // opened, or false if there was a problem. 00309 //////////////////////////////////////////////////////////////////// 00310 bool SubprocessWindow:: 00311 open_window() { 00312 if (!internal_open_window()) { 00313 return false; 00314 } 00315 00316 WindowProperties properties; 00317 properties.set_open(true); 00318 properties.set_foreground(true); 00319 system_changed_properties(properties); 00320 00321 return true; 00322 } 00323 00324 //////////////////////////////////////////////////////////////////// 00325 // Function: SubprocessWindow::internal_close_window 00326 // Access: Private 00327 // Description: Closes the "window" and resets the buffer, without 00328 // changing the WindowProperties. 00329 //////////////////////////////////////////////////////////////////// 00330 void SubprocessWindow:: 00331 internal_close_window() { 00332 if (_swbuffer != NULL) { 00333 SubprocessWindowBuffer::close_buffer 00334 (_fd, _mmap_size, _filename.to_os_specific(), _swbuffer); 00335 _fd = -1; 00336 _filename = string(); 00337 00338 _swbuffer = NULL; 00339 } 00340 00341 if (_buffer != NULL) { 00342 _buffer->request_close(); 00343 _buffer->process_events(); 00344 _engine->remove_window(_buffer); 00345 _buffer = NULL; 00346 } 00347 00348 // Tell our parent window (if any) that we're no longer its child. 00349 if (_window_handle != (WindowHandle *)NULL && 00350 _parent_window_handle != (WindowHandle *)NULL) { 00351 _parent_window_handle->detach_child(_window_handle); 00352 } 00353 00354 _window_handle = NULL; 00355 _parent_window_handle = NULL; 00356 _is_valid = false; 00357 } 00358 00359 //////////////////////////////////////////////////////////////////// 00360 // Function: SubprocessWindow::internal_open_window 00361 // Access: Private 00362 // Description: Opens the "window" and the associated offscreen 00363 // buffer, without changing the WindowProperties. 00364 //////////////////////////////////////////////////////////////////// 00365 bool SubprocessWindow:: 00366 internal_open_window() { 00367 nassertr(_buffer == NULL, false); 00368 00369 // Create a buffer with the same properties as the window. 00370 int flags = _creation_flags; 00371 flags = ((flags & ~GraphicsPipe::BF_require_window) | GraphicsPipe::BF_refuse_window); 00372 WindowProperties win_props = WindowProperties::size(_properties.get_x_size(), _properties.get_y_size()); 00373 00374 GraphicsOutput *buffer = 00375 _engine->make_output(_pipe, _name, 0, _fb_properties, win_props, 00376 flags, _gsg, _host); 00377 if (buffer != NULL) { 00378 _buffer = DCAST(GraphicsBuffer, buffer); 00379 // However, the buffer is not itself intended to be rendered. We 00380 // only render it indirectly, via callbacks in here. 00381 _buffer->set_active(false); 00382 00383 _buffer->request_open(); 00384 _buffer->process_events(); 00385 00386 _is_valid = _buffer->is_valid(); 00387 } 00388 00389 if (!_is_valid) { 00390 display_cat.error() 00391 << "Failed to open SubprocessWindowBuffer's internal offscreen buffer.\n"; 00392 return false; 00393 } 00394 00395 _gsg = _buffer->get_gsg(); 00396 00397 WindowHandle *window_handle = _properties.get_parent_window(); 00398 if (window_handle != NULL) { 00399 WindowHandle::OSHandle *os_handle = window_handle->get_os_handle(); 00400 if (os_handle != NULL) { 00401 if (os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) { 00402 NativeWindowHandle::SubprocessHandle *subprocess_handle = DCAST(NativeWindowHandle::SubprocessHandle, os_handle); 00403 _filename = subprocess_handle->get_filename(); 00404 } 00405 } 00406 } 00407 _parent_window_handle = window_handle; 00408 00409 if (_filename.empty()) { 00410 _is_valid = false; 00411 display_cat.error() 00412 << "No filename given to SubprocessWindow.\n"; 00413 return false; 00414 } 00415 00416 _swbuffer = SubprocessWindowBuffer::open_buffer 00417 (_fd, _mmap_size, _filename.to_os_specific()); 00418 00419 if (_swbuffer == NULL) { 00420 close(_fd); 00421 _fd = -1; 00422 _filename = string(); 00423 _is_valid = false; 00424 display_cat.error() 00425 << "Failed to open SubprocessWindowBuffer's shared-memory buffer " 00426 << _filename << "\n"; 00427 return false; 00428 } 00429 00430 if (display_cat.is_debug()) { 00431 display_cat.debug() 00432 << "SubprocessWindow reading " << _filename << "\n"; 00433 } 00434 00435 // Create a WindowHandle for ourselves 00436 _window_handle = NativeWindowHandle::make_subprocess(_filename); 00437 00438 // And tell our parent window that we're now its child. 00439 if (_parent_window_handle != (WindowHandle *)NULL) { 00440 _parent_window_handle->attach_child(_window_handle); 00441 } 00442 00443 return true; 00444 } 00445 00446 //////////////////////////////////////////////////////////////////// 00447 // Function: SubprocessWindow::translate_key 00448 // Access: Private 00449 // Description: Converts the os-specific keycode into the appropriate 00450 // ButtonHandle object. Also stores the corresponding 00451 // Unicode keycode in keycode, if any; or 0 otherwise. 00452 //////////////////////////////////////////////////////////////////// 00453 ButtonHandle SubprocessWindow:: 00454 translate_key(int &keycode, int os_code, unsigned int flags) const { 00455 keycode = 0; 00456 ButtonHandle nk = ButtonHandle::none(); 00457 00458 #ifdef __APPLE__ 00459 switch ((os_code >> 8) & 0xff) { 00460 case 0: nk = KeyboardButton::ascii_key('a'); break; 00461 case 11: nk = KeyboardButton::ascii_key('b'); break; 00462 case 8: nk = KeyboardButton::ascii_key('c'); break; 00463 case 2: nk = KeyboardButton::ascii_key('d'); break; 00464 case 14: nk = KeyboardButton::ascii_key('e'); break; 00465 case 3: nk = KeyboardButton::ascii_key('f'); break; 00466 case 5: nk = KeyboardButton::ascii_key('g'); break; 00467 case 4: nk = KeyboardButton::ascii_key('h'); break; 00468 case 34: nk = KeyboardButton::ascii_key('i'); break; 00469 case 38: nk = KeyboardButton::ascii_key('j'); break; 00470 case 40: nk = KeyboardButton::ascii_key('k'); break; 00471 case 37: nk = KeyboardButton::ascii_key('l'); break; 00472 case 46: nk = KeyboardButton::ascii_key('m'); break; 00473 case 45: nk = KeyboardButton::ascii_key('n'); break; 00474 case 31: nk = KeyboardButton::ascii_key('o'); break; 00475 case 35: nk = KeyboardButton::ascii_key('p'); break; 00476 case 12: nk = KeyboardButton::ascii_key('q'); break; 00477 case 15: nk = KeyboardButton::ascii_key('r'); break; 00478 case 1: nk = KeyboardButton::ascii_key('s'); break; 00479 case 17: nk = KeyboardButton::ascii_key('t'); break; 00480 case 32: nk = KeyboardButton::ascii_key('u'); break; 00481 case 9: nk = KeyboardButton::ascii_key('v'); break; 00482 case 13: nk = KeyboardButton::ascii_key('w'); break; 00483 case 7: nk = KeyboardButton::ascii_key('x'); break; 00484 case 16: nk = KeyboardButton::ascii_key('y'); break; 00485 case 6: nk = KeyboardButton::ascii_key('z'); break; 00486 00487 // top row numbers 00488 case 29: nk = KeyboardButton::ascii_key('0'); break; 00489 case 18: nk = KeyboardButton::ascii_key('1'); break; 00490 case 19: nk = KeyboardButton::ascii_key('2'); break; 00491 case 20: nk = KeyboardButton::ascii_key('3'); break; 00492 case 21: nk = KeyboardButton::ascii_key('4'); break; 00493 case 23: nk = KeyboardButton::ascii_key('5'); break; 00494 case 22: nk = KeyboardButton::ascii_key('6'); break; 00495 case 26: nk = KeyboardButton::ascii_key('7'); break; 00496 case 28: nk = KeyboardButton::ascii_key('8'); break; 00497 case 25: nk = KeyboardButton::ascii_key('9'); break; 00498 00499 // key pad ... do they really map to the top number in panda ? 00500 case 82: nk = KeyboardButton::ascii_key('0'); break; 00501 case 83: nk = KeyboardButton::ascii_key('1'); break; 00502 case 84: nk = KeyboardButton::ascii_key('2'); break; 00503 case 85: nk = KeyboardButton::ascii_key('3'); break; 00504 case 86: nk = KeyboardButton::ascii_key('4'); break; 00505 case 87: nk = KeyboardButton::ascii_key('5'); break; 00506 case 88: nk = KeyboardButton::ascii_key('6'); break; 00507 case 89: nk = KeyboardButton::ascii_key('7'); break; 00508 case 91: nk = KeyboardButton::ascii_key('8'); break; 00509 case 92: nk = KeyboardButton::ascii_key('9'); break; 00510 00511 // case 36: nk = KeyboardButton::ret(); break; // no return in panda ??? 00512 case 49: nk = KeyboardButton::space(); break; 00513 case 51: nk = KeyboardButton::backspace(); break; 00514 case 48: nk = KeyboardButton::tab(); break; 00515 case 53: nk = KeyboardButton::escape(); break; 00516 case 76: nk = KeyboardButton::enter(); break; 00517 case 36: nk = KeyboardButton::enter(); break; 00518 00519 case 123: nk = KeyboardButton::left(); break; 00520 case 124: nk = KeyboardButton::right(); break; 00521 case 125: nk = KeyboardButton::down(); break; 00522 case 126: nk = KeyboardButton::up(); break; 00523 case 116: nk = KeyboardButton::page_up(); break; 00524 case 121: nk = KeyboardButton::page_down(); break; 00525 case 115: nk = KeyboardButton::home(); break; 00526 case 119: nk = KeyboardButton::end(); break; 00527 case 114: nk = KeyboardButton::help(); break; 00528 case 117: nk = KeyboardButton::del(); break; 00529 00530 // case 71: nk = KeyboardButton::num_lock() break; 00531 00532 case 122: nk = KeyboardButton::f1(); break; 00533 case 120: nk = KeyboardButton::f2(); break; 00534 case 99: nk = KeyboardButton::f3(); break; 00535 case 118: nk = KeyboardButton::f4(); break; 00536 case 96: nk = KeyboardButton::f5(); break; 00537 case 97: nk = KeyboardButton::f6(); break; 00538 case 98: nk = KeyboardButton::f7(); break; 00539 case 100: nk = KeyboardButton::f8(); break; 00540 case 101: nk = KeyboardButton::f9(); break; 00541 case 109: nk = KeyboardButton::f10(); break; 00542 case 103: nk = KeyboardButton::f11(); break; 00543 case 111: nk = KeyboardButton::f12(); break; 00544 00545 case 105: nk = KeyboardButton::f13(); break; 00546 case 107: nk = KeyboardButton::f14(); break; 00547 case 113: nk = KeyboardButton::f15(); break; 00548 case 106: nk = KeyboardButton::f16(); break; 00549 00550 // shiftable chartablet 00551 case 50: nk = KeyboardButton::ascii_key('`'); break; 00552 case 27: nk = KeyboardButton::ascii_key('-'); break; 00553 case 24: nk = KeyboardButton::ascii_key('='); break; 00554 case 33: nk = KeyboardButton::ascii_key('['); break; 00555 case 30: nk = KeyboardButton::ascii_key(']'); break; 00556 case 42: nk = KeyboardButton::ascii_key('\\'); break; 00557 case 41: nk = KeyboardButton::ascii_key(';'); break; 00558 case 39: nk = KeyboardButton::ascii_key('\''); break; 00559 case 43: nk = KeyboardButton::ascii_key(','); break; 00560 case 47: nk = KeyboardButton::ascii_key('.'); break; 00561 case 44: nk = KeyboardButton::ascii_key('/'); break; 00562 00563 default: 00564 // Punt. 00565 nk = KeyboardButton::ascii_key(os_code & 0xff); 00566 } 00567 00568 if (nk.has_ascii_equivalent()) { 00569 // If we assigned an ASCII button, then get the original ASCII 00570 // code from the event (it will include shift et al). 00571 00572 // TODO: is it possible to get any international characters via 00573 // this old EventRecord interface? 00574 keycode = os_code & 0xff; 00575 } 00576 00577 #endif __APPLE__ 00578 00579 return nk; 00580 } 00581 00582 //////////////////////////////////////////////////////////////////// 00583 // Function: SubprocessWindow::transition_button 00584 // Access: Private 00585 // Description: Sends the appropriate up/down transition for the 00586 // indicated modifier key, as determined implicitly from 00587 // the flags. 00588 //////////////////////////////////////////////////////////////////// 00589 void SubprocessWindow:: 00590 transition_button(unsigned int flags, ButtonHandle button) { 00591 if (flags) { 00592 _input_devices[0].button_down(button); 00593 } else { 00594 _input_devices[0].button_up(button); 00595 } 00596 } 00597 00598 00599 #endif // SUPPORT_SUBPROCESS_WINDOW