/* * Copyright (C) 2023 Purism SPC * * This file is part of Calls. * * Calls is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Calls is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Calls. If not, see . * * Author: Evangelos Ribeiro Tzaras * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "calls-plugin.h" /** * SECTION:calls-plugin * @short_description: A plugin for calls * @Title: CallsPlugin * * Holds information about plugins and allows loading/unloading. */ struct _CallsPlugin { GObject parent_instance; PeasPluginInfo *info; CallsProvider *provider; gboolean is_loaded; }; G_DEFINE_TYPE (CallsPlugin, calls_plugin, G_TYPE_OBJECT) enum { PROP_0, PROP_PLUGIN_INFO, PROP_NAME, PROP_DESCRIPTION, PROP_AUTHORS, PROP_COPYRIGHT, PROP_VERSION, PROP_LOADED, PROP_PROVIDER, PROP_LAST_PROP }; static GParamSpec *props[PROP_LAST_PROP]; static void calls_plugin_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CallsPlugin *self = CALLS_PLUGIN (object); switch (prop_id) { case PROP_PLUGIN_INFO: self->info = g_value_get_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void calls_plugin_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CallsPlugin *self = CALLS_PLUGIN (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, calls_plugin_get_name (self)); break; case PROP_DESCRIPTION: g_value_set_string (value, calls_plugin_get_description (self)); break; case PROP_AUTHORS: g_value_set_boxed (value, calls_plugin_get_authors (self)); break; case PROP_COPYRIGHT: g_value_set_string (value, calls_plugin_get_copyright (self)); break; case PROP_VERSION: g_value_set_string (value, calls_plugin_get_version (self)); break; case PROP_LOADED: g_value_set_boolean (value, calls_plugin_is_loaded (self)); break; case PROP_PROVIDER: g_value_set_object (value, calls_plugin_get_provider (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void calls_plugin_dispose (GObject *object) { CallsPlugin *self = CALLS_PLUGIN (object); g_clear_object (&self->info); g_clear_object (&self->provider); G_OBJECT_CLASS (calls_plugin_parent_class)->dispose (object); } static void calls_plugin_class_init (CallsPluginClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = calls_plugin_dispose; object_class->get_property = calls_plugin_get_property; object_class->set_property = calls_plugin_set_property; /** * CallsPlugin:plugin-info: * * The #PeasPluginInfo containing information about the plugin */ props[PROP_PLUGIN_INFO] = g_param_spec_boxed ("plugin-info", "", "", PEAS_TYPE_PLUGIN_INFO, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * CallsPlugin:name: * * The name of the plugin */ props[PROP_NAME] = g_param_spec_string ("name", "", "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * CallsPlugin:description: * * A description of the plugin */ props[PROP_DESCRIPTION] = g_param_spec_string ("description", "", "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * CallsPlugin:authors: * * The name of the plugin */ props[PROP_AUTHORS] = g_param_spec_boxed ("authors", "", "", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * CallsPlugin:description: * * The copyright holder of the plugin */ props[PROP_COPYRIGHT] = g_param_spec_string ("copyright", "", "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * CallsPlugin:version: * * The version of the plugin */ props[PROP_VERSION] = g_param_spec_string ("version", "", "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * CallsPlugin:loaded: * * Whether the plugin is loaded. This property is set after the plugin was loaded * and unset before the plugin was unloaded. This means at notification time * the e.g. provider property still points to a valid object. */ props[PROP_LOADED] = g_param_spec_boolean ("loaded", "", "", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * CallsPlugin:provider: * * The #CallsProvider provided by this plugin, if any */ props[PROP_PROVIDER] = g_param_spec_object ("provider", "", "", CALLS_TYPE_PROVIDER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST_PROP, props); } static void calls_plugin_init (CallsPlugin *self) { } CallsPlugin * calls_plugin_new (PeasPluginInfo *info) { g_return_val_if_fail (info, NULL); return g_object_new (CALLS_TYPE_PLUGIN, "plugin-info", info, NULL); } gboolean calls_plugin_load (CallsPlugin *self, GError **error) { PeasEngine *peas = peas_engine_get_default (); PeasExtension *extension; g_return_val_if_fail (CALLS_IS_PLUGIN (self), FALSE); if (calls_plugin_is_loaded (self)) return TRUE; if (!peas_engine_load_plugin (peas, self->info)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Plugin '%s' could not be loaded", peas_plugin_info_get_module_name (self->info)); return FALSE; } g_assert (peas_plugin_info_is_loaded (self->info)); if (!peas_engine_provides_extension (peas, self->info, CALLS_TYPE_PROVIDER)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Plugin '%s' does not provide a CallsProvider", peas_plugin_info_get_module_name (self->info)); peas_engine_unload_plugin (peas, self->info); return FALSE; } g_debug ("Successfully loaded plugin '%s'", peas_plugin_info_get_module_name (self->info)); extension = peas_engine_create_extension (peas, self->info, CALLS_TYPE_PROVIDER, NULL); if (!extension) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not create CallsProvider from plugin '%s'", peas_plugin_info_get_module_name (self->info)); peas_engine_unload_plugin (peas, self->info); return FALSE; } self->provider = CALLS_PROVIDER (extension); g_object_notify_by_pspec (G_OBJECT (self), props[PROP_PROVIDER]); self->is_loaded = TRUE; g_object_notify_by_pspec (G_OBJECT (self), props[PROP_LOADED]); return TRUE; } gboolean calls_plugin_unload (CallsPlugin *self, GError **error) { PeasEngine *peas = peas_engine_get_default (); g_return_val_if_fail (CALLS_IS_PLUGIN (self), FALSE); if (!calls_plugin_is_loaded (self)) return TRUE; if (!peas_engine_unload_plugin (peas, self->info)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not unload plugin '%s'", peas_plugin_info_get_module_name (self->info)); return FALSE; } self->is_loaded = FALSE; g_object_notify_by_pspec (G_OBJECT (self), props[PROP_LOADED]); g_clear_object (&self->provider); g_object_notify_by_pspec (G_OBJECT (self), props[PROP_PROVIDER]); return TRUE; } gboolean calls_plugin_is_loaded (CallsPlugin *self) { g_return_val_if_fail (CALLS_IS_PLUGIN (self), FALSE); return self->is_loaded; } CallsProvider * calls_plugin_get_provider (CallsPlugin *self) { g_return_val_if_fail (CALLS_IS_PLUGIN (self), NULL); return self->provider; } const char * calls_plugin_get_module_name (CallsPlugin *self) { g_return_val_if_fail (CALLS_IS_PLUGIN (self), NULL); g_return_val_if_fail (self->info, NULL); return peas_plugin_info_get_module_name (self->info); } const char * calls_plugin_get_name (CallsPlugin *self) { g_return_val_if_fail (CALLS_IS_PLUGIN (self), NULL); g_return_val_if_fail (self->info, NULL); return peas_plugin_info_get_name (self->info); } const char * calls_plugin_get_description (CallsPlugin *self) { g_return_val_if_fail (CALLS_IS_PLUGIN (self), NULL); g_return_val_if_fail (self->info, NULL); return peas_plugin_info_get_name (self->info); } const char ** calls_plugin_get_authors (CallsPlugin *self) { g_return_val_if_fail (CALLS_IS_PLUGIN (self), NULL); g_return_val_if_fail (self->info, NULL); return peas_plugin_info_get_authors (self->info); } const char * calls_plugin_get_copyright (CallsPlugin *self) { g_return_val_if_fail (CALLS_IS_PLUGIN (self), NULL); g_return_val_if_fail (self->info, NULL); return peas_plugin_info_get_copyright (self->info); } const char * calls_plugin_get_version (CallsPlugin *self) { g_return_val_if_fail (CALLS_IS_PLUGIN (self), NULL); g_return_val_if_fail (self->info, NULL); return peas_plugin_info_get_version (self->info); }