src: implement module loading
This commit is contained in:
@@ -38,3 +38,9 @@ gnome.generate_gir(wp_lib,
|
|||||||
includes: ['GLib-2.0', 'GObject-2.0'],
|
includes: ['GLib-2.0', 'GObject-2.0'],
|
||||||
install: true,
|
install: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
wp_dep = declare_dependency(
|
||||||
|
link_with: wp_lib,
|
||||||
|
include_directories: wp_lib_include_dir,
|
||||||
|
dependencies: [gobject_dep]
|
||||||
|
)
|
||||||
|
@@ -11,9 +11,13 @@ project('wireplumber', ['c'],
|
|||||||
wireplumber_api_version = '0.1'
|
wireplumber_api_version = '0.1'
|
||||||
|
|
||||||
gobject_dep = dependency('gobject-2.0')
|
gobject_dep = dependency('gobject-2.0')
|
||||||
|
gmodule_dep = dependency('gmodule-2.0')
|
||||||
|
gio_dep = dependency('gio-2.0')
|
||||||
pipewire_dep = dependency('libpipewire-0.3')
|
pipewire_dep = dependency('libpipewire-0.3')
|
||||||
|
|
||||||
gnome = import('gnome')
|
gnome = import('gnome')
|
||||||
|
|
||||||
|
wp_lib_include_dir = include_directories('lib')
|
||||||
|
|
||||||
subdir('lib')
|
subdir('lib')
|
||||||
subdir('src')
|
subdir('src')
|
||||||
|
193
src/core.c
193
src/core.c
@@ -10,10 +10,17 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "loop-source.h"
|
#include "loop-source.h"
|
||||||
|
#include "module-loader.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <wp/plugin-registry.h>
|
||||||
|
#include <wp/proxy-registry.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <glib-unix.h>
|
#include <glib-unix.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#define WIREPLUMBER_DEFAULT_CONFIG_FILE "wireplumber.conf"
|
||||||
|
|
||||||
struct _WpCore
|
struct _WpCore
|
||||||
{
|
{
|
||||||
@@ -26,32 +33,27 @@ struct _WpCore
|
|||||||
struct pw_remote *remote;
|
struct pw_remote *remote;
|
||||||
struct spa_hook remote_listener;
|
struct spa_hook remote_listener;
|
||||||
|
|
||||||
struct pw_core_proxy *core_proxy;
|
WpModuleLoader *module_loader;
|
||||||
struct spa_hook core_proxy_listener;
|
WpPluginRegistry *plugin_registry;
|
||||||
|
WpProxyRegistry *proxy_registry;
|
||||||
struct pw_registry_proxy *registry_proxy;
|
|
||||||
struct spa_hook registry_proxy_listener;
|
|
||||||
|
|
||||||
GError *exit_error;
|
GError *exit_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (WpCore, wp_core, G_TYPE_OBJECT);
|
G_DEFINE_TYPE (WpCore, wp_core, G_TYPE_OBJECT);
|
||||||
|
|
||||||
static const struct pw_registry_proxy_events registry_events = {
|
static gboolean
|
||||||
PW_VERSION_REGISTRY_PROXY_EVENTS,
|
signal_handler (gpointer data)
|
||||||
//.global = registry_global,
|
{
|
||||||
//.global_remove = registry_global_remove,
|
WpCore *self = WP_CORE (data);
|
||||||
};
|
wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_INTERRUPTED,
|
||||||
|
"interrupted by signal");
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct pw_core_proxy_events core_events = {
|
static void
|
||||||
PW_VERSION_CORE_EVENTS,
|
remote_state_changed (void * data, enum pw_remote_state old_state,
|
||||||
//.done = core_done
|
enum pw_remote_state new_state, const char * error)
|
||||||
};
|
|
||||||
|
|
||||||
static void on_state_changed (void * data,
|
|
||||||
enum pw_remote_state old_state,
|
|
||||||
enum pw_remote_state new_state,
|
|
||||||
const char * error)
|
|
||||||
{
|
{
|
||||||
WpCore *self = WP_CORE (data);
|
WpCore *self = WP_CORE (data);
|
||||||
|
|
||||||
@@ -60,20 +62,7 @@ static void on_state_changed (void * data,
|
|||||||
pw_remote_state_as_string (new_state));
|
pw_remote_state_as_string (new_state));
|
||||||
|
|
||||||
switch (new_state) {
|
switch (new_state) {
|
||||||
case PW_REMOTE_STATE_CONNECTED:
|
|
||||||
self->core_proxy = pw_remote_get_core_proxy (self->remote);
|
|
||||||
pw_core_proxy_add_listener (self->core_proxy, &self->core_proxy_listener,
|
|
||||||
&core_events, self);
|
|
||||||
|
|
||||||
self->registry_proxy = pw_core_proxy_get_registry (self->core_proxy,
|
|
||||||
PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0);
|
|
||||||
pw_registry_proxy_add_listener (self->registry_proxy,
|
|
||||||
&self->registry_proxy_listener, ®istry_events, self);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PW_REMOTE_STATE_UNCONNECTED:
|
case PW_REMOTE_STATE_UNCONNECTED:
|
||||||
self->core_proxy = NULL;
|
|
||||||
self->registry_proxy = NULL;
|
|
||||||
wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_DISCONNECTED, "disconnected");
|
wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_DISCONNECTED, "disconnected");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -89,9 +78,122 @@ static void on_state_changed (void * data,
|
|||||||
|
|
||||||
static const struct pw_remote_events remote_events = {
|
static const struct pw_remote_events remote_events = {
|
||||||
PW_VERSION_REMOTE_EVENTS,
|
PW_VERSION_REMOTE_EVENTS,
|
||||||
.state_changed = on_state_changed,
|
.state_changed = remote_state_changed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
wp_core_parse_commands_file (WpCore * self, GInputStream * stream,
|
||||||
|
GError ** error)
|
||||||
|
{
|
||||||
|
gchar buffer[4096];
|
||||||
|
gssize bytes_read;
|
||||||
|
gchar *cur, *linestart, *saveptr;
|
||||||
|
gchar *cmd, *abi, *module;
|
||||||
|
gint lineno = 1;
|
||||||
|
gboolean eof = FALSE;
|
||||||
|
|
||||||
|
linestart = cur = buffer;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bytes_read = g_input_stream_read (stream, cur, sizeof (buffer), NULL, error);
|
||||||
|
if (bytes_read < 0)
|
||||||
|
return FALSE;
|
||||||
|
else if (bytes_read == 0) {
|
||||||
|
eof = TRUE;
|
||||||
|
/* terminate the remaining data, so that we consume it all */
|
||||||
|
if (cur != linestart) {
|
||||||
|
*cur = '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_read += (cur - linestart);
|
||||||
|
|
||||||
|
while (cur - buffer < bytes_read) {
|
||||||
|
while (cur - buffer < bytes_read && *cur != '\n')
|
||||||
|
cur++;
|
||||||
|
|
||||||
|
if (*cur == '\n') {
|
||||||
|
/* found the end of a line */
|
||||||
|
*cur = '\0';
|
||||||
|
|
||||||
|
/* tokenize and execute */
|
||||||
|
cmd = strtok_r (linestart, " ", &saveptr);
|
||||||
|
|
||||||
|
if (g_strcmp0 (cmd, "load-module")) {
|
||||||
|
abi = strtok_r (NULL, " ", &saveptr);
|
||||||
|
module = strtok_r (NULL, " ", &saveptr);
|
||||||
|
|
||||||
|
if (!abi || !module) {
|
||||||
|
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT,
|
||||||
|
"expected ABI and MODULE at line %i", lineno);
|
||||||
|
return FALSE;
|
||||||
|
} else if (!wp_module_loader_load (self->module_loader,
|
||||||
|
self->plugin_registry, abi, module, error)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT,
|
||||||
|
"unknown command '%s' at line %i", cmd, lineno);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* continue with the next line */
|
||||||
|
linestart = ++cur;
|
||||||
|
lineno++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reached the end of the data that was read */
|
||||||
|
|
||||||
|
if (cur - linestart >= sizeof (buffer)) {
|
||||||
|
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_OPERATION_FAILED,
|
||||||
|
"line %i exceeds the maximum allowed line size (%d bytes)",
|
||||||
|
lineno, (gint) sizeof (buffer));
|
||||||
|
return FALSE;
|
||||||
|
} else if (cur - linestart > 0) {
|
||||||
|
/* we have unparsed data, move it to the
|
||||||
|
* beginning of the buffer and continue */
|
||||||
|
strncpy (buffer, linestart, cur - linestart);
|
||||||
|
linestart = buffer;
|
||||||
|
cur = buffer + (cur - linestart);
|
||||||
|
}
|
||||||
|
} while (!eof);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
wp_core_load_commands_file (WpCore * self)
|
||||||
|
{
|
||||||
|
g_autoptr (GFile) file = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
g_autoptr (GFileInputStream) istream = NULL;
|
||||||
|
const gchar *filename;
|
||||||
|
|
||||||
|
filename = g_getenv ("WIREPLUMBER_CONFIG_FILE");
|
||||||
|
if (!filename)
|
||||||
|
filename = WIREPLUMBER_DEFAULT_CONFIG_FILE;
|
||||||
|
|
||||||
|
file = g_file_new_for_path (filename);
|
||||||
|
istream = g_file_read (file, NULL, &error);
|
||||||
|
if (!istream) {
|
||||||
|
g_propagate_error (&self->exit_error, error);
|
||||||
|
error = NULL;
|
||||||
|
g_main_loop_quit (self->loop);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wp_core_parse_commands_file (self, G_INPUT_STREAM (istream), &error)) {
|
||||||
|
g_propagate_prefixed_error (&self->exit_error, error, "Failed to read %s: ",
|
||||||
|
filename);
|
||||||
|
error = NULL;
|
||||||
|
g_main_loop_quit (self->loop);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wp_core_init (WpCore * self)
|
wp_core_init (WpCore * self)
|
||||||
{
|
{
|
||||||
@@ -104,6 +206,9 @@ wp_core_init (WpCore * self)
|
|||||||
|
|
||||||
pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events,
|
pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events,
|
||||||
self);
|
self);
|
||||||
|
|
||||||
|
self->proxy_registry = wp_proxy_registry_new (self->remote);
|
||||||
|
self->plugin_registry = wp_plugin_registry_new ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -111,6 +216,15 @@ wp_core_finalize (GObject * obj)
|
|||||||
{
|
{
|
||||||
WpCore *self = WP_CORE (obj);
|
WpCore *self = WP_CORE (obj);
|
||||||
|
|
||||||
|
/* ensure all proxies and plugins are unrefed,
|
||||||
|
* so that the registries can be disposed */
|
||||||
|
g_object_run_dispose (G_OBJECT (self->plugin_registry));
|
||||||
|
g_object_run_dispose (G_OBJECT (self->proxy_registry));
|
||||||
|
|
||||||
|
g_clear_object (&self->plugin_registry);
|
||||||
|
g_clear_object (&self->proxy_registry);
|
||||||
|
g_clear_object (&self->module_loader);
|
||||||
|
|
||||||
spa_hook_remove (&self->remote_listener);
|
spa_hook_remove (&self->remote_listener);
|
||||||
|
|
||||||
pw_remote_destroy (self->remote);
|
pw_remote_destroy (self->remote);
|
||||||
@@ -143,12 +257,13 @@ wp_core_get_instance (void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
signal_handler (gpointer data)
|
wp_core_run_in_idle (WpCore * self)
|
||||||
{
|
{
|
||||||
WpCore *self = WP_CORE (data);
|
if (!wp_core_load_commands_file (self)) goto out;
|
||||||
wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_INTERRUPTED,
|
if (pw_remote_connect (self->remote) < 0) goto out;
|
||||||
"interrupted by signal");
|
|
||||||
return G_SOURCE_CONTINUE;
|
out:
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -158,7 +273,7 @@ wp_core_run (WpCore * self, GError ** error)
|
|||||||
g_unix_signal_add (SIGTERM, signal_handler, self);
|
g_unix_signal_add (SIGTERM, signal_handler, self);
|
||||||
g_unix_signal_add (SIGHUP, signal_handler, self);
|
g_unix_signal_add (SIGHUP, signal_handler, self);
|
||||||
|
|
||||||
g_idle_add ((GSourceFunc) pw_remote_connect, self->remote);
|
g_idle_add ((GSourceFunc) wp_core_run_in_idle, self);
|
||||||
|
|
||||||
g_main_loop_run (self->loop);
|
g_main_loop_run (self->loop);
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@ wp_sources = [
|
|||||||
'core.c',
|
'core.c',
|
||||||
'loop-source.c',
|
'loop-source.c',
|
||||||
'main.c',
|
'main.c',
|
||||||
|
'module-loader.c',
|
||||||
'utils.c',
|
'utils.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -9,5 +10,5 @@ executable('wireplumber',
|
|||||||
wp_sources,
|
wp_sources,
|
||||||
c_args : [ '-D_GNU_SOURCE', '-DG_LOG_USE_STRUCTURED' ],
|
c_args : [ '-D_GNU_SOURCE', '-DG_LOG_USE_STRUCTURED' ],
|
||||||
install: true,
|
install: true,
|
||||||
dependencies : [gobject_dep, pipewire_dep],
|
dependencies : [gobject_dep, gmodule_dep, gio_dep, pipewire_dep, wp_dep],
|
||||||
)
|
)
|
||||||
|
80
src/module-loader.c
Normal file
80
src/module-loader.c
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "module-loader.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <wp/plugin.h>
|
||||||
|
|
||||||
|
struct _WpModuleLoader
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
const gchar *module_dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (WpModuleLoader, wp_module_loader, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
static void
|
||||||
|
wp_module_loader_init (WpModuleLoader * self)
|
||||||
|
{
|
||||||
|
self->module_dir = g_getenv ("WIREPLUMBER_MODULE_DIR");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wp_module_loader_class_init (WpModuleLoaderClass * klass)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WpModuleLoader *
|
||||||
|
wp_module_loader_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (wp_module_loader_get_type (), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
wp_module_loader_load_c (WpModuleLoader * self, WpPluginRegistry * registry,
|
||||||
|
const gchar * module_name, GError ** error)
|
||||||
|
{
|
||||||
|
g_autofree gchar *module_path = NULL;
|
||||||
|
GModule *module;
|
||||||
|
gpointer module_init;
|
||||||
|
typedef void (*WpModuleInitFunc)(WpPluginRegistry *);
|
||||||
|
|
||||||
|
module_path = g_module_build_path (self->module_dir, module_name);
|
||||||
|
module = g_module_open (module_path, G_MODULE_BIND_LOCAL);
|
||||||
|
if (!module) {
|
||||||
|
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_OPERATION_FAILED,
|
||||||
|
"Failed to open module %s: %s", module_path, g_module_error ());
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_module_symbol (module, G_STRINGIFY (WP_MODULE_INIT_SYMBOL),
|
||||||
|
&module_init)) {
|
||||||
|
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_OPERATION_FAILED,
|
||||||
|
"Failed to locate symbol " G_STRINGIFY (WP_MODULE_INIT_SYMBOL) " in %s",
|
||||||
|
module_path);
|
||||||
|
g_module_close (module);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
((WpModuleInitFunc) module_init) (registry);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
wp_module_loader_load (WpModuleLoader * self, WpPluginRegistry * registry,
|
||||||
|
const gchar * abi, const gchar * module_name, GError ** error)
|
||||||
|
{
|
||||||
|
if (!g_strcmp0 (abi, "C")) {
|
||||||
|
return wp_module_loader_load_c (self, registry, module_name, error);
|
||||||
|
} else {
|
||||||
|
g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT,
|
||||||
|
"unknown module ABI %s", abi);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
27
src/module-loader.h
Normal file
27
src/module-loader.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __WIREPLUMBER_MODULE_LOADER_H__
|
||||||
|
#define __WIREPLUMBER_MODULE_LOADER_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <wp/plugin-registry.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE (WpModuleLoader, wp_module_loader, WP, MODULE_LOADER, GObject)
|
||||||
|
|
||||||
|
WpModuleLoader * wp_module_loader_new (void);
|
||||||
|
|
||||||
|
gboolean wp_module_loader_load (WpModuleLoader * self,
|
||||||
|
WpPluginRegistry * registry, const gchar * abi, const gchar * module_name,
|
||||||
|
GError ** error);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user