dwl-patches/patches/dbus/dbus.patch
2025-01-01 09:46:53 +08:00

422 lines
14 KiB
Diff

From b8dd8095b721de71d36baeb0a8af7395f25a9cdd Mon Sep 17 00:00:00 2001
From: icedman <m4rvin2005@gmail.com>
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 <xcb/xcb_icccm.h>
#endif
+#include <gio/gio.h>
+#include <glib.h>
+
#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 =
+ "<node>"
+ " <interface name='com.dwl.DBus.Interface'>"
+ " <method name='FocusWindow'>"
+ " <arg type='s' name='window' direction='in'/>"
+ " <arg type='s' name='window' direction='out'/>"
+ " </method>"
+ " <method name='CloseWindow'>"
+ " <arg type='s' name='window' direction='in'/>"
+ " <arg type='s' name='window' direction='out'/>"
+ " </method>"
+ " <method name='QuitApp'>"
+ " <arg type='s' name='appid' direction='in'/>"
+ " <arg type='s' name='appid' direction='out'/>"
+ " </method>"
+ " <method name='GetWindows'>"
+ " <arg type='s' name='windows' direction='out'/>"
+ " </method>"
+ " <property name='Message' type='s' access='readwrite'/>"
+ " <property name='Count' type='i' access='read'/>"
+ " <signal name='WindowFocused'>"
+ " <arg type='s' name='window' direction='out'/>"
+ " </signal>"
+ " <signal name='WindowOpened'>"
+ " <arg type='s' name='window' direction='out'/>"
+ " </signal>"
+ " <signal name='WindowClosed'>"
+ " <arg type='s' name='window' direction='out'/>"
+ " </signal>"
+ " </interface>"
+ "</node>";
+
+#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