commit 43c44fb7cf8e2e8e40a2d8f9b6b8e7431207da4c Author: George Kiagiadakis Date: Wed Apr 3 18:31:05 2019 +0300 Initial commit diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..54aac353 --- /dev/null +++ b/meson.build @@ -0,0 +1,14 @@ +project('wireplumber', ['c'], + version : '0.0.1', + license : 'LGPL', + meson_version : '>= 0.47.0', + default_options : [ + 'warning_level=1', + 'buildtype=debugoptimized' + ] +) + +glib_dep = dependency('gobject-2.0') +pipewire_dep = dependency('libpipewire-0.3') + +subdir('src') diff --git a/src/core.c b/src/core.c new file mode 100644 index 00000000..a47346bc --- /dev/null +++ b/src/core.c @@ -0,0 +1,191 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . +*/ + +#define G_LOG_DOMAIN "wireplumber-core" + +#include "core.h" +#include "loop-source.h" +#include "utils.h" + +#include +#include + +struct _WpCore +{ + GObject parent; + + GMainLoop *loop; + GSource *source; + + struct pw_core *core; + 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; + + 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 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) +{ + WpCore *self = WP_CORE (data); + + g_debug ("remote state changed, old:%s new:%s", + pw_remote_state_as_string (old_state), + 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; + + case PW_REMOTE_STATE_ERROR: + wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_REMOTE_ERROR, + "pipewire remote error: %s", error); + break; + + default: + break; + } +} + +static const struct pw_remote_events remote_events = { + PW_VERSION_REMOTE_EVENTS, + .state_changed = on_state_changed, +}; + +static void +wp_core_init (WpCore * self) +{ + self->loop = g_main_loop_new (NULL, FALSE); + self->source = wp_loop_source_new (); + g_source_attach (self->source, NULL); + + self->core = pw_core_new (wp_loop_source_get_loop (self->source), NULL, 0); + self->remote = pw_remote_new (self->core, NULL, 0); + + pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events, + self); +} + +static void +wp_core_finalize (GObject * obj) +{ + WpCore *self = WP_CORE (obj); + + spa_hook_remove (&self->remote_listener); + + pw_remote_destroy (self->remote); + pw_core_destroy (self->core); + + g_source_destroy (self->source); + g_source_unref (self->source); + g_main_loop_unref (self->loop); + + g_warn_if_fail (self->exit_error == NULL); + g_clear_error (&self->exit_error); +} + +static void +wp_core_class_init (WpCoreClass * klass) +{ + GObjectClass * object_class = (GObjectClass *) klass; + + object_class->finalize = wp_core_finalize; +} + +WpCore * +wp_core_get_instance (void) +{ + static WpCore *instance = NULL; + if (G_UNLIKELY (!instance)) { + instance = g_object_new (wp_core_get_type (), NULL); + } + return instance; +} + +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; +} + +void +wp_core_run (WpCore * self, GError ** error) +{ + g_unix_signal_add (SIGINT, signal_handler, self); + 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_main_loop_run (self->loop); + + if (self->exit_error) { + g_propagate_error (error, self->exit_error); + self->exit_error = NULL; + } +} + +void +wp_core_exit (WpCore * self, GQuark domain, gint code, + const gchar *format, ...) +{ + va_list args; + va_start (args, format); + self->exit_error = g_error_new_valist (domain, code, format, args); + va_end (args); + g_main_loop_quit (self->loop); +} diff --git a/src/core.h b/src/core.h new file mode 100644 index 00000000..ce0b10eb --- /dev/null +++ b/src/core.h @@ -0,0 +1,37 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . +*/ + +#ifndef __WIREPLUMBER_CORE_H__ +#define __WIREPLUMBER_CORE_H__ + +#include + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE (WpCore, wp_core, WP, CORE, GObject); + +WpCore * wp_core_get_instance (void); +void wp_core_run (WpCore * self, GError ** error); + +void wp_core_exit (WpCore * self, GQuark domain, gint code, + const gchar *format, ...); + +G_END_DECLS + +#endif diff --git a/src/loop-source.c b/src/loop-source.c new file mode 100644 index 00000000..b9f4905d --- /dev/null +++ b/src/loop-source.c @@ -0,0 +1,79 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . +*/ + +#define G_LOG_DOMAIN "wireplumber-loop" + +#include "loop-source.h" + +#define WP_LOOP_SOURCE(x) ((WpLoopSource *) x) + +typedef struct _WpLoopSource WpLoopSource; + +struct _WpLoopSource +{ + GSource parent; + struct pw_loop *loop; +}; + +static gboolean +wp_loop_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data) +{ + int result; + + pw_loop_enter (WP_LOOP_SOURCE(s)->loop); + result = pw_loop_iterate (WP_LOOP_SOURCE(s)->loop, 0); + pw_loop_leave (WP_LOOP_SOURCE(s)->loop); + + if (G_UNLIKELY (result < 0)) + g_warning ("pw_loop_iterate failed: %s", spa_strerror (result)); + + return G_SOURCE_CONTINUE; +} + +static void +wp_loop_source_finalize (GSource * s) +{ + pw_loop_destroy (WP_LOOP_SOURCE(s)->loop); +} + +static GSourceFuncs source_funcs = { + NULL, + NULL, + wp_loop_source_dispatch, + wp_loop_source_finalize +}; + +GSource * +wp_loop_source_new (void) +{ + GSource *s = g_source_new (&source_funcs, sizeof (WpLoopSource)); + WP_LOOP_SOURCE(s)->loop = pw_loop_new (NULL); + + g_source_add_unix_fd (s, + pw_loop_get_fd (WP_LOOP_SOURCE(s)->loop), + G_IO_IN | G_IO_ERR | G_IO_HUP); + + return (GSource *) s; +} + +struct pw_loop * +wp_loop_source_get_loop (GSource *s) +{ + return WP_LOOP_SOURCE(s)->loop; +} diff --git a/src/loop-source.h b/src/loop-source.h new file mode 100644 index 00000000..764bc416 --- /dev/null +++ b/src/loop-source.h @@ -0,0 +1,39 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . +*/ + +#ifndef __WIREPLUMBER_LOOP_SOURCE_H__ +#define __WIREPLUMBER_LOOP_SOURCE_H__ + +#include +#include + +G_BEGIN_DECLS + +/* + * A GSource that integrates a pw_loop with GMainLoop. + * Use g_source_ref/unref to manage lifetime. + * The pw_loop is owned by the GSource. + */ + +GSource * wp_loop_source_new (void); +struct pw_loop * wp_loop_source_get_loop (GSource * s); + +G_END_DECLS + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..8590b401 --- /dev/null +++ b/src/main.c @@ -0,0 +1,58 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . +*/ + +#define G_LOG_DOMAIN "wireplumber" + +#include "core.h" +#include "utils.h" + +#include + +static GOptionEntry entries[] = +{ + { NULL } +}; + +gint +main (gint argc, gchar **argv) +{ + g_autoptr (GOptionContext) context = NULL; + g_autoptr (GError) error = NULL; + g_autoptr (WpCore) core = NULL; + gint ret = 0; + + context = g_option_context_new ("- PipeWire Session/Policy Manager"); + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + goto out; + + pw_init (NULL, NULL); + + core = wp_core_get_instance (); + wp_core_run (core, &error); + +out: + if (error) { + ret = error->code; + if (error->domain != WP_DOMAIN_CORE) + ret += 100; + g_message ("exit code %d; %s", ret, error->message); + } + return ret; +} diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 00000000..74de5f83 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,13 @@ +wp_sources = [ + 'core.c', + 'loop-source.c', + 'main.c', + 'utils.c', +] + +executable('wireplumber', + wp_sources, + c_args : [ '-D_GNU_SOURCE', '-DG_LOG_USE_STRUCTURED' ], + install: true, + dependencies : [glib_dep, pipewire_dep], +) diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 00000000..93d47989 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,22 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . +*/ + +#include "utils.h" + +G_DEFINE_QUARK (wireplumber-core, wp_domain_core); diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 00000000..825bcb90 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,40 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . +*/ + +#ifndef __WIREPLUMBER_UTILS_H__ +#define __WIREPLUMBER_UTILS_H__ + +#include + +G_BEGIN_DECLS + +GQuark wp_domain_core_quark (void); +#define WP_DOMAIN_CORE (wp_domain_core_quark ()) + +enum WpCoreCode { + WP_CODE_DISCONNECTED = 0, + WP_CODE_INTERRUPTED, + WP_CODE_OPERATION_FAILED, + WP_CODE_INVALID_ARGUMENT, + WP_CODE_REMOTE_ERROR, +}; + +G_END_DECLS + +#endif