diff --git a/spa/include/spa/support/loop.h b/spa/include/spa/support/loop.h index cf0e03da3..520a465d5 100644 --- a/spa/include/spa/support/loop.h +++ b/spa/include/spa/support/loop.h @@ -189,6 +189,26 @@ SPA_API_LOOP void spa_loop_control_hook_after(struct spa_hook_list *l) /** * Control an event loop + * + * The event loop control function provide API to run the event loop. + * + * The below (pseudo)code is a minimal example outlining the use of the loop + * control: + * \code{.c} + * spa_loop_control_enter(loop); + * while (running) { + * spa_loop_control_iterate(loop, -1); + * } + * spa_loop_control_leave(loop); + * \endcode + * + * It is also possible to add the loop to an existing event loop by using the + * spa_loop_control_get_fd() call. This fd will become readable when activity + * has been detected on the sources in the loop. spa_loop_control_iterate() with + * a 0 timeout should be called to process the pending sources. + * + * spa_loop_control_enter() and spa_loop_control_leave() should be called once + * from the thread that will run the iterate() function. */ struct spa_loop_control_methods { /* the version of this structure. This can be used to expand this @@ -196,10 +216,19 @@ struct spa_loop_control_methods { #define SPA_VERSION_LOOP_CONTROL_METHODS 1 uint32_t version; + /** get the loop fd + * \param object the control to query + * + * Get the fd of this loop control. This fd will be readable when a + * source in the loop has activity. The user should call iterate() + * with a 0 timeout to schedule one iteration of the loop and dispatch + * the sources. + * \return the fd of the loop + */ int (*get_fd) (void *object); /** Add a hook - * \param ctrl the control to change + * \param object the control to change * \param hooks the hooks to add * * Adds hooks to the loop controlled by \a ctrl. @@ -210,18 +239,19 @@ struct spa_loop_control_methods { void *data); /** Enter a loop - * \param ctrl the control + * \param object the control * - * Start an iteration of the loop. This function should be called - * before calling iterate and is typically used to capture the thread - * that this loop will run in. + * This function should be called before calling iterate and is + * typically used to capture the thread that this loop will run in. + * It should ideally be called once from the thread that will run + * the loop. */ void (*enter) (void *object); /** Leave a loop - * \param ctrl the control + * \param object the control * - * Ends the iteration of a loop. This should be called after calling - * iterate. + * It should ideally be called once after calling iterate when the loop + * will no longer be iterated from the thread that called enter(). */ void (*leave) (void *object); diff --git a/src/examples/gmain.c b/src/examples/gmain.c new file mode 100644 index 000000000..6a13b03b6 --- /dev/null +++ b/src/examples/gmain.c @@ -0,0 +1,101 @@ + +#include + +#include +#include + +typedef struct _PipeWireSource +{ + GSource base; + + struct pw_loop *loop; +} PipeWireSource; + +static gboolean +pipewire_loop_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + PipeWireSource *s = (PipeWireSource *) source; + int result; + + result = pw_loop_iterate (s->loop, 0); + if (result < 0) + g_warning ("pipewire_loop_iterate failed: %s", spa_strerror (result)); + + return TRUE; +} + +static GSourceFuncs pipewire_source_funcs = +{ + .dispatch = pipewire_loop_source_dispatch, +}; + +static void registry_event_global(void *data, uint32_t id, + uint32_t permissions, const char *type, uint32_t version, + const struct spa_dict *props) +{ + printf("object: id:%u type:%s/%d\n", id, type, version); +} + +static const struct pw_registry_events registry_events = { + PW_VERSION_REGISTRY_EVENTS, + .global = registry_event_global, +}; + +int main(int argc, char *argv[]) +{ + GMainLoop *main_loop; + PipeWireSource *source; + struct pw_loop *loop; + struct pw_context *context; + struct pw_core *core; + struct pw_registry *registry; + struct spa_hook registry_listener; + + main_loop = g_main_loop_new (NULL, FALSE); + + pw_init(&argc, &argv); + + loop = pw_loop_new(NULL /* properties */); + /* wrap */ + source = (PipeWireSource *) g_source_new (&pipewire_source_funcs, + sizeof (PipeWireSource)); + source->loop = loop; + g_source_add_unix_fd (&source->base, + pw_loop_get_fd (loop), + G_IO_IN | G_IO_ERR); + g_source_attach (&source->base, NULL); + g_source_unref (&source->base); + + context = pw_context_new(loop, + NULL /* properties */, + 0 /* user_data size */); + + core = pw_context_connect(context, + NULL /* properties */, + 0 /* user_data size */); + + registry = pw_core_get_registry(core, PW_VERSION_REGISTRY, + 0 /* user_data size */); + + spa_zero(registry_listener); + pw_registry_add_listener(registry, ®istry_listener, + ®istry_events, NULL); + + /* enter and leave must be called from the same thread that runs + * the mainloop */ + pw_loop_enter(loop); + g_main_loop_run(main_loop); + pw_loop_leave(loop); + + pw_proxy_destroy((struct pw_proxy*)registry); + pw_core_disconnect(core); + pw_context_destroy(context); + pw_loop_destroy(loop); + + g_main_loop_unref(main_loop); + + return 0; +} +/* [code] */ diff --git a/src/examples/meson.build b/src/examples/meson.build index a47e24618..7d45a34f9 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -24,6 +24,7 @@ examples = [ 'export-spa-device', 'bluez-session', 'local-v4l2', + 'gmain', ] if not get_option('examples').allowed() @@ -39,6 +40,7 @@ examples_extra_deps = { 'video-dsp-play': [sdl_dep], 'local-v4l2': [sdl_dep], 'export-sink': [sdl_dep], + 'gmain': [glib2_dep], } foreach c : examples