/* wxRuby3 * Copyright (c) Martin J.N. Corino */ // Copyright 2004-2008, wxRuby development team // released under the MIT-like wxRuby2 license // mark_free_impl.i - this contains the C++ implementation of various // common GC-related functions, such as shared %mark functions and // methods for checking and setting whether a wxWidgets window has been // destroyed. It is compiled into wx.cpp. %header %{ #include <wx/cshelp.h> %} %{ // Code to be run when the ruby object is swept by GC - this only // unlinks the C++ object from the ruby VALUE but doesn't delete // it because it is still needed and will be managed by WxWidgets. WXRUBY_EXPORT void GcNullFreeFunc(void *ptr) { SWIG_RubyRemoveTracking(ptr); } // Tests if the window has been signalled as destroyed by a // WindowDestroyEvent handled by wxRubyApp WXRUBY_EXPORT bool GC_IsWindowDeleted(void *ptr) { // If objects have been 'unlinked' then DATA_PTR = 0 if ( ! ptr ) return true; if ( wxRuby_IsAppRunning () ) return false; else return true; } // Records when a wxWindow has been signalled as destroyed by a // WindowDestroyEvent, handled by wxRubyApp (see swig/classes/App.i). WXRUBY_EXPORT void GC_SetWindowDeleted(void *ptr) { // All Windows are EvtHandlers, so prevent any pending events being // sent after destruction (otherwise ObjectPreviouslyDeleted errors result) wxEvtHandler* evt_handler = (wxEvtHandler*)ptr; evt_handler->SetEvtHandlerEnabled(false); // Allow the Ruby procs that are event handlers on this window to be // garbage collected at next phase wxRuby_ReleaseEvtHandlerProcs(ptr); // Wx calls this by standard after the window destroy event is // handled, but we need to call it before while the object link is // still around wxHelpProvider *helpProvider = wxHelpProvider::Get(); if ( helpProvider ) helpProvider->RemoveHelp((wxWindowBase*)ptr); // Disassociate the C++ and Ruby objects SWIG_RubyUnlinkObjects(ptr); SWIG_RubyRemoveTracking(ptr); } // Code to be run when the ruby object is swept by GC - this only // unlinks the C++ object from the ruby VALUE and decrements the // reference counter. WXRUBY_EXPORT void GcRefCountedFreeFunc(void *ptr) { SWIG_RubyRemoveTracking(ptr); if (ptr) ((wxRefCounter*)ptr)->DecRef(); } // Code to be run when the ruby object is swept by GC - this checks // for unattached sizers and deletes those, only unlinking others // as these will be managed by WxWidgets. WXRUBY_EXPORT void GcSizerFreeFunc(void *ptr) { wxSizer* wx_szr = (wxSizer*)ptr; // unlink in all cases SWIG_RubyRemoveTracking(ptr); delete wx_szr; // delete unattached sizers } void GC_mark_SizerBelongingToWindow(wxSizer *wx_sizer, VALUE rb_sizer); WXRUBY_EXPORT void GC_mark_wxSizer(void* ptr) { VALUE rb_szr = SWIG_RubyInstanceFor(ptr); if (rb_szr && rb_szr != Qnil) { wxSizer* wx_szr = (wxSizer*)ptr; // as long as the dfree function is still the GCSizeFreeFunc the sizer has not been attached to a window // or added to a parent sizer (as that would 'disown' and replace the free function by the tracking removal function) // but it may hay have already had child sizers added which need to be marked if (RDATA(rb_szr)->dfree == (void (*)(void *))GcSizerFreeFunc) { // Then loop over the sizer's content and mark each sub-sizer wxSizerItemList& children = wx_szr->GetChildren(); for ( wxSizerItemList::compatibility_iterator node = children.GetFirst(); node; node = node->GetNext() ) { wxSizerItem* item = node->GetData(); wxSizer* child_sizer = item->GetSizer(); if ( child_sizer ) { VALUE rb_child_sizer = SWIG_RubyInstanceFor(child_sizer); if ( rb_child_sizer != Qnil ) { // just reuse this function here (will do exactly what we want) GC_mark_SizerBelongingToWindow(child_sizer, rb_child_sizer); } } } } } } // Carries out marking of Sizer objects belonging to a Wx::Window. Note // that this isn't done as a standard mark routine because ONLY sizers // that are known to belong to a still-alive window should be marked, // not those picked up as marked by in-scope variables by // Ruby. Otherwise, segfaults may result. Because Sizers are SWIG // directors, they must be preserved from GC. void GC_mark_SizerBelongingToWindow(wxSizer *wx_sizer, VALUE rb_sizer) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "> GC_mark_SizerBelongingToWindow : " << wx_sizer << std::endl; #endif // First, mark this sizer rb_gc_mark( rb_sizer ); // Then loop over hte sizer's content and mark each sub-sizer in turn wxSizerItemList& children = wx_sizer->GetChildren(); for ( wxSizerItemList::compatibility_iterator node = children.GetFirst(); node; node = node->GetNext() ) { wxSizerItem* item = node->GetData(); wxSizer* child_sizer = item->GetSizer(); if ( child_sizer ) { VALUE rb_child_sizer = SWIG_RubyInstanceFor(child_sizer); if ( rb_child_sizer != Qnil ) { GC_mark_SizerBelongingToWindow(child_sizer, rb_child_sizer); } } } #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "< GC_mark_SizerBelongingToWindow : " << wx_sizer << std::endl; #endif } // Similar to Sizers, MenuBar requires a special mark routine. This is // because Wx::Menu is not a subclass of Window so isn't automatically // protected in the mark phase by Wx::App. However, the ruby object // still must not be destroyed while it is still accessible on screen, // because it may still handle events. Rather than a SWIG %markfunc, // which can catch destroyed MenuBars linked to an in-scope ruby // variable and cause segfaults, MenuBars are always marked via the // containing Frame. void GC_mark_MenuBarBelongingToFrame(wxMenuBar *menu_bar) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "> GC_mark_MenuBarBelongingToFrame : " << menu_bar << std::endl; #endif rb_gc_mark( SWIG_RubyInstanceFor(menu_bar) ); // Mark each menu in the menubar in turn for ( size_t i = 0; i < menu_bar->GetMenuCount(); i++ ) { wxMenu* menu = menu_bar->GetMenu(i); rb_gc_mark( SWIG_RubyInstanceFor(menu) ); } #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "< GC_mark_MenuBarBelongingToFrame : " << menu_bar << std::endl; #endif } // Default mark routine for Windows - preserve the main sizer and caret // belong to this window WXRUBY_EXPORT void GC_mark_wxWindow(void *ptr) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "> GC_mark_wxWindow : " << ptr << std::endl; #endif if ( GC_IsWindowDeleted(ptr) ) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "< GC_mark_wxWindow : deleted" << std::endl; #endif return; } wxWindow* wx_win = (wxWindow*)ptr; #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>2) std::wcout << "* GC_mark_wxWindow - getting sizer" << std::endl; #endif wxSizer* wx_sizer = wx_win->GetSizer(); if ( wx_sizer ) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>2) std::wcout << "* GC_mark_wxWindow - found sizer" << std::endl; #endif VALUE rb_sizer = SWIG_RubyInstanceFor(wx_sizer); if ( rb_sizer != Qnil ) GC_mark_SizerBelongingToWindow(wx_sizer, rb_sizer); } #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>2) std::wcout << "* GC_mark_wxWindow - getting caret" << std::endl; #endif wxCaret* wx_caret = wx_win->GetCaret(); if ( wx_caret ) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>2) std::wcout << "* GC_mark_wxWindow - found caret" << std::endl; #endif VALUE rb_caret = SWIG_RubyInstanceFor(wx_caret); rb_gc_mark(rb_caret); } #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>2) std::wcout << "* GC_mark_wxWindow - getting droptarget" << std::endl; #endif wxDropTarget* wx_droptarget = wx_win->GetDropTarget(); if ( wx_droptarget ) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>2) std::wcout << "* GC_mark_wxWindow - found droptarget" << std::endl; #endif VALUE rb_droptarget = SWIG_RubyInstanceFor(wx_droptarget); rb_gc_mark(rb_droptarget); } #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>2) std::wcout << "* GC_mark_wxWindow - getting validator" << std::endl; #endif wxValidator* wx_validator = wx_win->GetValidator(); if ( wx_validator ) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>2) std::wcout << "* GC_mark_wxWindow - found validator" << std::endl; #endif VALUE rb_validator = SWIG_RubyInstanceFor(wx_validator); rb_gc_mark(rb_validator); } #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "< GC_mark_wxWindow : " << ptr << std::endl; #endif } WXRUBY_EXPORT void GC_mark_wxFrame(void *ptr) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "> GC_mark_wxFrame : " << ptr << std::endl; #endif if ( GC_IsWindowDeleted(ptr) ) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "> GC_mark_wxFrame : deleted" << std::endl; #endif return; } // Frames are also a subclass of wxWindow, so must do all the marking // of sizers and carets associated with that class GC_mark_wxWindow(ptr); wxFrame* wx_frame = (wxFrame*)ptr; // Then mark the MenuBar, if one is associated with this Frame wxMenuBar* menu_bar = wx_frame->GetMenuBar(); if ( menu_bar ) { GC_mark_MenuBarBelongingToFrame(menu_bar); } #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "> GC_mark_wxFrame : " << ptr << std::endl; #endif } // wxRuby must preserve ruby objects attached as the ClientData of // command events that have been user-defined in ruby. Some of the // standard wxWidgets CommandEvent classes (which have a constant event // id less than wxEVT_USER_FIRST, from wx/event.h) also use ClientData // for their own purposes, and this must not be marked as the data is // not a ruby object, and will thus crash. WXRUBY_EXPORT void GC_mark_wxEvent(void *ptr) { #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "> GC_mark_wxEvent : " << ptr << std::endl; #endif if ( ! ptr ) return; wxEvent* wx_event = (wxEvent*)ptr; #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>2) std::wcout << "* GC_mark_wxEvent(" << ptr << ":{" << wx_event->GetEventType() << "})" << std::endl; #endif if ( wx_event->GetEventType() > wxEVT_USER_FIRST && wx_event->IsCommandEvent() ) { wxCommandEvent* wx_cm_event = (wxCommandEvent*)ptr; VALUE rb_client_data = (VALUE)wx_cm_event->GetClientData(); rb_gc_mark(rb_client_data); } #ifdef __WXRB_DEBUG__ if (wxRuby_TraceLevel()>1) std::wcout << "< GC_mark_wxEvent : " << ptr << std::endl; #endif } %}