Обработчик сигнала не вызывается

Я создал для своего класса device_manager сигнал added

enum {
    SIGNAL_ADDED,
    LAST_SIGNAL
};

static guint device_manager_signals[LAST_SIGNAL] = { 0 };

static void
device_manager_class_init(DeviceManagerClass * class){
    
    device_manager_signals[SIGNAL_ADDED] =
        g_signal_new(
            "added",
            G_TYPE_FROM_CLASS(class),
            G_SIGNAL_RUN_FIRST,
            0,
            NULL, NULL,
            g_cclosure_user_marshal_VOID__STRING_STRING_BOOLEAN,
            G_TYPE_NONE,
            3,
            G_TYPE_STRING,
            G_TYPE_STRING,
            G_TYPE_BOOLEAN 
        );
}

Маршалер g_cclosure_user_marshal_VOID__STRING_STRING_BOOLEAN сгенерировал при помощи glib-genmarshal. У меня есть vapi обвязка:

[CCode (cheader_filename = "device_manager.h")]
public class DeviceManager : GLib.Object {

    public signal void added (string name, string nick, bool is_default);
    
    [CCode (cname = "device_manager_new")]
    public DeviceManager();

    [CCode (cname = "device_manager_run")]
    public void run();
}

в vala коде я создаю объект и привязываю обработчик

var deviceManager = new DeviceManager();
deviceManager.added.connect(on_added);

сам обработчик выглядит так

private void on_added(string name, string nick, bool is_default){
    print("added!\n");
}

но когда я посылаю сигнал, обработчик не срабатывает.

static void
on_addet(
    WpObjectManager * self,
    gpointer object,
    gpointer user_data
){
    DeviceManager *manager = user_data;
    g_autoptr (WpPlugin) def_nodes_api = NULL;
    def_nodes_api = wp_plugin_find (manager->core, "default-nodes-api");
    guint32 default_node_id = -1;
    guint32 id = wp_proxy_get_bound_id (WP_PROXY (object));
    gboolean is_default = FALSE;
    
    const gchar *class = wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (object), "media.class");
    const gchar *name = wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (object), "node.name");
    const gchar *nick = wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (object), "node.description");

    if(g_strcmp0 (class, "Audio/Sink") == 0){
        g_signal_emit_by_name (def_nodes_api, "get-default-node", "Audio/Sink", &default_node_id);
        is_default = (default_node_id == id);
        
        g_signal_emit_by_name(manager, "added", name, nick, is_default);
    }
}

сигнал точно посылается, код точно доходит до этой строки. Но обработчик в vala ни как не реагирует. Не происходит ошибки сегментации, или какой-либо другой ошибки, просто ноль реакции. Я немного в отчаянии, не могу понять где искать ошибку, я пробовал исключить маршалер, послать сигнал без параметров, но он так же не реагирует. Может я написал неправильную обвязку? Вот полный код device_manager.c на всякий случай

#include <wp/wp.h>
#include <glib.h>
#include <stdio.h>
#include <pipewire/pipewire.h>
#include <pipewire/keys.h>
#include "device_manager.h"
#include "node_marshal.h"

struct _DeviceManager {
    GMainContext *context;
    GObject parent_instance;
    GMainLoop *loop;
    WpCore *core;
    WpObjectManager *object_manager;
    guint pending_plugins;
    WpSettings * settings;
};

struct _DeviceManagerClass
{
    GObjectClass parent_class;
    void (*run) (DeviceManager*);
};

G_DEFINE_TYPE (DeviceManager, device_manager, G_TYPE_OBJECT);

static void device_manager_clear(DeviceManager * self){
    g_clear_object (&self->object_manager);
    g_clear_object (&self->core);
    g_clear_pointer (&self->loop, g_main_loop_unref);
}

G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (DeviceManager, device_manager_clear);

enum {
    SIGNAL_ADDED,
    LAST_SIGNAL
};

static guint device_manager_signals[LAST_SIGNAL] = { 0 };

static void
device_manager_class_init(DeviceManagerClass * class){
    
    device_manager_signals[SIGNAL_ADDED] =
        g_signal_new(
            "added",
            G_TYPE_FROM_CLASS(class),
            G_SIGNAL_RUN_FIRST,
            0,
            NULL, NULL,
            g_cclosure_user_marshal_VOID__STRING_STRING_BOOLEAN,
            G_TYPE_NONE,
            3,
            G_TYPE_STRING,
            G_TYPE_STRING,
            G_TYPE_BOOLEAN 
        );
}

static void
device_manager_init(DeviceManager * self){
    /**/
}

DeviceManager *
device_manager_new(){
    return g_object_new(TYPE_DEVICE_MANAGER, NULL);
}

WpObjectManager *
create_object_manager(){
    WpObjectManager * manager = wp_object_manager_new();

    wp_object_manager_add_interest(
        manager,
        WP_TYPE_METADATA,
        NULL
    );

    wp_object_manager_request_object_features (
        manager,
        WP_TYPE_METADATA,
        WP_OBJECT_FEATURES_ALL
    );

    wp_object_manager_add_interest(
        manager,
        WP_TYPE_NODE,
        WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "#s", "Audio/Sink*",
        NULL
    );

    wp_object_manager_add_interest(
        manager,
        WP_TYPE_NODE,
        WP_CONSTRAINT_TYPE_PW_PROPERTY, PW_KEY_MEDIA_CLASS, "#s", "Audio/Source*",
        NULL
    );

    return manager;
}

static void on_settings_activated (WpSettings *s, GAsyncResult *res, DeviceManager *deviceManager)
{
    GError *error = NULL;

    if (!wp_object_activate_finish (WP_OBJECT (s), res, &error)) {
        //error code
        g_main_loop_quit (deviceManager->loop);
        return;
    }

    wp_core_register_object (deviceManager->core, g_object_ref (s));
}

static void on_plugin_loaded (WpCore * core, GAsyncResult * res, DeviceManager *deviceManager)
{
    GError *error = NULL;
    if (!wp_core_load_component_finish (core, res, &error)) {
        fprintf (stderr, "%s\n", error->message);
        g_main_loop_quit (deviceManager->loop);
        return;
    }

    if (--deviceManager->pending_plugins == 0) {
        g_autoptr (WpPlugin) mixer_api = wp_plugin_find (core, "mixer-api");
        g_object_set (mixer_api, "scale", 1 /* cubic */, NULL);
        wp_core_install_object_manager (deviceManager->core, deviceManager->object_manager);
    }
}

void load_plugins(DeviceManager * deviceManager){
    deviceManager->pending_plugins++;
    wp_core_load_component (
        deviceManager->core,
        "libwireplumber-module-default-nodes-api",
        "module",
        NULL,
        NULL,
        NULL,
        (GAsyncReadyCallback) on_plugin_loaded,
        deviceManager
    );
    deviceManager->pending_plugins++;
    wp_core_load_component (
        deviceManager->core,
        "libwireplumber-module-mixer-api",
        "module",
        NULL,
        NULL,
        NULL,
        (GAsyncReadyCallback) on_plugin_loaded,
        deviceManager
    );
}

static void
on_addet(
    WpObjectManager * self,
    gpointer object,
    gpointer user_data
){
    DeviceManager *manager = user_data;
    g_autoptr (WpPlugin) def_nodes_api = NULL;
    def_nodes_api = wp_plugin_find (manager->core, "default-nodes-api");
    guint32 default_node_id = -1;
    guint32 id = wp_proxy_get_bound_id (WP_PROXY (object));
    gboolean is_default = FALSE;
    
    const gchar *class = wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (object), "media.class");
    const gchar *name = wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (object), "node.name");
    const gchar *nick = wp_pipewire_object_get_property (WP_PIPEWIRE_OBJECT (object), "node.description");

    if(g_strcmp0 (class, "Audio/Sink") == 0){
        g_signal_emit_by_name (def_nodes_api, "get-default-node", "Audio/Sink", &default_node_id);
        is_default = (default_node_id == id);
        
        g_signal_emit_by_name(manager, "added", name, nick, is_default);
    }
}

static void on_removed(WpObjectManager * self, WpObject * object, gpointer user_data){
    /**/
}

static void
device_manager_connect_signals(DeviceManager *self) {
    g_signal_connect_swapped (
        self->core,
        "disconnected",
        (GCallback) g_main_loop_quit,
        self->loop
    );
    g_signal_connect(self->object_manager, "object-added", (GCallback)on_addet, self);
    g_signal_connect(self->object_manager, "object-removed", (GCallback)on_removed, self);
}


void
device_manager_run(DeviceManager * self){
    wp_init(WP_INIT_ALL);

    self->context = g_main_context_default();
    self->loop = g_main_loop_new(self->context, FALSE);
    self->core = wp_core_new(self->context, NULL, NULL);
    self->object_manager = create_object_manager();

    self->settings = wp_settings_new (self->core, NULL);
    wp_object_activate (
        WP_OBJECT (self->settings),
        WP_OBJECT_FEATURES_ALL,
        NULL,
        (GAsyncReadyCallback)on_settings_activated,
        self
    );

    load_plugins(self);
    
    /* connect */
    if (!wp_core_connect (self->core)) {
        fprintf (stderr, "Could not connect to PipeWire\n");
        
    }

    device_manager_connect_signals(self);
}

device_manager.h

#ifndef _DEVICE_MANAGER_H_
#define _DEVICE_MANAGER_H_
#include <glib-object.h>

G_BEGIN_DECLS

#define TYPE_DEVICE_MANAGER device_manager_get_type()
G_DECLARE_FINAL_TYPE (DeviceManager, device_manager, DEVICE, MANAGER, GObject) 

DeviceManager* 
device_manager_new();

void 
device_manager_run(DeviceManager* self);

G_END_DECLS

#endif /* _DEVICE_MANAGER_H_ */

Ответы (1 шт):

Автор решения: Николай Михайлов

Причина крылась тут:

struct _DeviceManager {
    GMainContext *context;
    GObject parent_instance; /* <- Ошибка */
    GMainLoop *loop;
    WpCore *core;
    WpObjectManager *object_manager;
    guint pending_plugins;
    WpSettings * settings;
};

parent_instance должен быть всегда указан на первом месте в структуре.

Рабочий вариант:

struct _DeviceManager {
    GObject parent_instance;
    GMainContext *context;
    GMainLoop *loop;
    WpCore *core;
    WpObjectManager *object_manager;
    guint pending_plugins;
    WpSettings * settings;
};

Из-за того, что я определил структуру неправильно, объект не возвращал свой тип, поэтому сигнал не срабатывал.

Вы можете проверить это при помощи G_OBJECT_TYPE_NAME(ваш объект), если возвращает NULL, значит возможно ошибка в структуре.

→ Ссылка