"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