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                                   _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
 All Classes Functions Variables Enumerations