// Filename: subprocessWindow.cxx // Created by: drose (11Jul09) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #include "subprocessWindow.h" #ifdef SUPPORT_SUBPROCESS_WINDOW #include "graphicsEngine.h" #include "config_display.h" #include "nativeWindowHandle.h" TypeHandle SubprocessWindow::_type_handle; //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::Constructor // Access: Protected // Description: Normally, the SubprocessWindow constructor is not // called directly; these are created instead via the // GraphicsEngine::make_window() function. //////////////////////////////////////////////////////////////////// SubprocessWindow:: SubprocessWindow(GraphicsEngine *engine, GraphicsPipe *pipe, const string &name, const FrameBufferProperties &fb_prop, const WindowProperties &win_prop, int flags, GraphicsStateGuardian *gsg, GraphicsOutput *host) : GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) { GraphicsWindowInputDevice device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse"); _input_devices.push_back(device); // This will be an offscreen buffer that we use to render the actual // contents. _buffer = NULL; // Create a texture to receive the contents of the framebuffer from // the offscreen buffer. _texture = new Texture(name); _fd = -1; _mmap_size = 0; _filename = string(); _swbuffer = NULL; _last_event_flags = 0; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::Destructor // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// SubprocessWindow:: ~SubprocessWindow() { nassertv(_buffer == NULL); nassertv(_swbuffer == NULL); } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::process_events // Access: Public, Virtual // Description: Do whatever processing is necessary to ensure that // the window responds to user events. Also, honor any // requests recently made via request_properties(). // // This function is called only within the window // thread. //////////////////////////////////////////////////////////////////// void SubprocessWindow:: process_events() { GraphicsWindow::process_events(); if (_swbuffer != NULL) { SubprocessWindowBuffer::Event swb_event; while (_swbuffer->get_event(swb_event)) { // Deal with this event. if (swb_event._flags & SubprocessWindowBuffer::EF_mouse_position) { _input_devices[0].set_pointer_in_window(swb_event._x, swb_event._y); } else if ((swb_event._flags & SubprocessWindowBuffer::EF_has_mouse) == 0) { _input_devices[0].set_pointer_out_of_window(); } unsigned int diff = swb_event._flags ^ _last_event_flags; _last_event_flags = swb_event._flags; if (diff & SubprocessWindowBuffer::EF_shift_held) { transition_button(swb_event._flags & SubprocessWindowBuffer::EF_shift_held, KeyboardButton::shift()); } if (diff & SubprocessWindowBuffer::EF_control_held) { transition_button(swb_event._flags & SubprocessWindowBuffer::EF_control_held, KeyboardButton::control()); } if (diff & SubprocessWindowBuffer::EF_alt_held) { transition_button(swb_event._flags & SubprocessWindowBuffer::EF_alt_held, KeyboardButton::alt()); } if (diff & SubprocessWindowBuffer::EF_meta_held) { transition_button(swb_event._flags & SubprocessWindowBuffer::EF_meta_held, KeyboardButton::meta()); } ButtonHandle button = ButtonHandle::none(); if (swb_event._source == SubprocessWindowBuffer::ES_mouse) { button = MouseButton::button(swb_event._code); } else if (swb_event._source == SubprocessWindowBuffer::ES_keyboard) { int keycode; button = translate_key(keycode, swb_event._code, swb_event._flags); if (keycode != 0 && swb_event._type != SubprocessWindowBuffer::ET_button_up) { _input_devices[0].keystroke(keycode); } } if (swb_event._type == SubprocessWindowBuffer::ET_button_up) { _input_devices[0].button_up(button); } else if (swb_event._type == SubprocessWindowBuffer::ET_button_down) { _input_devices[0].button_down(button); } } } } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::begin_frame // Access: Public, Virtual // Description: This function will be called within the draw thread // before beginning rendering for a given frame. It // should do whatever setup is required, and return true // if the frame should be rendered, or false if it // should be skipped. //////////////////////////////////////////////////////////////////// bool SubprocessWindow:: begin_frame(FrameMode mode, Thread *current_thread) { if (_swbuffer == NULL || _buffer == NULL) { return false; } bool result = _buffer->begin_frame(mode, current_thread); return result; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::end_frame // Access: Public, Virtual // Description: This function will be called within the draw thread // after rendering is completed for a given frame. It // should do whatever finalization is required. //////////////////////////////////////////////////////////////////// void SubprocessWindow:: end_frame(FrameMode mode, Thread *current_thread) { _buffer->end_frame(mode, current_thread); if (mode == FM_render) { _flip_ready = true; } } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::begin_flip // Access: Public, Virtual // Description: This function will be called within the draw thread // after end_frame() has been called on all windows, to // initiate the exchange of the front and back buffers. // // This should instruct the window to prepare for the // flip at the next video sync, but it should not wait. // // We have the two separate functions, begin_flip() and // end_flip(), to make it easier to flip all of the // windows at the same time. //////////////////////////////////////////////////////////////////// void SubprocessWindow:: begin_flip() { nassertv(_buffer != (GraphicsBuffer *)NULL); if (_swbuffer == NULL) { return; } RenderBuffer buffer(_gsg, DrawableRegion::get_renderbuffer_type(RTP_color)); buffer = _gsg->get_render_buffer(_buffer->get_draw_buffer_type(), _buffer->get_fb_properties()); bool copied = _gsg->framebuffer_copy_to_ram(_texture, -1, _default_display_region, buffer); if (copied) { CPTA_uchar image = _texture->get_ram_image(); size_t framebuffer_size = _swbuffer->get_framebuffer_size(); nassertv(image.size() == framebuffer_size); if (!_swbuffer->ready_for_write()) { // We have to wait for the other end to remove the last frame we // rendered. We only wait so long before we give up, so we // don't completely starve the Python process just because the // render window is offscreen or something. ClockObject *clock = ClockObject::get_global_clock(); double start = clock->get_real_time(); while (!_swbuffer->ready_for_write()) { Thread::force_yield(); double now = clock->get_real_time(); if (now - start > subprocess_window_max_wait) { // Never mind. return; } } } // We're ready to go. Copy the image to our shared framebuffer. void *target = _swbuffer->open_write_framebuffer(); memcpy(target, image.p(), framebuffer_size); _swbuffer->close_write_framebuffer(); } } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::set_properties_now // Access: Public, Virtual // Description: Applies the requested set of properties to the // window, if possible, for instance to request a change // in size or minimization status. // // The window properties are applied immediately, rather // than waiting until the next frame. This implies that // this method may *only* be called from within the // window thread. // // The properties that have been applied are cleared // from the structure by this function; so on return, // whatever remains in the properties structure are // those that were unchanged for some reason (probably // because the underlying interface does not support // changing that property on an open window). //////////////////////////////////////////////////////////////////// void SubprocessWindow:: set_properties_now(WindowProperties &properties) { Filename filename; WindowHandle *window_handle = properties.get_parent_window(); if (window_handle != NULL) { WindowHandle::OSHandle *os_handle = window_handle->get_os_handle(); if (os_handle != NULL) { if (os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) { NativeWindowHandle::SubprocessHandle *subprocess_handle = DCAST(NativeWindowHandle::SubprocessHandle, os_handle); filename = subprocess_handle->get_filename(); } } } if (!filename.empty() && filename != _filename) { // We're changing the subprocess buffer filename; that means we // might as well completely close and re-open the window. display_cat.info() << "Re-opening SubprocessWindow\n"; internal_close_window(); _properties.add_properties(properties); properties.clear(); internal_open_window(); set_size_and_recalc(_properties.get_x_size(), _properties.get_y_size()); throw_event(get_window_event(), this); return; } GraphicsWindow::set_properties_now(properties); if (!properties.is_any_specified()) { // The base class has already handled this case. return; } if (properties.has_parent_window()) { // Redundant parent-window specification. properties.clear_parent_window(); } } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::close_window // Access: Protected, Virtual // Description: Closes the window right now. Called from the window // thread. //////////////////////////////////////////////////////////////////// void SubprocessWindow:: close_window() { internal_close_window(); WindowProperties properties; properties.set_open(false); properties.set_foreground(false); system_changed_properties(properties); } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::open_window // Access: Protected, Virtual // Description: Opens the window right now. Called from the window // thread. Returns true if the window is successfully // opened, or false if there was a problem. //////////////////////////////////////////////////////////////////// bool SubprocessWindow:: open_window() { if (!internal_open_window()) { return false; } WindowProperties properties; properties.set_open(true); properties.set_foreground(true); system_changed_properties(properties); return true; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::internal_close_window // Access: Private // Description: Closes the "window" and resets the buffer, without // changing the WindowProperties. //////////////////////////////////////////////////////////////////// void SubprocessWindow:: internal_close_window() { if (_swbuffer != NULL) { SubprocessWindowBuffer::close_buffer (_fd, _mmap_size, _filename.to_os_specific(), _swbuffer); _fd = -1; _filename = string(); _swbuffer = NULL; } if (_buffer != NULL) { _buffer->request_close(); _buffer->process_events(); _engine->remove_window(_buffer); _buffer = NULL; } // Tell our parent window (if any) that we're no longer its child. if (_window_handle != (WindowHandle *)NULL && _parent_window_handle != (WindowHandle *)NULL) { _parent_window_handle->detach_child(_window_handle); } _window_handle = NULL; _parent_window_handle = NULL; _is_valid = false; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::internal_open_window // Access: Private // Description: Opens the "window" and the associated offscreen // buffer, without changing the WindowProperties. //////////////////////////////////////////////////////////////////// bool SubprocessWindow:: internal_open_window() { nassertr(_buffer == NULL, false); // Create a buffer with the same properties as the window. int flags = _creation_flags; flags = ((flags & ~GraphicsPipe::BF_require_window) | GraphicsPipe::BF_refuse_window); GraphicsOutput *buffer = _engine->make_output(_pipe, _name, 0, _fb_properties, _properties, flags, _gsg, _host); if (buffer != NULL) { _buffer = DCAST(GraphicsBuffer, buffer); // However, the buffer is not itself intended to be rendered. We // only render it indirectly, via callbacks in here. _buffer->set_active(false); _buffer->request_open(); _buffer->process_events(); _is_valid = _buffer->is_valid(); } if (!_is_valid) { display_cat.error() << "Failed to open SubprocessWindowBuffer's internal offscreen buffer.\n"; return false; } _gsg = _buffer->get_gsg(); WindowHandle *window_handle = _properties.get_parent_window(); if (window_handle != NULL) { WindowHandle::OSHandle *os_handle = window_handle->get_os_handle(); if (os_handle != NULL) { if (os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) { NativeWindowHandle::SubprocessHandle *subprocess_handle = DCAST(NativeWindowHandle::SubprocessHandle, os_handle); _filename = subprocess_handle->get_filename(); } } } _parent_window_handle = window_handle; if (_filename.empty()) { _is_valid = false; display_cat.error() << "No filename given to SubprocessWindow.\n"; return false; } _swbuffer = SubprocessWindowBuffer::open_buffer (_fd, _mmap_size, _filename.to_os_specific()); if (_swbuffer == NULL) { close(_fd); _fd = -1; _filename = string(); _is_valid = false; display_cat.error() << "Failed to open SubprocessWindowBuffer's shared-memory buffer " << _filename << "\n"; return false; } if (display_cat.is_debug()) { display_cat.debug() << "SubprocessWindow reading " << _filename << "\n"; } // Create a WindowHandle for ourselves _window_handle = NativeWindowHandle::make_subprocess(_filename); // And tell our parent window that we're now its child. if (_parent_window_handle != (WindowHandle *)NULL) { _parent_window_handle->attach_child(_window_handle); } return true; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::translate_key // Access: Private // Description: Converts the os-specific keycode into the appropriate // ButtonHandle object. Also stores the corresponding // Unicode keycode in keycode, if any; or 0 otherwise. //////////////////////////////////////////////////////////////////// ButtonHandle SubprocessWindow:: translate_key(int &keycode, int os_code, unsigned int flags) const { keycode = 0; ButtonHandle nk = ButtonHandle::none(); #ifdef __APPLE__ switch ((os_code >> 8) & 0xff) { case 0: nk = KeyboardButton::ascii_key('a'); break; case 11: nk = KeyboardButton::ascii_key('b'); break; case 8: nk = KeyboardButton::ascii_key('c'); break; case 2: nk = KeyboardButton::ascii_key('d'); break; case 14: nk = KeyboardButton::ascii_key('e'); break; case 3: nk = KeyboardButton::ascii_key('f'); break; case 5: nk = KeyboardButton::ascii_key('g'); break; case 4: nk = KeyboardButton::ascii_key('h'); break; case 34: nk = KeyboardButton::ascii_key('i'); break; case 38: nk = KeyboardButton::ascii_key('j'); break; case 40: nk = KeyboardButton::ascii_key('k'); break; case 37: nk = KeyboardButton::ascii_key('l'); break; case 46: nk = KeyboardButton::ascii_key('m'); break; case 45: nk = KeyboardButton::ascii_key('n'); break; case 31: nk = KeyboardButton::ascii_key('o'); break; case 35: nk = KeyboardButton::ascii_key('p'); break; case 12: nk = KeyboardButton::ascii_key('q'); break; case 15: nk = KeyboardButton::ascii_key('r'); break; case 1: nk = KeyboardButton::ascii_key('s'); break; case 17: nk = KeyboardButton::ascii_key('t'); break; case 32: nk = KeyboardButton::ascii_key('u'); break; case 9: nk = KeyboardButton::ascii_key('v'); break; case 13: nk = KeyboardButton::ascii_key('w'); break; case 7: nk = KeyboardButton::ascii_key('x'); break; case 16: nk = KeyboardButton::ascii_key('y'); break; case 6: nk = KeyboardButton::ascii_key('z'); break; // top row numbers case 29: nk = KeyboardButton::ascii_key('0'); break; case 18: nk = KeyboardButton::ascii_key('1'); break; case 19: nk = KeyboardButton::ascii_key('2'); break; case 20: nk = KeyboardButton::ascii_key('3'); break; case 21: nk = KeyboardButton::ascii_key('4'); break; case 23: nk = KeyboardButton::ascii_key('5'); break; case 22: nk = KeyboardButton::ascii_key('6'); break; case 26: nk = KeyboardButton::ascii_key('7'); break; case 28: nk = KeyboardButton::ascii_key('8'); break; case 25: nk = KeyboardButton::ascii_key('9'); break; // key pad ... do they really map to the top number in panda ? case 82: nk = KeyboardButton::ascii_key('0'); break; case 83: nk = KeyboardButton::ascii_key('1'); break; case 84: nk = KeyboardButton::ascii_key('2'); break; case 85: nk = KeyboardButton::ascii_key('3'); break; case 86: nk = KeyboardButton::ascii_key('4'); break; case 87: nk = KeyboardButton::ascii_key('5'); break; case 88: nk = KeyboardButton::ascii_key('6'); break; case 89: nk = KeyboardButton::ascii_key('7'); break; case 91: nk = KeyboardButton::ascii_key('8'); break; case 92: nk = KeyboardButton::ascii_key('9'); break; // case 36: nk = KeyboardButton::ret(); break; // no return in panda ??? case 49: nk = KeyboardButton::space(); break; case 51: nk = KeyboardButton::backspace(); break; case 48: nk = KeyboardButton::tab(); break; case 53: nk = KeyboardButton::escape(); break; case 76: nk = KeyboardButton::enter(); break; case 36: nk = KeyboardButton::enter(); break; case 123: nk = KeyboardButton::left(); break; case 124: nk = KeyboardButton::right(); break; case 125: nk = KeyboardButton::down(); break; case 126: nk = KeyboardButton::up(); break; case 116: nk = KeyboardButton::page_up(); break; case 121: nk = KeyboardButton::page_down(); break; case 115: nk = KeyboardButton::home(); break; case 119: nk = KeyboardButton::end(); break; case 114: nk = KeyboardButton::help(); break; case 117: nk = KeyboardButton::del(); break; // case 71: nk = KeyboardButton::num_lock() break; case 122: nk = KeyboardButton::f1(); break; case 120: nk = KeyboardButton::f2(); break; case 99: nk = KeyboardButton::f3(); break; case 118: nk = KeyboardButton::f4(); break; case 96: nk = KeyboardButton::f5(); break; case 97: nk = KeyboardButton::f6(); break; case 98: nk = KeyboardButton::f7(); break; case 100: nk = KeyboardButton::f8(); break; case 101: nk = KeyboardButton::f9(); break; case 109: nk = KeyboardButton::f10(); break; case 103: nk = KeyboardButton::f11(); break; case 111: nk = KeyboardButton::f12(); break; case 105: nk = KeyboardButton::f13(); break; case 107: nk = KeyboardButton::f14(); break; case 113: nk = KeyboardButton::f15(); break; case 106: nk = KeyboardButton::f16(); break; // shiftable chartablet case 50: nk = KeyboardButton::ascii_key('`'); break; case 27: nk = KeyboardButton::ascii_key('-'); break; case 24: nk = KeyboardButton::ascii_key('='); break; case 33: nk = KeyboardButton::ascii_key('['); break; case 30: nk = KeyboardButton::ascii_key(']'); break; case 42: nk = KeyboardButton::ascii_key('\\'); break; case 41: nk = KeyboardButton::ascii_key(';'); break; case 39: nk = KeyboardButton::ascii_key('\''); break; case 43: nk = KeyboardButton::ascii_key(','); break; case 47: nk = KeyboardButton::ascii_key('.'); break; case 44: nk = KeyboardButton::ascii_key('/'); break; default: // Punt. nk = KeyboardButton::ascii_key(os_code & 0xff); } if (nk.has_ascii_equivalent()) { // If we assigned an ASCII button, then get the original ASCII // code from the event (it will include shift et al). // TODO: is it possible to get any international characters via // this old EventRecord interface? keycode = os_code & 0xff; } #endif __APPLE__ return nk; } //////////////////////////////////////////////////////////////////// // Function: SubprocessWindow::transition_button // Access: Private // Description: Sends the appropriate up/down transition for the // indicated modifier key, as determined implicitly from // the flags. //////////////////////////////////////////////////////////////////// void SubprocessWindow:: transition_button(unsigned int flags, ButtonHandle button) { if (flags) { _input_devices[0].button_down(button); } else { _input_devices[0].button_up(button); } } #endif // SUPPORT_SUBPROCESS_WINDOW