diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index dfc59ce403..3745119c43 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -20,7 +20,7 @@ //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::set_render_traverser -// Access: Public +// Access: Published // Description: Sets the traverser that will be used to render the // scene graph. If this is unset, nothing will be // rendered. @@ -32,7 +32,7 @@ set_render_traverser(RenderTraverser *rt) { //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_render_traverser -// Access: Public +// Access: Published // Description: Returns the traverser that will be used to render the // scene graph. //////////////////////////////////////////////////////////////////// @@ -41,6 +41,18 @@ get_render_traverser() const { return _render_traverser; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::is_closed +// Access: Public +// Description: Returns true if the window associated with this GSG +// has been closed, and hence the resources associated +// with this GSG have been freed. +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsStateGuardian:: +is_closed() const { + return (_win == (GraphicsWindow *)NULL); +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_state // Access: Public diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index d453292124..38dbcc70b3 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -733,6 +733,19 @@ unmark_prepared_geom_node(GeomNodeContext *gnc) { return removed; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::close_gsg +// Access: Protected, Virtual +// Description: This is called by the associated GraphicsWindow when +// close_window() is called. It should null out the +// _win pointer and possibly free any open resources +// associated with the GSG. +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +close_gsg() { + _win = (GraphicsWindow *)NULL; +} + #ifdef DO_PSTATS //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 83905c0bf0..4b566691fa 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -84,6 +84,8 @@ PUBLISHED: void clear_attribute(TypeHandle type); public: + INLINE bool is_closed() const; + virtual TextureContext *prepare_texture(Texture *tex); virtual void apply_texture(TextureContext *tc); virtual void release_texture(TextureContext *tc); @@ -165,6 +167,8 @@ protected: bool mark_prepared_geom_node(GeomNodeContext *gnc); bool unmark_prepared_geom_node(GeomNodeContext *gnc); + virtual void close_gsg(); + #ifdef DO_PSTATS // These functions are used to update the active texture memory // usage record (and other frame-based measurements) in Pstats. @@ -300,7 +304,8 @@ public: private: static TypeHandle _type_handle; -friend class GraphicsPipe; + friend class GraphicsPipe; + friend class GraphicsWindow; }; #include "graphicsStateGuardian.I" diff --git a/panda/src/display/graphicsWindow.I b/panda/src/display/graphicsWindow.I index ff9bf68c64..6baff9a4ee 100644 --- a/panda/src/display/graphicsWindow.I +++ b/panda/src/display/graphicsWindow.I @@ -20,7 +20,7 @@ //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_properties -// Access: Public +// Access: Published // Description: Returns the full Properties structure that describes // the window. //////////////////////////////////////////////////////////////////// @@ -31,7 +31,7 @@ get_properties() const { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_width -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE int GraphicsWindow:: @@ -41,7 +41,7 @@ get_width() const { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_height -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE int GraphicsWindow:: @@ -51,7 +51,7 @@ get_height() const { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_xorg -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE int GraphicsWindow:: @@ -61,7 +61,7 @@ get_xorg() const { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_yorg -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE int GraphicsWindow:: @@ -71,19 +71,24 @@ get_yorg() const { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_gsg -// Access: Public +// Access: Published // Description: Returns the GSG that is associated with this window. // There is a one-to-one association between windows and // GSG's. +// +// It is invalid to call this if the window has been +// closed; that is, if is_closed() returns true. In +// this case the GSG has been closed as well. //////////////////////////////////////////////////////////////////// INLINE GraphicsStateGuardian *GraphicsWindow:: get_gsg() const { + nassertr(!is_closed(), NULL); return _gsg; } //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_pipe -// Access: Public +// Access: Published // Description: Returns the GraphicsPipe that this window is // associated with. It is possible that the // GraphicsPipe might have been deleted while an @@ -97,99 +102,35 @@ get_pipe() const { } //////////////////////////////////////////////////////////////////// -// Function: GraphicsWindow::get_num_input_devices -// Access: Public -// Description: Returns the number of separate input devices -// associated with the window. Typically, a window will -// have exactly one input device: the keyboard/mouse -// pair. However, some windows may have no input -// devices, and others may add additional devices, for -// instance for a joystick. +// Function: GraphicsWindow::close_window +// Access: Published +// Description: Closes the window and frees all resources associated +// with it, including the GSG. The window may not be +// opened again. //////////////////////////////////////////////////////////////////// -INLINE int GraphicsWindow:: -get_num_input_devices() const { - return _input_devices.size(); +INLINE void GraphicsWindow:: +close_window() { + if (!is_closed()) { + do_close_window(); + release_gsg(); + } } //////////////////////////////////////////////////////////////////// -// Function: GraphicsWindow::get_input_device_name -// Access: Public -// Description: Returns the name of the nth input device. -//////////////////////////////////////////////////////////////////// -INLINE string GraphicsWindow:: -get_input_device_name(int device) const { - nassertr(device >= 0 && device < (int)_input_devices.size(), ""); - return _input_devices[device].get_name(); -} - -//////////////////////////////////////////////////////////////////// -// Function: GraphicsWindow::has_pointer -// Access: Public -// Description: Returns true if the nth input device has a -// screen-space pointer (for instance, a mouse), false -// otherwise. +// Function: GraphicsWindow::is_closed +// Access: Published +// Description: Returns true if close_window() has been called on +// this window, false otherwise. If the window has been +// closed, most of its interface is no longer valid. //////////////////////////////////////////////////////////////////// INLINE bool GraphicsWindow:: -has_pointer(int device) const { - nassertr(device >= 0 && device < (int)_input_devices.size(), false); - return _input_devices[device].has_pointer(); -} - -//////////////////////////////////////////////////////////////////// -// Function: GraphicsWindow::has_keyboard -// Access: Public -// Description: Returns true if the nth input device has a keyboard, -// false otherwise. -//////////////////////////////////////////////////////////////////// -INLINE bool GraphicsWindow:: -has_keyboard(int device) const { - nassertr(device >= 0 && device < (int)_input_devices.size(), false); - return _input_devices[device].has_keyboard(); -} - -//////////////////////////////////////////////////////////////////// -// Function: GraphicsWindow::get_mouse_data -// Access: Public -// Description: Returns the MouseData associated with the nth input -// device. -//////////////////////////////////////////////////////////////////// -INLINE const MouseData &GraphicsWindow:: -get_mouse_data(int device) const { - nassertr(device >= 0 && device < (int)_input_devices.size(), - *(new MouseData)); - return _input_devices[device].get_mouse_data(); -} - -//////////////////////////////////////////////////////////////////// -// Function: GraphicsWindow::has_button_event -// Access: Public -// Description: Returns true if the indicated device has a pending -// button event (a mouse button or keyboard button -// down/up), false otherwise. If this returns true, the -// particular event may be extracted via -// get_button_event(). -//////////////////////////////////////////////////////////////////// -INLINE bool GraphicsWindow:: -has_button_event(int device) const { - nassertr(device >= 0 && device < (int)_input_devices.size(), false); - return _input_devices[device].has_button_event(); -} - -//////////////////////////////////////////////////////////////////// -// Function: GraphicsWindow::get_button_event -// Access: Public -// Description: Assuming a previous call to has_button_event() -// returned true, this returns the pending button event. -//////////////////////////////////////////////////////////////////// -INLINE ButtonEvent GraphicsWindow:: -get_button_event(int device) { - nassertr(has_button_event(device), ButtonEvent()); - return _input_devices[device].get_button_event(); +is_closed() const { + return (_gsg == (GraphicsStateGuardian *)NULL); } //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::set_frame_number -// Access: Public +// Access: Published // Description: Sets the current frame number of the window. This // affects the frame numbers written for %f in a RIB or // image filename template. The frame number is @@ -203,7 +144,7 @@ set_frame_number(const int f) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_frame_number -// Access: Public +// Access: Published // Description: Returns the current frame number of the window. See // set_frame_number(). //////////////////////////////////////////////////////////////////// @@ -256,3 +197,94 @@ call_idle_callback() { } } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_num_input_devices +// Access: Published +// Description: Returns the number of separate input devices +// associated with the window. Typically, a window will +// have exactly one input device: the keyboard/mouse +// pair. However, some windows may have no input +// devices, and others may add additional devices, for +// instance for a joystick. +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsWindow:: +get_num_input_devices() const { + return _input_devices.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_input_device_name +// Access: Published +// Description: Returns the name of the nth input device. +//////////////////////////////////////////////////////////////////// +INLINE string GraphicsWindow:: +get_input_device_name(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), ""); + return _input_devices[device].get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::has_pointer +// Access: Published +// Description: Returns true if the nth input device has a +// screen-space pointer (for instance, a mouse), false +// otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindow:: +has_pointer(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), false); + return _input_devices[device].has_pointer(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::has_keyboard +// Access: Published +// Description: Returns true if the nth input device has a keyboard, +// false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindow:: +has_keyboard(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), false); + return _input_devices[device].has_keyboard(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_mouse_data +// Access: Published +// Description: Returns the MouseData associated with the nth input +// device. +//////////////////////////////////////////////////////////////////// +INLINE const MouseData &GraphicsWindow:: +get_mouse_data(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), + *(new MouseData)); + return _input_devices[device].get_mouse_data(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::has_button_event +// Access: Published +// Description: Returns true if the indicated device has a pending +// button event (a mouse button or keyboard button +// down/up), false otherwise. If this returns true, the +// particular event may be extracted via +// get_button_event(). +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindow:: +has_button_event(int device) const { + nassertr(device >= 0 && device < (int)_input_devices.size(), false); + return _input_devices[device].has_button_event(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_button_event +// Access: Published +// Description: Assuming a previous call to has_button_event() +// returned true, this returns the pending button event. +//////////////////////////////////////////////////////////////////// +INLINE ButtonEvent GraphicsWindow:: +get_button_event(int device) { + nassertr(has_button_event(device), ButtonEvent()); + return _input_devices[device].get_button_event(); +} + diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index 464ab00bab..1936e979ca 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -184,11 +184,22 @@ operator=(const GraphicsWindow&) { //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::Destructor -// Access: Public +// Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// GraphicsWindow:: ~GraphicsWindow() { + // First, call close_window(). This tells our GSG to let go of its + // pointer to us, and also eventually calls do_close_window(). + // However, do_close_window() is a virtual function that might be + // extended in a derived class, but we don't have any derived + // virtual functions by the time the destructor is called. + + // Therefore, if a derived class has redefined do_close_window(), it + // should also call close_window() in its own destructor. + close_window(); + + // We don't have to destruct our child channels explicitly, since // they are all reference-counted and will go away when their // pointers do. However, we do need to zero out their pointers to @@ -514,6 +525,47 @@ make_gsg() { nassertv(_gsg != (GraphicsStateGuardian *)NULL); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::release_gsg +// Access: Protected +// Description: Releases the current GSG pointer, if it is currently +// held. This invalidates the window and marks it +// closed; it should not be called unless the code to +// close the window is also called. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +release_gsg() { + if (_gsg != (GraphicsStateGuardian *)NULL) { + // First, we save the GSG pointer and then NULL it out. That way, + // if the GSG happens to call close_window() while it is closing + // itself, it won't be recursive (because we'll already be marked + // closed). + PT(GraphicsStateGuardian) gsg = _gsg; + _gsg.clear(); + + // Now we tell the GSG it's time to sleep. + gsg->close_gsg(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::do_close_window +// Access: Protected, Virtual +// Description: An internal function to release whatever system +// resources are held by the window and actually close +// it. This is called by close_window(). +// +// If a derived class redefines this function, it should +// also arrange to call close_window() (or its +// equivalent) from its own destructor, since we cannot +// call a virtual function from this destructor. +//////////////////////////////////////////////////////////////////// +void GraphicsWindow:: +do_close_window() { + display_cat.info() + << "Closing " << get_type() << "\n"; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_factory // Access: Public, Static diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index bc4d1f80c8..392df5e857 100644 --- a/panda/src/display/graphicsWindow.h +++ b/panda/src/display/graphicsWindow.h @@ -113,14 +113,14 @@ PUBLISHED: INLINE int get_yorg() const; INLINE GraphicsStateGuardian *get_gsg() const; - INLINE GraphicsPipe *get_pipe() const; + INLINE void close_window(); + INLINE bool is_closed() const; + INLINE void set_frame_number(const int); INLINE int get_frame_number() const; - virtual void close_window(int exit_status) {return;}; //release windowing system resources - public: virtual void resized(const int, const int); @@ -179,6 +179,8 @@ public: protected: void make_gsg(); + void release_gsg(); + virtual void do_close_window(); typedef vector_GraphicsWindowInputDevice InputDevices; InputDevices _input_devices; diff --git a/panda/src/framework/framework.cxx b/panda/src/framework/framework.cxx index 3cf9f7a3c1..aeb6c33ce8 100644 --- a/panda/src/framework/framework.cxx +++ b/panda/src/framework/framework.cxx @@ -431,11 +431,11 @@ void event_esc(CPT_Event ev) { #endif if(ev->get_name()=="q") { - // if app ever exits using exit() without close_window, hopefully this will work - framework_cat.debug() << "Testing unsafe, no close_window(), direct exit() of framework\n"; + // if app ever exits using exit() without close_window, hopefully this will work + framework_cat.debug() << "Testing unsafe, no close_window(), direct exit() of framework\n"; } else { - main_win->close_window(0); - framework_cat.debug() << "Exiting framework\n"; + main_win->close_window(); + framework_cat.debug() << "Exiting framework\n"; } main_win = NULL; diff --git a/panda/src/glgsg/glGraphicsStateGuardian.cxx b/panda/src/glgsg/glGraphicsStateGuardian.cxx index 5b2e933a06..fe6b734917 100644 --- a/panda/src/glgsg/glGraphicsStateGuardian.cxx +++ b/panda/src/glgsg/glGraphicsStateGuardian.cxx @@ -1768,9 +1768,9 @@ release_texture(TextureContext *tc) { GLTextureContext *gtc = DCAST(GLTextureContext, tc); Texture *tex = tc->_texture; - HGLRC curcxt=wglGetCurrentContext(); - if(curcxt!=NULL) - glDeleteTextures(1, >c->_index); + if (!is_closed()) { + glDeleteTextures(1, >c->_index); + } gtc->_index = 0; bool erased = unmark_prepared_texture(gtc); diff --git a/panda/src/glxdisplay/glxGraphicsWindow.cxx b/panda/src/glxdisplay/glxGraphicsWindow.cxx index 4822ad5501..cf767c6c01 100644 --- a/panda/src/glxdisplay/glxGraphicsWindow.cxx +++ b/panda/src/glxdisplay/glxGraphicsWindow.cxx @@ -1155,10 +1155,14 @@ void glxGraphicsWindow::process_event(XEvent event) handle_mouse_motion(event.xcrossing.x, event.xcrossing.y); } break; + + case DestroyNotify: + close_window(); + break; + case UnmapNotify: case VisibilityNotify: case ClientMessage: - case DestroyNotify: case CirculateNotify: case CreateNotify: case GravityNotify: diff --git a/panda/src/wdxdisplay/wdxGraphicsWindow.cxx b/panda/src/wdxdisplay/wdxGraphicsWindow.cxx index af36528e15..479c25cc5b 100644 --- a/panda/src/wdxdisplay/wdxGraphicsWindow.cxx +++ b/panda/src/wdxdisplay/wdxGraphicsWindow.cxx @@ -178,6 +178,7 @@ void wdxGraphicsWindow::DestroyMe(bool bAtExitFnCalled) { _dxgsg->dx_cleanup(_props._fullscreen, bAtExitFnCalled); _dxgsg=NULL; } + release_gsg(); if(_hdc!=NULL) { ReleaseDC(_mwindow,_hdc); @@ -195,7 +196,8 @@ void wdxGraphicsWindow::DestroyMe(bool bAtExitFnCalled) { } } -void wdxGraphicsWindow::close_window(int exit_status) { +void wdxGraphicsWindow::do_close_window() { + GraphicsWindow::do_close_window(); DestroyMe(false); } @@ -205,7 +207,7 @@ void wdxGraphicsWindow::close_window(int exit_status) { // Description: //////////////////////////////////////////////////////////////////// wdxGraphicsWindow::~wdxGraphicsWindow(void) { - DestroyMe(false); + close_window(); } void DestroyAllWindows(bool bAtExitFnCalled) { @@ -262,7 +264,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { break; case WM_CLOSE: - DestroyMe(false); + close_window(); // BUGBUG: right now there is no way to tell the panda app the graphics window is invalid or // has been closed by the user, to prevent further methods from being called on the window. diff --git a/panda/src/wdxdisplay/wdxGraphicsWindow.h b/panda/src/wdxdisplay/wdxGraphicsWindow.h index d6d3c415d8..c3a6ac0595 100644 --- a/panda/src/wdxdisplay/wdxGraphicsWindow.h +++ b/panda/src/wdxdisplay/wdxGraphicsWindow.h @@ -115,7 +115,7 @@ public: virtual TypeHandle force_init_type() {init_type(); return get_class_type();} void DestroyMe(bool bAtExitFnCalled); - virtual void close_window(int exit_status); + virtual void do_close_window(); private: static TypeHandle _type_handle; diff --git a/panda/src/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx index cdd7ef65a0..f24959ac24 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.cxx +++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx @@ -96,7 +96,7 @@ void wglGraphicsWindow::DestroyMe(bool bAtExitFnCalled) { report_errors(); // implicitly calls gsg destructors which release GL objects (textures, display lists, etc) - _gsg = NULL; + release_gsg(); HGLRC curcxt=wglGetCurrentContext(); if(curcxt!=NULL) @@ -134,7 +134,8 @@ void wglGraphicsWindow::DestroyMe(bool bAtExitFnCalled) { } } -void wglGraphicsWindow::close_window(int exit_status) { +void wglGraphicsWindow::do_close_window() { + GraphicsWindow::do_close_window(); DestroyMe(false); } @@ -144,7 +145,7 @@ void wglGraphicsWindow::close_window(int exit_status) { // Description: //////////////////////////////////////////////////////////////////// wglGraphicsWindow::~wglGraphicsWindow(void) { - DestroyMe(false); + close_window(); } void DestroyAllWindows(bool bAtExitFnCalled) { @@ -1330,7 +1331,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { break; case WM_CLOSE: - DestroyMe(false); + close_window(); // BUGBUG: right now there is no way to tell the panda app the graphics window is invalid or // has been closed by the user, to prevent further methods from being called on the window. diff --git a/panda/src/wgldisplay/wglGraphicsWindow.h b/panda/src/wgldisplay/wglGraphicsWindow.h index 68fa016748..58a7b9d4d8 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.h +++ b/panda/src/wgldisplay/wglGraphicsWindow.h @@ -143,7 +143,9 @@ public: LONG WINAPI window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); ButtonHandle lookup_key(WPARAM wparam) const; void DestroyMe(bool bAtExitFnCalled); - virtual void close_window(int exit_status); + +protected: + virtual void do_close_window(); private: static TypeHandle _type_handle;