Browse Source

WxPandaWindow

David Rose 14 years ago
parent
commit
56f22e0692

+ 28 - 1
direct/src/showbase/ShowBase.py

@@ -559,11 +559,18 @@ class ShowBase(DirectObject.DirectObject):
     def openWindow(self, props = None, pipe = None, gsg = None,
                    type = None, name = None, size = None, aspectRatio = None,
                    makeCamera = 1, keepCamera = 0,
-                   scene = None, stereo = None, rawmice = 0):
+                   scene = None, stereo = None, rawmice = 0,
+                   callbackWindowDict = None):
         """
         Creates a window and adds it to the list of windows that are
         to be updated every frame.
+
+        If callbackWindowDict is not None, a CallbackGraphicWindow is
+        created instead, which allows the caller to create the actual
+        window with its own OpenGL context, and direct Panda's
+        rendering into that window.
         """
+
         if pipe == None:
             pipe = self.pipe
 
@@ -614,6 +621,9 @@ class ShowBase(DirectObject.DirectObject):
         elif type == 'offscreen':
             flags = flags | GraphicsPipe.BFRefuseWindow
 
+        if callbackWindowDict:
+            flags = flags | GraphicsPipe.BFRequireCallbackWindow
+
         if gsg:
             win = self.graphicsEngine.makeOutput(pipe, name, 0, fbprops,
                                                  props, flags, gsg)
@@ -625,6 +635,23 @@ class ShowBase(DirectObject.DirectObject):
             # Couldn't create a window!
             return None
 
+        if callbackWindowDict:
+            # If we asked for (and received) a CallbackGraphicsWindow,
+            # we now have to assign the callbacks, before we start
+            # trying to do anything with the window.
+            for callbackName in ['Events', 'Properties', 'Render']:
+                func = callbackWindowDict.get(callbackName, None)
+                if not func:
+                    continue
+                
+                setCallbackName = 'set%sCallback' % (callbackName)
+                setCallback = getattr(win, setCallbackName)
+                setCallback(PythonCallbackObject(func))
+
+            # We also need to set up the mouse/keyboard objects.
+            for inputName in callbackWindowDict.get('inputDevices', ['mouse']):
+                win.createInputDevice(inputName)
+
         if hasattr(win, "requestProperties"):
             win.requestProperties(props)
 

+ 6 - 2
direct/src/showbase/WxGlobal.py

@@ -2,16 +2,20 @@ import wx
 from direct.task.Task import Task
 
 def wxLoop(self):
-    # Do all the wxPython events waiting on this frame
+    # First we need to ensure that the OS message queue is processed.
+    base.wxApp.Yield()
+
+    # Now do all the wxPython events waiting on this frame.
     while base.wxApp.Pending():
         base.wxApp.Dispatch()
+
     return Task.cont
 
 def spawnWxLoop():
     if not getattr(base, 'wxApp', None):
         # Create a new base.wxApp, but only if it's not already
         # created.
-        base.wxApp = wx.App(False)
+        base.wxApp = wx.PySimpleApp(redirect = False)
 
     # Spawn this task
     taskMgr.add(wxLoop, "wxLoop")

+ 206 - 0
direct/src/showbase/WxPandaWindow.py

@@ -0,0 +1,206 @@
+""" This module implements a Panda3D window that can be embedded
+within a wx.Frame.  The window itself is either an embedded window,
+which is a wx.Window with the Panda window attached to it, or it is a
+wxgl.GLCanvas, with Panda directed to draw into it, depending on the
+platform.  In either case, you may simply embed this window into a
+wx.Frame of your choosing, using sizers or whatever you like. """
+
+import wx
+import platform
+
+try:
+    import wx.glcanvas as wxgl
+except ImportError:
+    wxgl = None
+
+from panda3d.core import *
+
+__all__ = ['WxPandaWindow']
+
+if platform.system() != 'Darwin':
+    class EmbeddedPandaWindow(wx.Window):
+        """ This class implements a Panda3D window that is directly
+        embedded within the frame.  It is fully supported on Windows,
+        partially supported on Linux, and not at all on OSX. """
+
+        def __init__(self, *args, **kw):
+            wx.Window.__init__(self, *args, **kw)
+
+            wp = WindowProperties.getDefault()
+            wp.setParentWindow(self.GetHandle())
+
+            if base.win:
+                self.win = base.openWindow(props = wp)
+            else:
+                base.openDefaultWindow(props = wp)
+                self.win = base.win
+
+            self.Bind(wx.EVT_SIZE, self.__resized)
+
+        def __resized(self, event):
+            wp = WindowProperties()
+            wp.setOrigin(0, 0)
+            wp.setSize(*self.GetClientSizeTuple())
+            self.win.requestProperties(wp)
+
+if hasattr(wxgl, 'GLCanvas'):
+    class OpenGLPandaWindow(wxgl.GLCanvas):
+        """ This class implements a Panda3D "window" that actually draws
+        within the wx GLCanvas object.  It is supported whenever OpenGL is
+        Panda's rendering engine, and GLCanvas is available in wx. """
+
+        Keymap = {
+            wx.WXK_BACK : KeyboardButton.backspace(),
+            wx.WXK_TAB : KeyboardButton.tab(),
+            wx.WXK_RETURN : KeyboardButton.enter(),
+            wx.WXK_ESCAPE : KeyboardButton.escape(),
+            wx.WXK_DELETE : KeyboardButton._del(),  # del is a Python keyword
+            wx.WXK_SHIFT : KeyboardButton.shift(),
+            wx.WXK_ALT : KeyboardButton.alt(),
+            wx.WXK_CONTROL : KeyboardButton.control(),
+            wx.WXK_MENU : KeyboardButton.meta(),
+            wx.WXK_PAUSE : KeyboardButton.pause(),
+            wx.WXK_END : KeyboardButton.end(),
+            wx.WXK_HOME : KeyboardButton.home(),
+            wx.WXK_LEFT : KeyboardButton.left(),
+            wx.WXK_UP : KeyboardButton.up(),
+            wx.WXK_RIGHT : KeyboardButton.right(),
+            wx.WXK_DOWN : KeyboardButton.down(),
+            wx.WXK_PRINT : KeyboardButton.printScreen(),
+            wx.WXK_INSERT : KeyboardButton.insert(),
+            wx.WXK_F1 : KeyboardButton.f1(),
+            wx.WXK_F2 : KeyboardButton.f2(),
+            wx.WXK_F3 : KeyboardButton.f3(),
+            wx.WXK_F4 : KeyboardButton.f4(),
+            wx.WXK_F5 : KeyboardButton.f5(),
+            wx.WXK_F6 : KeyboardButton.f6(),
+            wx.WXK_F7 : KeyboardButton.f7(),
+            wx.WXK_F8 : KeyboardButton.f8(),
+            wx.WXK_F9 : KeyboardButton.f9(),
+            wx.WXK_F10 : KeyboardButton.f10(),
+            wx.WXK_F11 : KeyboardButton.f11(),
+            wx.WXK_F12 : KeyboardButton.f12(),
+            wx.WXK_NUMLOCK : KeyboardButton.numLock(),
+            wx.WXK_SCROLL : KeyboardButton.scrollLock(),
+            wx.WXK_PAGEUP : KeyboardButton.pageUp(),
+            wx.WXK_PAGEDOWN : KeyboardButton.pageDown(),
+            wx.WXK_COMMAND : KeyboardButton.meta(),
+            }
+
+        def __init__(self, *args, **kw):
+            wxgl.GLCanvas.__init__(self, *args, **kw)
+
+            callbackWindowDict = {
+                'Events' : self.__eventsCallback,
+                'Properties' : self.__propertiesCallback,
+                'Render' : self.__renderCallback,
+                }
+
+            # Make sure we have an OpenGL GraphicsPipe.
+            if not base.pipe:
+                base.makeDefaultPipe()
+            pipe = base.pipe
+            if pipe.getInterfaceName() != 'OpenGL':
+                base.makeAllPipes()
+                for pipe in base.pipeList:
+                    if pipe.getInterfaceName() == 'OpenGL':
+                        break
+
+            if pipe.getInterfaceName() != 'OpenGL':
+                raise StandardError, "Couldn't get an OpenGL pipe."
+
+            self.SetCurrent()
+            if base.win:
+                self.win = base.openWindow(callbackWindowDict = callbackWindowDict, pipe = pipe)
+            else:
+                base.openDefaultWindow(callbackWindowDict = callbackWindowDict, pipe = pipe)
+                self.win = base.win
+
+            self.inputDevice = self.win.getInputDevice(0)
+
+            self.Bind(wx.EVT_SIZE, self.__resized)
+            self.Bind(wx.EVT_LEFT_DOWN, lambda event: self.__buttonDown(MouseButton.one()))
+            self.Bind(wx.EVT_LEFT_UP, lambda event: self.__buttonUp(MouseButton.one()))
+            self.Bind(wx.EVT_MIDDLE_DOWN, lambda event: self.__buttonDown(MouseButton.two()))
+            self.Bind(wx.EVT_MIDDLE_UP, lambda event: self.__buttonUp(MouseButton.two()))
+            self.Bind(wx.EVT_RIGHT_DOWN, lambda event: self.__buttonDown(MouseButton.three()))
+            self.Bind(wx.EVT_RIGHT_UP, lambda event: self.__buttonUp(MouseButton.three()))
+            self.Bind(wx.EVT_MOTION, self.__mouseMotion)
+            self.Bind(wx.EVT_LEAVE_WINDOW, self.__mouseLeaveWindow)
+            self.Bind(wx.EVT_KEY_DOWN, self.__keyDown)
+            self.Bind(wx.EVT_KEY_UP, self.__keyUp)
+            self.Bind(wx.EVT_CHAR, self.__keystroke)
+
+        def __buttonDown(self, button):
+            self.inputDevice.buttonDown(button)
+
+        def __buttonUp(self, button):
+            self.inputDevice.buttonUp(button)
+
+        def __mouseMotion(self, event):
+            self.inputDevice.setPointerInWindow(*event.GetPosition())
+
+        def __mouseLeaveWindow(self, event):
+            self.inputDevice.setPointerOutOfWindow()
+
+        def __keyDown(self, event):
+            key = self.__getkey(event)
+            if key:
+                self.inputDevice.buttonDown(key)
+
+        def __keyUp(self, event):
+            key = self.__getkey(event)
+            if key:
+                self.inputDevice.buttonUp(key)
+
+        def __getkey(self, event):
+            code = event.GetKeyCode()
+            key = self.Keymap.get(code, None)
+            if key is not None:
+                return key
+
+            if code >= 0x41 and code <= 0x5a:
+                # wxWidgets returns uppercase letters, but Panda expects
+                # lowercase letters.
+                return KeyboardButton.asciiKey(code + 0x20)
+            if code >= 0x20 and code <= 0x80:
+                # Other ASCII keys are treated the same way in wx and Panda.
+                return KeyboardButton.asciiKey(code)
+
+            return None
+
+        def __keystroke(self, event):
+            if hasattr(event, 'GetUnicodeKey'):
+                code = event.GetUnicodeKey()
+            else:
+                code = event.GetKeyCode()
+                if code < 0x20 or code >= 0x80:
+                    return
+
+            self.inputDevice.keystroke(code)
+
+        def __eventsCallback(self, data):
+            data.upcall()
+
+        def __propertiesCallback(self, data):
+            data.upcall()
+
+        def __renderCallback(self, data):
+            cbType = data.getCallbackType()
+            if cbType == CallbackGraphicsWindow.RCTBeginFrame:
+                self.SetCurrent()
+            elif cbType == CallbackGraphicsWindow.RCTEndFlip:
+                self.SwapBuffers()
+
+            data.upcall()
+
+        def __resized(self, event):
+            wp = WindowProperties()
+            wp.setSize(*self.GetClientSizeTuple())
+            self.win.requestProperties(wp)
+
+# Choose the best implementation of WxPandaWindow for the platform.
+if platform.system() == 'Darwin':
+    WxPandaWindow = OpenGLPandaWindow
+else:
+    WxPandaWindow = EmbeddedPandaWindow

+ 3 - 0
panda/src/display/Sources.pp

@@ -14,6 +14,7 @@
     standardMunger.I standardMunger.h \
     config_display.h \
     $[if $[HAVE_PYTHON], pythonGraphicsWindowProc.h] \
+    callbackGraphicsWindow.I callbackGraphicsWindow.h \
     drawableRegion.I drawableRegion.h \
     displayRegion.I displayRegion.h  \
     displayRegionCullCallbackData.I displayRegionCullCallbackData.h \
@@ -53,6 +54,7 @@
     standardMunger.cxx \
     config_display.cxx \
     $[if $[HAVE_PYTHON], pythonGraphicsWindowProc.cxx] \
+    callbackGraphicsWindow.cxx \
     drawableRegion.cxx \
     displayRegion.cxx \
     displayRegionCullCallbackData.cxx \
@@ -85,6 +87,7 @@
     standardMunger.I standardMunger.h \
     config_display.h \
     $[if $[HAVE_PYTHON], pythonGraphicsWindowProc.h] \
+    callbackGraphicsWindow.I callbackGraphicsWindow.h \
     drawableRegion.I drawableRegion.h \
     displayInformation.h \
     displayRegion.I displayRegion.h \

+ 260 - 0
panda/src/display/callbackGraphicsWindow.I

@@ -0,0 +1,260 @@
+// Filename: callbackGraphicsWindow.I
+// Created by:  drose (06Jan11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: CallbackGraphicsWindow::set_events_callback
+//       Access: Published
+//  Description: Sets the CallbackObject that will be notified when
+//               this window is polled for window events, including
+//               mouse and keyboard events, as well as window resize
+//               events and other system-generated events.
+//
+//               This callback will receive a
+//               CallbackGraphicsWindow::EventsCallbackData.
+//
+//               This callback should process any system-generated
+//               events, and call data->upcall() to process requested
+//               property change requests made via
+//               request_properties().
+////////////////////////////////////////////////////////////////////
+INLINE void CallbackGraphicsWindow::
+set_events_callback(CallbackObject *object) {
+  _events_callback = object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::clear_events_callback
+//       Access: Published
+//  Description: Removes the callback set by an earlier call to
+//               set_events_callback().
+////////////////////////////////////////////////////////////////////
+INLINE void CallbackGraphicsWindow::
+clear_events_callback() {
+  set_events_callback(NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::get_events_callback
+//       Access: Published
+//  Description: Returns the CallbackObject set by set_events_callback().
+////////////////////////////////////////////////////////////////////
+INLINE CallbackObject *CallbackGraphicsWindow::
+get_events_callback() const {
+  return _events_callback;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::set_properties_callback
+//       Access: Published
+//  Description: Sets the CallbackObject that will be notified when
+//               this window receives a property change request from
+//               user code (e.g. via request_properties).
+//
+//               This callback will receive a
+//               CallbackGraphicsWindow::PropertiesCallbackData, which
+//               provides a get_properties() method that returns a
+//               modifiable reference to a WindowsProperties object.
+//               This object will contain only those properties
+//               requested by user code.  The callback should handle
+//               any of the requests it finds, including and
+//               especially set_open(), and remove them from the
+//               object when it has handled them.  Any unhandled
+//               properties should be left unchanged in the properties
+//               object.
+////////////////////////////////////////////////////////////////////
+INLINE void CallbackGraphicsWindow::
+set_properties_callback(CallbackObject *object) {
+  _properties_callback = object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::clear_properties_callback
+//       Access: Published
+//  Description: Removes the callback set by an earlier call to
+//               set_properties_callback().
+////////////////////////////////////////////////////////////////////
+INLINE void CallbackGraphicsWindow::
+clear_properties_callback() {
+  set_properties_callback(NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::get_properties_callback
+//       Access: Published
+//  Description: Returns the CallbackObject set by set_properties_callback().
+////////////////////////////////////////////////////////////////////
+INLINE CallbackObject *CallbackGraphicsWindow::
+get_properties_callback() const {
+  return _properties_callback;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::set_render_callback
+//       Access: Published
+//  Description: Sets the CallbackObject that will be notified when
+//               this window is invoked (in the draw thread) to render
+//               its contents, and/or flip the graphics buffers.
+//
+//               This callback will actually serve several different
+//               functions.  It receivces a RenderCallbackData, and
+//               you can query data->get_callback_type() to return the
+//               actual function of each particular callback.
+////////////////////////////////////////////////////////////////////
+INLINE void CallbackGraphicsWindow::
+set_render_callback(CallbackObject *object) {
+  _render_callback = object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::clear_render_callback
+//       Access: Published
+//  Description: Removes the callback set by an earlier call to
+//               set_render_callback().
+////////////////////////////////////////////////////////////////////
+INLINE void CallbackGraphicsWindow::
+clear_render_callback() {
+  set_render_callback(NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::get_render_callback
+//       Access: Published
+//  Description: Returns the CallbackObject set by set_render_callback().
+////////////////////////////////////////////////////////////////////
+INLINE CallbackObject *CallbackGraphicsWindow::
+get_render_callback() const {
+  return _render_callback;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::WindowCallbackData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CallbackGraphicsWindow::WindowCallbackData::
+WindowCallbackData(CallbackGraphicsWindow *window) : _window(window) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::WindowCallbackData::get_window
+//       Access: Published
+//  Description: Returns the window this callback was triggered from.
+////////////////////////////////////////////////////////////////////
+INLINE CallbackGraphicsWindow *CallbackGraphicsWindow::WindowCallbackData::
+get_window() const {
+  return _window;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::EventsCallbackData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CallbackGraphicsWindow::EventsCallbackData::
+EventsCallbackData(CallbackGraphicsWindow *window) :
+  WindowCallbackData(window)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::PropertiesCallbackData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CallbackGraphicsWindow::PropertiesCallbackData::
+PropertiesCallbackData(CallbackGraphicsWindow *window, WindowProperties &properties) :
+  WindowCallbackData(window),
+  _properties(properties)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::PropertiesCallbackData::get_properties
+//       Access: Published
+//  Description: Returns the WindowProperties object that this
+//               callback should process.  Any properties that are
+//               handled should be removed from this object;
+//               properties that are unhandled should be left alone.
+////////////////////////////////////////////////////////////////////
+INLINE WindowProperties &CallbackGraphicsWindow::PropertiesCallbackData::
+get_properties() const {
+  return _properties;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::RenderCallbackData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CallbackGraphicsWindow::RenderCallbackData::
+RenderCallbackData(CallbackGraphicsWindow *window, RenderCallbackType callback_type, FrameMode frame_mode) : 
+  WindowCallbackData(window),
+  _callback_type(callback_type),
+  _frame_mode(frame_mode),
+  _render_flag(true)
+{
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::RenderCallbackData::get_callback_type
+//       Access: Published
+//  Description: Since the render callback is shared for several
+//               functions, this method is needed to indicate which
+//               particular function is being invoked with this
+//               callback.
+////////////////////////////////////////////////////////////////////
+INLINE CallbackGraphicsWindow::RenderCallbackType CallbackGraphicsWindow::RenderCallbackData::
+get_callback_type() const {
+  return _callback_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::RenderCallbackData::get_frame_mode
+//       Access: Published
+//  Description: If the callback type (returned by get_callback_type)
+//               is RCT_begin_frame or RCT_end_frame, then this method
+//               will return the particular frame mode indicating
+//               what, precisely, we want to do this frame.
+////////////////////////////////////////////////////////////////////
+INLINE GraphicsOutput::FrameMode CallbackGraphicsWindow::RenderCallbackData::
+get_frame_mode() const {
+  return _frame_mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::RenderCallbackData::set_render_flag
+//       Access: Published
+//  Description: If the callback type is RCT_begin_frame, this call is
+//               available to specify the return value from the
+//               begin_frame() call.  If this is true (the default),
+//               the frame is rendered normally; if it is false, the
+//               frame is omitted.
+////////////////////////////////////////////////////////////////////
+INLINE void CallbackGraphicsWindow::RenderCallbackData::
+set_render_flag(bool render_flag) {
+  _render_flag = render_flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::RenderCallbackData::get_render_flag
+//       Access: Published
+//  Description: Returns the current setting of the render flag.  See
+//               set_render_flag().
+////////////////////////////////////////////////////////////////////
+INLINE bool CallbackGraphicsWindow::RenderCallbackData::
+get_render_flag() const {
+  return _render_flag;
+}

+ 299 - 0
panda/src/display/callbackGraphicsWindow.cxx

@@ -0,0 +1,299 @@
+// Filename: callbackGraphicsWindow.cxx
+// Created by:  drose (06Jan11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "callbackGraphicsWindow.h"
+
+TypeHandle CallbackGraphicsWindow::_type_handle;
+TypeHandle CallbackGraphicsWindow::WindowCallbackData::_type_handle;
+TypeHandle CallbackGraphicsWindow::EventsCallbackData::_type_handle;
+TypeHandle CallbackGraphicsWindow::PropertiesCallbackData::_type_handle;
+TypeHandle CallbackGraphicsWindow::RenderCallbackData::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::Constructor
+//       Access: Protected
+//  Description: Use GraphicsEngine::make_output() to construct a
+//               CallbackGraphicsWindow.
+////////////////////////////////////////////////////////////////////
+CallbackGraphicsWindow::
+CallbackGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
+                       const string &name,
+                       const FrameBufferProperties &fb_prop,
+                       const WindowProperties &win_prop,
+                       int flags,
+                       GraphicsStateGuardian *gsg) :
+  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, NULL)
+{
+#ifdef DO_MEMORY_USAGE
+  MemoryUsage::update_type(this, this);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::Destructor
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CallbackGraphicsWindow::
+~CallbackGraphicsWindow() {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::get_input_device
+//       Access: Published
+//  Description: Returns a writable reference to the nth input device
+//               (mouse).  This is intended to be used for the window
+//               implementation to record mouse and keyboard input
+//               information for the Panda system.
+////////////////////////////////////////////////////////////////////
+GraphicsWindowInputDevice &CallbackGraphicsWindow::
+get_input_device(int device) {
+  LightMutexHolder holder(_input_lock);
+  nassertr(device >= 0 && device < (int)_input_devices.size(), _input_devices[0]);
+  return _input_devices[device];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::create_input_device
+//       Access: Published
+//  Description: Adds a new input device (mouse) to the window with
+//               the indicated name.  Returns the index of the new
+//               device.
+////////////////////////////////////////////////////////////////////
+int CallbackGraphicsWindow::
+create_input_device(const string &name) {
+  GraphicsWindowInputDevice device =
+    GraphicsWindowInputDevice::pointer_and_keyboard(this, name);
+  return add_input_device(device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::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 CallbackGraphicsWindow::
+begin_frame(FrameMode mode, Thread *current_thread) {
+  bool result = false;
+  if (_render_callback != NULL) {
+    RenderCallbackData data(this, RCT_begin_frame, mode);
+    _render_callback->do_callback(&data);
+    result = data.get_render_flag();
+  } else {
+    result = GraphicsWindow::begin_frame(mode, current_thread);
+  }
+
+  if (!result) {
+    return false;
+  }
+
+  _gsg->reset_if_new();
+  _gsg->set_current_properties(&get_fb_properties());
+  
+  return _gsg->begin_frame(current_thread);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::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 CallbackGraphicsWindow::
+end_frame(FrameMode mode, Thread *current_thread) {
+  if (_render_callback != NULL) {
+    RenderCallbackData data(this, RCT_end_frame, mode);
+    _render_callback->do_callback(&data);
+  } else {
+    GraphicsWindow::end_frame(mode, current_thread);
+  }
+
+  _gsg->end_frame(current_thread);
+
+  if (mode == FM_render) {
+    trigger_flip();
+    clear_cube_map_selection();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::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 CallbackGraphicsWindow::
+begin_flip() {
+  if (_render_callback != NULL) {
+    RenderCallbackData data(this, RCT_begin_flip, FM_render);
+    _render_callback->do_callback(&data);
+  } else {
+    GraphicsWindow::begin_flip();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::end_flip
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after begin_flip() has been called on all windows, to
+//               finish the exchange of the front and back buffers.
+//
+//               This should cause the window to wait for the flip, if
+//               necessary.
+////////////////////////////////////////////////////////////////////
+void CallbackGraphicsWindow::
+end_flip() {
+  if (_render_callback != NULL) {
+    RenderCallbackData data(this, RCT_end_flip, FM_render);
+    _render_callback->do_callback(&data);
+  } else {
+    GraphicsWindow::end_flip();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::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 CallbackGraphicsWindow::
+process_events() {
+  if (_events_callback != NULL) {
+    EventsCallbackData data(this);
+    _events_callback->do_callback(&data);
+  } else {
+    GraphicsWindow::process_events();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::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.
+////////////////////////////////////////////////////////////////////
+void CallbackGraphicsWindow::
+set_properties_now(WindowProperties &properties) {
+  if (_properties_callback != NULL) {
+    PropertiesCallbackData data(this, properties);
+    _properties_callback->do_callback(&data);
+  } else {
+    GraphicsWindow::set_properties_now(properties);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::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 CallbackGraphicsWindow::
+open_window() {
+  // In this case, we assume the callback has handled the window
+  // opening.
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::do_reshape_request
+//       Access: Protected, Virtual
+//  Description: Called from the window thread in response to a request
+//               from within the code (via request_properties()) to
+//               change the size and/or position of the window.
+//               Returns true if the window is successfully changed,
+//               or false if there was a problem.
+////////////////////////////////////////////////////////////////////
+bool CallbackGraphicsWindow::
+do_reshape_request(int x_origin, int y_origin, bool has_origin,
+                   int x_size, int y_size) {
+  // In this case, we assume the callback has handled the window
+  // resizing.
+  WindowProperties properties;
+  if (has_origin) {
+    properties.set_origin(x_origin, y_origin);
+  }
+  properties.set_size(x_size, y_size);
+  system_changed_properties(properties);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::EventsCallbackData::upcall
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CallbackGraphicsWindow::EventsCallbackData::
+upcall() {
+  _window->GraphicsWindow::process_events();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::PropertiesCallbackData::upcall
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CallbackGraphicsWindow::PropertiesCallbackData::
+upcall() {
+  _window->GraphicsWindow::set_properties_now(_properties);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackGraphicsWindow::RenderCallbackData::upcall
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CallbackGraphicsWindow::RenderCallbackData::
+upcall() {
+  switch (_callback_type) {
+  case RCT_begin_frame:
+    _window->GraphicsWindow::begin_frame(_frame_mode, Thread::get_current_thread());
+    break;
+
+  case RCT_end_frame:
+    _window->GraphicsWindow::end_frame(_frame_mode, Thread::get_current_thread());
+    break;
+
+  case RCT_begin_flip:
+    _window->GraphicsWindow::begin_flip();
+    break;
+
+  case RCT_end_flip:
+    _window->GraphicsWindow::end_flip();
+    break;
+  }
+}

+ 231 - 0
panda/src/display/callbackGraphicsWindow.h

@@ -0,0 +1,231 @@
+// Filename: callbackGraphicsWindow.h
+// Created by:  drose (06Jan11)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CALLBACKGRAPHICSWINDOW_H
+#define CALLBACKGRAPHICSWINDOW_H
+
+#include "pandabase.h"
+#include "graphicsWindow.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CallbackGraphicsWindow
+// Description : This special window object doesn't represent a window
+//               in its own right, but instead hooks into some
+//               third-party API for creating and rendering to windows
+//               via callbacks.  This can be used to allow Panda to
+//               render into an already-created OpenGL context, for
+//               instance.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_DISPLAY CallbackGraphicsWindow : public GraphicsWindow {
+protected:
+  CallbackGraphicsWindow(GraphicsEngine *engine,
+                         GraphicsPipe *pipe, 
+                         const string &name,
+                         const FrameBufferProperties &fb_prop,
+                         const WindowProperties &win_prop,
+                         int flags,
+                         GraphicsStateGuardian *gsg);
+
+PUBLISHED:
+  virtual ~CallbackGraphicsWindow();
+
+  class WindowCallbackData : public CallbackData {
+  public:
+    INLINE WindowCallbackData(CallbackGraphicsWindow *window);
+
+  PUBLISHED:
+    INLINE CallbackGraphicsWindow *get_window() const;
+    
+  protected:
+    PT(CallbackGraphicsWindow) _window;
+    
+  public:
+    static TypeHandle get_class_type() {
+      return _type_handle;
+    }
+    static void init_type() {
+      CallbackData::init_type();
+      register_type(_type_handle, "CallbackGraphicsWindow::WindowCallbackData",
+                    CallbackData::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;
+  };
+
+
+  class EventsCallbackData : public WindowCallbackData {
+  public:
+    INLINE EventsCallbackData(CallbackGraphicsWindow *window);
+
+  PUBLISHED:
+    virtual void upcall();
+    
+  public:
+    static TypeHandle get_class_type() {
+      return _type_handle;
+    }
+    static void init_type() {
+      WindowCallbackData::init_type();
+      register_type(_type_handle, "CallbackGraphicsWindow::EventsCallbackData",
+                    WindowCallbackData::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;
+  };
+
+  class PropertiesCallbackData : public WindowCallbackData {
+  public:
+    INLINE PropertiesCallbackData(CallbackGraphicsWindow *window, WindowProperties &properties);
+
+  PUBLISHED:
+    INLINE WindowProperties &get_properties() const;
+
+    virtual void upcall();
+
+  private:
+    WindowProperties &_properties;
+    
+  public:
+    static TypeHandle get_class_type() {
+      return _type_handle;
+    }
+    static void init_type() {
+      WindowCallbackData::init_type();
+      register_type(_type_handle, "CallbackGraphicsWindow::PropertiesCallbackData",
+                    WindowCallbackData::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;
+  };
+
+  enum RenderCallbackType {
+    RCT_begin_frame,
+    RCT_end_frame,
+    RCT_begin_flip,
+    RCT_end_flip,
+  };
+
+  class RenderCallbackData : public WindowCallbackData {
+  public:
+    INLINE RenderCallbackData(CallbackGraphicsWindow *window, RenderCallbackType callback_type, FrameMode frame_mode);
+
+  PUBLISHED:
+    INLINE CallbackGraphicsWindow::RenderCallbackType get_callback_type() const;
+    INLINE GraphicsOutput::FrameMode get_frame_mode() const;
+
+    INLINE void set_render_flag(bool render_flag);
+    INLINE bool get_render_flag() const;
+
+    virtual void upcall();
+
+  private:
+    RenderCallbackType _callback_type;
+    FrameMode _frame_mode;
+    bool _render_flag;
+    
+  public:
+    static TypeHandle get_class_type() {
+      return _type_handle;
+    }
+    static void init_type() {
+      WindowCallbackData::init_type();
+      register_type(_type_handle, "CallbackGraphicsWindow::RenderCallbackData",
+                    WindowCallbackData::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;
+  };
+  
+  INLINE void set_events_callback(CallbackObject *object);
+  INLINE void clear_events_callback();
+  INLINE CallbackObject *get_events_callback() const;
+
+  INLINE void set_properties_callback(CallbackObject *object);
+  INLINE void clear_properties_callback();
+  INLINE CallbackObject *get_properties_callback() const;
+
+  INLINE void set_render_callback(CallbackObject *object);
+  INLINE void clear_render_callback();
+  INLINE CallbackObject *get_render_callback() const;
+
+  GraphicsWindowInputDevice &get_input_device(int device);
+  int create_input_device(const string &name);
+
+public:
+  virtual bool begin_frame(FrameMode mode, Thread *current_thread);
+  virtual void end_frame(FrameMode mode, Thread *current_thread);
+
+  virtual void begin_flip();
+  virtual void end_flip();
+
+  virtual void process_events();
+  virtual void set_properties_now(WindowProperties &properties);
+
+protected:
+  virtual bool open_window();
+  virtual bool do_reshape_request(int x_origin, int y_origin, bool has_origin,
+                                  int x_size, int y_size);
+
+private:
+  PT(CallbackObject) _events_callback;
+  PT(CallbackObject) _properties_callback;
+  PT(CallbackObject) _render_callback;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsWindow::init_type();
+    register_type(_type_handle, "CallbackGraphicsWindow",
+                  GraphicsWindow::get_class_type());
+    WindowCallbackData::init_type();
+    EventsCallbackData::init_type();
+    PropertiesCallbackData::init_type();
+    RenderCallbackData::init_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;
+
+  friend class GraphicsEngine;
+};
+
+#include "callbackGraphicsWindow.I"
+
+#endif

+ 2 - 0
panda/src/display/config_display.cxx

@@ -13,6 +13,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "config_display.h"
+#include "callbackGraphicsWindow.h"
 #include "displayRegion.h"
 #include "displayRegionCullCallbackData.h"
 #include "displayRegionDrawCallbackData.h"
@@ -449,6 +450,7 @@ init_libdisplay() {
   }
   initialized = true;
 
+  CallbackGraphicsWindow::init_type();
   DisplayRegion::init_type();
   DisplayRegionCullCallbackData::init_type();
   DisplayRegionDrawCallbackData::init_type();

+ 23 - 1
panda/src/display/graphicsEngine.cxx

@@ -336,12 +336,35 @@ make_output(GraphicsPipe *pipe,
     nassertr(threading_model.get_draw_name() ==
              gsg->get_threading_model().get_draw_name(), NULL);
   }
+
+  // Are we really asking for a callback window?
+  if ((flags & GraphicsPipe::BF_require_callback_window)!=0) {
+    PT(GraphicsStateGuardian) this_gsg = gsg;
+    if (this_gsg == (GraphicsStateGuardian *)NULL) {
+      // If we don't already have a GSG, we have to ask the pipe to
+      // make a new one, unencumbered by window dressing.
+      this_gsg = pipe->make_callback_gsg(this);
+    }
+    if (this_gsg != (GraphicsStateGuardian *)NULL) {
+      CallbackGraphicsWindow *window = new CallbackGraphicsWindow(this, pipe, name, fb_prop, win_prop, flags, this_gsg);
+      window->_sort = sort;
+      do_add_window(window, threading_model);
+      do_add_gsg(window->get_gsg(), pipe, threading_model);
+      display_cat.info() << "Created output of type CallbackGraphicsWindow\n";
+      return window;
+    }
+
+    // Couldn't make a callback window, because the pipe wouldn't make
+    // an unencumbered GSG.
+    return NULL;
+  }
   
   // Determine if a parasite buffer meets the user's specs.
 
   bool can_use_parasite = false;
   if ((host != 0)&&
       ((flags&GraphicsPipe::BF_require_window)==0)&&
+      ((flags&GraphicsPipe::BF_require_callback_window)==0)&&
       ((flags&GraphicsPipe::BF_refuse_parasite)==0)&&
       ((flags&GraphicsPipe::BF_can_bind_color)==0)&&
       ((flags&GraphicsPipe::BF_can_bind_every)==0)&&
@@ -1998,7 +2021,6 @@ void GraphicsEngine::
 do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe,
            const GraphicsThreadingModel &threading_model) {
   ReMutexHolder holder(_lock);
-
   nassertv(gsg->get_pipe() == pipe && gsg->get_engine() == this);
   gsg->_threading_model = threading_model;
   if (!_default_loader.is_null()) {

+ 14 - 0
panda/src/display/graphicsPipe.cxx

@@ -138,6 +138,20 @@ make_output(const string &name,
   return NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsPipe::make_callback_gsg
+//       Access: Protected, Virtual
+//  Description: This is called when make_output() is used to create a
+//               CallbackGraphicsWindow.  If the GraphicsPipe can
+//               construct a GSG that's not associated with any
+//               particular window object, do so now, assuming the
+//               correct graphics context has been set up externally.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsStateGuardian) GraphicsPipe::
+make_callback_gsg(GraphicsEngine *engine) {
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsPipe::get_display_information
 //       Access: Published

+ 2 - 0
panda/src/display/graphicsPipe.h

@@ -78,6 +78,7 @@ PUBLISHED:
     BF_require_parasite    = 0x0002,
     BF_refuse_window       = 0x0004,
     BF_require_window      = 0x0008,
+    BF_require_callback_window = 0x0010,
 
     // Miscellaneous control flags.
     BF_can_bind_color      = 0x0040, // Need capability: bind the color bitplane to a tex.
@@ -124,6 +125,7 @@ protected:
                                          GraphicsOutput *host,
                                          int retry,
                                          bool &precertify);
+  virtual PT(GraphicsStateGuardian) make_callback_gsg(GraphicsEngine *engine);
   
   LightMutex _lock;
 

+ 3 - 2
panda/src/display/graphicsStateGuardian.cxx

@@ -2398,8 +2398,7 @@ determine_target_texture() {
 
   nassertv(target_texture != (TextureAttrib *)NULL &&
            target_tex_gen != (TexGenAttrib *)NULL);
-  int max_texture_stages = get_max_texture_stages();
-  _target_texture = target_texture->filter_to_max(max_texture_stages);
+  _target_texture = target_texture;
   _target_tex_gen = target_tex_gen;
   
   if (_has_texture_alpha_scale) {
@@ -2411,6 +2410,8 @@ determine_target_texture() {
                                (stage, TexGenAttrib::M_constant, LTexCoord3(_current_color_scale[3], 0.0f, 0.0f)));
   }
 
+  int max_texture_stages = get_max_texture_stages();
+  _target_texture = _target_texture->filter_to_max(max_texture_stages);
   nassertv(_target_texture->get_num_on_stages() <= max_texture_stages);
 }
 

+ 0 - 11
panda/src/display/graphicsWindow.I

@@ -56,14 +56,3 @@ get_window_handle() const {
   return _window_handle;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindow::add_input_device
-//       Access: Protected
-//  Description: Adds a GraphicsWindowInputDevice to the vector.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindow::
-add_input_device(const GraphicsWindowInputDevice &device) {
-  _input_devices.push_back(device);
-  _input_devices.back().set_device_index(_input_devices.size()-1);
-}
-

+ 15 - 0
panda/src/display/graphicsWindow.cxx

@@ -853,6 +853,21 @@ system_changed_size(int x_size, int y_size) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsWindow::add_input_device
+//       Access: Protected
+//  Description: Adds a GraphicsWindowInputDevice to the vector.
+//               Returns the index of the new device.
+////////////////////////////////////////////////////////////////////
+int GraphicsWindow::
+add_input_device(const GraphicsWindowInputDevice &device) {
+  LightMutexHolder holder(_input_lock);
+  int index = (int)_input_devices.size();
+  _input_devices.push_back(device);
+  _input_devices.back().set_device_index(index);
+  return index;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindow::mouse_mode_relative
 //       Access: Protected, Virtual

+ 1 - 1
panda/src/display/graphicsWindow.h

@@ -141,7 +141,7 @@ protected:
   void system_changed_size(int x_size, int y_size);
 
 protected:
-  INLINE void add_input_device(const GraphicsWindowInputDevice &device);
+  int add_input_device(const GraphicsWindowInputDevice &device);
   typedef vector_GraphicsWindowInputDevice InputDevices;
   InputDevices _input_devices;
   LightMutex _input_lock;

+ 1 - 1
panda/src/display/graphicsWindowInputDevice.h

@@ -71,7 +71,7 @@ public:
   bool has_pointer_event() const;
   PT(PointerEventList) get_pointer_events();
 
-public:
+PUBLISHED:
   // The following interface is for the various kinds of
   // GraphicsWindows to record the data incoming on the device.
   void button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());

+ 1 - 0
panda/src/display/p3display_composite1.cxx

@@ -1,4 +1,5 @@
 #include "config_display.cxx"
+#include "callbackGraphicsWindow.cxx"
 #include "displayInformation.cxx"
 #include "displayRegion.cxx"
 #include "displayRegionCullCallbackData.cxx"

+ 14 - 1
panda/src/osxdisplay/osxGraphicsPipe.cxx

@@ -383,7 +383,7 @@ make_output(const string &name,
   if (gsg != 0) {
     DCAST_INTO_R(osxgsg, gsg, NULL);
   }
-  
+
   // First thing to try: an osxGraphicsWindow
 
   if (retry == 0) {
@@ -464,3 +464,16 @@ make_output(const string &name,
   return NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: osxGraphicsPipe::make_callback_gsg
+//       Access: Protected, Virtual
+//  Description: This is called when make_output() is used to create a
+//               CallbackGraphicsWindow.  If the GraphicsPipe can
+//               construct a GSG that's not associated with any
+//               particular window object, do so now, assuming the
+//               correct graphics context has been set up externally.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsStateGuardian) osxGraphicsPipe::
+make_callback_gsg(GraphicsEngine *engine) {
+  return new osxGraphicsStateGuardian(engine, this, NULL);
+}

+ 1 - 0
panda/src/osxdisplay/osxGraphicsPipe.h

@@ -50,6 +50,7 @@ protected:
                                          GraphicsOutput *host,
                                          int retry,
                                          bool &precertify);
+  virtual PT(GraphicsStateGuardian) make_callback_gsg(GraphicsEngine *engine);
 
 public:
   static TypeHandle get_class_type() {

+ 6 - 4
panda/src/osxdisplay/osxGraphicsStateGuardian.cxx

@@ -100,10 +100,12 @@ void osxGraphicsStateGuardian::reset()
 
   GLGraphicsStateGuardian::reset();
 
-  // Apply the video-sync setting.
-  GLint value = sync_video ? 1 : 0;
-  aglSetInteger(_aglcontext, AGL_SWAP_INTERVAL, &value);
- }
+  if (_aglcontext != (AGLContext)NULL) {
+    // Apply the video-sync setting.
+    GLint value = sync_video ? 1 : 0;
+    aglSetInteger(_aglcontext, AGL_SWAP_INTERVAL, &value);
+  }
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: osxGraphicsStateGuardian::draw_resize_box