subprocess window

This commit is contained in:
David Rose 2009-07-13 07:14:01 +00:00
parent d24c039650
commit 37e76f402d
20 changed files with 1192 additions and 49 deletions

View File

@ -37,7 +37,9 @@
stencilRenderStates.h \
stereoDisplayRegion.I stereoDisplayRegion.h \
displaySearchParameters.h \
displayInformation.h
displayInformation.h \
subprocessWindow.h subprocessWindow.I \
$[if $[and $[OSX_PLATFORM],$[HAVE_P3D_PLUGIN]], subprocessWindowBuffer.h subprocessWindowBuffer.I subprocessWindowBuffer.cxx]
#define INCLUDED_SOURCES \
standardMunger.cxx \
@ -62,7 +64,8 @@
stencilRenderStates.cxx \
stereoDisplayRegion.cxx \
displaySearchParameters.cxx \
displayInformation.cxx
displayInformation.cxx \
subprocessWindow.cxx
#define INSTALL_HEADERS \
standardMunger.I standardMunger.h \
@ -90,12 +93,31 @@
stencilRenderStates.h \
stereoDisplayRegion.I stereoDisplayRegion.h \
displaySearchParameters.h \
displayInformation.h
displayInformation.h \
subprocessWindow.h subprocessWindow.I \
subprocessWindowBuffer.h subprocessWindowBuffer.I
#define IGATESCAN all
#end lib_target
#begin static_lib_target
// We build a static library of just these files, so the plugin can
// link with it in direct/src/plugin, without pulling in the rest of
// Panda.
#define BUILD_TARGET $[and $[OSX_PLATFORM],$[HAVE_P3D_PLUGIN]]
#define TARGET subprocbuffer
#define SOURCES \
subprocessWindowBuffer.h subprocessWindowBuffer.I \
subprocessWindowBuffer.cxx
#end static_lib_target
#begin test_bin_target
#define TARGET test_display
#define LOCAL_LIBS \

View File

@ -26,6 +26,7 @@
#include "parasiteBuffer.h"
#include "pandaSystem.h"
#include "stereoDisplayRegion.h"
#include "subprocessWindow.h"
ConfigureDef(config_display);
NotifyCategoryDef(display, "");
@ -289,6 +290,15 @@ ConfigVariableInt parent_window_handle
"an HWND on Windows, or the NSWindow pointer or XWindow pointer "
"converted to an integer, on OSX and X11."));
ConfigVariableFilename subprocess_window
("subprocess-window", "",
PRC_DESC("The filename of a SubprocessWindowBuffer's temporary mmap file, "
"used for opening a window in a child process and rendering "
"to a different window in the parent process. "
"This is specifically used for OSX when the plugin is compiled, "
"and is not used or needed in other environments. See "
"WindowProperties::set_subprocess_window()."));
ConfigVariableString framebuffer_mode
("framebuffer-mode", "",
PRC_DESC("No longer has any effect. Do not use."));
@ -400,6 +410,9 @@ init_libdisplay() {
ParasiteBuffer::init_type();
StandardMunger::init_type();
StereoDisplayRegion::init_type();
#ifdef SUPPORT_SUBPROCESS_WINDOW
SubprocessWindow::init_type();
#endif
#if defined(HAVE_THREADS) && defined(DO_PIPELINING)
PandaSystem *ps = PandaSystem::get_global_ptr();

View File

@ -73,6 +73,7 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableFilename cursor_filename;
extern EXPCL_PANDA_DISPLAY ConfigVariableEnum<WindowProperties::ZOrder> z_order;
extern EXPCL_PANDA_DISPLAY ConfigVariableString window_title;
extern EXPCL_PANDA_DISPLAY ConfigVariableInt parent_window_handle;
extern EXPCL_PANDA_DISPLAY ConfigVariableFilename subprocess_window;
extern EXPCL_PANDA_DISPLAY ConfigVariableString framebuffer_mode;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool framebuffer_hardware;

View File

@ -13,3 +13,4 @@
#include "displaySearchParameters.cxx"
#include "displayInformation.cxx"
#include "stereoDisplayRegion.cxx"
#include "subprocessWindow.cxx"

View File

@ -0,0 +1,14 @@
// Filename: osxSubprocessWindow.I
// 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."
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,275 @@
// 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"
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,
const string &filename) :
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);
// Create a buffer with the same properties as the window.
flags = ((flags & ~GraphicsPipe::BF_require_window) | GraphicsPipe::BF_refuse_window);
GraphicsOutput *buffer =
engine->make_output(pipe, name, 0,
fb_prop, win_prop, 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);
}
// Now create a texture to receive the contents of the framebuffer
// from the buffer.
_texture = new Texture(name);
_fd = -1;
_mmap_size = 0;
_filename = filename;
_swbuffer = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindow::Destructor
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
SubprocessWindow::
~SubprocessWindow() {
if (_buffer != NULL) {
_engine->remove_window(_buffer);
}
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 event;
while (_swbuffer->get_event(event)) {
// Deal with this event. For now, we only have mouse down/up.
_input_devices[0].set_pointer_in_window(event._x, event._y);
if (event._up) {
_input_devices[0].button_up(MouseButton::one());
} else {
_input_devices[0].button_down(MouseButton::one());
}
}
}
}
////////////////////////////////////////////////////////////////////
// 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;
}
if (!_swbuffer->ready_for_write()) {
// The other end hasn't removed a frame lately; don't bother to
// render.
Thread::force_yield();
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);
nassertv(_swbuffer != NULL);
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);
// Now 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::close_window
// Access: Protected, Virtual
// Description: Closes the window right now. Called from the window
// thread.
////////////////////////////////////////////////////////////////////
void SubprocessWindow::
close_window() {
if (_swbuffer != NULL) {
SubprocessWindowBuffer::close_buffer(_fd, _mmap_size, _filename, _swbuffer);
_fd = -1;
_filename = string();
_swbuffer = NULL;
}
if (_buffer != NULL) {
_buffer->request_close();
_buffer->process_events();
}
_is_valid = false;
}
////////////////////////////////////////////////////////////////////
// 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() {
nout << "open_window\n";
if (_buffer != NULL) {
_buffer->request_open();
_buffer->process_events();
_is_valid = _buffer->is_valid();
}
if (!_is_valid) {
return false;
}
_gsg = _buffer->get_gsg();
_swbuffer = SubprocessWindowBuffer::open_buffer(_fd, _mmap_size, _filename);
if (_swbuffer == NULL) {
close(_fd);
_fd = -1;
_filename = string();
_is_valid = false;
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// 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) {
GraphicsWindow::set_properties_now(properties);
}
#endif // SUPPORT_SUBPROCESS_WINDOW

View File

@ -0,0 +1,106 @@
// Filename: subprocessWindow.h
// 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."
//
////////////////////////////////////////////////////////////////////
#ifndef SUBPROCESSWINDOW_H
#define SUBPROCESSWINDOW_H
#include "pandabase.h"
// For now, a simple trigger whether to enable the subprocess window
// support. We only build it on OSX, and only when the plugin is
// enabled; because this is (presently) the only case where it's
// useful.
#if defined(HAVE_P3D_PLUGIN) && defined(IS_OSX)
#define SUPPORT_SUBPROCESS_WINDOW 1
#else
#undef SUPPORT_SUBPROCESS_WINDOW
#endif
#ifdef SUPPORT_SUBPROCESS_WINDOW
#include "graphicsWindow.h"
#include "graphicsBuffer.h"
#include "texture.h"
#include "subprocessWindowBuffer.h"
////////////////////////////////////////////////////////////////////
// Class : SubprocessWindow
// Description : This is a special "window" that actually renders to
// an offscreen buffer, copies the pixels to RAM, and
// then ships them to a parent process via shared memory
// for rendering to the window.
//
// This whole nonsense is necessary because OSX doesn't
// allow child processes to draw to, or attach windows
// to, windows created in the parent process. There's a
// rumor that 10.6 fixes this nonsense; this will remain
// to be seen.
////////////////////////////////////////////////////////////////////
class SubprocessWindow : public GraphicsWindow {
public:
SubprocessWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
const string &name,
const FrameBufferProperties &fb_prop,
const WindowProperties &win_prop,
int flags,
GraphicsStateGuardian *gsg,
GraphicsOutput *host,
const string &filename);
virtual ~SubprocessWindow();
virtual void process_events();
virtual bool begin_frame(FrameMode mode, Thread *current_thread);
virtual void end_frame(FrameMode mode, Thread *current_thread);
virtual void begin_flip();
virtual void set_properties_now(WindowProperties &properties);
protected:
virtual void close_window();
virtual bool open_window();
private:
PT(GraphicsBuffer) _buffer;
PT(Texture) _texture;
int _fd;
size_t _mmap_size;
string _filename;
SubprocessWindowBuffer *_swbuffer;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
GraphicsWindow::init_type();
register_type(_type_handle, "SubprocessWindow",
GraphicsWindow::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "subprocessWindow.I"
#endif // SUPPORT_SUBPROCESS_WINDOW
#endif

View File

@ -0,0 +1,176 @@
// Filename: subprocessWindowBuffer.I
// 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."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::get_x_size
// Access: Public
// Description: Returns the width of the framebuffer in pixels.
////////////////////////////////////////////////////////////////////
inline int SubprocessWindowBuffer::
get_x_size() const {
return _x_size;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::get_y_size
// Access: Public
// Description: Returns the height of the framebuffer in pixels.
////////////////////////////////////////////////////////////////////
inline int SubprocessWindowBuffer::
get_y_size() const {
return _y_size;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::get_row_size
// Access: Public
// Description: Returns the length of a row of the framebuffer, in
// bytes.
////////////////////////////////////////////////////////////////////
inline size_t SubprocessWindowBuffer::
get_row_size() const {
return _row_size;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::get_framebuffer_size
// Access: Public
// Description: Returns the total number of bytes in the framebuffer.
////////////////////////////////////////////////////////////////////
inline size_t SubprocessWindowBuffer::
get_framebuffer_size() const {
return _framebuffer_size;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::ready_for_read
// Access: Public
// Description: Returns true if the framebuffer data has been updated
// since open_read_framebuffer() was last called.
////////////////////////////////////////////////////////////////////
inline bool SubprocessWindowBuffer::
ready_for_read() const {
return (_last_written != _last_read);
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::ready_for_write
// Access: Public
// Description: Returns true if the framebuffer data has been read
// since open_write_framebuffer() was last called.
////////////////////////////////////////////////////////////////////
inline bool SubprocessWindowBuffer::
ready_for_write() const {
return (_last_written == _last_read);
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::open_read_framebuffer
// Access: Public
// Description: Returns a read-only pointer to the framebuffer. It
// is only valid to call this if ready_for_read() has
// returned true.
//
// You must call close_read_framebuffer() to indicate
// you have finished reading.
////////////////////////////////////////////////////////////////////
inline const void *SubprocessWindowBuffer::
open_read_framebuffer() {
assert(ready_for_read());
return (void *)(this + 1);
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::close_read_framebuffer
// Access: Public
// Description: Releases the framebuffer after a previous call to
// open_read_framebuffer().
////////////////////////////////////////////////////////////////////
inline void SubprocessWindowBuffer::
close_read_framebuffer() {
_last_read = _last_written;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::open_write_framebuffer
// Access: Public
// Description: Returns a writable pointer to the framebuffer. It
// is only valid to call this if ready_for_write() has
// returned true.
//
// You must call close_write_framebuffer() to indicate
// you have finished writing.
////////////////////////////////////////////////////////////////////
inline void *SubprocessWindowBuffer::
open_write_framebuffer() {
assert(ready_for_write());
return (void *)(this + 1);
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::close_write_framebuffer
// Access: Public
// Description: Releases the framebuffer after a previous call to
// open_write_framebuffer().
////////////////////////////////////////////////////////////////////
inline void SubprocessWindowBuffer::
close_write_framebuffer() {
++_last_written;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::add_event
// Access: Public
// Description: Adds a new Event to the queue. Returns false
// if the queue was full.
////////////////////////////////////////////////////////////////////
inline bool SubprocessWindowBuffer::
add_event(const SubprocessWindowBuffer::Event &event) {
if (((_event_in + 1) % max_events) == _event_out) {
// The queue is full.
return false;
}
_events[_event_in] = event;
_event_in = (_event_in + 1) % max_events;
return true;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::has_event
// Access: Public
// Description: Returns true if the queue has at least one
// Event to extract, false if it is empty.
////////////////////////////////////////////////////////////////////
inline bool SubprocessWindowBuffer::
has_event() const {
return (_event_in != _event_out);
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::get_event
// Access: Public
// Description: If the queue is nonempty, fills event with the first
// Event on the queue and returns true. If the queue is
// empty, returns false.
////////////////////////////////////////////////////////////////////
inline bool SubprocessWindowBuffer::
get_event(SubprocessWindowBuffer::Event &event) {
if (_event_in == _event_out) {
return false;
}
event = _events[_event_out];
_event_out = (_event_out + 1) % max_events;
return true;
}

View File

@ -0,0 +1,291 @@
// Filename: subprocessWindowBuffer.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 "subprocessWindowBuffer.h"
#include <sys/mman.h>
#include <fcntl.h>
#include <iostream>
using namespace std;
const char SubprocessWindowBuffer::
_magic_number[SubprocessWindowBuffer::magic_number_length] = "pNdaSWB";
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::operator new
// Access: Private
// Description: Placement operator. Returns addr, a trivial
// pass-through.
////////////////////////////////////////////////////////////////////
void *SubprocessWindowBuffer::
operator new(size_t, void *addr) {
cerr << "operator new: " << addr << "\n";
return addr;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::Constructor
// Access: Private
// Description: This constructor is private; it is not intended to be
// called directly. It is used in make_buffer() to
// create a temporary local object, to determine the
// required mmap_size for a given window size.
////////////////////////////////////////////////////////////////////
SubprocessWindowBuffer::
SubprocessWindowBuffer(int x_size, int y_size) {
cerr << "Constructing " << this << "\n";
memcpy(_this_magic, _magic_number, magic_number_length);
_x_size = x_size;
_y_size = y_size;
_row_size = _x_size * 4;
_framebuffer_size = _row_size * y_size;
_event_in = 0;
_event_out = 0;
_last_written = 0;
_last_read = 0;
_mmap_size = sizeof(*this) + _framebuffer_size;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::Copy Constructor
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
SubprocessWindowBuffer::
SubprocessWindowBuffer(const SubprocessWindowBuffer &copy) :
_x_size(copy._x_size),
_y_size(copy._y_size),
_row_size(copy._row_size),
_framebuffer_size(copy._framebuffer_size),
_mmap_size(copy._mmap_size)
{
memcpy(_this_magic, _magic_number, magic_number_length);
_event_in = 0;
_event_out = 0;
_last_written = 0;
_last_read = 0;
cerr << "Copy Constructing " << this << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::Destructor
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
SubprocessWindowBuffer::
~SubprocessWindowBuffer() {
cerr << "Destructing " << this << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::new_buffer
// Access: Public, Static
// Description: Call this method to create a new buffer in shared
// memory space. Supply the desired size of the window.
//
// This method will create the required shared-memory
// buffer and return a SubprocessWindowBuffer allocated
// within that shared memory, or NULL if there is a
// failure allocating sufficient shared memory.
//
// It also creates a temporary file on disk and returns
// fd, mmap_size, and filename, which the caller must
// retain and eventually pass to destroy_buffer(). The
// filename should be passed to the child process to
// open with open_buffer().
////////////////////////////////////////////////////////////////////
SubprocessWindowBuffer *SubprocessWindowBuffer::
new_buffer(int &fd, size_t &mmap_size, string &filename,
int x_size, int y_size) {
mmap_size = 0;
fd = -1;
filename = tmpnam(NULL);
cerr << "new_buffer: " << filename << "\n";
fd = open(filename.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd == -1) {
perror(filename.c_str());
return NULL;
}
// Create a temporary object to determine the required size.
SubprocessWindowBuffer temp(x_size, y_size);
mmap_size = temp._mmap_size;
// Ensure the disk file is large enough.
size_t zero_size = 1024;
char zero[zero_size];
memset(zero, 0, zero_size);
for (size_t bi = 0; bi < mmap_size; bi += zero_size) {
write(fd, zero, zero_size);
}
cerr << "size = " << x_size << " * " << y_size << " = "
<< mmap_size << " bytes\n";
void *shared_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (shared_mem == (void *)-1) {
// Failure to map.
close(fd);
fd = -1;
mmap_size = 0;
return NULL;
}
cerr << "shared_mem = " << shared_mem << "\n";
// Now create the actual object in the shared-memory buffer.
return new(shared_mem) SubprocessWindowBuffer(temp);
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::destroy_buffer
// Access: Public, Static
// Description: Destroys a buffer object created via a previous call
// to new_buffer(). This destructs objects within the
// buffer, unmaps the shared memory, and closes the file
// descriptor.
////////////////////////////////////////////////////////////////////
void SubprocessWindowBuffer::
destroy_buffer(int fd, size_t mmap_size, const string &filename,
SubprocessWindowBuffer *buffer) {
buffer->~SubprocessWindowBuffer();
close_buffer(fd, mmap_size, filename, buffer);
// This isn't really necessary, since our child process should have
// unlinked it; but we do it anyway just for good measure (for
// instance, in case the child process never got started). I
// suppose there is some risk that we will accidentally delete
// someone else's file this way, but the risk is small.
unlink(filename.c_str());
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::open_buffer
// Access: Public, Static
// Description: Call this method to open a reference to an existing
// buffer in shared memory space. Supply the temporary
// filename returned by new_buffer(), above (presumably
// from the parent process).
//
// This method will mmap the required shared-memory
// buffer and return a SubprocessWindowBuffer allocated
// within that shared memory, or NULL if there is some
// failure. The caller must retain fd, mmap_size, and
// filename and eventually pass all three to
// close_buffer().
////////////////////////////////////////////////////////////////////
SubprocessWindowBuffer *SubprocessWindowBuffer::
open_buffer(int &fd, size_t &mmap_size, const string &filename) {
cerr << "open_buffer: " << filename << "\n";
mmap_size = 0;
fd = open(filename.c_str(), O_RDWR);
if (fd == -1) {
perror(filename.c_str());
return NULL;
}
// Check that the disk file is large enough.
off_t file_size = lseek(fd, 0, SEEK_END);
if (file_size < sizeof(SubprocessWindowBuffer)) {
cerr << filename << " not large enough.\n";
close(fd);
fd = -1;
return NULL;
}
// First, map enough memory to read the buffer object.
size_t initial_size = sizeof(SubprocessWindowBuffer);
void *shared_mem = mmap(NULL, initial_size, PROT_READ,
MAP_SHARED, fd, 0);
if (shared_mem == (void *)-1) {
perror("mmap");
cerr << "Couldn't map.\n";
close(fd);
fd = -1;
return NULL;
}
SubprocessWindowBuffer *temp = (SubprocessWindowBuffer *)shared_mem;
if (!temp->verify_magic_number()) {
cerr << "Not a subprocess window buffer: " << filename << "\n";
munmap(shared_mem, initial_size);
close(fd);
fd = -1;
return NULL;
}
mmap_size = temp->_mmap_size;
// Now unmap that and remap the proper-size buffer.
munmap(shared_mem, initial_size);
if (file_size < mmap_size) {
cerr << filename << " not large enough.\n";
close(fd);
fd = -1;
return NULL;
}
shared_mem = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (shared_mem == (void *)-1) {
perror("mmap");
cerr << "Couldn't map 2.\n";
return NULL;
}
// Now that we've successfully opened and mapped the file, we can
// safely delete it from the file system.
unlink(filename.c_str());
SubprocessWindowBuffer *buffer = (SubprocessWindowBuffer *)shared_mem;
assert(buffer->_mmap_size == mmap_size);
return buffer;
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::close_buffer
// Access: Public, Static
// Description: Closes a buffer object created via a previous call
// to open_buffer(). This unmaps the shared memory
// and closes the file descriptor, but does not molest
// the shared buffer itself.
////////////////////////////////////////////////////////////////////
void SubprocessWindowBuffer::
close_buffer(int fd, size_t mmap_size, const string &filename,
SubprocessWindowBuffer *buffer) {
munmap((void *)buffer, mmap_size);
close(fd);
// Guess we shouldn't unlink() the file here, since we already did
// in open_buffer().
}
////////////////////////////////////////////////////////////////////
// Function: SubprocessWindowBuffer::verify_magic_number
// Access: Public
// Description: Returns true if the buffer's magic number matches,
// false otherwise.
////////////////////////////////////////////////////////////////////
bool SubprocessWindowBuffer::
verify_magic_number() const {
return (memcmp(_this_magic, _magic_number, magic_number_length) == 0);
}

View File

@ -0,0 +1,124 @@
// Filename: subprocessWindowBuffer.h
// 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."
//
////////////////////////////////////////////////////////////////////
#ifndef SUBPROCESSWINDOWBUFFER_H
#define SUBPROCESSWINDOWBUFFER_H
#include <stdio.h> // perror
#include <assert.h>
#include <string>
using namespace std;
////////////////////////////////////////////////////////////////////
// Class : SubprocessWindowBuffer
// Description : This is a special class that is designed to faciliate
// SubprocessWindow. It's intended to be allocated
// within a shared memory buffer, and it contains space
// for a framebuffer image to be stored for transferring
// between processes, as well as appropriate
// synchronization primitives.
//
// It's designed to be compiled outside of Panda, so
// that code that doesn't link with Panda (in
// particular, the Panda3D plugin core API) may still
// link with this and use it.
//
// At the moment, and maybe indefinitely, it is only
// compiled on OSX, and only when we are building
// support for the plugin; because it is only needed
// then.
////////////////////////////////////////////////////////////////////
class SubprocessWindowBuffer {
private:
void *operator new(size_t, void *addr);
SubprocessWindowBuffer(int x_size, int y_size);
SubprocessWindowBuffer(const SubprocessWindowBuffer &copy);
~SubprocessWindowBuffer();
public:
static SubprocessWindowBuffer *new_buffer(int &fd, size_t &mmap_size,
string &filename,
int x_size, int y_size);
static void destroy_buffer(int fd, size_t mmap_size,
const string &filename,
SubprocessWindowBuffer *buffer);
static SubprocessWindowBuffer *open_buffer(int &fd, size_t &mmap_size,
const string &filename);
static void close_buffer(int fd, size_t mmap_size,
const string &filename,
SubprocessWindowBuffer *buffer);
bool verify_magic_number() const;
inline int get_x_size() const;
inline int get_y_size() const;
inline size_t get_row_size() const;
inline size_t get_framebuffer_size() const;
inline bool ready_for_read() const;
inline bool ready_for_write() const;
inline const void *open_read_framebuffer();
inline void close_read_framebuffer();
inline void *open_write_framebuffer();
inline void close_write_framebuffer();
class Event {
public:
// int _key; TODO.
bool _up;
int _x, _y; // position of mouse at the time of the event
};
inline bool add_event(const Event &event);
inline bool has_event() const;
inline bool get_event(Event &event);
private:
// The first thing we store in the buffer is a magic number, so we
// don't accidentally memory-map the wrong file and attempt to treat
// it as a window buffer.
enum { magic_number_length = 8 };
static const char _magic_number[magic_number_length];
char _this_magic[magic_number_length];
// Then we have the required size of the entire structure, including
// its data blocks.
size_t _mmap_size;
// Then some other important parameters.
int _x_size, _y_size;
size_t _row_size;
size_t _framebuffer_size;
// A circular queue of events.
enum { max_events = 64 };
int _event_in; // next slot to write an event to
int _event_out; // next slot to read an event from
Event _events[max_events];
// The queue is empty when _event_in == _event_out.
// It is full when _event_in == _event_out - 1, circularly.
// These sequence numbers are incremented as frames are written and
// read.
int _last_written;
int _last_read;
// The framebuffer data begins immediately at the end of this class.
};
#include "subprocessWindowBuffer.I"
#endif

View File

@ -749,7 +749,18 @@ clear_z_order() {
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::set_mouse_mode
// Access: Published
// Description: Removes the z_order specification from the properties.
// Description: Specifies the mode in which the window is to operate
// its mouse pointer. The default is M_absolute, which
// is the normal mode in which a mouse pointer operates;
// but you can also set M_relative, which is
// particularly useful for FPS-style mouse movements
// where you have hidden the mouse pointer and are are
// more interested in how fast the mouse is moving,
// rather than precisely where the pointer is hovering.
//
// This has no effect on Windows and Linux, which do not
// have this concept; but is important to do on OSX to
// properly enable a smooth FPS-style mouselook mode.
////////////////////////////////////////////////////////////////////
INLINE void WindowProperties::
set_mouse_mode(MouseMode mode) {
@ -760,7 +771,7 @@ set_mouse_mode(MouseMode mode) {
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::get_mouse_mode
// Access: Published
// Description: Removes the z_order specification from the properties.
// Description: See set_mouse_mode().
////////////////////////////////////////////////////////////////////
INLINE WindowProperties::MouseMode WindowProperties::
get_mouse_mode() const {
@ -768,9 +779,9 @@ get_mouse_mode() const {
}
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::has_moude_mode
// Function: WindowProperties::has_mouse_mode
// Access: Published
// Description: Removes the z_order specification from the properties.
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool WindowProperties::
has_mouse_mode() const {
@ -780,7 +791,7 @@ has_mouse_mode() const {
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::clear_mouse_mode
// Access: Published
// Description: Removes the z_order specification from the properties.
// Description: Removes the mouse_mode specification from the properties.
////////////////////////////////////////////////////////////////////
INLINE void WindowProperties::
clear_mouse_mode() {
@ -791,7 +802,22 @@ clear_mouse_mode() {
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::set_parent_window
// Access: Published
// Description: Removes the z_order specification from the properties.
// Description: Specifies the window that this window should be
// attached to. If this is zero or unspecified, the
// window will be created as a toplevel window on the
// desktop; if this is nonzero, the window will be bound
// as a child window to the indicated parent window.
//
// The actual value for "parent" is platform-specific.
// On Windows, it is the HWND of the parent window, cast
// to an unsigned integer. On X11, it is the Window
// pointer of the parent window, similarly cast (yes,
// it's a portability issue).
//
// On OSX, this is the NSWindow pointer, though there
// appear to be some compatibility issues (OSX doesn't
// easily support the parent-window model). On OSX,
// consider using subprocess_window instead.
////////////////////////////////////////////////////////////////////
INLINE void WindowProperties::
set_parent_window(size_t parent) {
@ -802,7 +828,7 @@ set_parent_window(size_t parent) {
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::get_parent_window
// Access: Published
// Description: Removes the parent Window
// Description: Returns the parent window specification.
////////////////////////////////////////////////////////////////////
INLINE size_t WindowProperties::
get_parent_window() const {
@ -830,6 +856,70 @@ clear_parent_window() {
_parent_window = (size_t)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::set_subprocess_window
// Access: Published
// Description: Specifies that the window should be created as a
// "subprocess window", which is a special concept
// needed on OSX, to support windows that may run in a
// subprocess and communicate the output of their
// rendering to a parent process.
//
// To use it, create a SubprocessWindowBuffer in the
// parent process, pass the resulting temporary filename
// to the child process, and set that filename here
// before opening a window. Panda will open a
// SubprocessWindow instead of a normal window; and that
// class will take the output of the rendering and write
// it to the SubprocessWindowBuffer for the parent
// process to extract.
//
// This is particularly useful for implementing the web
// browser plugin on OSX, which requires exactly this
// sort of process isolation in order to render to the
// browser page.
//
// This feature is not currently available on other
// platforms (and they have no need of it).
////////////////////////////////////////////////////////////////////
INLINE void WindowProperties::
set_subprocess_window(const Filename &filename) {
_subprocess_window = filename;
_specified |= S_subprocess_window;
}
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::get_subprocess_window
// Access: Published
// Description: Returns the filename specified to set_subprocess_window().
////////////////////////////////////////////////////////////////////
INLINE const Filename &WindowProperties::
get_subprocess_window() const {
return _subprocess_window;
}
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::has_subprocess_window
// Access: Published
// Description: Returns true if set_subprocess_window() was set.
////////////////////////////////////////////////////////////////////
INLINE bool WindowProperties::
has_subprocess_window() const {
return ((_specified & S_subprocess_window) != 0);
}
////////////////////////////////////////////////////////////////////
// Function: WindowProperties::clear_subprocess_window
// Access: Published
// Description: Removes the subprocess_window specification from the
// properties.
////////////////////////////////////////////////////////////////////
INLINE void WindowProperties::
clear_subprocess_window() {
_specified &= ~S_subprocess_window;
_subprocess_window = Filename();
}
INLINE ostream &
operator << (ostream &out, const WindowProperties &properties) {

View File

@ -45,6 +45,7 @@ operator = (const WindowProperties &copy) {
_flags = copy._flags;
_mouse_mode = copy._mouse_mode;
_parent_window = copy._parent_window;
_subprocess_window = copy._subprocess_window;
}
////////////////////////////////////////////////////////////////////
@ -86,7 +87,11 @@ get_default() {
if (parent_window_handle.get_value() != 0) {
props.set_parent_window(parent_window_handle);
}
if (subprocess_window.has_value()) {
props.set_subprocess_window(subprocess_window);
}
props.set_mouse_mode(M_absolute);
return props;
}
@ -122,8 +127,8 @@ operator == (const WindowProperties &other) const {
_icon_filename == other._icon_filename &&
_cursor_filename == other._cursor_filename &&
_mouse_mode == other._mouse_mode &&
_parent_window == other._parent_window);
_parent_window == other._parent_window &&
_subprocess_window == other._subprocess_window);
}
////////////////////////////////////////////////////////////////////
@ -147,6 +152,7 @@ clear() {
_flags = 0;
_mouse_mode = M_absolute;
_parent_window = 0;
_subprocess_window = Filename();
}
////////////////////////////////////////////////////////////////////
@ -200,15 +206,15 @@ add_properties(const WindowProperties &other) {
if (other.has_z_order()) {
set_z_order(other.get_z_order());
}
if (other.has_mouse_mode()) {
set_mouse_mode(other.get_mouse_mode());
}
if (other.has_parent_window()) {
set_parent_window(other.get_parent_window());
}
if (other.has_subprocess_window()) {
set_subprocess_window(other.get_subprocess_window());
}
}
////////////////////////////////////////////////////////////////////
@ -268,6 +274,9 @@ output(ostream &out) const {
if (has_parent_window()) {
out << "parent:" << get_parent_window() << " ";
}
if (has_subprocess_window()) {
out << "subprocess_window:" << get_subprocess_window() << " ";
}
}

View File

@ -28,8 +28,6 @@
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_DISPLAY WindowProperties {
PUBLISHED:
enum ZOrder {
Z_bottom,
Z_normal,
@ -132,12 +130,16 @@ PUBLISHED:
INLINE bool has_z_order() const;
INLINE void clear_z_order();
INLINE void set_parent_window(size_t parent);
INLINE size_t get_parent_window() const;
INLINE size_t get_parent_window() const;
INLINE bool has_parent_window() const;
INLINE void clear_parent_window();
INLINE void set_subprocess_window(const Filename &filename);
INLINE const Filename &get_subprocess_window() const;
INLINE bool has_subprocess_window() const;
INLINE void clear_subprocess_window();
void add_properties(const WindowProperties &other);
void output(ostream &out) const;
@ -147,22 +149,23 @@ private:
// structure have been filled in by the user, and which remain
// unspecified.
enum Specified {
S_origin = 0x0001,
S_size = 0x0002,
S_title = 0x0004,
S_undecorated = 0x0008,
S_fullscreen = 0x0010,
S_foreground = 0x0020,
S_minimized = 0x0040,
S_open = 0x0080,
S_cursor_hidden = 0x0100,
S_fixed_size = 0x0200,
S_z_order = 0x0400,
S_icon_filename = 0x0800,
S_cursor_filename = 0x1000,
S_mouse_mode = 0x2000,
S_parent_window = 0x4000,
S_raw_mice = 0x8000,
S_origin = 0x00001,
S_size = 0x00002,
S_title = 0x00004,
S_undecorated = 0x00008,
S_fullscreen = 0x00010,
S_foreground = 0x00020,
S_minimized = 0x00040,
S_open = 0x00080,
S_cursor_hidden = 0x00100,
S_fixed_size = 0x00200,
S_z_order = 0x00400,
S_icon_filename = 0x00800,
S_cursor_filename = 0x01000,
S_mouse_mode = 0x02000,
S_parent_window = 0x04000,
S_raw_mice = 0x08000,
S_subprocess_window = 0x10000,
};
// This bitmask represents the true/false settings for various
@ -189,8 +192,9 @@ private:
Filename _cursor_filename;
Filename _icon_filename;
ZOrder _z_order;
int _flags;
size_t _parent_window; // a HWND or WindowRef or .. what ever it is on X win...
unsigned int _flags;
size_t _parent_window; // a HWND or WindowRef or ..
Filename _subprocess_window;
};
EXPCL_PANDA_DISPLAY ostream &

View File

@ -15,6 +15,8 @@
#include "osxGraphicsBuffer.h"
#include "osxGraphicsStateGuardian.h"
#include "pnmImage.h"
#include "subprocessWindow.h"
TypeHandle osxGraphicsPipe::_type_handle;
@ -221,7 +223,7 @@ make_output(const string &name,
DCAST_INTO_R(osxgsg, gsg, NULL);
}
// First thing to try: a osxGraphicsWindow
// First thing to try: an osxGraphicsWindow
if (retry == 0) {
if (((flags&BF_require_parasite)!=0)||
@ -232,6 +234,13 @@ make_output(const string &name,
((flags&BF_can_bind_every)!=0)) {
return NULL;
}
#ifdef SUPPORT_SUBPROCESS_WINDOW
if (win_prop.has_subprocess_window()) {
return new SubprocessWindow(engine, this, name, fb_prop, win_prop,
flags, gsg, host,
win_prop.get_subprocess_window().to_os_specific());
}
#endif // SUPPORT_SUBPROCESS_WINDOW
return new osxGraphicsWindow(engine, this, name, fb_prop, win_prop,
flags, gsg, host);
}

View File

@ -60,7 +60,7 @@
reMutex.I reMutex.h \
reMutexDirect.h reMutexDirect.I \
reMutexHolder.I reMutexHolder.h \
semaphore.h semaphore.I \
psemaphore.h psemaphore.I \
thread.h thread.I threadImpl.h \
threadDummyImpl.h threadDummyImpl.I \
threadPosixImpl.h threadPosixImpl.I \
@ -113,7 +113,7 @@
reMutex.cxx \
reMutexDirect.cxx \
reMutexHolder.cxx \
semaphore.cxx \
psemaphore.cxx \
thread.cxx \
threadDummyImpl.cxx \
threadPosixImpl.cxx \
@ -172,7 +172,7 @@
reMutex.I reMutex.h \
reMutexDirect.h reMutexDirect.I \
reMutexHolder.I reMutexHolder.h \
semaphore.h semaphore.I \
psemaphore.h psemaphore.I \
thread.h thread.I threadImpl.h \
threadDummyImpl.h threadDummyImpl.I \
threadPosixImpl.h threadPosixImpl.I \

View File

@ -9,11 +9,11 @@
#include "pipelineCyclerTrivialImpl.cxx"
#include "pipelineCyclerTrueImpl.cxx"
#include "pmutex.cxx"
#include "psemaphore.cxx"
#include "pythonThread.cxx"
#include "reMutex.cxx"
#include "reMutexDirect.cxx"
#include "reMutexHolder.cxx"
#include "semaphore.cxx"
#include "thread.cxx"
#include "threadDummyImpl.cxx"
#include "threadPosixImpl.cxx"

View File

@ -1,4 +1,4 @@
// Filename: semaphore.I
// Filename: psemaphore.I
// Created by: drose (13Oct08)
//
////////////////////////////////////////////////////////////////////

View File

@ -1,4 +1,4 @@
// Filename: semaphore.cxx
// Filename: psemaphore.cxx
// Created by: drose (13Oct08)
//
////////////////////////////////////////////////////////////////////
@ -12,7 +12,7 @@
//
////////////////////////////////////////////////////////////////////
#include "semaphore.h"
#include "psemaphore.h"
////////////////////////////////////////////////////////////////////
// Function: Semaphore::output

View File

@ -1,4 +1,4 @@
// Filename: semaphore.h
// Filename: psemaphore.h
// Created by: drose (13Oct08)
//
////////////////////////////////////////////////////////////////////
@ -12,8 +12,8 @@
//
////////////////////////////////////////////////////////////////////
#ifndef SEMAPHORE_H
#define SEMAPHORE_H
#ifndef PSEMAPHORE_H
#define PSEMAPHORE_H
#include "pandabase.h"
#include "pmutex.h"
@ -58,6 +58,6 @@ operator << (ostream &out, const Semaphore &sem) {
return out;
}
#include "semaphore.I"
#include "psemaphore.I"
#endif

View File

@ -21,6 +21,7 @@
#include "tinyOsxGraphicsWindow.h"
#include "tinyGraphicsBuffer.h"
#include "pnmImage.h"
#include "subprocessWindow.h"
TypeHandle TinyOsxGraphicsPipe::_type_handle;
@ -234,6 +235,13 @@ make_output(const string &name,
return NULL;
}
}
#ifdef SUPPORT_SUBPROCESS_WINDOW
if (win_prop.has_subprocess_window()) {
return new SubprocessWindow(engine, this, name, fb_prop, win_prop,
flags, gsg, host,
win_prop.get_subprocess_window().to_os_specific());
}
#endif // SUPPORT_SUBPROCESS_WINDOW
return new TinyOsxGraphicsWindow(engine, this, name, fb_prop, win_prop,
flags, gsg, host);
}