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'],
|
||||
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'
|
||||
|
||||
gobject_dep = dependency('gobject-2.0')
|
||||
gmodule_dep = dependency('gmodule-2.0')
|
||||
gio_dep = dependency('gio-2.0')
|
||||
pipewire_dep = dependency('libpipewire-0.3')
|
||||
|
||||
gnome = import('gnome')
|
||||
|
||||
wp_lib_include_dir = include_directories('lib')
|
||||
|
||||
subdir('lib')
|
||||
subdir('src')
|
||||
|
193
src/core.c
193
src/core.c
@@ -10,10 +10,17 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "loop-source.h"
|
||||
#include "module-loader.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <wp/plugin-registry.h>
|
||||
#include <wp/proxy-registry.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <glib-unix.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#define WIREPLUMBER_DEFAULT_CONFIG_FILE "wireplumber.conf"
|
||||
|
||||
struct _WpCore
|
||||
{
|
||||
@@ -26,32 +33,27 @@ struct _WpCore
|
||||
struct pw_remote *remote;
|
||||
struct spa_hook remote_listener;
|
||||
|
||||
struct pw_core_proxy *core_proxy;
|
||||
struct spa_hook core_proxy_listener;
|
||||
|
||||
struct pw_registry_proxy *registry_proxy;
|
||||
struct spa_hook registry_proxy_listener;
|
||||
WpModuleLoader *module_loader;
|
||||
WpPluginRegistry *plugin_registry;
|
||||
WpProxyRegistry *proxy_registry;
|
||||
|
||||
GError *exit_error;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (WpCore, wp_core, G_TYPE_OBJECT);
|
||||
|
||||
static const struct pw_registry_proxy_events registry_events = {
|
||||
PW_VERSION_REGISTRY_PROXY_EVENTS,
|
||||
//.global = registry_global,
|
||||
//.global_remove = registry_global_remove,
|
||||
};
|
||||
static gboolean
|
||||
signal_handler (gpointer data)
|
||||
{
|
||||
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 = {
|
||||
PW_VERSION_CORE_EVENTS,
|
||||
//.done = core_done
|
||||
};
|
||||
|
||||
static void on_state_changed (void * data,
|
||||
enum pw_remote_state old_state,
|
||||
enum pw_remote_state new_state,
|
||||
const char * error)
|
||||
static void
|
||||
remote_state_changed (void * data, enum pw_remote_state old_state,
|
||||
enum pw_remote_state new_state, const char * error)
|
||||
{
|
||||
WpCore *self = WP_CORE (data);
|
||||
|
||||
@@ -60,20 +62,7 @@ static void on_state_changed (void * data,
|
||||
pw_remote_state_as_string (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:
|
||||
self->core_proxy = NULL;
|
||||
self->registry_proxy = NULL;
|
||||
wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_DISCONNECTED, "disconnected");
|
||||
break;
|
||||
|
||||
@@ -89,9 +78,122 @@ static void on_state_changed (void * data,
|
||||
|
||||
static const struct pw_remote_events 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
|
||||
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,
|
||||
self);
|
||||
|
||||
self->proxy_registry = wp_proxy_registry_new (self->remote);
|
||||
self->plugin_registry = wp_plugin_registry_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -111,6 +216,15 @@ wp_core_finalize (GObject * 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);
|
||||
|
||||
pw_remote_destroy (self->remote);
|
||||
@@ -143,12 +257,13 @@ wp_core_get_instance (void)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
signal_handler (gpointer data)
|
||||
wp_core_run_in_idle (WpCore * self)
|
||||
{
|
||||
WpCore *self = WP_CORE (data);
|
||||
wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_INTERRUPTED,
|
||||
"interrupted by signal");
|
||||
return G_SOURCE_CONTINUE;
|
||||
if (!wp_core_load_commands_file (self)) goto out;
|
||||
if (pw_remote_connect (self->remote) < 0) goto out;
|
||||
|
||||
out:
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -158,7 +273,7 @@ wp_core_run (WpCore * self, GError ** error)
|
||||
g_unix_signal_add (SIGTERM, 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);
|
||||
|
||||
|
@@ -2,6 +2,7 @@ wp_sources = [
|
||||
'core.c',
|
||||
'loop-source.c',
|
||||
'main.c',
|
||||
'module-loader.c',
|
||||
'utils.c',
|
||||
]
|
||||
|
||||
@@ -9,5 +10,5 @@ executable('wireplumber',
|
||||
wp_sources,
|
||||
c_args : [ '-D_GNU_SOURCE', '-DG_LOG_USE_STRUCTURED' ],
|
||||
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