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