diff --git a/lib/wp/meson.build b/lib/wp/meson.build index 577e65b7..84316267 100644 --- a/lib/wp/meson.build +++ b/lib/wp/meson.build @@ -2,12 +2,18 @@ wp_lib_sources = [ 'error.c', 'interface-impl.c', 'object.c', + 'plugin-registry.c', + 'plugin.c', + 'proxy.c', ] wp_lib_headers = [ 'error.h', 'interface-impl.h', 'object.h', + 'plugin-registry.h', + 'plugin.h', + 'proxy.h', ] enums = gnome.mkenums_simple('wpenums', sources: wp_lib_headers) diff --git a/lib/wp/plugin-registry.c b/lib/wp/plugin-registry.c new file mode 100644 index 00000000..f5bdee5b --- /dev/null +++ b/lib/wp/plugin-registry.c @@ -0,0 +1,223 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "plugin-registry.h" +#include "plugin.h" + +typedef struct { + gsize block_size; + const WpPluginMetadata *metadata; + WpPlugin *instance; +} PluginData; + +struct _WpPluginRegistry +{ + GObject parent; + + GList *plugins; + GStringChunk *metadata_strings; +}; + +G_DEFINE_TYPE (WpPluginRegistry, wp_plugin_registry, G_TYPE_OBJECT); + +static void +wp_plugin_registry_init (WpPluginRegistry * self) +{ + self->metadata_strings = g_string_chunk_new (200); +} + +static void +wp_plugin_registry_dispose (GObject * object) +{ + WpPluginRegistry *self = WP_PLUGIN_REGISTRY (object); + GList *list; + PluginData *plugin_data; + + for (list = self->plugins; list != NULL; list = g_list_next (list)) { + plugin_data = list->data; + g_clear_object (&plugin_data->instance); + } + + G_OBJECT_CLASS (wp_plugin_registry_parent_class)->dispose (object); +} + +static void +plugin_data_free (PluginData *data) +{ + g_slice_free1 (data->block_size, data); +} + +static void +wp_plugin_registry_finalize (GObject * object) +{ + WpPluginRegistry *self = WP_PLUGIN_REGISTRY (object); + + g_list_free_full (self->plugins, (GDestroyNotify) plugin_data_free); + g_string_chunk_free (self->metadata_strings); + + G_OBJECT_CLASS (wp_plugin_registry_parent_class)->finalize (object); +} + +static void +wp_plugin_registry_class_init (WpPluginRegistryClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + object_class->dispose = wp_plugin_registry_dispose; + object_class->finalize = wp_plugin_registry_finalize; +} + +/** + * wp_plugin_registry_new: (constructor) + * + * Create a new registry. + */ +WpPluginRegistry * +wp_plugin_registry_new (void) +{ + return g_object_new (wp_plugin_registry_get_type (), NULL); +} + +static gint +compare_ranks (const WpPluginMetadata * a, const WpPluginMetadata * b) +{ + return (gint) b->rank - (gint) a->rank; +} + +/** + * wp_plugin_registry_register_with_metadata: (skip) + * @metadata: the metadata + * @metadata_size: the sizeof (@metadata), to allow ABI-compatible future + * expansion of the structure + * + * Registers a plugin in the registry. + * This method is used internally by WP_PLUGIN_REGISTER(). + * Avoid using it directly. + */ +void +wp_plugin_registry_register_with_metadata (WpPluginRegistry * self, + const WpPluginMetadata * metadata, + gsize metadata_size) +{ + PluginData *data; + + g_return_if_fail (WP_IS_PLUGIN_REGISTRY (self)); + g_return_if_fail (metadata_size == sizeof (WpPluginMetadata)); + g_return_if_fail (g_type_is_a (metadata->gtype, wp_plugin_get_type ())); + g_return_if_fail (metadata->name != NULL); + g_return_if_fail (metadata->description != NULL); + g_return_if_fail (metadata->author != NULL); + g_return_if_fail (metadata->license != NULL); + g_return_if_fail (metadata->version != NULL); + g_return_if_fail (metadata->origin != NULL); + + data = g_slice_alloc (sizeof (PluginData)); + data->block_size = sizeof (PluginData); + data->metadata = metadata; + data->instance = NULL; + + self->plugins = g_list_insert_sorted (self->plugins, data, + (GCompareFunc) compare_ranks); +} + +/** + * wp_plugin_registry_register: (method) + * @plugin_type: the #GType of the #WpPlugin subclass + * @rank: the rank of the plugin + * @name: the name of the plugin + * @description: plugin description + * @author: author , author2 + * @license: a SPDX license ID or "Proprietary" + * @version: the version of the plugin + * @origin: URL or short reference of where this plugin came from + * + * Registers a plugin in the registry. + * This method creates a dynamically allocated #WpPluginMetadata and is meant + * to be used by bindings that have no way of representing #WpPluginMetadata. + * In C/C++, you should use WP_PLUGIN_REGISTER() + */ +void +wp_plugin_registry_register (WpPluginRegistry * self, + GType plugin_type, + guint16 rank, + const gchar *name, + const gchar *description, + const gchar *author, + const gchar *license, + const gchar *version, + const gchar *origin) +{ + PluginData *data; + WpPluginMetadata *metadata; + + g_return_if_fail (WP_IS_PLUGIN_REGISTRY (self)); + g_return_if_fail (g_type_is_a (plugin_type, wp_plugin_get_type ())); + g_return_if_fail (name != NULL); + g_return_if_fail (description != NULL); + g_return_if_fail (author != NULL); + g_return_if_fail (license != NULL); + g_return_if_fail (version != NULL); + g_return_if_fail (origin != NULL); + + data = g_slice_alloc (sizeof (PluginData) + sizeof (WpPluginMetadata)); + data->block_size = sizeof (PluginData) + sizeof (WpPluginMetadata); + + metadata = (WpPluginMetadata *) ((guint8 *) data) + sizeof (PluginData); + metadata->gtype = plugin_type; + metadata->rank = rank; + metadata->name = g_string_chunk_insert (self->metadata_strings, name); + metadata->description = g_string_chunk_insert (self->metadata_strings, + description); + metadata->author = g_string_chunk_insert (self->metadata_strings, author); + metadata->license = g_string_chunk_insert (self->metadata_strings, license); + metadata->version = g_string_chunk_insert (self->metadata_strings, version); + metadata->origin = g_string_chunk_insert (self->metadata_strings, origin); + + data->metadata = metadata; + data->instance = NULL; + + self->plugins = g_list_insert_sorted (self->plugins, data, + (GCompareFunc) compare_ranks); +} + +static inline void +make_plugin (WpPluginRegistry * self, PluginData * plugin_data) +{ + plugin_data->instance = g_object_new (plugin_data->metadata->gtype, + "registry", self, "metadata", plugin_data->metadata, NULL); +} + +/** + * WpPluginFunc: (skip) + */ + +/** + * wp_plugin_registry_invoke_internal: (skip) + * @self: the registry + * @func: a vfunc invocation function of #WpPlugin + * @data: data to pass to @func + * + * Used internally only. + */ +gboolean +wp_plugin_registry_invoke_internal (WpPluginRegistry * self, WpPluginFunc func, + gpointer data) +{ + GList *list; + PluginData *plugin_data; + + for (list = self->plugins; list != NULL; list = g_list_next (list)) { + plugin_data = list->data; + if (!plugin_data->instance) + make_plugin (self, plugin_data); + + if (func (plugin_data->instance, data)) + return TRUE; + } + + return FALSE; +} diff --git a/lib/wp/plugin-registry.h b/lib/wp/plugin-registry.h new file mode 100644 index 00000000..4d0c604a --- /dev/null +++ b/lib/wp/plugin-registry.h @@ -0,0 +1,53 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __WP_PLUGIN_REGISTRY_H__ +#define __WP_PLUGIN_REGISTRY_H__ + +#include + +G_BEGIN_DECLS + +/* declared in plugin.h */ +typedef struct _WpPluginMetadata WpPluginMetadata; + +G_DECLARE_FINAL_TYPE (WpPluginRegistry, wp_plugin_registry, WP, PLUGIN_REGISTRY, GObject) + +WpPluginRegistry * wp_plugin_registry_new (void); + +void wp_plugin_registry_register_with_metadata (WpPluginRegistry * self, + const WpPluginMetadata * metadata, + gsize metadata_size); + +void wp_plugin_registry_register (WpPluginRegistry * self, + GType plugin_type, + guint16 rank, + const gchar *name, + const gchar *description, + const gchar *author, + const gchar *license, + const gchar *version, + const gchar *origin); + + +typedef gboolean (*WpPluginFunc) (gpointer plugin, gpointer data); +gboolean wp_plugin_registry_invoke_internal (WpPluginRegistry * self, + WpPluginFunc func, gpointer data); + +#define wp_plugin_registry_invoke(r, func, data) \ + G_STMT_START \ + if (!(0 ? func ((WpPlugin *) NULL, data) : \ + wp_plugin_registry_invoke_internal (r, (WpPluginFunc) func, \ + (gpointer) data))) { \ + g_warning ("No plugin handled invocation to " ##func); \ + } \ + G_STMT_END + +G_END_DECLS + +#endif diff --git a/lib/wp/plugin.c b/lib/wp/plugin.c new file mode 100644 index 00000000..5cb0344d --- /dev/null +++ b/lib/wp/plugin.c @@ -0,0 +1,262 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "plugin.h" + +enum { + PROP_0, + PROP_RANK, + PROP_NAME, + PROP_DESCRIPTION, + PROP_AUTHOR, + PROP_LICENSE, + PROP_VERSION, + PROP_ORIGIN, + PROP_REGISTRY, + PROP_METADATA, +}; + +typedef struct { + WpPluginRegistry *registry; + const WpPluginMetadata *metadata; +} WpPluginPrivate; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpPlugin, wp_plugin, G_TYPE_OBJECT); + +static void +wp_plugin_init (WpPlugin * self) +{ +} + +static void +wp_plugin_dispose (GObject * object) +{ + WpPlugin *plugin = WP_PLUGIN (object); + WpPluginPrivate *priv = wp_plugin_get_instance_private (plugin); + + g_clear_object (&priv->registry); + + G_OBJECT_CLASS (wp_plugin_parent_class)->dispose (object); +} + +static void +wp_plugin_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + WpPlugin *plugin = WP_PLUGIN (object); + WpPluginPrivate *priv = wp_plugin_get_instance_private (plugin); + + switch (property_id) { + case PROP_REGISTRY: + priv->registry = g_value_get_object (value); + break; + case PROP_METADATA: + priv->metadata = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_plugin_get_property (GObject * object, guint property_id, GValue * value, + GParamSpec * pspec) +{ + WpPlugin *plugin = WP_PLUGIN (object); + WpPluginPrivate *priv = wp_plugin_get_instance_private (plugin); + + switch (property_id) { + case PROP_RANK: + g_value_set_uint (value, priv->metadata->rank); + break; + case PROP_NAME: + g_value_set_string (value, priv->metadata->name); + break; + case PROP_DESCRIPTION: + g_value_set_string (value, priv->metadata->description); + break; + case PROP_AUTHOR: + g_value_set_string (value, priv->metadata->author); + break; + case PROP_LICENSE: + g_value_set_string (value, priv->metadata->license); + break; + case PROP_VERSION: + g_value_set_string (value, priv->metadata->version); + break; + case PROP_ORIGIN: + g_value_set_string (value, priv->metadata->origin); + break; + case PROP_REGISTRY: + g_value_set_object (value, priv->registry); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +default_handle_pw_proxy (WpPlugin * self, WpProxy * proxy) +{ + return FALSE; +} + +static void +wp_plugin_class_init (WpPluginClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + klass->handle_pw_proxy = default_handle_pw_proxy; + + object_class->dispose = wp_plugin_dispose; + object_class->get_property = wp_plugin_get_property; + object_class->set_property = wp_plugin_set_property; + + g_object_class_install_property (object_class, PROP_RANK, + g_param_spec_uint ("rank", "Rank", "The plugin rank", 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_NAME, + g_param_spec_string ("name", "Name", "The plugin's name", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_DESCRIPTION, + g_param_spec_string ("description", "Description", + "The plugin's description", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_AUTHOR, + g_param_spec_string ("author", "Author", "The plugin's author", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_LICENSE, + g_param_spec_string ("license", "License", "The plugin's license", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_VERSION, + g_param_spec_string ("version", "Version", "The plugin's version", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_ORIGIN, + g_param_spec_string ("origin", "Origin", "The plugin's origin", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_REGISTRY, + g_param_spec_object ("registry", "Registry", + "The WpPluginRegistry that owns this plugin", + wp_plugin_registry_get_type (), + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_METADATA, + g_param_spec_pointer ("metadata", "metadata", "metadata", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | + G_PARAM_PRIVATE)); +} + +/** + * wp_plugin_handle_pw_proxy: (virtual handle_pw_proxy) + */ +gboolean +wp_plugin_handle_pw_proxy (WpPlugin * self, WpProxy * proxy) +{ + if (WP_PLUGIN_GET_CLASS (self)->handle_pw_proxy) + return WP_PLUGIN_GET_CLASS (self)->handle_pw_proxy (self, proxy); + else + return FALSE; +} + +/** + * wp_plugin_handle_pw_device: (virtual handle_pw_device) + */ +gboolean +wp_plugin_handle_pw_device (WpPlugin * self, WpProxy * proxy) +{ + if (WP_PLUGIN_GET_CLASS (self)->handle_pw_device) + return WP_PLUGIN_GET_CLASS (self)->handle_pw_device (self, proxy); + else + return FALSE; +} + +/** + * wp_plugin_handle_pw_device_node: (virtual handle_pw_device_node) + */ +gboolean +wp_plugin_handle_pw_device_node (WpPlugin * self, WpProxy * proxy) +{ + if (WP_PLUGIN_GET_CLASS (self)->handle_pw_device_node) + return WP_PLUGIN_GET_CLASS (self)->handle_pw_device_node (self, proxy); + else + return FALSE; +} + +/** + * wp_plugin_handle_pw_client: (virtual handle_pw_client) + */ +gboolean +wp_plugin_handle_pw_client (WpPlugin * self, WpProxy * proxy) +{ + if (WP_PLUGIN_GET_CLASS (self)->handle_pw_client) + return WP_PLUGIN_GET_CLASS (self)->handle_pw_client (self, proxy); + else + return FALSE; +} + +/** + * wp_plugin_handle_pw_client_node: (virtual handle_pw_client_node) + */ +gboolean +wp_plugin_handle_pw_client_node (WpPlugin * self, WpProxy * proxy) +{ + if (WP_PLUGIN_GET_CLASS (self)->handle_pw_client_node) + return WP_PLUGIN_GET_CLASS (self)->handle_pw_client_node (self, proxy); + else + return FALSE; +} + +/** + * wp_plugin_provide_interfaces: (virtual provide_interfaces) + */ +gboolean +wp_plugin_provide_interfaces (WpPlugin * self, WpObject * object) +{ + if (WP_PLUGIN_GET_CLASS (self)->provide_interfaces) + return WP_PLUGIN_GET_CLASS (self)->provide_interfaces (self, object); + else + return FALSE; +} + +/** + * wp_plugin_get_registry: (method) + * @self: the plugin + * + * Returns: (transfer full): the registry where this plugin is registered + */ +WpPluginRegistry * +wp_plugin_get_registry (WpPlugin * self) +{ + WpPluginPrivate *priv = wp_plugin_get_instance_private (self); + g_object_ref (priv->registry); + return priv->registry; +} + +/** + * wp_plugin_get_metadata: (skip) + * @self: the plugin + * + * This is intended for C/C++ only. Use the #WpPlugin properties in bindings. + * + * Returns: the metadata structure associated with this plugin + */ +const WpPluginMetadata * +wp_plugin_get_metadata (WpPlugin * self) +{ + WpPluginPrivate *priv = wp_plugin_get_instance_private (self); + return priv->metadata; +} diff --git a/lib/wp/plugin.h b/lib/wp/plugin.h new file mode 100644 index 00000000..09928acf --- /dev/null +++ b/lib/wp/plugin.h @@ -0,0 +1,258 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __WP_PLUGIN_H__ +#define __WP_PLUGIN_H__ + +#include "object.h" +#include "proxy.h" +#include "plugin-registry.h" + +#include + +G_BEGIN_DECLS + +/** + * WpPluginRank: + * @WP_PLUGIN_RANK_UPSTREAM: should only be used inside WirePlumber + * @WP_PLUGIN_RANK_PLATFORM_OVERRIDE: plugins provided by the platform, + * possibly to provide a platform-specific policy + * @WP_PLUGIN_RANK_VENDOR_OVERRIDE: plugins provided by hardware vendors + * to provide hardware-specific device handling and/or policies + * + * The rank of a plugin is an unsigned integer that can take an arbitrary + * value. On invocation, plugins ranked with a higher number are tried first, + * which is how one can implement overrides. This enum provides default + * values for certain kinds of plugins. Feel free to add/substract numbers + * to these constants in order to make a hierarchy, if you are implementing + * multiple different plugins that need to be tried in a certain order. + */ +typedef enum { + WP_PLUGIN_RANK_UPSTREAM = 0, + WP_PLUGIN_RANK_PLATFORM_OVERRIDE = 128, + WP_PLUGIN_RANK_VENDOR_OVERRIDE = 256, +} WpPluginRank; + +/** + * WpPluginMetadata: (skip) + * @gtype: the #GType of the plugin + * @rank: the rank of the plugin + * @name: the name of the plugin + * @description: plugin description + * @author: author , author2 + * @license: a SPDX license ID or "Proprietary" + * @version: the version of the plugin + * @origin: URL or short reference of where this plugin came from + * + * Metadata for registering a plugin (for the C API). + * You should normally never need to use this directly. + * Use WP_PLUGIN_DEFINE() instead. + */ +struct _WpPluginMetadata +{ + union { + struct { + GType gtype; + guint rank; + }; + gpointer _unused_for_alignment[2]; + }; + const gchar *name; + const gchar *description; + const gchar *author; + const gchar *license; + const gchar *version; + const gchar *origin; +}; + +G_DECLARE_DERIVABLE_TYPE (WpPlugin, wp_plugin, WP, PLUGIN, GObject) + +struct _WpPluginClass +{ + GObjectClass parent_class; + + /** + * handle_pw_proxy: + * @self: the plugin + * @proxy: (transfer none): the proxy + * + * This method is called for every new proxy that appears in PipeWire. + * The default implementation will inspect the proxy type and will dispatch + * the call to one of the specialized methods available below. + * Override only for very special cases. + */ + gboolean (*handle_pw_proxy) (WpPlugin * self, WpProxy * proxy); + + /** + * handle_pw_device: + * @self: the plugin + * @proxy: (transfer none): the device proxy + * + * This method is called for every new PipeWire proxy of type + * `PipeWire:Interface:Device`. The implementation is expected to create + * a new #WpDevice and register it with the #WpDeviceManager. + * + * The default implementation returns FALSE. + * Override if you are implementing custom device management. + * + * Returns: TRUE if the device was handled, FALSE otherwise. + */ + gboolean (*handle_pw_device) (WpPlugin * self, WpProxy * proxy); + + /** + * handle_pw_device_node: + * @self: the plugin + * @proxy: (transfer none): the node proxy + * + * This method is called for every new PipeWire proxy of type + * `PipeWire:Interface:Node` whose parent proxy is a + * `PipeWire:Interface:Device`. + * + * The default implementation returns FALSE. + * Override if you are implementing custom device management. + * + * Returns: TRUE if the node was handled, FALSE otherwise. + */ + gboolean (*handle_pw_device_node) (WpPlugin * self, WpProxy * proxy); + + /** + * handle_pw_client: + * @self: the plugin + * @proxy: (transfer none): the client proxy + * + * This method is called for every new PipeWire proxy of type + * `PipeWire:Interface:Client`. The implementation is expected to update + * the client's permissions, if necessary. + * + * The default implementation returns FALSE. + * Override if you are implementing custom policy management. + * + * Returns: TRUE if the client was handled, FALSE otherwise. + */ + gboolean (*handle_pw_client) (WpPlugin * self, WpProxy * proxy); + + /** + * handle_pw_client_node: + * @self: the plugin + * @proxy: (transfer none): the node proxy + * + * This method is called for every new PipeWire proxy of type + * `PipeWire:Interface:Node` whose parent proxy is a + * `PipeWire:Interface:Client`. The implementation is expected to create + * a new #WpStream in some #WpSession. + * + * The default implementation returns FALSE. + * Override if you are implementing custom policy management. + * + * Returns: TRUE if the node was handled, FALSE otherwise. + */ + gboolean (*handle_pw_client_node) (WpPlugin * self, WpProxy * proxy); + + /** + * provide_interfaces: + * @self: the plugin + * @object: (transfer none): a #WpObject + * + * This method is called for every new #WpObject created in WirePlumber. + * The implementation is expected to attach any interface implementations + * that it can provide for this kind of object, if necessary, only if + * these interfaces have not already been attached on the @object. + * + * The default implementation returns FALSE. + * Override if you are providing custom interface implementations for objects. + * + * Returns: TRUE if the node was handled, FALSE otherwise. + */ + gboolean (*provide_interfaces) (WpPlugin * self, WpObject * object); +}; + +gboolean wp_plugin_handle_pw_proxy (WpPlugin * self, WpProxy * proxy); +gboolean wp_plugin_handle_pw_device (WpPlugin * self, WpProxy * proxy); +gboolean wp_plugin_handle_pw_device_node (WpPlugin * self, WpProxy * proxy); +gboolean wp_plugin_handle_pw_client (WpPlugin * self, WpProxy * proxy); +gboolean wp_plugin_handle_pw_client_node (WpPlugin * self, WpProxy * proxy); +gboolean wp_plugin_provide_interfaces (WpPlugin * self, WpObject * object); + +WpPluginRegistry * wp_plugin_get_registry (WpPlugin * self); +const WpPluginMetadata * wp_plugin_get_metadata (WpPlugin * self); + + +/** + * WP_MODULE_INIT_SYMBOL: (skip) + * + * The linker symbol that serves as an entry point in modules + */ +#define WP_MODULE_INIT_SYMBOL wireplumber__module_init + +/** + * WP_MODULE_DEFINE: (skip) + * + * A convenience macro to register modules in C/C++. + * A module can contain multiple plugins, which are meant to be registered + * with WP_PLUGIN_REGISTER in the place of @plugin_reg + * + * Example usage: + * |[ + * WP_MODULE_DEFINE ( + * WP_PLUGIN_REGISTER ( + * MY_PLUGIN_TYPE, + * WP_PLUGIN_RANK_PLATFORM_OVERRIDE, + * "myplugin", + * "A custom policy plugin for Awesome Platform", + * "George Kiagiadakis ", + * "LGPL-2.1-or-later", + * "3.0.1", + * "https://awesome-platform.example" + * ); + * WP_PLUGIN_REGISTER ( + * SECONDARY_PLUGIN_TYPE, + * WP_PLUGIN_RANK_PLATFORM_OVERRIDE - 1, + * "secondaryplugin", + * "A secondary policy plugin for Awesome Platform", + * "George Kiagiadakis ", + * "LGPL-2.1-or-later", + * "3.0.1", + * "https://awesome-platform.example" + * ); + * ) + * ]| + */ +#define WP_MODULE_DEFINE(plugin_reg) \ + G_MODULE_EXPORT void \ + WP_MODULE_INIT_SYMBOL (WpPluginRegistry * registry) \ + { \ + plugin_reg; \ + } + +/** + * WP_PLUGIN_REGISTER: (skip) + * + * A convenience macro to register plugins in C/C++. + * See WP_MODULE_DEFINE() for a usage example. + * See wp_plugin_registry_register() for a description of the parameters. + */ +#define WP_PLUGIN_REGISTER(gtype_, rank_, name_, description_, author_, license_, version_, origin_) \ + G_STMT_START \ + static const WpPluginMetadata plugin_metadata = { \ + .gtype = gtype_, \ + .rank = rank_, \ + .name = name_, \ + .description = description_, \ + .author = author_, \ + .license = license_, \ + .version = version_, \ + .origin = origin_ \ + }; \ + wp_plugin_registry_register_with_metadata (registry, &plugin_metadata, \ + sizeof (plugin_metadata)); \ + G_STMT_END + + +G_END_DECLS + +#endif diff --git a/lib/wp/proxy.c b/lib/wp/proxy.c new file mode 100644 index 00000000..950de514 --- /dev/null +++ b/lib/wp/proxy.c @@ -0,0 +1,175 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "proxy.h" +#include + +struct _WpProxy +{ + GObject parent; + + struct pw_proxy *proxy; + guint32 id; + guint32 parent_id; + guint32 type; + const gchar *type_string; +}; + +enum { + PROP_0, + PROP_PROXY, + PROP_ID, + PROP_PARENT_ID, + PROP_SPA_TYPE, + PROP_SPA_TYPE_STRING, +}; + +G_DEFINE_TYPE (WpProxy, wp_proxy, G_TYPE_OBJECT); + +static void +wp_proxy_init (WpProxy * self) +{ +} + +static void +wp_proxy_constructed (GObject * object) +{ + WpProxy *self = WP_PROXY (object); + const struct spa_type_info *info = pw_type_info (); + + while (info->type) { + if (info->type == self->type) { + self->type_string = info->name; + break; + } + info++; + } + + G_OBJECT_CLASS (wp_proxy_parent_class)->constructed (object); +} + +static void +wp_proxy_finalize (GObject * object) +{ + WpProxy *self = WP_PROXY (object); + + G_OBJECT_CLASS (wp_proxy_parent_class)->finalize (object); +} + +static void +wp_proxy_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + WpProxy *self = WP_PROXY (object); + + switch (property_id) { + case PROP_PROXY: + self->proxy = g_value_get_pointer (value); + break; + case PROP_ID: + self->id = g_value_get_uint (value); + break; + case PROP_PARENT_ID: + self->parent_id = g_value_get_uint (value); + break; + case PROP_SPA_TYPE: + self->type = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_proxy_get_property (GObject * object, guint property_id, GValue * value, + GParamSpec * pspec) +{ + WpProxy *self = WP_PROXY (object); + + switch (property_id) { + case PROP_PROXY: + g_value_set_pointer (value, self->proxy); + break; + case PROP_ID: + g_value_set_uint (value, self->id); + break; + case PROP_PARENT_ID: + g_value_set_uint (value, self->parent_id); + break; + case PROP_SPA_TYPE: + g_value_set_uint (value, self->type); + break; + case PROP_SPA_TYPE_STRING: + g_value_set_string (value, wp_proxy_get_spa_type_string (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_proxy_class_init (WpProxyClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->constructed = wp_proxy_constructed; + object_class->finalize = wp_proxy_finalize; + object_class->get_property = wp_proxy_get_property; + object_class->set_property = wp_proxy_set_property; + + g_object_class_install_property (object_class, PROP_PROXY, + g_param_spec_pointer ("proxy", "proxy", + "The underlying struct pw_proxy *", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_ID, + g_param_spec_uint ("id", "id", + "The global ID of the object", 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_PARENT_ID, + g_param_spec_uint ("parent-id", "parent-id", + "The global ID of the parent object", 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_SPA_TYPE, + g_param_spec_uint ("spa-type", "spa-type", + "The SPA type of the object", 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_SPA_TYPE_STRING, + g_param_spec_string ("spa-type-string", "spa-type-string", + "The string representation of the SPA type of the object", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); +} + +guint32 +wp_proxy_get_id (WpProxy * self) +{ + return self->id; +} + +guint32 +wp_proxy_get_parent_id (WpProxy * self) +{ + return self->parent_id; +} + +guint32 +wp_proxy_get_spa_type (WpProxy * self) +{ + return self->type; +} + +const gchar * +wp_proxy_get_spa_type_string (WpProxy * self) +{ + return self->type_string; +} diff --git a/lib/wp/proxy.h b/lib/wp/proxy.h new file mode 100644 index 00000000..c7965f9b --- /dev/null +++ b/lib/wp/proxy.h @@ -0,0 +1,27 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __WP_PROXY_H__ +#define __WP_PROXY_H__ + +#include + +G_BEGIN_DECLS + +struct pw_proxy; + +G_DECLARE_FINAL_TYPE (WpProxy, wp_proxy, WP, PROXY, GObject) + +guint32 wp_proxy_get_id (WpProxy * self); +guint32 wp_proxy_get_parent_id (WpProxy * self); +guint32 wp_proxy_get_spa_type (WpProxy * self); +const gchar * wp_proxy_get_spa_type_string (WpProxy * self); + +G_END_DECLS + +#endif