"Fossies" - the Fresh Open Source Software Archive 
Member "glibmm-2.76.0/gio/src/application.ccg" (12 Mar 2023, 16668 Bytes) of package /linux/misc/glibmm-2.76.0.tar.xz:
As a special service "Fossies" has tried to format the requested text file into HTML format (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /* Copyright (C) 2007 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 <gio/gio.h>
18 #include <giomm/file.h>
19 #include <giomm/actiongroup.h>
20 #include <giomm/init.h>
21 #include <cstring> // std::memset()
22 #include <map>
23 #include <mutex>
24 #include <vector>
25
26 using Flags = Gio::Application::Flags;
27
28 namespace // anonymous
29 {
30
31 static void
32 Application_signal_open_callback(
33 GApplication* self, GFile** files, gint n_files, const gchar* hint, void* data)
34 {
35 using SlotType = sigc::slot<void(const Gio::Application::type_vec_files&, const Glib::ustring&)>;
36
37 Gio::Application::type_vec_files vec_files(n_files);
38 for (int i = 0; i < n_files; ++i)
39 {
40 vec_files[i] = Glib::wrap(files[i], true);
41 }
42
43 const auto hint_str = (hint ? hint : Glib::ustring());
44
45 // Do not try to call a signal on a disassociated wrapper.
46 if (Glib::ObjectBase::_get_current_wrapper((GObject*)self))
47 {
48 try
49 {
50 if (const auto slot = Glib::SignalProxyNormal::data_to_slot(data))
51 {
52 (*static_cast<SlotType*>(slot))(vec_files, hint_str);
53 return;
54 }
55 }
56 catch (...)
57 {
58 Glib::exception_handlers_invoke();
59 }
60 }
61
62 return;
63 }
64
65 static void
66 Application_signal_open_notify_callback(
67 GApplication* self, GFile** files, gint n_files, const gchar* hint, void* data)
68 {
69 using namespace Gio;
70 using SlotType = sigc::slot<void(const Application::type_vec_files&, const Glib::ustring&)>;
71
72 Application::type_vec_files vec_files(n_files);
73 for (int i = 0; i < n_files; i++)
74 {
75 vec_files[i] = Glib::wrap(files[i], true);
76 }
77
78 const auto hint_str = (hint ? hint : Glib::ustring());
79
80 // Do not try to call a signal on a disassociated wrapper.
81 if (Glib::ObjectBase::_get_current_wrapper((GObject*)self))
82 {
83 try
84 {
85 if (const auto slot = Glib::SignalProxyNormal::data_to_slot(data))
86 {
87 (*static_cast<SlotType*>(slot))(vec_files, hint_str);
88 return;
89 }
90 }
91 catch (...)
92 {
93 Glib::exception_handlers_invoke();
94 }
95 }
96
97 return;
98 }
99
100 static const Glib::SignalProxyInfo Application_signal_open_info = { "open",
101 (GCallback)&Application_signal_open_callback,
102 (GCallback)&Application_signal_open_notify_callback };
103
104 // The add_main_option_entry*() methods that take a slot parameter are handled
105 // similarly to the corresponding Glib::OptionGroup::add_entry*() methods.
106 // There is an important difference: In add_main_option_entry*() we can't pass
107 // an Application pointer to the used GOptionGroup.
108 // g_application_add_main_option_entries() creates a GOptionGroup with user_data == nullptr.
109 // Therefore Application_option_arg_callback() is called with data == nullptr.
110 // Application_option_arg_callback() does not know which Application instance
111 // the command-line option belongs to. All Application instances (usually only one)
112 // share a map, mapping the long command option name to an OptionArgCallbackData.
113 class OptionArgCallbackData
114 {
115 public:
116 explicit OptionArgCallbackData(const Gio::Application* application,
117 const Glib::ustring& long_name, gchar short_name,
118 const Glib::ustring& description, const Glib::ustring& arg_description,
119 const Glib::OptionGroup::SlotOptionArgString& slot)
120 :
121 application_(application),
122 long_name_(g_strdup(long_name.c_str())), // must not be nullptr
123 short_name_(short_name),
124 description_(g_strdup(Glib::c_str_or_nullptr(description))),
125 arg_description_(g_strdup(Glib::c_str_or_nullptr(arg_description))),
126 slot_string_(new Glib::OptionGroup::SlotOptionArgString(slot)),
127 slot_filename_(nullptr)
128 {
129 }
130
131 explicit OptionArgCallbackData(const Gio::Application* application,
132 const Glib::ustring& long_name, gchar short_name,
133 const Glib::ustring& description, const Glib::ustring& arg_description,
134 const Glib::OptionGroup::SlotOptionArgFilename& slot)
135 :
136 application_(application),
137 long_name_(g_strdup(long_name.c_str())), // must not be nullptr
138 short_name_(short_name),
139 description_(g_strdup(Glib::c_str_or_nullptr(description))),
140 arg_description_(g_strdup(Glib::c_str_or_nullptr(arg_description))),
141 slot_string_(nullptr),
142 slot_filename_(new Glib::OptionGroup::SlotOptionArgFilename(slot))
143 {
144 }
145
146 const Gio::Application* get_application() const { return application_; }
147 const gchar* get_long_name() const { return long_name_; }
148 gchar get_short_name() const { return short_name_; }
149 const gchar* get_description() const { return description_; }
150 const gchar* get_arg_description() const { return arg_description_; }
151 bool is_filename_option() const { return slot_filename_ != nullptr; }
152
153 const Glib::OptionGroup::SlotOptionArgString* get_slot_string() const { return slot_string_; }
154
155 const Glib::OptionGroup::SlotOptionArgFilename* get_slot_filename() const
156 {
157 return slot_filename_;
158 }
159
160 ~OptionArgCallbackData()
161 {
162 g_free(long_name_);
163 g_free(description_);
164 g_free(arg_description_);
165 delete slot_string_;
166 delete slot_filename_;
167 // Don't delete application_. It's not owned by this class.
168 }
169
170 private:
171 const Gio::Application* application_;
172 gchar* long_name_;
173 gchar short_name_;
174 gchar* description_;
175 gchar* arg_description_;
176 // One of these slot pointers is nullptr and the other one points to a slot.
177 Glib::OptionGroup::SlotOptionArgString* slot_string_;
178 Glib::OptionGroup::SlotOptionArgFilename* slot_filename_;
179
180 // Not copyable
181 OptionArgCallbackData(const OptionArgCallbackData&) = delete;
182 OptionArgCallbackData& operator=(const OptionArgCallbackData&) = delete;
183
184 }; // end class OptionArgCallbackData
185
186 using OptionArgCallbackDataMap = std::map<Glib::ustring, OptionArgCallbackData*>;
187 OptionArgCallbackDataMap option_arg_callback_data;
188
189 // Gio::Application instances may be used in different threads.
190 // Accesses to option_arg_callback_data must be thread-safe.
191 std::mutex option_arg_callback_data_mutex;
192
193 gboolean
194 Application_option_arg_callback(
195 const gchar* option_name, const gchar* value, gpointer /* data */, GError** error)
196 {
197 const Glib::ustring cpp_option_name(option_name);
198
199 // option_name is either a single dash followed by a single letter (for a
200 // short name) or two dashes followed by a long option name.
201 std::unique_lock<std::mutex> lock(option_arg_callback_data_mutex);
202 OptionArgCallbackDataMap::const_iterator iterFind = option_arg_callback_data.end();
203 if (option_name[1] == '-')
204 {
205 // Long option name.
206 const auto long_option_name = Glib::ustring(option_name + 2);
207 iterFind = option_arg_callback_data.find(long_option_name);
208 }
209 else
210 {
211 // Short option name.
212 const auto short_option_name = option_name[1];
213 for (iterFind = option_arg_callback_data.begin(); iterFind != option_arg_callback_data.end();
214 ++iterFind)
215 {
216 if (iterFind->second->get_short_name() == short_option_name)
217 break;
218 }
219 }
220
221 if (iterFind == option_arg_callback_data.end())
222 {
223 Glib::OptionError(Glib::OptionError::UNKNOWN_OPTION, "Application_option_arg_callback(): "
224 "Unknown option " +
225 cpp_option_name)
226 .propagate(error);
227 return false;
228 }
229
230 const bool has_value = (value != nullptr);
231 const OptionArgCallbackData* const option_arg = iterFind->second;
232 try
233 {
234 if (option_arg->is_filename_option())
235 {
236 const auto the_slot = option_arg->get_slot_filename();
237 lock.unlock();
238 const std::string cpp_value(value ? value : "");
239 return (*the_slot)(cpp_option_name, cpp_value, has_value);
240 }
241 else
242 {
243 const auto the_slot = option_arg->get_slot_string();
244 lock.unlock();
245 const Glib::ustring cpp_value(value ? value : "");
246 return (*the_slot)(cpp_option_name, cpp_value, has_value);
247 }
248 }
249 catch (Glib::Error& err)
250 {
251 err.propagate(error);
252 }
253 catch (...)
254 {
255 Glib::exception_handlers_invoke();
256 }
257 return false;
258 }
259
260 } // anonymous namespace
261
262 namespace Gio
263 {
264
265 const Glib::Class&
266 Application::custom_class_init()
267 {
268 Glib::init();
269 Gio::init();
270 return application_class_.init();
271 }
272
273 Application::Application(const Glib::ustring& application_id, Flags flags)
274 : // Mark this class as non-derived to allow C++ vfuncs to be skipped.
275 // GApplication complains about "" but allows nullptr, so we avoid passing "".
276 Glib::ObjectBase(nullptr),
277 Glib::Object(Glib::ConstructParams(custom_class_init(),
278 "application_id", Glib::c_str_or_nullptr(application_id),
279 "flags", static_cast<GApplicationFlags>(flags), nullptr))
280 {
281 }
282
283 Application::~Application() noexcept
284 {
285 // Delete all OptionArgCallbackData instances that belong to this application.
286 std::lock_guard<std::mutex> lock(option_arg_callback_data_mutex);
287 OptionArgCallbackDataMap::iterator iter = option_arg_callback_data.begin();
288 while (iter != option_arg_callback_data.end())
289 {
290 OptionArgCallbackDataMap::iterator saved_iter = iter;
291 ++iter;
292 if (saved_iter->second->get_application() == this)
293 {
294 delete saved_iter->second;
295 option_arg_callback_data.erase(saved_iter);
296 }
297 }
298 }
299
300 // static
301 void
302 Application::unset_default()
303 {
304 g_application_set_default(nullptr);
305 }
306
307 void
308 Application_Class::open_callback(GApplication* self, GFile** files, gint n_files, const gchar* hint)
309 {
310 const auto obj_base =
311 static_cast<Glib::ObjectBase*>(Glib::ObjectBase::_get_current_wrapper((GObject*)self));
312
313 // Non-gtkmmproc-generated custom classes implicitly call the default
314 // Glib::ObjectBase constructor, which sets is_derived_. But gtkmmproc-
315 // generated classes can use this optimisation, which avoids the unnecessary
316 // parameter conversions if there is no possibility of the virtual function
317 // being overridden:
318
319 if (obj_base && obj_base->is_derived_())
320 {
321 const auto obj = dynamic_cast<CppObjectType* const>(obj_base);
322 if (obj) // This can be nullptr during destruction.
323 {
324 try // Trap C++ exceptions which would normally be lost because this is a C callback.
325 {
326 // Call the virtual member method, which derived classes might override.
327 Application::type_vec_files vec_files(n_files);
328 for (int i = 0; i < n_files; i++)
329 {
330 vec_files[i] = Glib::wrap(files[i], true);
331 }
332
333 const auto hint_str = (hint ? hint : Glib::ustring());
334
335 obj->on_open(vec_files, hint_str);
336 return;
337 }
338 catch (...)
339 {
340 Glib::exception_handlers_invoke();
341 }
342 }
343 }
344
345 const auto base = static_cast<BaseClassType*>(g_type_class_peek_parent(G_OBJECT_GET_CLASS(
346 self)) // Get the parent class of the object class (The original underlying C class).
347 );
348
349 // Call the original underlying C function:
350 if (base && base->open)
351 (*base->open)(self, files, n_files, hint);
352 }
353
354 Glib::SignalProxy<void(const Application::type_vec_files&, const Glib::ustring&)>
355 Application::signal_open()
356 {
357 return Glib::SignalProxy<void(const Application::type_vec_files&, const Glib::ustring&)>(
358 this, &Application_signal_open_info);
359 }
360
361 void
362 Gio::Application::on_open(const Application::type_vec_files& files, const Glib::ustring& hint)
363 {
364 const auto base = static_cast<BaseClassType*>(g_type_class_peek_parent(G_OBJECT_GET_CLASS(
365 gobject_)) // Get the parent class of the object class (The original underlying C class).
366 );
367
368 if (base && base->open)
369 {
370 (*base->open)(gobj(),
371 Glib::ArrayHandler<type_vec_files::value_type>::vector_to_array(files).data(), files.size(),
372 hint.c_str());
373 }
374 }
375
376 void
377 Application::open(const type_vec_files& files, const Glib::ustring& hint)
378 {
379 g_application_open(gobj(),
380 Glib::ArrayHandler<type_vec_files::value_type>::vector_to_array(files).data(), files.size(),
381 hint.c_str());
382 }
383
384 void
385 Application::open(const Glib::RefPtr<Gio::File>& file, const Glib::ustring& hint)
386 {
387 type_vec_files files(1);
388 files[0] = file;
389 open(files, hint);
390 }
391
392 void
393 Application::add_main_option_entry(OptionType arg_type, const Glib::ustring& long_name,
394 gchar short_name, const Glib::ustring& description, const Glib::ustring& arg_description,
395 Glib::OptionEntry::Flags flags)
396 {
397 // g_application_add_main_option() saves copies of the strings.
398 // No need to keep copies in Gio::Application.
399 g_application_add_main_option(gobj(), long_name.c_str(), short_name,
400 static_cast<GOptionFlags>(flags), static_cast<GOptionArg>(arg_type),
401 description.c_str(), Glib::c_str_or_nullptr(arg_description));
402 }
403
404 void
405 Application::add_main_option_entry(const Glib::OptionGroup::SlotOptionArgString& slot,
406 const Glib::ustring& long_name, gchar short_name, const Glib::ustring& description,
407 const Glib::ustring& arg_description, Glib::OptionEntry::Flags flags)
408 {
409 OptionArgCallbackData* callback_data = nullptr;
410 {
411 std::lock_guard<std::mutex> lock(option_arg_callback_data_mutex);
412 OptionArgCallbackDataMap::iterator iterFind = option_arg_callback_data.find(long_name);
413 if (iterFind != option_arg_callback_data.end())
414 return; // Ignore duplicates
415
416 callback_data = new OptionArgCallbackData(
417 this, long_name, short_name, description, arg_description, slot);
418 option_arg_callback_data[long_name] = callback_data;
419 } // option_arg_callback_data_mutex.unlock()
420
421 add_main_option_entry_private(callback_data->get_long_name(), short_name,
422 callback_data->get_description(), callback_data->get_arg_description(),
423 flags & ~Glib::OptionEntry::Flags::FILENAME);
424 }
425
426 void
427 Application::add_main_option_entry_filename(const Glib::OptionGroup::SlotOptionArgFilename& slot,
428 const Glib::ustring& long_name, gchar short_name, const Glib::ustring& description,
429 const Glib::ustring& arg_description, Glib::OptionEntry::Flags flags)
430 {
431 OptionArgCallbackData* callback_data = nullptr;
432 {
433 std::lock_guard<std::mutex> lock(option_arg_callback_data_mutex);
434 OptionArgCallbackDataMap::iterator iterFind = option_arg_callback_data.find(long_name);
435 if (iterFind != option_arg_callback_data.end())
436 return; // Ignore duplicates
437
438 callback_data = new OptionArgCallbackData(
439 this, long_name, short_name, description, arg_description, slot);
440 option_arg_callback_data[long_name] = callback_data;
441 } // option_arg_callback_data_mutex.unlock()
442
443 add_main_option_entry_private(callback_data->get_long_name(), short_name,
444 callback_data->get_description(), callback_data->get_arg_description(),
445 flags | Glib::OptionEntry::Flags::FILENAME);
446 }
447
448 void
449 Application::add_main_option_entry_private(const gchar* long_name,
450 gchar short_name, const gchar* description,
451 const gchar* arg_description, Glib::OptionEntry::Flags flags)
452 {
453 // g_application_add_main_option() can't be used for options with
454 // a callback slot, because GOptionEntry.arg_data must be non-null.
455
456 // Create a temporary array, just so we can give the correct thing to
457 // g_application_add_main_option_entries():
458 GOptionEntry array[2];
459 std::memset(array, 0, 2 * sizeof(GOptionEntry)); // null-termination
460
461 // g_application_add_main_option_entries() does not take its own copy
462 // of the strings. We must keep them alive, and keep pointers to them,
463 // so we can delete them when the Application instance is deleted.
464 // This is handled in OptionArgCallbackData.
465
466 // Fill in array[0].
467 array[0].arg = G_OPTION_ARG_CALLBACK;
468 array[0].long_name = long_name;
469 array[0].short_name = short_name;
470 array[0].description = description;
471 array[0].arg_description = arg_description;
472 array[0].flags = static_cast<int>(flags);
473
474 // GoptionEntry.arg_data is a function pointer, cast to void*.
475 // See Glib::OptionGroup::CppOptionEntry::allocate_c_arg() for a discussion
476 // of this hack.
477 union {
478 void* dp;
479 GOptionArgFunc fp;
480 } u;
481 u.fp = &Application_option_arg_callback;
482 array[0].arg_data = u.dp;
483
484 g_application_add_main_option_entries(gobj(), array);
485 }
486
487 void
488 Application::unset_resource_base_path()
489 {
490 g_application_set_resource_base_path(gobj(), nullptr /* see the C docs. */);
491 }
492
493 } // namespace Gio