1 /* Copyright (C) 2014 The gtkmm Development Team
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 // This test case is a result of https://bugzilla.gnome.org/show_bug.cgi?id=731444
18 // Bug 731444 - gtkmm::builder - derived widget's destructor is not called -> memory leaks
19 
20 #include <iostream>
21 #include <cstring>
22 #include <gtkmm.h>
23 
24 namespace
25 {
26 
27 const char gladefile[] =
28 "<?xml version='1.0' encoding='UTF-8'?>"
29 "<!-- Generated with glade 3.16.1 -->"
30 "<interface>"
31   "<requires lib='gtk+' version='3.10'/>"
32   "<object class='GtkWindow' id='main_window'>"
33     "<property name='can_focus'>False</property>"
34     "<property name='title' translatable='yes'>Gtk::Builder ref count</property>"
35     "<property name='default_width'>440</property>"
36     "<property name='default_height'>150</property>"
37     "<child>"
38       "<object class='GtkBox' id='vbox'>"
39         "<property name='visible'>True</property>"
40         "<property name='can_focus'>False</property>"
41         "<property name='orientation'>vertical</property>"
42         "<child>"
43           "<object class='GtkButton' id='derived_button'>"
44             "<property name='label' translatable='yes'>DerivedButton</property>"
45             "<property name='visible'>True</property>"
46             "<property name='can_focus'>True</property>"
47             "<property name='receives_default'>True</property>"
48           "</object>"
49           "<packing>"
50             "<property name='expand'>False</property>"
51             "<property name='fill'>True</property>"
52             "<property name='position'>0</property>"
53           "</packing>"
54         "</child>"
55         "<child>"
56           "<object class='GtkButton' id='standard_button'>"
57             "<property name='label' translatable='yes'>Gtk::Button</property>"
58             "<property name='visible'>True</property>"
59             "<property name='can_focus'>True</property>"
60             "<property name='receives_default'>True</property>"
61           "</object>"
62           "<packing>"
63             "<property name='expand'>False</property>"
64             "<property name='fill'>True</property>"
65             "<property name='position'>1</property>"
66           "</packing>"
67         "</child>"
68       "</object>"
69     "</child>"
70   "</object>"
71   "<object class='GtkButton' id='orphaned_button'>"
72     "<property name='label' translatable='yes'>Gtk::Button</property>"
73     "<property name='visible'>True</property>"
74     "<property name='can_focus'>True</property>"
75     "<property name='receives_default'>True</property>"
76   "</object>"
77 "</interface>";
78 
on_managed_button_deleted(void *)79 void* on_managed_button_deleted(void* /* data */)
80 {
81   std::cout << "Gtk::Button in window deleted" << std::endl;
82   return nullptr;
83 }
84 
on_orphaned_button_deleted(void *)85 void* on_orphaned_button_deleted(void* /* data */)
86 {
87   std::cout << "Orphaned Gtk::Button deleted" << std::endl;
88   return nullptr;
89 }
90 
91 class DerivedButton : public Gtk::Button
92 {
93 public:
DerivedButton(BaseObjectType * cobject,const Glib::RefPtr<Gtk::Builder> &,const Glib::ustring icon_name=Glib::ustring ())94   DerivedButton(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& /* refBuilder */,
95     const Glib::ustring icon_name = Glib::ustring())
96   : Gtk::Button(cobject)
97   {
98     std::cout << "DerivedButton::ctor" << std::endl;
99 
100     if (!icon_name.empty())
101     {
102       set_image_from_icon_name(icon_name);
103       property_always_show_image() = true;
104     }
105   }
106 
~DerivedButton()107   virtual ~DerivedButton()
108   {
109     std::cout << "DerivedButton::dtor" << std::endl;
110   }
111 };
112 
113 class MainWindow : public Gtk::Window
114 {
115 public:
MainWindow(BaseObjectType * cobject,const Glib::RefPtr<Gtk::Builder> & refBuilder)116   MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder)
117   : Gtk::Window(cobject), m_pDerivedButton(nullptr), m_pStandardButton(nullptr)
118   {
119     std::cout << "MainWindow::ctor" << std::endl;
120 
121     // Called twice just to see if two calls affect the ref count.
122     refBuilder->get_widget_derived("derived_button", m_pDerivedButton, "face-smile");
123     refBuilder->get_widget_derived("derived_button", m_pDerivedButton);
124     refBuilder->get_widget("standard_button", m_pStandardButton);
125     refBuilder->get_widget("standard_button", m_pStandardButton);
126 
127     m_pStandardButton->add_destroy_notify_callback(nullptr, on_managed_button_deleted);
128   }
129 
~MainWindow()130   virtual ~MainWindow()
131   {
132     std::cout << "MainWindow::dtor" << std::endl;
133   }
134 
get_derived_button() const135   const DerivedButton* get_derived_button() const { return m_pDerivedButton; }
get_standard_button() const136   const Gtk::Button* get_standard_button() const { return m_pStandardButton; }
137 
138 private:
139   DerivedButton* m_pDerivedButton;
140   Gtk::Button* m_pStandardButton;
141 };
142 
143 } // end of anonymous namespace
144 
main(int argc,char * argv[])145 int main(int argc, char* argv[])
146 {
147   // With the command-line parameter --p-a-d, ref counts are printed
148   // after the widgets have been deleted. This means accesses to deallocated
149   // memory, possibly with bad side effects.
150   bool print_after_deletion = false;
151   int argc1 = argc;
152   if (argc > 1 && std::strcmp(argv[1], "--p-a-d") == 0)
153   {
154     print_after_deletion = true;
155     argc1 = 1; // Don't give the command line arguments to Gtk::Application.
156   }
157 
158   Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc1, argv);
159 
160   Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_string(gladefile);
161 
162   MainWindow* main_win = nullptr;
163   builder->get_widget_derived("main_window", main_win);
164 
165   Gtk::Button* orph_button = nullptr;
166   builder->get_widget("orphaned_button", orph_button);
167   orph_button->add_destroy_notify_callback(nullptr, on_orphaned_button_deleted);
168 
169   const GObject* const window = (GObject*)main_win->gobj();
170   const GObject* const orphaned_button = (GObject*)orph_button->gobj();
171   const GObject* const derived_button = (GObject*)main_win->get_derived_button()->gobj();
172   const GObject* const standard_button = (GObject*)main_win->get_standard_button()->gobj();
173 
174   std::cout << "Before app->run(*main_win)" << std::endl
175     << "  ref_count(MainWindow)=" << window->ref_count << std::endl
176     << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
177     << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
178     << "  ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl;
179 
180   const int result = app->run(*main_win);
181 
182   std::cout << "After app->run(*main_win)" << std::endl
183     << "  ref_count(MainWindow)=" << window->ref_count << std::endl
184     << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
185     << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
186     << "  ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl;
187 
188   delete main_win;
189 
190   std::cout << "After delete main_win" << std::endl
191     << "  ref_count(MainWindow)=" << window->ref_count << std::endl
192     << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
193     << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
194     << "  ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl;
195 
196   builder.reset();
197 
198   if (print_after_deletion)
199   {
200     // If Builder is correct, this code will access deallocated memory.
201     std::cout << "After builder.reset()" << std::endl
202       << "  ref_count(MainWindow)=" << window->ref_count << std::endl
203       << "  ref_count(DerivedButton)=" << derived_button->ref_count << std::endl
204       << "  ref_count(Gtk::Button)=" << standard_button->ref_count << std::endl
205       << "  ref_count(orphaned_button)=" << orphaned_button->ref_count << std::endl;
206   }
207 
208   return result;
209 }
210