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