Panda3D

subprocessWindow.cxx

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                                   _default_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 
00373   GraphicsOutput *buffer = 
00374     _engine->make_output(_pipe, _name, 0, _fb_properties, _properties, 
00375                          flags, _gsg, _host);
00376   if (buffer != NULL) {
00377     _buffer = DCAST(GraphicsBuffer, buffer);
00378     // However, the buffer is not itself intended to be rendered.  We
00379     // only render it indirectly, via callbacks in here.
00380     _buffer->set_active(false);
00381 
00382     _buffer->request_open();
00383     _buffer->process_events();
00384 
00385     _is_valid = _buffer->is_valid();
00386   }
00387 
00388   if (!_is_valid) {
00389     display_cat.error()
00390       << "Failed to open SubprocessWindowBuffer's internal offscreen buffer.\n";
00391     return false;
00392   }
00393 
00394   _gsg = _buffer->get_gsg();
00395 
00396   WindowHandle *window_handle = _properties.get_parent_window();
00397   if (window_handle != NULL) {
00398     WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
00399     if (os_handle != NULL) {
00400       if (os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) {
00401         NativeWindowHandle::SubprocessHandle *subprocess_handle = DCAST(NativeWindowHandle::SubprocessHandle, os_handle);
00402         _filename = subprocess_handle->get_filename();
00403       }
00404     }
00405   }
00406   _parent_window_handle = window_handle;
00407 
00408   if (_filename.empty()) {
00409     _is_valid = false;
00410     display_cat.error()
00411       << "No filename given to SubprocessWindow.\n";
00412     return false;
00413   }
00414 
00415   _swbuffer = SubprocessWindowBuffer::open_buffer
00416     (_fd, _mmap_size, _filename.to_os_specific());
00417 
00418   if (_swbuffer == NULL) {
00419     close(_fd);
00420     _fd = -1;
00421     _filename = string();
00422     _is_valid = false;
00423     display_cat.error()
00424       << "Failed to open SubprocessWindowBuffer's shared-memory buffer "
00425       << _filename << "\n";
00426     return false;
00427   }
00428 
00429   if (display_cat.is_debug()) {
00430     display_cat.debug()
00431       << "SubprocessWindow reading " << _filename << "\n";
00432   }
00433 
00434   // Create a WindowHandle for ourselves
00435   _window_handle = NativeWindowHandle::make_subprocess(_filename);
00436 
00437   // And tell our parent window that we're now its child.
00438   if (_parent_window_handle != (WindowHandle *)NULL) {
00439     _parent_window_handle->attach_child(_window_handle);
00440   }
00441 
00442   return true;
00443 }
00444 
00445 ////////////////////////////////////////////////////////////////////
00446 //     Function: SubprocessWindow::translate_key
00447 //       Access: Private
00448 //  Description: Converts the os-specific keycode into the appropriate
00449 //               ButtonHandle object.  Also stores the corresponding
00450 //               Unicode keycode in keycode, if any; or 0 otherwise.
00451 ////////////////////////////////////////////////////////////////////
00452 ButtonHandle SubprocessWindow::
00453 translate_key(int &keycode, int os_code, unsigned int flags) const {
00454   keycode = 0;
00455   ButtonHandle nk = ButtonHandle::none();
00456 
00457 #ifdef __APPLE__
00458   switch ((os_code >> 8) & 0xff) {
00459   case   0: nk = KeyboardButton::ascii_key('a'); break;
00460   case  11: nk = KeyboardButton::ascii_key('b'); break;
00461   case   8: nk = KeyboardButton::ascii_key('c'); break;
00462   case   2: nk = KeyboardButton::ascii_key('d'); break;
00463   case  14: nk = KeyboardButton::ascii_key('e'); break;
00464   case   3: nk = KeyboardButton::ascii_key('f'); break;
00465   case   5: nk = KeyboardButton::ascii_key('g'); break;
00466   case   4: nk = KeyboardButton::ascii_key('h'); break;
00467   case  34: nk = KeyboardButton::ascii_key('i'); break;
00468   case  38: nk = KeyboardButton::ascii_key('j'); break;
00469   case  40: nk = KeyboardButton::ascii_key('k'); break;
00470   case  37: nk = KeyboardButton::ascii_key('l'); break;
00471   case  46: nk = KeyboardButton::ascii_key('m'); break;
00472   case  45: nk = KeyboardButton::ascii_key('n'); break;
00473   case  31: nk = KeyboardButton::ascii_key('o'); break;
00474   case  35: nk = KeyboardButton::ascii_key('p'); break;
00475   case  12: nk = KeyboardButton::ascii_key('q'); break;
00476   case  15: nk = KeyboardButton::ascii_key('r'); break;
00477   case   1: nk = KeyboardButton::ascii_key('s'); break;
00478   case  17: nk = KeyboardButton::ascii_key('t'); break;
00479   case  32: nk = KeyboardButton::ascii_key('u'); break;
00480   case   9: nk = KeyboardButton::ascii_key('v'); break;
00481   case  13: nk = KeyboardButton::ascii_key('w'); break;
00482   case   7: nk = KeyboardButton::ascii_key('x'); break;
00483   case  16: nk = KeyboardButton::ascii_key('y'); break;
00484   case   6: nk = KeyboardButton::ascii_key('z'); break;
00485 
00486     // top row numbers
00487   case  29: nk = KeyboardButton::ascii_key('0'); break;
00488   case  18: nk = KeyboardButton::ascii_key('1'); break;
00489   case  19: nk = KeyboardButton::ascii_key('2'); break;
00490   case  20: nk = KeyboardButton::ascii_key('3'); break;
00491   case  21: nk = KeyboardButton::ascii_key('4'); break;
00492   case  23: nk = KeyboardButton::ascii_key('5'); break;
00493   case  22: nk = KeyboardButton::ascii_key('6'); break;
00494   case  26: nk = KeyboardButton::ascii_key('7'); break;
00495   case  28: nk = KeyboardButton::ascii_key('8'); break;
00496   case  25: nk = KeyboardButton::ascii_key('9'); break;
00497 
00498     // key pad ... do they really map to the top number in panda ?
00499   case  82: nk = KeyboardButton::ascii_key('0'); break;
00500   case  83: nk = KeyboardButton::ascii_key('1'); break;
00501   case  84: nk = KeyboardButton::ascii_key('2'); break;
00502   case  85: nk = KeyboardButton::ascii_key('3'); break;
00503   case  86: nk = KeyboardButton::ascii_key('4'); break;
00504   case  87: nk = KeyboardButton::ascii_key('5'); break;
00505   case  88: nk = KeyboardButton::ascii_key('6'); break;
00506   case  89: nk = KeyboardButton::ascii_key('7'); break;
00507   case  91: nk = KeyboardButton::ascii_key('8'); break;
00508   case  92: nk = KeyboardButton::ascii_key('9'); break;
00509 
00510     // case  36: nk = KeyboardButton::ret(); break; // no return in panda ???
00511   case  49: nk = KeyboardButton::space(); break;
00512   case  51: nk = KeyboardButton::backspace(); break;
00513   case  48: nk = KeyboardButton::tab(); break;
00514   case  53: nk = KeyboardButton::escape(); break;
00515   case  76: nk = KeyboardButton::enter(); break; 
00516   case  36: nk = KeyboardButton::enter(); break; 
00517 
00518   case 123: nk = KeyboardButton::left(); break;
00519   case 124: nk = KeyboardButton::right(); break;
00520   case 125: nk = KeyboardButton::down(); break;
00521   case 126: nk = KeyboardButton::up(); break;
00522   case 116: nk = KeyboardButton::page_up(); break;
00523   case 121: nk = KeyboardButton::page_down(); break;
00524   case 115: nk = KeyboardButton::home(); break;
00525   case 119: nk = KeyboardButton::end(); break;
00526   case 114: nk = KeyboardButton::help(); break; 
00527   case 117: nk = KeyboardButton::del(); break; 
00528 
00529     // case  71: nk = KeyboardButton::num_lock() break; 
00530 
00531   case 122: nk = KeyboardButton::f1(); break;
00532   case 120: nk = KeyboardButton::f2(); break;
00533   case  99: nk = KeyboardButton::f3(); break;
00534   case 118: nk = KeyboardButton::f4(); break;
00535   case  96: nk = KeyboardButton::f5(); break;
00536   case  97: nk = KeyboardButton::f6(); break;
00537   case  98: nk = KeyboardButton::f7(); break;
00538   case 100: nk = KeyboardButton::f8(); break;
00539   case 101: nk = KeyboardButton::f9(); break;
00540   case 109: nk = KeyboardButton::f10(); break;
00541   case 103: nk = KeyboardButton::f11(); break;
00542   case 111: nk = KeyboardButton::f12(); break;
00543 
00544   case 105: nk = KeyboardButton::f13(); break;
00545   case 107: nk = KeyboardButton::f14(); break;
00546   case 113: nk = KeyboardButton::f15(); break;
00547   case 106: nk = KeyboardButton::f16(); break;
00548 
00549     // shiftable chartablet 
00550   case  50: nk = KeyboardButton::ascii_key('`'); break;
00551   case  27: nk = KeyboardButton::ascii_key('-'); break;
00552   case  24: nk = KeyboardButton::ascii_key('='); break;
00553   case  33: nk = KeyboardButton::ascii_key('['); break;
00554   case  30: nk = KeyboardButton::ascii_key(']'); break;
00555   case  42: nk = KeyboardButton::ascii_key('\\'); break;
00556   case  41: nk = KeyboardButton::ascii_key(';'); break;
00557   case  39: nk = KeyboardButton::ascii_key('\''); break;
00558   case  43: nk = KeyboardButton::ascii_key(','); break;
00559   case  47: nk = KeyboardButton::ascii_key('.'); break;
00560   case  44: nk = KeyboardButton::ascii_key('/'); break;
00561 
00562   default:
00563     // Punt.
00564     nk = KeyboardButton::ascii_key(os_code & 0xff); 
00565   }
00566 
00567   if (nk.has_ascii_equivalent()) {
00568     // If we assigned an ASCII button, then get the original ASCII
00569     // code from the event (it will include shift et al).
00570 
00571     // TODO: is it possible to get any international characters via
00572     // this old EventRecord interface?
00573     keycode = os_code & 0xff;
00574   }
00575   
00576 #endif __APPLE__
00577 
00578   return nk;
00579 }
00580 
00581 ////////////////////////////////////////////////////////////////////
00582 //     Function: SubprocessWindow::transition_button
00583 //       Access: Private
00584 //  Description: Sends the appropriate up/down transition for the
00585 //               indicated modifier key, as determined implicitly from
00586 //               the flags.
00587 ////////////////////////////////////////////////////////////////////
00588 void SubprocessWindow::
00589 transition_button(unsigned int flags, ButtonHandle button) {
00590   if (flags) {
00591     _input_devices[0].button_down(button);
00592   } else {  
00593     _input_devices[0].button_up(button);
00594   }
00595 }
00596 
00597 
00598 #endif  // SUPPORT_SUBPROCESS_WINDOW
 All Classes Functions Variables Enumerations