From b8dd8095b721de71d36baeb0a8af7395f25a9cdd Mon Sep 17 00:00:00 2001 From: icedman Date: Wed, 1 Jan 2025 09:38:05 +0800 Subject: [PATCH] add dbus patch --- Makefile | 2 +- dwl.c | 321 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 322 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 578194f..5df8984 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \ -Wfloat-conversion # CFLAGS / LDFLAGS -PKGS = wayland-server xkbcommon libinput $(XLIBS) +PKGS = wayland-server xkbcommon libinput gio-2.0 glib-2.0 $(XLIBS) DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS) diff --git a/dwl.c b/dwl.c index 0eba3e9..c4a07d0 100644 --- a/dwl.c +++ b/dwl.c @@ -68,6 +68,9 @@ #include #endif +#include +#include + #include "util.h" /* macros */ @@ -243,6 +246,58 @@ typedef struct { struct wl_listener destroy; } SessionLock; +typedef struct { + GMainContext *context; + GMainLoop *loop; + guint owner_id; + void* timer; + int interval; + + GDBusConnection *connection; + gchar *property_message; + gint property_count; +} DBusService; + +static DBusService *dbus; + +static GDBusNodeInfo *introspection_data = NULL; + +const gchar *introspection_xml = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +#define SERVICE_NAME "com.dwl.DBus" +#define OBJECT_PATH "/com/dwl/DBus" +#define INTERFACE_NAME "com.dwl.DBus.Interface" + /* function declarations */ static void applybounds(Client *c, struct wlr_box *bbox); static void applyrules(Client *c); @@ -356,6 +411,39 @@ static void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny); static void zoom(const Arg *arg); + +static void dbus_on_name_acquired(GDBusConnection *connection, const gchar *name, + gpointer user_data); +static void dbus_on_name_lost(GDBusConnection *connection, const gchar *name, + gpointer user_data); +static void dbus_on_bus_acquired(GDBusConnection *connection, const gchar *name, + gpointer user_data); +static int dbus_service_update(void *data); +static int dbus_service_init(void); +static void dbus_service_cleanup(void); + +// Method handler +static void dbus_handle_method_call(GDBusConnection *connection, const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data); +// Property handlers +static GVariant *dbus_get_property(GDBusConnection *connection, const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, GError **error, + gpointer user_data); +static gboolean dbus_set_property(GDBusConnection *connection, const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, GVariant *value, + GError **error, gpointer user_data); + +// Emit the MessageChanged signal +static void dbus_emit_client_signal(GDBusConnection *connection, const char* signal, Client *c); + /* variables */ static pid_t child_pid = -1; static int locked; @@ -424,6 +512,13 @@ static struct wlr_xwayland *xwayland; static xcb_atom_t netatom[NetLast]; #endif +// VTable +static const GDBusInterfaceVTable interface_vtable = { + .method_call = dbus_handle_method_call, + .get_property = dbus_get_property, + .set_property = dbus_set_property, +}; + /* configuration, allows nested code to access above variables */ #include "config.h" @@ -670,6 +765,8 @@ checkidleinhibitor(struct wlr_surface *exclude) void cleanup(void) { + dbus_service_cleanup(); + #ifdef XWAYLAND wlr_xwayland_destroy(xwayland); xwayland = NULL; @@ -1404,6 +1501,8 @@ focusclient(Client *c, int lift) /* Activate the new client */ client_activate_surface(client_surface(c), 1); + + dbus_emit_client_signal(dbus->connection, "WindowFocused", c); } void @@ -1737,6 +1836,8 @@ mapnotify(struct wl_listener *listener, void *data) } printstatus(); + dbus_emit_client_signal(dbus->connection, "WindowOpened", c); + unset_fullscreen: m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); wl_list_for_each(w, &clients, link) { @@ -2604,6 +2705,8 @@ setup(void) fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); } #endif + + dbus_service_init(); } void @@ -2755,6 +2858,7 @@ unmapnotify(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ Client *c = wl_container_of(listener, c, unmap); + dbus_emit_client_signal(dbus->connection, "WindowClosed", c); if (c == grabc) { cursor_mode = CurNormal; grabc = NULL; @@ -3151,6 +3255,223 @@ xwaylandready(struct wl_listener *listener, void *data) } #endif +GString* gstring_append_client_json(GString *gstring, Client *c) { + const char *appid, *title; + const char *fmt = "{ \"id\": \"0x%x\", \"title\": \"%s\", \"app_id\": \"%s\" }"; + + GString *gstringTemp = g_string_new(""); + appid = client_get_appid(c); + title = client_get_title(c); + g_string_assign(gstringTemp, title); + g_string_replace(gstringTemp, "\"", "'", 0); + g_string_append_printf(gstring, fmt, c, gstringTemp->str, appid); + + g_string_free(gstringTemp, TRUE); + return gstring; +} + +static void dbus_handle_method_call(GDBusConnection *connection, const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) { + if (g_strcmp0(method_name, "GetWindows") == 0) { + const char *response = "Hello from D-Bus!"; + g_print("HelloWorld method called by %s\n", sender); + + Client *c = NULL; + GString *gstring = g_string_new("["); + wl_list_for_each(c, &clients, link) { + gstring_append_client_json(gstring, c); + if (c->link.next != &clients) { + g_string_append_printf(gstring, ","); + } + } + g_string_append_printf(gstring, "]"); + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(s)", gstring->str)); + g_string_free(gstring, TRUE); + } else if (g_strcmp0(method_name, "FocusWindow") == 0) { + const gchar *window; + g_variant_get(parameters, "(s)", &window); + + uintptr_t address = strtol(window, NULL, 16); // Base 16 for hexadecimal + Client *c = NULL; + wl_list_for_each(c, &clients, link) { + if ((uintptr_t)c == address) { + focusclient(c, true); + } + } + + // g_print("focus %s\n", window); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", window)); + g_free(window); + + } else if (g_strcmp0(method_name, "CloseWindow") == 0) { + const gchar *window; + g_variant_get(parameters, "(s)", &window); + + uintptr_t address = strtol(window, NULL, 16); // Base 16 for hexadecimal + Client *c = NULL; + wl_list_for_each(c, &clients, link) { + if ((uintptr_t)c == address) { + client_send_close(c); + } + } + + // g_print("focus %s\n", window); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", window)); + g_free(window); + + } else if (g_strcmp0(method_name, "QuitApp") == 0) { + const gchar *appid; + g_variant_get(parameters, "(s)", &appid); + + Client *c = NULL; + wl_list_for_each(c, &clients, link) { + const char *c_appid = client_get_appid(c); + g_print("[%s] [%s]", appid, c_appid); + if (g_strcmp0(appid, c_appid) == 0) { + client_send_close(c); + } + } + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", appid)); + g_free(appid); + + } else { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method: %s", method_name); + } +} + +static GVariant *dbus_get_property(GDBusConnection *connection, const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, GError **error, + gpointer user_data) { + if (g_strcmp0(property_name, "Message") == 0) { + return g_variant_new_string(dbus->property_message ? dbus->property_message + : "Default Message"); + } else if (g_strcmp0(property_name, "Count") == 0) { + dbus->property_count = 0; + // Monitor *m = NULL; + // wl_list_for_each(m, &mons, link) { + // dbus->property_count++; + // } + + Client *c = NULL; + wl_list_for_each(c, &clients, link) { + dbus->property_count++; + } + + return g_variant_new_int32(dbus->property_count); + } + return NULL; // Property not found +} + +static gboolean dbus_set_property(GDBusConnection *connection, const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, GVariant *value, + GError **error, gpointer user_data) { + if (g_strcmp0(property_name, "Message") == 0) { + g_free(dbus->property_message); + dbus->property_message = g_strdup(g_variant_get_string(value, NULL)); + return TRUE; + } + g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_PROPERTY_READ_ONLY, + "The 'Count' property is read-only."); + return FALSE; +} + +static void dbus_emit_client_signal(GDBusConnection *connection, const char* signal, Client *c) { + g_print("emit %s\n", signal); + GString *gstring = g_string_new(""); + gstring_append_client_json(gstring, c); + + g_dbus_connection_emit_signal(connection, + NULL, // No sender (broadcast to all clients) + OBJECT_PATH, // Object path + INTERFACE_NAME, // Interface name + signal, // Signal name + g_variant_new("(s)", gstring->str), // Arguments + NULL); // No error + g_string_free(gstring, TRUE); +} + +static void dbus_on_name_acquired(GDBusConnection *connection, const gchar *name, + gpointer user_data) { + g_print("Service name '%s' acquired.\n", SERVICE_NAME); +} + +static void dbus_on_name_lost(GDBusConnection *connection, const gchar *name, + gpointer user_data) { + g_print("Service name '%s' lost.\n", SERVICE_NAME); +} + +static void dbus_on_bus_acquired(GDBusConnection *connection, const gchar *name, + gpointer user_data) { + GError *error = NULL; + + // Register the object + guint registration_id = g_dbus_connection_register_object( + connection, OBJECT_PATH, introspection_data->interfaces[0], + &interface_vtable, + NULL, // user data + NULL, // user data free function + &error); + + if (registration_id == 0) { + g_printerr("Failed to register object: %s\n", error->message); + g_error_free(error); + } + + dbus->connection = connection; + +} + +static int dbus_service_update(void *data) +{ + g_main_context_iteration(dbus->context, FALSE); + wl_event_source_timer_update(dbus->timer, dbus->interval); + return 0; +} + +static int dbus_service_init(void) { + dbus = ecalloc(1, sizeof(DBusService)); + DBusService *d = dbus; + d->interval = 150; + d->property_message = NULL; + d->property_count = 0; + + d->loop = g_main_loop_new(NULL, FALSE); + d->context = g_main_loop_get_context(d->loop); + + dbus->timer = wl_event_loop_add_timer(event_loop, dbus_service_update, dbus); + + // Create introspection data + introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); + + // Acquire the bus name + d->owner_id = g_bus_own_name(G_BUS_TYPE_SESSION, SERVICE_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, dbus_on_bus_acquired, + dbus_on_name_acquired, dbus_on_name_lost, NULL, NULL); + + wl_event_source_timer_update(dbus->timer, dbus->interval); + return 0; +} + +static void dbus_service_cleanup(void) { + g_bus_unown_name(dbus->owner_id); + g_main_loop_unref(dbus->loop); + g_dbus_node_info_unref(introspection_data); + free(dbus); + dbus = NULL; +} + int main(int argc, char *argv[]) { -- 2.47.1