/* Copyright (C) 2014 The gtkmm Development Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*/
// This test case is a result of https://bugzilla.gnome.org/show_bug.cgi?id=731444
// Bug 731444 - gtkmm::builder - derived widget's destructor is not called -> memory leaks
#include
#include
#include
namespace
{
const char gladefile[] =
""
""
""
""
""
""
"Gtk::Button"
"True"
"True"
"True"
""
"";
void* on_managed_button_deleted(void* /* data */)
{
std::cout << "Gtk::Button in window deleted" << std::endl;
return nullptr;
}
void* on_orphaned_button_deleted(void* /* data */)
{
std::cout << "Orphaned Gtk::Button deleted" << std::endl;
return nullptr;
}
class DerivedButton : public Gtk::Button
{
public:
DerivedButton(BaseObjectType* cobject, const Glib::RefPtr& /* refBuilder */,
const Glib::ustring icon_name = Glib::ustring())
: Gtk::Button(cobject)
{
std::cout << "DerivedButton::ctor" << std::endl;
if (!icon_name.empty())
{
set_image_from_icon_name(icon_name);
property_always_show_image() = true;
}
}
virtual ~DerivedButton()
{
std::cout << "DerivedButton::dtor" << std::endl;
}
};
class MainWindow : public Gtk::Window
{
public:
MainWindow(BaseObjectType* cobject, const Glib::RefPtr& refBuilder)
: Gtk::Window(cobject), m_pDerivedButton(nullptr), m_pStandardButton(nullptr)
{
std::cout << "MainWindow::ctor" << std::endl;
// Called twice just to see if two calls affect the ref count.
refBuilder->get_widget_derived("derived_button", m_pDerivedButton, "face-smile");
refBuilder->get_widget_derived("derived_button", m_pDerivedButton);
refBuilder->get_widget("standard_button", m_pStandardButton);
refBuilder->get_widget("standard_button", m_pStandardButton);
m_pStandardButton->add_destroy_notify_callback(nullptr, on_managed_button_deleted);
}
virtual ~MainWindow()
{
std::cout << "MainWindow::dtor" << std::endl;
}
const DerivedButton* get_derived_button() const { return m_pDerivedButton; }
const Gtk::Button* get_standard_button() const { return m_pStandardButton; }
private:
DerivedButton* m_pDerivedButton;
Gtk::Button* m_pStandardButton;
};
} // end of anonymous namespace
int main(int argc, char* argv[])
{
// With the command-line parameter --p-a-d, ref counts are printed
// after the widgets have been deleted. This means accesses to deallocated
// memory, possibly with bad side effects.
bool print_after_deletion = false;
int argc1 = argc;
if (argc > 1 && std::strcmp(argv[1], "--p-a-d") == 0)
{
print_after_deletion = true;
argc1 = 1; // Don't give the command line arguments to Gtk::Application.
}
Glib::RefPtr app = Gtk::Application::create(argc1, argv);
Glib::RefPtr builder = Gtk::Builder::create_from_string(gladefile);
MainWindow* main_win = nullptr;
builder->get_widget_derived("main_window", main_win);
Gtk::Button* orph_button = nullptr;
builder->get_widget("orphaned_button", orph_button);
orph_button->add_destroy_notify_callback(nullptr, on_orphaned_button_deleted);
const GObject* const window = (GObject*)main_win->gobj();
const GObject* const orphaned_button = (GObject*)orph_button->gobj();
const GObject* const derived_button = (GObject*)main_win->get_derived_button()->gobj();
const GObject* const standard_button = (GObject*)main_win->get_standard_button()->gobj();
std::cout << "Before app->run(*main_win)" << std::endl
<< " ref_count(MainWindow)=" << window->ref_count << std::endl
<< " ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
<< " ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
<< " ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl;
const int result = app->run(*main_win);
std::cout << "After app->run(*main_win)" << std::endl
<< " ref_count(MainWindow)=" << window->ref_count << std::endl
<< " ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
<< " ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
<< " ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl;
delete main_win;
std::cout << "After delete main_win" << std::endl
<< " ref_count(MainWindow)=" << window->ref_count << std::endl
<< " ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
<< " ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
<< " ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl;
builder.reset();
if (print_after_deletion)
{
// If Builder is correct, this code will access deallocated memory.
std::cout << "After builder.reset()" << std::endl
<< " ref_count(MainWindow)=" << window->ref_count << std::endl
<< " ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
<< " ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
<< " ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl;
}
return result;
}