1 /* Copyright 2002 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 #include <glib-object.h>
18
19 #include <glibmm/quark.h>
20 #include <glibmm/objectbase.h>
21 #include <glibmm/propertyproxy_base.h> //For PropertyProxyConnectionNode
22 #include <glibmm/interface.h>
23 #include <glibmm/private/interface_p.h>
24 #include <utility> // For std::move()
25
26 namespace
27 {
28
29 // Used by the Glib::ObjectBase default ctor. Using an explicitly defined
30 // char array rather than a string literal allows for fast pointer comparison,
31 // which is otherwise not guaranteed to work.
32
33 static const char anonymous_custom_type_name[] = "gtkmm__anonymous_custom_type";
34
35 } // anonymous namespace
36
37 namespace Glib
38 {
39
40 /**** Glib::ObjectBase *****************************************************/
41
42 // static data members
43 ObjectBase::extra_object_base_data_type ObjectBase::extra_object_base_data;
44 std::mutex ObjectBase::extra_object_base_data_mutex;
45
ObjectBase()46 ObjectBase::ObjectBase()
47 : gobject_(nullptr),
48 custom_type_name_(anonymous_custom_type_name),
49 cpp_destruction_in_progress_(false)
50 {
51 }
52
ObjectBase(const char * custom_type_name)53 ObjectBase::ObjectBase(const char* custom_type_name)
54 : gobject_(nullptr), custom_type_name_(custom_type_name), cpp_destruction_in_progress_(false)
55 {
56 }
57
ObjectBase(const std::type_info & custom_type_info)58 ObjectBase::ObjectBase(const std::type_info& custom_type_info)
59 : gobject_(nullptr), custom_type_name_(custom_type_info.name()), cpp_destruction_in_progress_(false)
60 {
61 }
62
63 // initialize() actually initializes the wrapper. Glib::ObjectBase is used
64 // as virtual base class, which means the most-derived class' ctor invokes
65 // the Glib::ObjectBase ctor -- thus it's useless for Glib::Object.
66 //
67 void
initialize(GObject * castitem)68 ObjectBase::initialize(GObject* castitem)
69 {
70 if (gobject_)
71 {
72 // initialize() might be called twice when used with MI, e.g. by the ctors
73 // of Glib::Object and Glib::Interface. However, they must both refer to
74 // the same underlying GObject instance.
75 //
76 g_assert(gobject_ == castitem);
77
78 // TODO: Think about it. Will this really be called twice?
79 g_printerr("ObjectBase::initialize() called twice for the same GObject\n");
80
81 return; // Don't initialize the wrapper twice.
82 }
83
84 gobject_ = castitem;
85 _set_current_wrapper(castitem);
86 }
87
88 void
initialize_move(GObject * castitem,Glib::ObjectBase * previous_wrapper)89 ObjectBase::initialize_move(GObject* castitem, Glib::ObjectBase* previous_wrapper)
90 {
91 if (gobject_)
92 {
93 g_assert(gobject_ == castitem);
94
95 // TODO: Think about it. Will this really be called twice?
96 g_printerr("ObjectBase::initialize_move() called twice for the same GObject\n");
97
98 return; // Don't initialize the wrapper twice.
99 }
100
101 gobject_ = castitem;
102 _move_current_wrapper(castitem, previous_wrapper);
103 custom_type_name_ = previous_wrapper->custom_type_name_;
104 cpp_destruction_in_progress_ = previous_wrapper->cpp_destruction_in_progress_;
105
106 // Clear the previous wrapper:
107 previous_wrapper->custom_type_name_ = nullptr;
108 previous_wrapper->cpp_destruction_in_progress_ = false;
109 }
110
ObjectBase(ObjectBase && src)111 ObjectBase::ObjectBase(ObjectBase&& src) noexcept
112 : sigc::trackable(std::move(src)), // not actually called because it's a virtual base
113 gobject_(nullptr),
114 custom_type_name_(src.custom_type_name_),
115 cpp_destruction_in_progress_(src.cpp_destruction_in_progress_)
116 {
117 }
118
119 ObjectBase&
operator =(ObjectBase && src)120 ObjectBase::operator=(ObjectBase&& src) noexcept
121 {
122 if (this == &src)
123 return *this;
124
125 sigc::trackable::operator=(std::move(src));
126
127 if (gobject_)
128 {
129 // Remove the wrapper, without invoking destroy_notify_callback_():
130 g_object_steal_qdata(gobject_, Glib::quark_);
131 // Remove a reference, without deleting *this.
132 unreference();
133 gobject_ = nullptr;
134 }
135 initialize_move(src.gobject_, &src);
136
137 return *this;
138 }
139
~ObjectBase()140 ObjectBase::~ObjectBase() noexcept
141 {
142 // Normally, gobject_ should always be 0 at this point, because:
143 //
144 // a) Gtk::Object handles memory management on its own and always resets
145 // the gobject_ pointer in its destructor.
146 //
147 // b) Glib::Object instances that aren't Gtk::Objects will always be
148 // deleted by the destroy_notify_() virtual method. Calling delete
149 // on a Glib::Object is a programming error.
150 //
151 // The *only* situation where gobject_ is validly not 0 at this point
152 // happens if a derived class's ctor throws an exception. In that case
153 // we have to call g_object_unref() on our own.
154 //
155
156 // Just a precaution. Unless a derived class's ctor has thrown an exception,
157 // 'this' should have been erased from extra_object_base_data by
158 // Glib::Object's constructor.
159 {
160 std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
161 extra_object_base_data.erase(this);
162 }
163
164 if (GObject* const gobject = gobject_)
165 {
166 #ifdef GLIBMM_DEBUG_REFCOUNTING
167 g_warning("(Glib::ObjectBase::~ObjectBase): gobject_ = %p", (void*)gobject_);
168 #endif
169
170 gobject_ = nullptr;
171
172 #ifdef GLIBMM_DEBUG_REFCOUNTING
173 g_warning("(Glib::ObjectBase::~ObjectBase): before g_object_steal_qdata()");
174 #endif
175
176 // Remove the pointer to the wrapper from the underlying instance.
177 // This does _not_ cause invocation of the destroy_notify callback.
178 g_object_steal_qdata(gobject, quark_);
179
180 #ifdef GLIBMM_DEBUG_REFCOUNTING
181 g_warning("(Glib::ObjectBase::~ObjectBase): calling g_object_unref()");
182 #endif
183
184 g_object_unref(gobject);
185 }
186 }
187
188 void
reference() const189 ObjectBase::reference() const
190 {
191 GLIBMM_DEBUG_REFERENCE(this, gobject_);
192 g_object_ref(gobject_);
193 }
194
195 void
unreference() const196 ObjectBase::unreference() const
197 {
198 GLIBMM_DEBUG_UNREFERENCE(this, gobject_);
199 g_object_unref(gobject_);
200 }
201
202 GObject*
gobj_copy() const203 ObjectBase::gobj_copy() const
204 {
205 reference();
206 return gobject_;
207 }
208
209 void
_set_current_wrapper(GObject * object)210 ObjectBase::_set_current_wrapper(GObject* object)
211 {
212 // Store a pointer to this wrapper in the underlying instance, so that we
213 // never create a second wrapper for the same underlying instance. Also,
214 // specify a callback that will tell us when it's time to delete this C++
215 // wrapper instance:
216
217 if (object)
218 {
219 if (!g_object_get_qdata(object, Glib::quark_))
220 {
221 g_object_set_qdata_full(object, Glib::quark_, this, &destroy_notify_callback_);
222 }
223 else
224 {
225 g_warning("This object, of type %s, already has a wrapper.\n"
226 "You should use wrap() instead of a constructor.",
227 G_OBJECT_TYPE_NAME(object));
228 }
229 }
230 }
231
232 void
_move_current_wrapper(GObject * object,Glib::ObjectBase * previous_wrapper)233 ObjectBase::_move_current_wrapper(GObject* object, Glib::ObjectBase* previous_wrapper) noexcept
234 {
235 // See _set_current_wrapper().
236 ObjectBase* current_wrapper = _get_current_wrapper(object);
237 if (current_wrapper != previous_wrapper)
238 {
239 g_warning("%s: Unexpected previous wrapper, for object of type %s.\n"
240 "previous_wrapper=%p, current_wrapper=%p",
241 G_STRFUNC, G_OBJECT_TYPE_NAME(object), static_cast<void*>(previous_wrapper),
242 static_cast<void*>(current_wrapper));
243 }
244
245 // Remove the previous wrapper, without invoking destroy_notify_callback_():
246 g_object_steal_qdata(object, Glib::quark_);
247
248 // Set the new wrapper:
249 g_object_set_qdata_full(object, Glib::quark_, this, &destroy_notify_callback_);
250
251 // Clear the previous wrapper:
252 previous_wrapper->gobject_ = nullptr;
253 }
254
255 // static
256 ObjectBase*
_get_current_wrapper(GObject * object)257 ObjectBase::_get_current_wrapper(GObject* object)
258 {
259 if (object)
260 return static_cast<ObjectBase*>(g_object_get_qdata(object, Glib::quark_));
261 else
262 return nullptr;
263 }
264
265 // static
266 void
destroy_notify_callback_(void * data)267 ObjectBase::destroy_notify_callback_(void* data)
268 {
269 // GLIBMM_LIFECYCLE
270
271 // This method is called (indirectly) from g_object_run_dispose().
272 // Get the C++ instance associated with the C instance:
273 ObjectBase* cppObject =
274 static_cast<ObjectBase*>(data); // Previously set with g_object_set_qdata_full().
275
276 #ifdef GLIBMM_DEBUG_REFCOUNTING
277 g_warning("ObjectBase::destroy_notify_callback_: cppObject = %p, gobject_ = %p, gtypename = %s\n",
278 (void*)cppObject, (void*)cppObject->gobject_, G_OBJECT_TYPE_NAME(cppObject->gobject_));
279 #endif
280
281 if (cppObject) // This will be 0 if the C++ destructor has already run.
282 {
283 cppObject->destroy_notify_(); // Virtual - it does different things for GObject and GtkObject.
284 }
285 }
286
287 void
destroy_notify_()288 ObjectBase::destroy_notify_()
289 {
290 // The C instance is about to be disposed, making it unusable. Now is a
291 // good time to delete the C++ wrapper of the C instance. There is no way
292 // to force the disposal of the GObject (though GtkObject has
293 // gtk_object_destroy()), So this is the *only* place where we delete the
294 // C++ wrapper.
295 //
296 // This will only happen after the last unreference(), which will be done by
297 // the RefPtr<> destructor. There should be no way to access the wrapper or
298 // the undobjecterlying instance after that, so it's OK to delete this.
299
300 #ifdef GLIBMM_DEBUG_REFCOUNTING
301 g_warning("Glib::ObjectBase::destroy_notify_: gobject_ = %p", (void*)gobject_);
302 #endif
303
304 gobject_ = nullptr; // Make sure we don't unref it again in the dtor.
305
306 delete this;
307 }
308
309 bool
is_anonymous_custom_() const310 ObjectBase::is_anonymous_custom_() const
311 {
312 // Doing high-speed pointer comparison is OK here.
313 return (custom_type_name_ == anonymous_custom_type_name);
314 }
315
316 bool
is_derived_() const317 ObjectBase::is_derived_() const
318 {
319 // gtkmmproc-generated classes initialize this to 0 by default.
320 return (custom_type_name_ != nullptr);
321 }
322
323 void
set_manage()324 ObjectBase::set_manage()
325 {
326 // This is a private method and Gtk::manage() is a template function.
327 // Thus this will probably never run, unless you do something like:
328 //
329 // manage(static_cast<Gtk::Object*>(refptr.operator->()));
330
331 g_error("Glib::ObjectBase::set_manage(): "
332 "only Gtk::Object instances can be managed");
333 }
334
335 bool
_cpp_destruction_is_in_progress() const336 ObjectBase::_cpp_destruction_is_in_progress() const
337 {
338 return cpp_destruction_in_progress_;
339 }
340
341 void
set_property_value(const Glib::ustring & property_name,const Glib::ValueBase & value)342 ObjectBase::set_property_value(const Glib::ustring& property_name, const Glib::ValueBase& value)
343 {
344 g_object_set_property(gobj(), property_name.c_str(), value.gobj());
345 }
346
347 void
get_property_value(const Glib::ustring & property_name,Glib::ValueBase & value) const348 ObjectBase::get_property_value(const Glib::ustring& property_name, Glib::ValueBase& value) const
349 {
350 g_object_get_property(const_cast<GObject*>(gobj()), property_name.c_str(), value.gobj());
351 }
352
353 void
connect_property_changed(const Glib::ustring & property_name,const sigc::slot<void> & slot)354 ObjectBase::connect_property_changed(
355 const Glib::ustring& property_name, const sigc::slot<void>& slot)
356 {
357 connect_property_changed_with_return(property_name, slot);
358 }
359
360 void
connect_property_changed(const Glib::ustring & property_name,sigc::slot<void> && slot)361 ObjectBase::connect_property_changed(const Glib::ustring& property_name, sigc::slot<void>&& slot)
362 {
363 connect_property_changed_with_return(property_name, std::move(slot));
364 }
365
366 sigc::connection
connect_property_changed_with_return(const Glib::ustring & property_name,const sigc::slot<void> & slot)367 ObjectBase::connect_property_changed_with_return(
368 const Glib::ustring& property_name, const sigc::slot<void>& slot)
369 {
370 // Create a proxy to hold our connection info
371 // This will be deleted by destroy_notify_handler.
372 auto pConnectionNode = new PropertyProxyConnectionNode(slot, gobj());
373
374 // connect it to glib
375 // pConnectionNode will be passed as the data argument to the callback.
376 return pConnectionNode->connect_changed(property_name);
377 }
378
379 sigc::connection
connect_property_changed_with_return(const Glib::ustring & property_name,sigc::slot<void> && slot)380 ObjectBase::connect_property_changed_with_return(
381 const Glib::ustring& property_name, sigc::slot<void>&& slot)
382 {
383 // Create a proxy to hold our connection info
384 // This will be deleted by destroy_notify_handler.
385 auto pConnectionNode = new PropertyProxyConnectionNode(std::move(slot), gobj());
386
387 // connect it to glib
388 // pConnectionNode will be passed as the data argument to the callback.
389 return pConnectionNode->connect_changed(property_name);
390 }
391
392 void
freeze_notify()393 ObjectBase::freeze_notify()
394 {
395 g_object_freeze_notify(gobj());
396 }
397
398 void
thaw_notify()399 ObjectBase::thaw_notify()
400 {
401 g_object_thaw_notify(gobj());
402 }
403
add_custom_interface_class(const Interface_Class * iface_class)404 void ObjectBase::add_custom_interface_class(const Interface_Class* iface_class)
405 {
406 // The GObject is not instantiated yet. Add to the custom_interface_classes
407 // and add the interface in the Glib::Object constructor.
408 std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
409 extra_object_base_data[this].custom_interface_classes.emplace_back(iface_class);
410 }
411
add_custom_class_init_function(GClassInitFunc class_init_func,void * class_data)412 void ObjectBase::add_custom_class_init_function(GClassInitFunc class_init_func, void* class_data)
413 {
414 std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
415 extra_object_base_data[this].custom_class_init_functions.emplace_back(
416 std::make_tuple(class_init_func, class_data));
417 }
418
set_custom_instance_init_function(GInstanceInitFunc instance_init_func)419 void ObjectBase::set_custom_instance_init_function(GInstanceInitFunc instance_init_func)
420 {
421 std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
422 extra_object_base_data[this].custom_instance_init_function = instance_init_func;
423 }
424
get_custom_interface_classes() const425 const Class::interface_class_vector_type* ObjectBase::get_custom_interface_classes() const
426 {
427 std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
428 const auto iter = extra_object_base_data.find(this);
429 return (iter != extra_object_base_data.end()) ? &iter->second.custom_interface_classes : nullptr;
430 }
431
get_custom_class_init_functions() const432 const Class::class_init_funcs_type* ObjectBase::get_custom_class_init_functions() const
433 {
434 std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
435 const auto iter = extra_object_base_data.find(this);
436 return (iter != extra_object_base_data.end()) ? &iter->second.custom_class_init_functions : nullptr;
437 }
438
get_custom_instance_init_function() const439 GInstanceInitFunc ObjectBase::get_custom_instance_init_function() const
440 {
441 std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
442 const auto iter = extra_object_base_data.find(this);
443 return (iter != extra_object_base_data.end()) ? iter->second.custom_instance_init_function : nullptr;
444 }
445
custom_class_init_finished()446 void ObjectBase::custom_class_init_finished()
447 {
448 std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
449 const auto iter = extra_object_base_data.find(this);
450 if (iter != extra_object_base_data.end())
451 extra_object_base_data.erase(iter);
452 }
453
454 /**** Global function *****************************************************/
455 bool
_gobject_cppinstance_already_deleted(GObject * gobject)456 _gobject_cppinstance_already_deleted(GObject* gobject)
457 {
458 // This function is used to prevent calling wrap() on a GTK+ instance whose gtkmm instance has
459 // been deleted.
460
461 if (gobject)
462 return (bool)g_object_get_qdata(
463 gobject, Glib::quark_cpp_wrapper_deleted_); // true means that something is odd.
464 else
465 return false; // Nothing is particularly wrong.
466 }
467
468 } // namespace Glib
469