Skip to content
Snippets Groups Projects
object-manager.c 43 KiB
Newer Older
      g_ptr_array_new_with_free_func ((GDestroyNotify) wp_global_unref);
  self->tmp_globals =
      g_ptr_array_new_with_free_func ((GDestroyNotify) wp_global_unref);
  self->objects = g_ptr_array_new_with_free_func (g_object_unref);
  self->object_managers = g_ptr_array_new ();
}
void
wp_registry_clear (WpRegistry *self)
{
  wp_registry_detach (self);
  g_clear_pointer (&self->globals, g_ptr_array_unref);
  g_clear_pointer (&self->tmp_globals, g_ptr_array_unref);
  /* remove all the registered objects
     this will normally also destroy the object managers, eventually, since
     they are normally ref'ed by modules, which are registered objects */
  {
    g_autoptr (GPtrArray) objlist = g_steal_pointer (&self->objects);
    while (objlist->len > 0) {
      g_autoptr (GObject) object = g_ptr_array_steal_index_fast (objlist,
          objlist->len - 1);
      wp_registry_notify_rm_object (self, object);
    }
  }
  /* in case there are any object managers left,
     remove the weak ref on them and let them be... */
  {
    g_autoptr (GPtrArray) object_mgrs;
    GObject *om;
    object_mgrs = g_steal_pointer (&self->object_managers);
    while (object_mgrs->len > 0) {
      om = g_ptr_array_steal_index_fast (object_mgrs, object_mgrs->len - 1);
      g_object_weak_unref (om, object_manager_destroyed, self);
    }
  }
void
wp_registry_attach (WpRegistry *self, struct pw_core *pw_core)
  self->pw_registry = pw_core_get_registry (pw_core,
      PW_VERSION_REGISTRY, 0);
  pw_registry_add_listener (self->pw_registry, &self->listener,
      &registry_events, self);
wp_registry_detach (WpRegistry *self)
  if (self->pw_registry) {
    spa_hook_remove (&self->listener);
    pw_proxy_destroy ((struct pw_proxy *) self->pw_registry);
    self->pw_registry = NULL;
  }
  /* remove pipewire globals */
  GPtrArray *objlist = self->globals;
  while (objlist && objlist->len > 0) {
    g_autoptr (WpGlobal) global = g_ptr_array_steal_index_fast (objlist,
        objlist->len - 1);
    if (global->proxy)
      wp_registry_notify_rm_object (self, global->proxy);
    /* remove the APPEARS_ON_REGISTRY flag to unref the proxy if it is owned
      by the registry; set registry to NULL to avoid further interference */
    global->registry = NULL;
    wp_global_rm_flag (global, WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY);

    /* the registry's ref on global is dropped here; it may still live if
      there is a proxy that owns a ref on it, but global->registry is set
      to NULL, so there is no further interference */
  /* drop tmp globals as well */
  objlist = self->tmp_globals;
  while (objlist && objlist->len > 0) {
    g_autoptr (WpGlobal) global = g_ptr_array_steal_index_fast (objlist,
        objlist->len - 1);
    wp_global_rm_flag (global, WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY);
  }
static void
expose_tmp_globals (WpCore *core, GAsyncResult *res, WpRegistry *self)
  g_autoptr (GError) error = NULL;
  g_autoptr (GPtrArray) tmp_globals = NULL;
  if (!wp_core_sync_finish (core, res, &error))
    wp_warning_object (core, "core sync error: %s", error->message);
  /* in case the registry was cleared in the meantime... */
  if (G_UNLIKELY (!self->tmp_globals))
    return;
  /* steal the tmp_globals list and replace it with an empty one */
  tmp_globals = self->tmp_globals;
  self->tmp_globals =
      g_ptr_array_new_with_free_func ((GDestroyNotify) wp_global_unref);
  wp_debug_object (core, "exposing %u new globals", tmp_globals->len);
  /* traverse in the order that the globals appeared on the registry */
  for (guint i = 0; i < tmp_globals->len; i++) {
    WpGlobal *g = g_ptr_array_index (tmp_globals, i);
    /* if global was already removed, drop it */
    if (g->flags == 0)
      continue;
    /* set the registry, so that wp_global_rm_flag() can work full-scale */
    g->registry = self;
    /* store it in the globals list */
    if (self->globals->len <= g->id)
      g_ptr_array_set_size (self->globals, g->id + 1);
    g_ptr_array_index (self->globals, g->id) = wp_global_ref (g);
  /* notify object managers */
  for (guint i = 0; i < self->object_managers->len; i++) {
    WpObjectManager *om = g_ptr_array_index (self->object_managers, i);

    for (guint i = 0; i < tmp_globals->len; i++) {
      WpGlobal *g = g_ptr_array_index (tmp_globals, i);
      wp_object_manager_add_global (om, g);
    }
    wp_object_manager_maybe_objects_changed (om);
/*
 * wp_registry_prepare_new_global:
 * @new_global: (out) (transfer full) (optional): the new global
 *
 * This is normally called up to 2 times in the same sync cycle:
 * one from registry_global(), another from the proxy bound event
 * Unfortunately the order in which those 2 events happen is specific
 * to the implementation of the object, which is why this is implemented
 * with a temporary globals list that get exposed later to the object managers
 */
wp_registry_prepare_new_global (WpRegistry * self, guint32 id,
    guint32 permissions, guint32 flag, GType type,
    WpProxy *proxy, const struct spa_dict *props,
    WpGlobal ** new_global)
{
  g_autoptr (WpGlobal) global = NULL;
  WpCore *core = wp_registry_get_core (self);
  g_return_if_fail (flag != 0);
  g_return_if_fail (self->globals->len <= id ||
      g_ptr_array_index (self->globals, id) == NULL);
  for (guint i = 0; i < self->tmp_globals->len; i++) {
    WpGlobal *g = g_ptr_array_index (self->tmp_globals, i);
    if (g->id == id) {
      global = wp_global_ref (g);
  wp_debug_object (core, "%s WpGlobal:%u type:%s proxy:%p",
      global ? "reuse" : "new", id, g_type_name (type),
      (global && global->proxy) ? global->proxy : proxy);

  if (!global) {
    global = g_rc_box_new0 (WpGlobal);
    global->flags = flag;
    global->id = id;
    global->type = type;
    global->permissions = permissions;
    global->properties = props ?
        wp_properties_new_copy_dict (props) : wp_properties_new_empty ();
    global->proxy = proxy;
    g_ptr_array_add (self->tmp_globals, wp_global_ref (global));

    /* schedule exposing when adding the first global */
    if (self->tmp_globals->len == 1) {
      wp_core_sync (core, NULL, (GAsyncReadyCallback) expose_tmp_globals, self);
  } else {
    /* store the most permissive permissions */
    if (permissions > global->permissions)
      global->permissions = permissions;

    global->flags |= flag;

    /* store the most deep type (i.e. WpImplNode instead of WpNode),
       so that object-manager interests can work more accurately
       if the interest is on a specific subclass */
    if (g_type_depth (type) > g_type_depth (global->type))
      global->type = type;

    if (proxy) {

    if (props)
      wp_properties_update_from_dict (global->properties, props);
  if (new_global)
    *new_global = g_steal_pointer (&global);
 * wp_registry_find_object:
 * @reg: the registry
 * @func: (scope call): a function that takes the object being searched
 *   as the first argument and @data as the second. it should return TRUE if
 *   the object is found or FALSE otherwise
 * @data: the second argument to @func
 *
 * Finds a registered object
 *
 * Returns: (transfer full) (type GObject *) (nullable): the registered object
 *   or NULL if not found
 */
gpointer
wp_registry_find_object (WpRegistry *reg, GEqualFunc func, gconstpointer data)
  /* prevent bad things when called from within wp_registry_clear() */
  if (G_UNLIKELY (!reg->objects))
    return NULL;

  for (i = 0; i < reg->objects->len; i++) {
    object = g_ptr_array_index (reg->objects, i);
    if (func (object, data))
      return g_object_ref (object);
 * wp_registry_register_object:
 * @reg: the registry
 * @obj: (transfer full) (type GObject*): the object to register
 *
 * Registers @obj with the core, making it appear on #WpObjectManager
 * instances as well. The core will also maintain a ref to that object
 * until it is removed.
 */
void
wp_registry_register_object (WpRegistry *reg, gpointer obj)
  g_return_if_fail (G_IS_OBJECT (obj));

  /* prevent bad things when called from within wp_registry_clear() */
  if (G_UNLIKELY (!reg->objects)) {
    g_object_unref (obj);
    return;
  g_ptr_array_add (reg->objects, obj);

  /* notify object managers */
  wp_registry_notify_add_object (reg, obj);
 * wp_registry_remove_object:
 * @reg: the registry
 * @obj: (transfer none) (type GObject*): a pointer to the object to remove
 *
 * Detaches and unrefs the specified object from this core
 */
void
wp_registry_remove_object (WpRegistry *reg, gpointer obj)
  g_return_if_fail (G_IS_OBJECT (obj));
  /* prevent bad things when called from within wp_registry_clear() */
  if (G_UNLIKELY (!reg->objects))
  /* notify object managers */
  wp_registry_notify_rm_object (reg, obj);

  g_ptr_array_remove_fast (reg->objects, obj);
 * wp_core_install_object_manager:
 * @self: the core
 * @om: (transfer none): a #WpObjectManager
 *
 * Installs the object manager on this core, activating its internal management
 * engine. This will immediately emit signals about objects added on @om
 * if objects that the @om is interested in were in existence already.
 */
void
wp_core_install_object_manager (WpCore * self, WpObjectManager * om)
  g_return_if_fail (WP_IS_CORE (self));
  g_return_if_fail (WP_IS_OBJECT_MANAGER (om));

  reg = wp_core_get_registry (self);

  g_object_weak_ref (G_OBJECT (om), object_manager_destroyed, reg);
  g_ptr_array_add (reg->object_managers, om);

  /* add pre-existing objects to the object manager,
     in case it's interested in them */
  for (i = 0; i < reg->globals->len; i++) {
    WpGlobal *g = g_ptr_array_index (reg->globals, i);
    /* check if null because the globals array can have gaps */
    if (g)
      wp_object_manager_add_global (om, g);
  }
  for (i = 0; i < reg->objects->len; i++) {
    GObject *o = g_ptr_array_index (reg->objects, i);
    wp_object_manager_add_object (om, o);
/* WpGlobal */

G_DEFINE_BOXED_TYPE (WpGlobal, wp_global, wp_global_ref, wp_global_unref)

void
wp_global_rm_flag (WpGlobal *global, guint rm_flag)
  WpRegistry *reg = global->registry;
  /* no flag to remove */
  if (!(global->flags & rm_flag))
    return;
  /* global was owned by the proxy; by removing the flag, we clear out
     also the proxy pointer, which is presumably no longer valid and we
     notify all listeners that the proxy is gone */
  if (rm_flag == WP_GLOBAL_FLAG_OWNED_BY_PROXY) {
    global->flags &= ~WP_GLOBAL_FLAG_OWNED_BY_PROXY;
    if (reg)
      wp_registry_notify_rm_object (reg, global->proxy);
    global->proxy = NULL;
  }
  /* registry removed the global */
  else if (rm_flag == WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY) {
    global->flags &= ~WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY;
    /* if there is a proxy and it's not owning the global, destroy it */
    if (global->flags == 0 && global->proxy) {
      if (reg)
        wp_registry_notify_rm_object (reg, global->proxy);
      wp_proxy_destroy (global->proxy);
      g_clear_object (&global->proxy);
    }
  /* drop the registry's ref on global when it has no flags anymore */
  if (global->flags == 0 && reg) {
    g_clear_pointer (&g_ptr_array_index (reg->globals, global->id), wp_global_unref);
struct pw_proxy *
wp_global_bind (WpGlobal * global)
  g_return_val_if_fail (global->proxy, NULL);
  g_return_val_if_fail (global->registry, NULL);

  WpProxyClass *klass = WP_PROXY_GET_CLASS (global->proxy);
  return pw_registry_bind (global->registry->pw_registry, global->id,
      klass->pw_iface_type, klass->pw_iface_version, 0);