From 52e8199496a9e4cd1b5e686d07a38e8708364e47 Mon Sep 17 00:00:00 2001 From: The Darsh <129121284+its-darsh@users.noreply.github.com> Date: Wed, 6 Nov 2024 22:47:09 +0200 Subject: [PATCH 1/4] fix: formatting --- .clang-format | 20 +++++ libglace/libglace-client-private.c | 110 ++++++++++------------------ libglace/libglace-client-private.h | 44 +++++------ libglace/libglace-client.c | 9 --- libglace/libglace-client.h | 34 ++++----- libglace/libglace-manager-private.c | 22 ++---- libglace/libglace-manager-private.h | 17 +++-- libglace/libglace-manager.h | 4 +- 8 files changed, 116 insertions(+), 144 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..183e13e --- /dev/null +++ b/.clang-format @@ -0,0 +1,20 @@ +BasedOnStyle: Google +IndentWidth: 4 +UseTab: Never + +BreakBeforeBraces: Attach +BinPackParameters: false +BinPackArguments: false +ColumnLimit: 0 + +AlignAfterOpenBracket: BlockIndent + +AllowShortIfStatementsOnASingleLine: false +IndentCaseLabels: false +AccessModifierOffset: -4 +NamespaceIndentation: All + +# Override Google style defaults +DerivePointerAlignment: false +PointerAlignment: Left + diff --git a/libglace/libglace-client-private.c b/libglace/libglace-client-private.c index 22112df..963930b 100644 --- a/libglace/libglace-client-private.c +++ b/libglace/libglace-client-private.c @@ -1,7 +1,9 @@ #include "libglace-private.h" static guint glace_client_signals[GLACE_CLIENT_N_SIGNALS] = {0}; -static GParamSpec* glace_client_properties[GLACE_CLIENT_N_PROPERTIES] = {NULL,}; +static GParamSpec* glace_client_properties[GLACE_CLIENT_N_PROPERTIES] = { + NULL, +}; static void glace_client_signal_changed_emit(GlaceClient* self) { g_signal_emit( @@ -19,7 +21,6 @@ static void glace_client_signal_close_emit(GlaceClient* self) { ); } - static void glace_client_get_property( GObject* object, guint prop_id, @@ -29,37 +30,36 @@ static void glace_client_get_property( GlaceClient* self = GLACE_CLIENT(object); switch (prop_id) { - case GLACE_CLIENT_PROPERTY_ID: - g_value_set_uint(value, (guint)self->priv->id); - break; - case GLACE_CLIENT_PROPERTY_APP_ID: - g_value_set_string(value, CLIENT_GET_CURRENT_PROP(self, app_id)); - break; - case GLACE_CLIENT_PROPERTY_TITLE: - g_value_set_string(value, CLIENT_GET_CURRENT_PROP(self, title)); - break; - case GLACE_CLIENT_PROPERTY_MAXIMIZED: - g_value_set_boolean(value, CLIENT_GET_CURRENT_PROP(self, maximized)); - break; - case GLACE_CLIENT_PROPERTY_MINIMIZED: - g_value_set_boolean(value, CLIENT_GET_CURRENT_PROP(self, minimized)); - break; - case GLACE_CLIENT_PROPERTY_ACTIVATED: - g_value_set_boolean(value, CLIENT_GET_CURRENT_PROP(self, activated)); - break; - case GLACE_CLIENT_PROPERTY_FULLSCREEN: - g_value_set_boolean(value, CLIENT_GET_CURRENT_PROP(self, fullscreen)); - break; - case GLACE_CLIENT_PROPERTY_CLOSED: - g_value_set_boolean(value, self->priv->closed); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; + case GLACE_CLIENT_PROPERTY_ID: + g_value_set_uint(value, (guint)self->priv->id); + break; + case GLACE_CLIENT_PROPERTY_APP_ID: + g_value_set_string(value, CLIENT_GET_CURRENT_PROP(self, app_id)); + break; + case GLACE_CLIENT_PROPERTY_TITLE: + g_value_set_string(value, CLIENT_GET_CURRENT_PROP(self, title)); + break; + case GLACE_CLIENT_PROPERTY_MAXIMIZED: + g_value_set_boolean(value, CLIENT_GET_CURRENT_PROP(self, maximized)); + break; + case GLACE_CLIENT_PROPERTY_MINIMIZED: + g_value_set_boolean(value, CLIENT_GET_CURRENT_PROP(self, minimized)); + break; + case GLACE_CLIENT_PROPERTY_ACTIVATED: + g_value_set_boolean(value, CLIENT_GET_CURRENT_PROP(self, activated)); + break; + case GLACE_CLIENT_PROPERTY_FULLSCREEN: + g_value_set_boolean(value, CLIENT_GET_CURRENT_PROP(self, fullscreen)); + break; + case GLACE_CLIENT_PROPERTY_CLOSED: + g_value_set_boolean(value, self->priv->closed); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; } } - // client event handlers static void on_toplevel_handle_title( void* data, @@ -71,7 +71,6 @@ static void on_toplevel_handle_title( CLIENT_SET_PENDING_PROP(self, title, (gchar*)(strdup(title))); } - static void on_toplevel_handle_app_id( void* data, struct zwlr_foreign_toplevel_handle_v1* wlr_handle, @@ -89,14 +88,12 @@ static void on_toplevel_handle_output_enter( struct wl_output* output ) {} - static void on_toplevel_handle_output_leave( void* data, struct zwlr_foreign_toplevel_handle_v1* wlr_handle, struct wl_output* output ) {} - static void on_toplevel_handle_state( void* data, struct zwlr_foreign_toplevel_handle_v1* wlr_handle, @@ -108,7 +105,7 @@ static void on_toplevel_handle_state( enum zwlr_foreign_toplevel_handle_v1_state* state; wl_array_for_each(state, states) { - switch (*state) { + switch (*state) { CLIENT_SET_STATE_FOR_CASE(self, state, MAXIMIZED, maximized); CLIENT_SET_STATE_FOR_CASE(self, state, MINIMIZED, minimized); CLIENT_SET_STATE_FOR_CASE(self, state, ACTIVATED, activated); @@ -117,7 +114,6 @@ static void on_toplevel_handle_state( } } - static void on_toplevel_handle_done( void* data, struct zwlr_foreign_toplevel_handle_v1* wlr_handle @@ -136,7 +132,6 @@ static void on_toplevel_handle_done( free(CLIENT_GET_CURRENT_PROP(self, app_id)); } - if (CLIENT_GET_PENDING_PROP(self, title)) { CLIENT_BRIDGE_PROPS(self, title, TITLE); CLIENT_SET_PENDING_PROP(self, title, NULL); @@ -156,7 +151,6 @@ static void on_toplevel_handle_done( glace_client_signal_changed_emit(self); } - static void on_toplevel_handle_closed( void* data, struct zwlr_foreign_toplevel_handle_v1* wlr_handle @@ -168,14 +162,12 @@ static void on_toplevel_handle_closed( glace_client_signal_close_emit(self); } - static void on_toplevel_handle_parent( void* data, struct zwlr_foreign_toplevel_handle_v1* wlr_handle, struct zwlr_foreign_toplevel_handle_v1* parent ) {} - static struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_listener = { .title = &on_toplevel_handle_title, .app_id = &on_toplevel_handle_app_id, @@ -187,7 +179,6 @@ static struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_listener .parent = &on_toplevel_handle_parent }; - static void glace_client_class_init(GlaceClientClass* klass) { GObjectClass* parent_class = G_OBJECT_CLASS(klass); @@ -231,9 +222,7 @@ static void glace_client_class_init(GlaceClientClass* klass) { 0 ); - glace_client_properties[ - GLACE_CLIENT_PROPERTY_ID - ] = g_param_spec_uint( + glace_client_properties[GLACE_CLIENT_PROPERTY_ID] = g_param_spec_uint( "id", "id", "the id of the client", @@ -243,9 +232,7 @@ static void glace_client_class_init(GlaceClientClass* klass) { G_PARAM_READABLE ); - glace_client_properties[ - GLACE_CLIENT_PROPERTY_APP_ID - ] = g_param_spec_string( + glace_client_properties[GLACE_CLIENT_PROPERTY_APP_ID] = g_param_spec_string( "app-id", "class", "the application id of the client (class name under X11)", @@ -253,9 +240,7 @@ static void glace_client_class_init(GlaceClientClass* klass) { G_PARAM_READABLE ); - glace_client_properties[ - GLACE_CLIENT_PROPERTY_TITLE - ] = g_param_spec_string( + glace_client_properties[GLACE_CLIENT_PROPERTY_TITLE] = g_param_spec_string( "title", "title", "the current title of the client", @@ -264,9 +249,7 @@ static void glace_client_class_init(GlaceClientClass* klass) { ); // state properties - glace_client_properties[ - GLACE_CLIENT_PROPERTY_MAXIMIZED - ] = g_param_spec_boolean( + glace_client_properties[GLACE_CLIENT_PROPERTY_MAXIMIZED] = g_param_spec_boolean( "maximized", "maximized", "whether this client is currently maximized or not", @@ -274,9 +257,7 @@ static void glace_client_class_init(GlaceClientClass* klass) { G_PARAM_READABLE ); - glace_client_properties[ - GLACE_CLIENT_PROPERTY_MINIMIZED - ] = g_param_spec_boolean( + glace_client_properties[GLACE_CLIENT_PROPERTY_MINIMIZED] = g_param_spec_boolean( "minimized", "minimized", "whether this client is currently minimized or not", @@ -284,9 +265,7 @@ static void glace_client_class_init(GlaceClientClass* klass) { G_PARAM_READABLE ); - glace_client_properties[ - GLACE_CLIENT_PROPERTY_ACTIVATED - ] = g_param_spec_boolean( + glace_client_properties[GLACE_CLIENT_PROPERTY_ACTIVATED] = g_param_spec_boolean( "activated", "focused", "whether this client is currently activated (focused) or not", @@ -294,17 +273,11 @@ static void glace_client_class_init(GlaceClientClass* klass) { G_PARAM_READABLE ); - glace_client_properties[ - GLACE_CLIENT_PROPERTY_FULLSCREEN - ] = g_param_spec_boolean( - "fullscreen", "fullscreen", "whether this client is currently in a fullscreen state or not", - false, - G_PARAM_READABLE + glace_client_properties[GLACE_CLIENT_PROPERTY_FULLSCREEN] = g_param_spec_boolean( + "fullscreen", "fullscreen", "whether this client is currently in a fullscreen state or not", false, G_PARAM_READABLE ); - glace_client_properties[ - GLACE_CLIENT_PROPERTY_CLOSED - ] = g_param_spec_boolean( + glace_client_properties[GLACE_CLIENT_PROPERTY_CLOSED] = g_param_spec_boolean( "closed", "closed", "whether this client is closed (killed) or not, it's guaranteed that you won't receive events for this client after it gets closed", @@ -319,7 +292,6 @@ static void glace_client_class_init(GlaceClientClass* klass) { ); } - static void glace_client_init(GlaceClient* self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, @@ -336,7 +308,6 @@ static void glace_client_init(GlaceClient* self) { CLIENT_SET_CURRENT_PROP(self, fullscreen, false); } - GlaceClient* glace_client_new( struct zwlr_foreign_toplevel_handle_v1* wlr_handle, GdkWaylandDisplay* gdk_display @@ -353,5 +324,4 @@ GlaceClient* glace_client_new( return self; } - G_DEFINE_TYPE(GlaceClient, glace_client, G_TYPE_OBJECT); diff --git a/libglace/libglace-client-private.h b/libglace/libglace-client-private.h index c55dfd9..237b800 100644 --- a/libglace/libglace-client-private.h +++ b/libglace/libglace-client-private.h @@ -16,36 +16,37 @@ #define CLIENT_GET_PENDING_PROP(client, prop) \ (client->priv->pending_properties.prop) -#define CLIENT_BRIDGE_PROPS(client, prop, PROP_UPPER) do { \ - if (CLIENT_GET_CURRENT_PROP(client, prop) != CLIENT_GET_PENDING_PROP(client, prop)) { \ - CLIENT_GET_CURRENT_PROP(client, prop) = CLIENT_GET_PENDING_PROP(client, prop); \ - g_object_notify_by_pspec( \ - G_OBJECT(client), \ - glace_client_properties[GLACE_CLIENT_PROPERTY_##PROP_UPPER] \ - ); \ - } \ -} while (0) +#define CLIENT_BRIDGE_PROPS(client, prop, PROP_UPPER) \ + do { \ + if (CLIENT_GET_CURRENT_PROP(client, prop) != CLIENT_GET_PENDING_PROP(client, prop)) { \ + CLIENT_GET_CURRENT_PROP(client, prop) = CLIENT_GET_PENDING_PROP(client, prop); \ + g_object_notify_by_pspec( \ + G_OBJECT(client), \ + glace_client_properties[GLACE_CLIENT_PROPERTY_##PROP_UPPER] \ + ); \ + } \ + } while (0) #define CLIENT_SET_STATE_FOR_CASE(client, state, ENUM_M, prop) \ - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_##ENUM_M: \ - CLIENT_SET_PENDING_PROP(client, prop, true); \ - break; \ + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_##ENUM_M: \ + CLIENT_SET_PENDING_PROP(client, prop, true); \ + break; -#define CLIENT_SET_DEFAULT_STATES(client) \ +#define CLIENT_SET_DEFAULT_STATES(client) \ CLIENT_SET_PENDING_PROP(client, maximized, false); \ CLIENT_SET_PENDING_PROP(client, minimized, false); \ CLIENT_SET_PENDING_PROP(client, activated, false); \ - CLIENT_SET_PENDING_PROP(client, fullscreen, false); \ + CLIENT_SET_PENDING_PROP(client, fullscreen, false); #define IF_INVALID_CLIENT(client) if (client == NULL || GLACE_IS_CLIENT(client) == false || client->priv->closed == true) -#define RETURN_IF_INVALID_CLIENT(client, r_value) do { \ - IF_INVALID_CLIENT(client) {\ - g_warning("[WARNING][CLIENT] function %s got an invalid client", __func__);\ - return r_value; \ - } \ -} while (0) - +#define RETURN_IF_INVALID_CLIENT(client, r_value) \ + do { \ + IF_INVALID_CLIENT(client) { \ + g_warning("[WARNING][CLIENT] function %s got an invalid client", __func__); \ + return r_value; \ + } \ + } while (0) // signal emitters static void glace_client_signal_changed_emit(GlaceClient* self); @@ -57,5 +58,4 @@ static void glace_client_class_init(GlaceClientClass* klass); GlaceClient* glace_client_new(struct zwlr_foreign_toplevel_handle_v1* wlr_handle, GdkWaylandDisplay* gdk_display); - #endif /* __LIBGLACE_CLIENT_PRIVATE_H__ */ diff --git a/libglace/libglace-client.c b/libglace/libglace-client.c index 3811ac2..413b1c8 100644 --- a/libglace/libglace-client.c +++ b/libglace/libglace-client.c @@ -33,7 +33,6 @@ gboolean glace_client_get_closed(GlaceClient* self) { return self->priv->closed; } - // client methods void glace_client_maximize(GlaceClient* self) { // replacing EMPTY_TOKEN with a trailing comma @@ -43,35 +42,30 @@ void glace_client_maximize(GlaceClient* self) { zwlr_foreign_toplevel_handle_v1_set_maximized(self->priv->wlr_handle); } - void glace_client_unmaximize(GlaceClient* self) { RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); zwlr_foreign_toplevel_handle_v1_unset_maximized(self->priv->wlr_handle); } - void glace_client_minimize(GlaceClient* self) { RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); zwlr_foreign_toplevel_handle_v1_set_minimized(self->priv->wlr_handle); } - void glace_client_unminimize(GlaceClient* self) { RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); zwlr_foreign_toplevel_handle_v1_unset_minimized(self->priv->wlr_handle); } - void glace_client_close(GlaceClient* self) { RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); zwlr_foreign_toplevel_handle_v1_close(self->priv->wlr_handle); } - void glace_client_activate(GlaceClient* self) { RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); @@ -84,7 +78,6 @@ void glace_client_activate(GlaceClient* self) { ); } - void glace_client_move( GlaceClient* self, GdkWindow* window, @@ -114,14 +107,12 @@ void glace_client_move( ); } - void glace_client_fullscreen(GlaceClient* self) { RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); zwlr_foreign_toplevel_handle_v1_set_fullscreen(self->priv->wlr_handle, self->priv->output); } - void glace_client_unfullscreen(GlaceClient* self) { RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); diff --git a/libglace/libglace-client.h b/libglace/libglace-client.h index a074c87..c37acc2 100644 --- a/libglace/libglace-client.h +++ b/libglace/libglace-client.h @@ -31,15 +31,15 @@ struct _GlaceClientClass { GObjectClass parent_class; // _class_ methods - void (*activate)(GlaceClient *self); - void (*maximize)(GlaceClient *self); - void (*minimize)(GlaceClient *self); - void (*fullscreen)(GlaceClient *self); - void (*unmaximize)(GlaceClient *self); - void (*unminimize)(GlaceClient *self); - void (*unfullscreen)(GlaceClient *self); + void (*activate)(GlaceClient* self); + void (*maximize)(GlaceClient* self); + void (*minimize)(GlaceClient* self); + void (*fullscreen)(GlaceClient* self); + void (*unmaximize)(GlaceClient* self); + void (*unminimize)(GlaceClient* self); + void (*unfullscreen)(GlaceClient* self); void (*move)(GlaceClient* self, GdkWindow* window, const GdkRectangle* rectangle); - void (*close)(GlaceClient *self); + void (*close)(GlaceClient* self); }; /** @@ -99,7 +99,7 @@ GType glace_client_get_type(); /** * glace_client_maximize: * @self: a #GlaceClient - * + * * to maximize a client (AKA make it to fill it's display area) */ void glace_client_maximize(GlaceClient* self); @@ -107,7 +107,7 @@ void glace_client_maximize(GlaceClient* self); /** * glace_client_unmaximize: * @self: a #GlaceClient - * + * * to unset a client from the maximized state */ void glace_client_unmaximize(GlaceClient* self); @@ -115,7 +115,7 @@ void glace_client_unmaximize(GlaceClient* self); /** * glace_client_minimize: * @self: a #GlaceClient - * + * * to minimize a client (AKA hide it to taskbar) */ void glace_client_minimize(GlaceClient* self); @@ -123,7 +123,7 @@ void glace_client_minimize(GlaceClient* self); /** * glace_client_unminimize: * @self: a #GlaceClient - * + * * to unset a client from the minimized state */ void glace_client_unminimize(GlaceClient* self); @@ -131,7 +131,7 @@ void glace_client_unminimize(GlaceClient* self); /** * glace_client_close: * @self: a #GlaceClient - * + * * to close a client * the `closed` property will get changed if the request was done successfully * it's guaranteed that you won't receive events for this client after it gets closed @@ -141,7 +141,7 @@ void glace_client_close(GlaceClient* self); /** * glace_client_activate: * @self: a #GlaceClient - * + * * to activate a client (AKA focus it) * the `activated` property will get changed if the request was done successfully */ @@ -152,7 +152,7 @@ void glace_client_activate(GlaceClient* self); * @self: a #GlaceClient * @window: (nullable): the #GdkWindow to use it's surface as a hint for the compositor, you can pass NULL for this * @rectangle: the #GdkRectangle to use - * + * * move this client to a X and Y coords / width and height using a rectangle * it's not guaranteed if the client will actually get moved or not * check if your target compositor(s) supports this feature or not @@ -162,7 +162,7 @@ void glace_client_move(GlaceClient* self, GdkWindow* window, const GdkRectangle* /** * glace_client_fullscreen: * @self: a #GlaceClient - * + * * to get a client in a fullscreen state * it will be ignore if the client is in a fullscreen state * the `fullscreen` property will get changed if the request was done successfully @@ -172,7 +172,7 @@ void glace_client_fullscreen(GlaceClient* self); /** * glace_client_unfullscreen: * @self: a #GlaceClient - * + * * to get a client out of fullscreen state * it will be ignore if the client is not in a fullscreen state * the `fullscreen` property will get changed if the request was done successfully diff --git a/libglace/libglace-manager-private.c b/libglace/libglace-manager-private.c index 23f5489..c6634ad 100644 --- a/libglace/libglace-manager-private.c +++ b/libglace/libglace-manager-private.c @@ -2,7 +2,6 @@ static guint glace_manager_signals[GLACE_MANAGER_N_SIGNALS] = {0}; - static void glace_manager_signal_changed_emit(GlaceManager* self) { g_signal_emit( self, @@ -11,7 +10,6 @@ static void glace_manager_signal_changed_emit(GlaceManager* self) { ); } - static void glace_manager_signal_client_added_emit(GlaceManager* self, GlaceClient* client) { g_signal_emit( self, @@ -21,7 +19,6 @@ static void glace_manager_signal_client_added_emit(GlaceManager* self, GlaceClie ); } - static void glace_manager_signal_client_removed_emit(GlaceManager* self, GlaceClient* client) { g_signal_emit( self, @@ -31,7 +28,6 @@ static void glace_manager_signal_client_removed_emit(GlaceManager* self, GlaceCl ); } - static void on_client_closed_cleanup(GlaceClient* self, gpointer data) { GlaceManager* manager = (GlaceManager*)data; @@ -39,7 +35,6 @@ static void on_client_closed_cleanup(GlaceClient* self, gpointer data) { glace_manager_signal_changed_emit(manager); } - static void on_manager_toplevel( void* data, struct zwlr_foreign_toplevel_manager_v1* manager, @@ -61,19 +56,16 @@ static void on_manager_toplevel( g_debug("[INFO][MANAGER] got a client with id %u\n", glace_client_get_id(client)); }; - static void on_manager_finished( void* data, struct zwlr_foreign_toplevel_manager_v1* manager ) {} - static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_listener = { .toplevel = &on_manager_toplevel, .finished = &on_manager_finished }; - static void on_registry_global( void* data, struct wl_registry* registry, @@ -103,16 +95,13 @@ static void on_registry_global( } }; - -static void on_registry_global_remove(void* data, struct wl_registry *registry, uint32_t name) {} - +static void on_registry_global_remove(void* data, struct wl_registry* registry, uint32_t name) {} static const struct wl_registry_listener registry_listener = { .global = &on_registry_global, .global_remove = &on_registry_global_remove, }; - static void glace_manager_class_init(GlaceManagerClass* klass) { GObjectClass* parent_class = G_OBJECT_CLASS(klass); @@ -157,7 +146,6 @@ static void glace_manager_class_init(GlaceManagerClass* klass) { ); } - static void glace_manager_init(GlaceManager* self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE( self, @@ -189,10 +177,10 @@ static void glace_manager_init(GlaceManager* self) { wl_display_roundtrip(self->priv->display); - if (self->priv->wlr_manager == NULL) g_warning( - "[WARNING][MANAGER] your compositor does not support the wlr-foreign-toplevel-management protocol, Glace will not work if the protocol support is missing!" - ); + if (self->priv->wlr_manager == NULL) + g_warning( + "[WARNING][MANAGER] your compositor does not support the wlr-foreign-toplevel-management protocol, Glace will not work if the protocol support is missing!" + ); } - G_DEFINE_TYPE(GlaceManager, glace_manager, G_TYPE_OBJECT); diff --git a/libglace/libglace-manager-private.h b/libglace/libglace-manager-private.h index d9e1b0f..bc04cde 100644 --- a/libglace/libglace-manager-private.h +++ b/libglace/libglace-manager-private.h @@ -1,8 +1,8 @@ #ifndef __LIBGLACE_MANAGER_PRIVATE_H__ #define __LIBGLACE_MANAGER_PRIVATE_H__ -#include "libglace-manager.h" #include "libglace-client-private.h" +#include "libglace-manager.h" #include "wlr-foreign-toplevel-management-unstable-v1.h" #define max(a, b) (a > b ? a : b) @@ -10,13 +10,16 @@ #define EMPTY_TOKEN -#define DISPLAY_CHECK_NULL(expr) do { \ - if G_LIKELY((expr) != NULL); else \ - g_error("[ERROR] '"#expr"' should NOT be NULL, please note that Glace can't work under anything other than Wayland"); \ - g_assert_nonnull(expr); \ -} while (0) +#define DISPLAY_CHECK_NULL(expr) \ + do { \ + if G_LIKELY ((expr) != NULL) \ + ; \ + else \ + g_error("[ERROR] '" #expr "' should NOT be NULL, please note that Glace can't work under anything other than Wayland"); \ + g_assert_nonnull(expr); \ + } while (0) -#define G_LIST_FOREACH(item, list) for (GList *__glist = list; __glist && (item = __glist->data, true); __glist = __glist->next) +#define G_LIST_FOREACH(item, list) for (GList* __glist = list; __glist && (item = __glist->data, true); __glist = __glist->next) // static void glace_manager_init(GlaceManager* self); // static void glace_manager_class_init(GlaceManagerClass* klass); diff --git a/libglace/libglace-manager.h b/libglace/libglace-manager.h index f208062..a3b1d38 100644 --- a/libglace/libglace-manager.h +++ b/libglace/libglace-manager.h @@ -1,12 +1,12 @@ #ifndef __LIBGLACE_MANAGER_H__ #define __LIBGLACE_MANAGER_H__ -#include +#include #include #include +#include #include #include -#include G_BEGIN_DECLS From bb4793956dc1a7df161d8dfc0d562847780d2f66 Mon Sep 17 00:00:00 2001 From: The Darsh <129121284+its-darsh@users.noreply.github.com> Date: Sat, 9 Nov 2024 15:27:08 +0200 Subject: [PATCH 2/4] feat: add a method for capturing Hyprland clients feat: improve project's structure --- examples/simple/simple.c | 4 +- .../glace-client-private.h | 6 +- .../glace-client.c | 120 ++++- .../libglace-client.h => glace/glace-client.h | 9 +- .../glace-manager-private.h | 34 +- glace/glace-manager.c | 415 ++++++++++++++++++ .../glace-manager.h | 16 +- glace/glace-private.h | 9 + glace/glace.h | 4 + {libglace => glace}/meson.build | 37 +- libglace/libglace-client.c | 120 ----- libglace/libglace-manager-private.c | 186 -------- libglace/libglace-manager.c | 5 - libglace/libglace-private.h | 2 - libglace/libglace.c | 2 - libglace/libglace.h | 2 - meson.build | 2 +- protocol/hyprland-toplevel-export-v1.xml | 204 +++++++++ protocol/meson.build | 42 +- 19 files changed, 846 insertions(+), 373 deletions(-) rename libglace/libglace-client-private.h => glace/glace-client-private.h (97%) rename libglace/libglace-client-private.c => glace/glace-client.c (74%) rename libglace/libglace-client.h => glace/glace-client.h (97%) rename libglace/libglace-manager-private.h => glace/glace-manager-private.h (71%) create mode 100644 glace/glace-manager.c rename libglace/libglace-manager.h => glace/glace-manager.h (70%) create mode 100644 glace/glace-private.h create mode 100644 glace/glace.h rename {libglace => glace}/meson.build (70%) delete mode 100644 libglace/libglace-client.c delete mode 100644 libglace/libglace-manager-private.c delete mode 100644 libglace/libglace-manager.c delete mode 100644 libglace/libglace-private.h delete mode 100644 libglace/libglace.c delete mode 100644 libglace/libglace.h create mode 100644 protocol/hyprland-toplevel-export-v1.xml diff --git a/examples/simple/simple.c b/examples/simple/simple.c index 056f698..403bded 100644 --- a/examples/simple/simple.c +++ b/examples/simple/simple.c @@ -1,5 +1,5 @@ -/* cc simple.c `pkg-config --libs --cflags libglace` */ -#include +/* cc simple.c `pkg-config --libs --cflags glace` */ +#include static void on_client_chagend(GlaceClient* client) { printf( diff --git a/libglace/libglace-client-private.h b/glace/glace-client-private.h similarity index 97% rename from libglace/libglace-client-private.h rename to glace/glace-client-private.h index 237b800..1d39967 100644 --- a/libglace/libglace-client-private.h +++ b/glace/glace-client-private.h @@ -1,8 +1,10 @@ +#pragma once + #ifndef __LIBGLACE_CLIENT_PRIVATE_H__ #define __LIBGLACE_CLIENT_PRIVATE_H__ -#include "libglace-client.h" -#include "libglace-manager-private.h" +#include "glace-client.h" +#include "glace-manager-private.h" #define CLIENT_SET_CURRENT_PROP(client, prop, value) \ (client->priv->current_properties.prop = value) diff --git a/libglace/libglace-client-private.c b/glace/glace-client.c similarity index 74% rename from libglace/libglace-client-private.c rename to glace/glace-client.c index 963930b..252b87c 100644 --- a/libglace/libglace-client-private.c +++ b/glace/glace-client.c @@ -1,4 +1,4 @@ -#include "libglace-private.h" +#include "glace-private.h" static guint glace_client_signals[GLACE_CLIENT_N_SIGNALS] = {0}; static GParamSpec* glace_client_properties[GLACE_CLIENT_N_PROPERTIES] = { @@ -324,4 +324,122 @@ GlaceClient* glace_client_new( return self; } +// client getters +guint glace_client_get_id(GlaceClient* self) { + return (guint)self->priv->id; +} + +const gchar* glace_client_get_app_id(GlaceClient* self) { + return CLIENT_GET_CURRENT_PROP(self, app_id); +} + +const gchar* glace_client_get_title(GlaceClient* self) { + return CLIENT_GET_CURRENT_PROP(self, title); +} + +gboolean glace_client_get_maximized(GlaceClient* self) { + return CLIENT_GET_CURRENT_PROP(self, maximized); +} + +gboolean glace_client_get_minimized(GlaceClient* self) { + return CLIENT_GET_CURRENT_PROP(self, minimized); +} + +gboolean glace_client_get_activated(GlaceClient* self) { + return CLIENT_GET_CURRENT_PROP(self, activated); +} + +gboolean glace_client_get_fullscreen(GlaceClient* self) { + return CLIENT_GET_CURRENT_PROP(self, fullscreen); +} + +gboolean glace_client_get_closed(GlaceClient* self) { + return self->priv->closed; +} + +// client methods +void glace_client_maximize(GlaceClient* self) { + // replacing EMPTY_TOKEN with a trailing comma + // will do the trick as well + RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); + + zwlr_foreign_toplevel_handle_v1_set_maximized(self->priv->wlr_handle); +} + +void glace_client_unmaximize(GlaceClient* self) { + RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); + + zwlr_foreign_toplevel_handle_v1_unset_maximized(self->priv->wlr_handle); +} + +void glace_client_minimize(GlaceClient* self) { + RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); + + zwlr_foreign_toplevel_handle_v1_set_minimized(self->priv->wlr_handle); +} + +void glace_client_unminimize(GlaceClient* self) { + RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); + + zwlr_foreign_toplevel_handle_v1_unset_minimized(self->priv->wlr_handle); +} + +void glace_client_close(GlaceClient* self) { + RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); + + zwlr_foreign_toplevel_handle_v1_close(self->priv->wlr_handle); +} + +void glace_client_activate(GlaceClient* self) { + RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); + + GdkSeat* gdk_seat = gdk_display_get_default_seat(self->priv->gdk_display); + struct wl_seat* seat = gdk_wayland_seat_get_wl_seat(gdk_seat); + + zwlr_foreign_toplevel_handle_v1_activate( + self->priv->wlr_handle, + seat + ); +} + +void glace_client_move( + GlaceClient* self, + GdkWindow* window, + const GdkRectangle* rectangle +) { + RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); + + if (window == NULL) { + zwlr_foreign_toplevel_handle_v1_set_rectangle( + self->priv->wlr_handle, + NULL, + rectangle->x, + rectangle->y, + rectangle->width, + rectangle->height + ); + return; + } + + zwlr_foreign_toplevel_handle_v1_set_rectangle( + self->priv->wlr_handle, + gdk_wayland_window_get_wl_surface(window), + rectangle->x, + rectangle->y, + rectangle->width, + rectangle->height + ); +} + +void glace_client_fullscreen(GlaceClient* self) { + RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); + + zwlr_foreign_toplevel_handle_v1_set_fullscreen(self->priv->wlr_handle, self->priv->output); +} + +void glace_client_unfullscreen(GlaceClient* self) { + RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); + + zwlr_foreign_toplevel_handle_v1_unset_fullscreen(self->priv->wlr_handle); +} G_DEFINE_TYPE(GlaceClient, glace_client, G_TYPE_OBJECT); diff --git a/libglace/libglace-client.h b/glace/glace-client.h similarity index 97% rename from libglace/libglace-client.h rename to glace/glace-client.h index c37acc2..280764f 100644 --- a/libglace/libglace-client.h +++ b/glace/glace-client.h @@ -1,7 +1,14 @@ +#pragma once + #ifndef __LIBGLACE_CLIENT_H__ #define __LIBGLACE_CLIENT_H__ -#include "libglace-manager.h" +#include +#include +#include +#include +#include +#include G_BEGIN_DECLS diff --git a/libglace/libglace-manager-private.h b/glace/glace-manager-private.h similarity index 71% rename from libglace/libglace-manager-private.h rename to glace/glace-manager-private.h index bc04cde..33713ca 100644 --- a/libglace/libglace-manager-private.h +++ b/glace/glace-manager-private.h @@ -1,9 +1,11 @@ +#pragma once + #ifndef __LIBGLACE_MANAGER_PRIVATE_H__ #define __LIBGLACE_MANAGER_PRIVATE_H__ -#include "libglace-client-private.h" -#include "libglace-manager.h" -#include "wlr-foreign-toplevel-management-unstable-v1.h" +#include "glace-client-private.h" +#include "glace-client.h" +#include "glace-manager.h" #define max(a, b) (a > b ? a : b) #define min(x, y) ((x) < (y) ? (x) : (y)) @@ -21,6 +23,32 @@ #define G_LIST_FOREACH(item, list) for (GList* __glist = list; __glist && (item = __glist->data, true); __glist = __glist->next) +typedef struct _GlaceFrameData GlaceFrameData; +typedef struct _GlaceFrameBuffer GlaceFrameBuffer; + +struct _GlaceFrameBuffer { + struct wl_buffer* wl_buffer; + void* raw_buffer; + + size_t size; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t format; +}; + +struct _GlaceFrameData { + GlaceManager* manager; + GlaceClient* client; + + GlaceFrameBuffer* buffer; + + GlaceManagerCaptureClientCallback callback; + gpointer callback_data; +}; + +const static struct hyprland_toplevel_export_frame_v1_listener export_manager_frame_listener; + // static void glace_manager_init(GlaceManager* self); // static void glace_manager_class_init(GlaceManagerClass* klass); diff --git a/glace/glace-manager.c b/glace/glace-manager.c new file mode 100644 index 0000000..3f00b5a --- /dev/null +++ b/glace/glace-manager.c @@ -0,0 +1,415 @@ +#include "glace-private.h" + +static guint glace_manager_signals[GLACE_MANAGER_N_SIGNALS] = {0}; + +static void glace_manager_signal_changed_emit(GlaceManager* self) { + g_signal_emit( + self, + glace_manager_signals[GLACE_MANAGER_SIGNAL_CHANGED], + 0 + ); +} + +static void glace_manager_signal_client_added_emit(GlaceManager* self, GlaceClient* client) { + g_signal_emit( + self, + glace_manager_signals[GLACE_MANAGER_SIGNAL_CLIENT_ADDED], + 1, + client + ); +} + +static void glace_manager_signal_client_removed_emit(GlaceManager* self, GlaceClient* client) { + g_signal_emit( + self, + glace_manager_signals[GLACE_MANAGER_SIGNAL_CLIENT_REMOVED], + 1, + client + ); +} + +static void on_client_closed_cleanup(GlaceClient* self, gpointer data) { + GlaceManager* manager = (GlaceManager*)data; + + glace_manager_signal_client_removed_emit(manager, self); + glace_manager_signal_changed_emit(manager); +} + +static void on_manager_toplevel( + void* data, + struct zwlr_foreign_toplevel_manager_v1* manager, + struct zwlr_foreign_toplevel_handle_v1* handle +) { + GlaceManager* self = data; + if (!GLACE_IS_MANAGER(self)) { + return; + } + + GlaceClient* client = glace_client_new(handle, self->priv->gdk_display); + RETURN_IF_INVALID_CLIENT(client, EMPTY_TOKEN); + + g_signal_connect(client, "close", G_CALLBACK(on_client_closed_cleanup), self); + + glace_manager_signal_client_added_emit(self, client); + glace_manager_signal_changed_emit(self); + + g_debug("[INFO][MANAGER] got a client with id %u\n", glace_client_get_id(client)); +}; + +static void on_manager_finished( + void* data, + struct zwlr_foreign_toplevel_manager_v1* manager +) {} + +static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_listener = { + .toplevel = &on_manager_toplevel, + .finished = &on_manager_finished +}; + +static void on_registry_global( + void* data, + struct wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version +) { + g_debug("[INFO][PROTOCOL] got protocol with name %s\n", interface); + + GlaceManager* self = data; + if (!GLACE_IS_MANAGER(self)) { + return; + } + + if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) { + g_debug("[INFO][PROTOCOL] connecting to zwlr_foreign_toplevel_manager_v1\n"); + struct zwlr_foreign_toplevel_manager_v1* manager = wl_registry_bind( + registry, + name, + &zwlr_foreign_toplevel_manager_v1_interface, + max(version, 1) + ); + self->priv->wlr_manager = manager; + + zwlr_foreign_toplevel_manager_v1_add_listener(manager, &toplevel_manager_listener, self); + } else if (strcmp(interface, hyprland_toplevel_export_manager_v1_interface.name) == 0) { + g_debug("[INFO][PROTOCOL] connecting to hyprland_toplevel_export_manager_v1\n"); + + struct hyprland_toplevel_export_manager_v1* export_manager = wl_registry_bind( + registry, + name, + &hyprland_toplevel_export_manager_v1_interface, + max(version, 1) + ); + self->priv->hl_export_manager = export_manager; + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + g_debug("[INFO][PROTOCOL] getting a shared memory buffer\n"); + + self->priv->wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, version); + } + + return; +}; + +static void on_registry_global_remove(void* data, struct wl_registry* registry, uint32_t name) {} + +static const struct wl_registry_listener registry_listener = { + .global = &on_registry_global, + .global_remove = &on_registry_global_remove, +}; + +static void glace_manager_class_init(GlaceManagerClass* klass) { + GObjectClass* parent_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(GlaceManagerPrivate)); + + glace_manager_signals[GLACE_MANAGER_SIGNAL_CHANGED] = g_signal_new( + "changed", + GLACE_TYPE_MANAGER, + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0 + ); + + glace_manager_signals[GLACE_MANAGER_SIGNAL_CLIENT_ADDED] = g_signal_new( + "client-added", + GLACE_TYPE_MANAGER, + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + GLACE_TYPE_CLIENT + ); + + glace_manager_signals[GLACE_MANAGER_SIGNAL_CLIENT_REMOVED] = g_signal_new( + "client-removed", + GLACE_TYPE_MANAGER, + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + GLACE_TYPE_CLIENT + ); +} + +static void glace_manager_init(GlaceManager* self) { + self->priv = G_TYPE_INSTANCE_GET_PRIVATE( + self, + GLACE_TYPE_MANAGER, + GlaceManagerPrivate + ); + + GdkWaylandDisplay* gdk_display = gdk_display_get_default(); + if (GDK_IS_WAYLAND_DISPLAY(gdk_display) == false) { + // trap it in next check + gdk_display = NULL; + } + DISPLAY_CHECK_NULL(gdk_display); + + g_debug( + "[INFO][MANAGER] got display with name %s", + gdk_display_get_name(gdk_display) + ); + + struct wl_display* display = gdk_wayland_display_get_wl_display(gdk_display); + DISPLAY_CHECK_NULL(display); + + self->priv->display = display; + self->priv->gdk_display = gdk_display; + + // all aboard... + struct wl_registry* registry = wl_display_get_registry(self->priv->display); + wl_registry_add_listener(registry, ®istry_listener, self); + + wl_display_roundtrip(self->priv->display); + + if (self->priv->wlr_manager == NULL) + g_warning( + "[WARNING][MANAGER] your compositor does not support the wlr-foreign-toplevel-management protocol, Glace will not work if the protocol support is missing!" + ); +} + +static int anonymous_shm_open() { + char* name; + int retries = 100; + do { + --retries; + name = g_strdup_printf("/glace-hyprland-frame-%i", g_random_int()); + + // shm_open guarantees that O_CLOEXEC is set + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + + return -1; +} + +static int create_shm_file(off_t size) { + int fd = anonymous_shm_open(); + if (fd < 0) { + return fd; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +static GlaceFrameBuffer* glace_frame_buffer_new(struct wl_shm* shm, enum wl_shm_format format, int32_t width, int32_t height, int32_t stride) { + size_t size = stride * height; + + int fd = create_shm_file(size); + if (fd == -1) { + return NULL; + } + + if (shm == NULL) { + return NULL; + } + + void* raw_buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (raw_buffer == MAP_FAILED) { + close(fd); + return NULL; + } + + struct wl_shm_pool* pool = wl_shm_create_pool(shm, fd, size); + + if (!pool) { + munmap(raw_buffer, size); + close(fd); + return NULL; + } + + struct wl_buffer* wl_buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format); + + wl_shm_pool_destroy(pool); + close(fd); + + GlaceFrameBuffer* buffer = calloc(1, sizeof(GlaceFrameBuffer)); + buffer->wl_buffer = wl_buffer; + buffer->raw_buffer = raw_buffer; + buffer->size = size; + buffer->width = width; + buffer->height = height; + buffer->stride = stride; + buffer->format = format; + + return buffer; +} + +static inline void frame_buffer_correct_format(GlaceFrameBuffer* buffer) { + // BGRA -> RGBA + uint32_t* agbr = (uint32_t*)buffer->raw_buffer; + for (size_t i = 0; i < buffer->width * buffer->height; i++) { + uint32_t pixel = agbr[i]; + + agbr[i] = (pixel & 0xFF00FF00) | ((pixel << 16) & 0x00FF0000) | ((pixel >> 16) & 0xFF); + } +} + +static void glace_frame_buffer_destroy(GlaceFrameBuffer* buffer) { + if (buffer == NULL) { + return; + } + munmap(buffer->raw_buffer, buffer->size); + wl_buffer_destroy(buffer->wl_buffer); + free(buffer); +} + +static void glace_frame_data_destroy(GlaceFrameData* data) { + // die and be a hero or live long enough to see yourself become a villain + if (!data) { + return; + } + + if (data->buffer != NULL) { + glace_frame_buffer_destroy(data->buffer); + } + + data->buffer = NULL; + data->client = NULL; + data->manager = NULL; + data->callback = NULL; + data->callback_data = NULL; + + free(data); + return; +} + +static void on_export_manager_frame_buffer(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { + GlaceFrameData* data = user_data; + data->buffer = glace_frame_buffer_new(data->manager->priv->wl_shm, format, width, height, stride); + return; +} + +static void on_export_manager_frame_damage(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {} + +static void on_export_manager_frame_flags(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t flags) {} + +static void on_export_manager_frame_ready(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { + GlaceFrameData* data = user_data; + + if (!data || !data->buffer) { + data->callback(NULL, data->callback_data); + return; + } + + frame_buffer_correct_format(data->buffer); + + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( + data->buffer->raw_buffer, + GDK_COLORSPACE_RGB, + TRUE, + 8, + data->buffer->width, + data->buffer->height, + data->buffer->stride, + NULL, + NULL + ); + + data->callback(pixbuf, data->callback_data); + glace_frame_data_destroy(data); + + hyprland_toplevel_export_frame_v1_destroy(export_frame); + + return; +} + +static void on_export_manager_frame_failed(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame) { + GlaceFrameData* data = user_data; + + data->callback(NULL, data->callback_data); + glace_frame_data_destroy(data); + + hyprland_toplevel_export_frame_v1_destroy(export_frame); + + return; +} + +static void on_export_manager_frame_linux_dmabuf(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t format, uint32_t width, uint32_t height) {} + +static void on_export_manager_frame_buffer_done(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame) { + GlaceFrameData* data = user_data; + + // all aboard... + hyprland_toplevel_export_frame_v1_copy(export_frame, data->buffer->wl_buffer, 1); + + return; +} + +static const struct hyprland_toplevel_export_frame_v1_listener export_manager_frame_listener = { + .buffer = &on_export_manager_frame_buffer, + .damage = &on_export_manager_frame_damage, + .flags = &on_export_manager_frame_flags, + .ready = &on_export_manager_frame_ready, + .failed = &on_export_manager_frame_failed, + .linux_dmabuf = &on_export_manager_frame_linux_dmabuf, + .buffer_done = &on_export_manager_frame_buffer_done +}; + +// public methods +GlaceManager* glace_manager_new() { + return g_object_new(GLACE_TYPE_MANAGER, NULL); +} + +void glace_manager_capture_client(GlaceManager* self, GlaceClient* client, gboolean overlay_cursor, GlaceManagerCaptureClientCallback callback, gpointer user_data, GDestroyNotify notify) { + if (!self->priv->hl_export_manager) { + g_warning_once("at the moment, capturing a client is only available for Hyprland users."); + callback(NULL, user_data); + return; + } + + GlaceFrameData* data = calloc(1, sizeof(GlaceFrameData)); + data->manager = self; + data->client = client; + data->callback = callback; + data->callback_data = user_data; + data->buffer = NULL; + + struct hyprland_toplevel_export_frame_v1* frame = hyprland_toplevel_export_manager_v1_capture_toplevel_with_wlr_toplevel_handle( + self->priv->hl_export_manager, (gint)overlay_cursor, client->priv->wlr_handle + ); + + hyprland_toplevel_export_frame_v1_add_listener(frame, &export_manager_frame_listener, data); + + return; +} + +G_DEFINE_TYPE(GlaceManager, glace_manager, G_TYPE_OBJECT); diff --git a/libglace/libglace-manager.h b/glace/glace-manager.h similarity index 70% rename from libglace/libglace-manager.h rename to glace/glace-manager.h index a3b1d38..0f34665 100644 --- a/libglace/libglace-manager.h +++ b/glace/glace-manager.h @@ -1,12 +1,9 @@ +#pragma once + #ifndef __LIBGLACE_MANAGER_H__ #define __LIBGLACE_MANAGER_H__ -#include -#include -#include -#include -#include -#include +#include "glace-client.h" G_BEGIN_DECLS @@ -22,6 +19,7 @@ G_BEGIN_DECLS typedef struct _GlaceManager GlaceManager; typedef struct _GlaceManagerPrivate GlaceManagerPrivate; typedef struct _GlaceManagerClass GlaceManagerClass; +typedef void (*GlaceManagerCaptureClientCallback)(GdkPixbuf* pixbuf, gpointer user_data); struct _GlaceManager { GObject parent_instance; @@ -30,12 +28,17 @@ struct _GlaceManager { struct _GlaceManagerClass { GObjectClass parent_class; + + // methods + void (*capture_client)(GlaceManager* self, GlaceClient* client, gboolean overlay_cursor, GlaceManagerCaptureClientCallback callback, gpointer user_data, GDestroyNotify notify); }; struct _GlaceManagerPrivate { GdkWaylandDisplay* gdk_display; struct wl_display* display; + struct wl_shm* wl_shm; struct zwlr_foreign_toplevel_manager_v1* wlr_manager; + struct hyprland_toplevel_export_manager_v1* hl_export_manager; }; enum { @@ -48,6 +51,7 @@ enum { // methods GType glace_manager_get_type(); GlaceManager* glace_manager_new(); +void glace_manager_capture_client(GlaceManager* self, GlaceClient* client, gboolean overlay_cursor, GlaceManagerCaptureClientCallback callback, gpointer user_data, GDestroyNotify notify); G_END_DECLS diff --git a/glace/glace-private.h b/glace/glace-private.h new file mode 100644 index 0000000..ae5dee0 --- /dev/null +++ b/glace/glace-private.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +#include "hyprland-toplevel-export-v1.h" +#include "glace-client-private.h" +#include "glace-manager-private.h" +#include "wlr-foreign-toplevel-management-unstable-v1.h" \ No newline at end of file diff --git a/glace/glace.h b/glace/glace.h new file mode 100644 index 0000000..5e27c94 --- /dev/null +++ b/glace/glace.h @@ -0,0 +1,4 @@ +#pragma once + +#include "glace-client.h" +#include "glace-manager.h" diff --git a/libglace/meson.build b/glace/meson.build similarity index 70% rename from libglace/meson.build rename to glace/meson.build index 5e5bc1a..c354f5a 100644 --- a/libglace/meson.build +++ b/glace/meson.build @@ -1,38 +1,31 @@ gnome = import('gnome') pkg_config = import('pkgconfig') +glace_name = meson.project_name() glace_version = meson.project_version() glace_description = 'a library for managing wayland clients' glace_url = 'https://github.com/Fabric-Development/glace' -glace_library_name = 'lib' + meson.project_name() +glace_library_name = 'lib' + glace_name glace_namespace = 'Glace' glace_package_name = glace_namespace + '-' + glace_version -glace_public_src = files( - 'libglace-manager.c', - 'libglace-client.c', - 'libglace.c', +glace_sources = files( + 'glace-manager.c', + 'glace-client.c' ) -glace_private_src = files( - 'libglace-manager-private.c', - 'libglace-client-private.c', -) - -glace_sources = glace_public_src + glace_private_src - glace_public_headers = files( - 'libglace-manager.h', - 'libglace-client.h', - 'libglace.h', + 'glace-manager.h', + 'glace-client.h', + 'glace.h' ) glace_private_headers = files( - 'libglace-manager-private.h', - 'libglace-client-private.h', - 'libglace-private.h', + 'glace-manager-private.h', + 'glace-client-private.h', + 'glace-private.h' ) glace_headers = glace_public_headers + glace_private_headers @@ -59,7 +52,7 @@ libglace = library( glace_gir = gnome.generate_gir( libglace, header: 'libglace.h', - sources: glace_public_src + glace_public_headers, + sources: glace_sources + glace_public_headers, namespace: glace_namespace, nsversion: glace_version, identifier_prefix: glace_namespace, @@ -80,12 +73,12 @@ libgpaste_vapi = gnome.generate_vapi( install_headers( glace_headers , - subdir: glace_library_name, + subdir: glace_name, ) pkg_config.generate( - name: meson.project_name(), - filebase: glace_library_name, + name: glace_name, + filebase: glace_name, version: meson.project_version(), libraries: libglace, requires: dependencies, diff --git a/libglace/libglace-client.c b/libglace/libglace-client.c deleted file mode 100644 index 413b1c8..0000000 --- a/libglace/libglace-client.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "libglace-private.h" - -// client getters -guint glace_client_get_id(GlaceClient* self) { - return (guint)self->priv->id; -} - -const gchar* glace_client_get_app_id(GlaceClient* self) { - return CLIENT_GET_CURRENT_PROP(self, app_id); -} - -const gchar* glace_client_get_title(GlaceClient* self) { - return CLIENT_GET_CURRENT_PROP(self, title); -} - -gboolean glace_client_get_maximized(GlaceClient* self) { - return CLIENT_GET_CURRENT_PROP(self, maximized); -} - -gboolean glace_client_get_minimized(GlaceClient* self) { - return CLIENT_GET_CURRENT_PROP(self, minimized); -} - -gboolean glace_client_get_activated(GlaceClient* self) { - return CLIENT_GET_CURRENT_PROP(self, activated); -} - -gboolean glace_client_get_fullscreen(GlaceClient* self) { - return CLIENT_GET_CURRENT_PROP(self, fullscreen); -} - -gboolean glace_client_get_closed(GlaceClient* self) { - return self->priv->closed; -} - -// client methods -void glace_client_maximize(GlaceClient* self) { - // replacing EMPTY_TOKEN with a trailing comma - // will do the trick as well - RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); - - zwlr_foreign_toplevel_handle_v1_set_maximized(self->priv->wlr_handle); -} - -void glace_client_unmaximize(GlaceClient* self) { - RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); - - zwlr_foreign_toplevel_handle_v1_unset_maximized(self->priv->wlr_handle); -} - -void glace_client_minimize(GlaceClient* self) { - RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); - - zwlr_foreign_toplevel_handle_v1_set_minimized(self->priv->wlr_handle); -} - -void glace_client_unminimize(GlaceClient* self) { - RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); - - zwlr_foreign_toplevel_handle_v1_unset_minimized(self->priv->wlr_handle); -} - -void glace_client_close(GlaceClient* self) { - RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); - - zwlr_foreign_toplevel_handle_v1_close(self->priv->wlr_handle); -} - -void glace_client_activate(GlaceClient* self) { - RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); - - GdkSeat* gdk_seat = gdk_display_get_default_seat(self->priv->gdk_display); - struct wl_seat* seat = gdk_wayland_seat_get_wl_seat(gdk_seat); - - zwlr_foreign_toplevel_handle_v1_activate( - self->priv->wlr_handle, - seat - ); -} - -void glace_client_move( - GlaceClient* self, - GdkWindow* window, - const GdkRectangle* rectangle -) { - RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); - - if (window == NULL) { - zwlr_foreign_toplevel_handle_v1_set_rectangle( - self->priv->wlr_handle, - NULL, - rectangle->x, - rectangle->y, - rectangle->width, - rectangle->height - ); - return; - } - - zwlr_foreign_toplevel_handle_v1_set_rectangle( - self->priv->wlr_handle, - gdk_wayland_window_get_wl_surface(window), - rectangle->x, - rectangle->y, - rectangle->width, - rectangle->height - ); -} - -void glace_client_fullscreen(GlaceClient* self) { - RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); - - zwlr_foreign_toplevel_handle_v1_set_fullscreen(self->priv->wlr_handle, self->priv->output); -} - -void glace_client_unfullscreen(GlaceClient* self) { - RETURN_IF_INVALID_CLIENT(self, EMPTY_TOKEN); - - zwlr_foreign_toplevel_handle_v1_unset_fullscreen(self->priv->wlr_handle); -} diff --git a/libglace/libglace-manager-private.c b/libglace/libglace-manager-private.c deleted file mode 100644 index c6634ad..0000000 --- a/libglace/libglace-manager-private.c +++ /dev/null @@ -1,186 +0,0 @@ -#include "libglace-private.h" - -static guint glace_manager_signals[GLACE_MANAGER_N_SIGNALS] = {0}; - -static void glace_manager_signal_changed_emit(GlaceManager* self) { - g_signal_emit( - self, - glace_manager_signals[GLACE_MANAGER_SIGNAL_CHANGED], - 0 - ); -} - -static void glace_manager_signal_client_added_emit(GlaceManager* self, GlaceClient* client) { - g_signal_emit( - self, - glace_manager_signals[GLACE_MANAGER_SIGNAL_CLIENT_ADDED], - 1, - client - ); -} - -static void glace_manager_signal_client_removed_emit(GlaceManager* self, GlaceClient* client) { - g_signal_emit( - self, - glace_manager_signals[GLACE_MANAGER_SIGNAL_CLIENT_REMOVED], - 1, - client - ); -} - -static void on_client_closed_cleanup(GlaceClient* self, gpointer data) { - GlaceManager* manager = (GlaceManager*)data; - - glace_manager_signal_client_removed_emit(manager, self); - glace_manager_signal_changed_emit(manager); -} - -static void on_manager_toplevel( - void* data, - struct zwlr_foreign_toplevel_manager_v1* manager, - struct zwlr_foreign_toplevel_handle_v1* handle -) { - GlaceManager* self = data; - if (GLACE_IS_MANAGER(self) != true) { - return; - } - - GlaceClient* client = glace_client_new(handle, self->priv->gdk_display); - RETURN_IF_INVALID_CLIENT(client, EMPTY_TOKEN); - - g_signal_connect(client, "close", G_CALLBACK(on_client_closed_cleanup), self); - - glace_manager_signal_client_added_emit(self, client); - glace_manager_signal_changed_emit(self); - - g_debug("[INFO][MANAGER] got a client with id %u\n", glace_client_get_id(client)); -}; - -static void on_manager_finished( - void* data, - struct zwlr_foreign_toplevel_manager_v1* manager -) {} - -static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_listener = { - .toplevel = &on_manager_toplevel, - .finished = &on_manager_finished -}; - -static void on_registry_global( - void* data, - struct wl_registry* registry, - uint32_t name, - const char* interface, - uint32_t version -) { - g_debug("[INFO][PROTOCOL] got protocol with name %s\n", interface); - - if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) { - g_debug("[INFO][PROTOCOL] connecting to zwlr_foreign_toplevel_manager_v1\n"); - GlaceManager* self = data; - - if (GLACE_IS_MANAGER(self) != true) { - return; - } - - struct zwlr_foreign_toplevel_manager_v1* manager = wl_registry_bind( - registry, - name, - &zwlr_foreign_toplevel_manager_v1_interface, - max(version, 1) - ); - self->priv->wlr_manager = manager; - - zwlr_foreign_toplevel_manager_v1_add_listener(manager, &toplevel_manager_listener, self); - } -}; - -static void on_registry_global_remove(void* data, struct wl_registry* registry, uint32_t name) {} - -static const struct wl_registry_listener registry_listener = { - .global = &on_registry_global, - .global_remove = &on_registry_global_remove, -}; - -static void glace_manager_class_init(GlaceManagerClass* klass) { - GObjectClass* parent_class = G_OBJECT_CLASS(klass); - - g_type_class_add_private(klass, sizeof(GlaceManagerPrivate)); - - glace_manager_signals[GLACE_MANAGER_SIGNAL_CHANGED] = g_signal_new( - "changed", - GLACE_TYPE_MANAGER, - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 0 - ); - - glace_manager_signals[GLACE_MANAGER_SIGNAL_CLIENT_ADDED] = g_signal_new( - "client-added", - GLACE_TYPE_MANAGER, - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, - NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, - GLACE_TYPE_CLIENT - ); - - glace_manager_signals[GLACE_MANAGER_SIGNAL_CLIENT_REMOVED] = g_signal_new( - "client-removed", - GLACE_TYPE_MANAGER, - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, - NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, - GLACE_TYPE_CLIENT - ); -} - -static void glace_manager_init(GlaceManager* self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE( - self, - GLACE_TYPE_MANAGER, - GlaceManagerPrivate - ); - - GdkWaylandDisplay* gdk_display = gdk_display_get_default(); - if (GDK_IS_WAYLAND_DISPLAY(gdk_display) == false) { - // trap it in next check - gdk_display = NULL; - } - DISPLAY_CHECK_NULL(gdk_display); - - g_debug( - "[INFO][MANAGER] got display with name %s", - gdk_display_get_name(gdk_display) - ); - - struct wl_display* display = gdk_wayland_display_get_wl_display(gdk_display); - DISPLAY_CHECK_NULL(display); - - self->priv->display = display; - self->priv->gdk_display = gdk_display; - - // all aboard... - struct wl_registry* registry = wl_display_get_registry(self->priv->display); - wl_registry_add_listener(registry, ®istry_listener, self); - - wl_display_roundtrip(self->priv->display); - - if (self->priv->wlr_manager == NULL) - g_warning( - "[WARNING][MANAGER] your compositor does not support the wlr-foreign-toplevel-management protocol, Glace will not work if the protocol support is missing!" - ); -} - -G_DEFINE_TYPE(GlaceManager, glace_manager, G_TYPE_OBJECT); diff --git a/libglace/libglace-manager.c b/libglace/libglace-manager.c deleted file mode 100644 index e136ebe..0000000 --- a/libglace/libglace-manager.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "libglace-private.h" - -GlaceManager* glace_manager_new() { - return g_object_new(GLACE_TYPE_MANAGER, NULL); -} diff --git a/libglace/libglace-private.h b/libglace/libglace-private.h deleted file mode 100644 index 075b751..0000000 --- a/libglace/libglace-private.h +++ /dev/null @@ -1,2 +0,0 @@ -#include "libglace-client-private.h" -#include "libglace-manager-private.h" diff --git a/libglace/libglace.c b/libglace/libglace.c deleted file mode 100644 index 5e5cb4c..0000000 --- a/libglace/libglace.c +++ /dev/null @@ -1,2 +0,0 @@ -#include "libglace-manager.h" -#include "libglace-client.h" diff --git a/libglace/libglace.h b/libglace/libglace.h deleted file mode 100644 index 5e5cb4c..0000000 --- a/libglace/libglace.h +++ /dev/null @@ -1,2 +0,0 @@ -#include "libglace-manager.h" -#include "libglace-client.h" diff --git a/meson.build b/meson.build index 1ec3de2..87a0964 100644 --- a/meson.build +++ b/meson.build @@ -5,4 +5,4 @@ if build_machine.system() == 'windows' endif subdir('protocol') -subdir('libglace') +subdir('glace') diff --git a/protocol/hyprland-toplevel-export-v1.xml b/protocol/hyprland-toplevel-export-v1.xml new file mode 100644 index 0000000..fd005f3 --- /dev/null +++ b/protocol/hyprland-toplevel-export-v1.xml @@ -0,0 +1,204 @@ + + + + Copyright © 2022 Vaxry + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + This protocol allows clients to ask for exporting another toplevel's + surface(s) to a buffer. + + Particularly useful for sharing a single window. + + + + This object is a manager which offers requests to start capturing from a + source. + + + + Capture the next frame of a toplevel. (window) + + The captured frame will not contain any server-side decorations and will + ignore the compositor-set geometry, like e.g. rounded corners. + + It will contain all the subsurfaces and popups, however the latter will be clipped + to the geometry of the base surface. + + The handle parameter refers to the address of the window as seen in `hyprctl clients`. + For example, for d161e7b0 it would be 3512854448. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + Same as capture_toplevel, but with a zwlr_foreign_toplevel_handle_v1 handle. + + + + + + + + + + This object represents a single frame. + + When created, a series of buffer events will be sent, each representing a + supported buffer type. The "buffer_done" event is sent afterwards to + indicate that all supported buffer types have been enumerated. The client + will then be able to send a "copy" request. If the capture is successful, + the compositor will send a "flags" followed by a "ready" event. + + wl_shm buffers are always supported, ie. the "buffer" event is guaranteed to be sent. + + If the capture failed, the "failed" event is sent. This can happen anytime + before the "ready" event. + + Once either a "ready" or a "failed" event is received, the client should + destroy the frame. + + + + Provides information about wl_shm buffer parameters that need to be + used for this frame. This event is sent once after the frame is created + if wl_shm buffers are supported. + + + + + + + + + Copy the frame to the supplied buffer. The buffer must have the + correct size, see hyprland_toplevel_export_frame_v1.buffer and + hyprland_toplevel_export_frame_v1.linux_dmabuf. The buffer needs to have a + supported format. + + If the frame is successfully copied, a "flags" and a "ready" event is + sent. Otherwise, a "failed" event is sent. + + This event will wait for appropriate damage to be copied, unless the ignore_damage + arg is set to a non-zero value. + + + + + + + This event is sent right before the ready event when ignore_damage was + not set. It may be generated multiple times for each copy + request. + + The arguments describe a box around an area that has changed since the + last copy request that was derived from the current screencopy manager + instance. + + The union of all regions received between the call to copy + and a ready event is the total damage since the prior ready event. + + + + + + + + + + + + + + + + Provides flags about the frame. This event is sent once before the + "ready" event. + + + + + + Called as soon as the frame is copied, indicating it is available + for reading. This event includes the time at which presentation happened + at. + + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, + each component being an unsigned 32-bit value. Whole seconds are in + tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, + and the additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part + may have an arbitrary offset at start. + + After receiving this event, the client should destroy the object. + + + + + + + + This event indicates that the attempted frame copy has failed. + + After receiving this event, the client should destroy the object. + + + + + Destroys the frame. This request can be sent at any time by the client. + + + + + Provides information about linux-dmabuf buffer parameters that need to + be used for this frame. This event is sent once after the frame is + created if linux-dmabuf buffers are supported. + + + + + + + + This event is sent once after all buffer events have been sent. + + The client should proceed to create a buffer of one of the supported + types, and send a "copy" request. + + + + diff --git a/protocol/meson.build b/protocol/meson.build index f869e4b..ec87aff 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,34 +1,40 @@ wayland_dep = dependency('wayland-client') -protocol_name = 'wlr-foreign-toplevel-management-unstable-v1' -protocol_file = files(protocol_name + '.xml') +protocol_names = [ + 'wlr-foreign-toplevel-management-unstable-v1', + 'hyprland-toplevel-export-v1', +] wayland_scanner_dep = dependency('wayland-scanner', native: true) wayland_scanner = find_program( - wayland_scanner_dep.get_variable( - pkgconfig: 'wayland_scanner' - ) + wayland_scanner_dep.get_variable( + pkgconfig: 'wayland_scanner' + ) ) protocol_sources = [] -protocol_sources += custom_target( - protocol_name + '.h', - command: [ wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@' ], - input: protocol_file, - output: protocol_name + '.h', -) - output_type = 'private-code' if wayland_scanner_dep.version().version_compare('< 1.14.91') output_type = 'code' endif -protocol_sources += custom_target( - protocol_name + '.c', - command: [ wayland_scanner, output_type, '@INPUT@', '@OUTPUT@' ], - input: protocol_file, - output: protocol_name + '.c', -) +foreach protocol_name : protocol_names + protocol_file = files(protocol_name + '.xml') + + protocol_sources += custom_target( + protocol_name + '.h', + command: [ wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@' ], + input: protocol_file, + output: protocol_name + '.h', + ) + + protocol_sources += custom_target( + protocol_name + '.c', + command: [ wayland_scanner, output_type, '@INPUT@', '@OUTPUT@' ], + input: protocol_file, + output: protocol_name + '.c', + ) +endforeach toplevel_management_dep = declare_dependency( dependencies: [ From aa83ad705de66c138a1ba4edd6a4f550b7239b71 Mon Sep 17 00:00:00 2001 From: The Darsh <129121284+its-darsh@users.noreply.github.com> Date: Fri, 15 Aug 2025 21:20:23 +0300 Subject: [PATCH 3/4] init toplevel mapping --- glace/glace-manager.c | 3 + protocol/hyprland-toplevel-mapping-v1.xml | 124 ++++++++++++++++++++++ protocol/meson.build | 1 + 3 files changed, 128 insertions(+) create mode 100644 protocol/hyprland-toplevel-mapping-v1.xml diff --git a/glace/glace-manager.c b/glace/glace-manager.c index 3f00b5a..fc64a88 100644 --- a/glace/glace-manager.c +++ b/glace/glace-manager.c @@ -122,6 +122,9 @@ static void glace_manager_class_init(GlaceManagerClass* klass) { g_type_class_add_private(klass, sizeof(GlaceManagerPrivate)); + // add public methods + klass->capture_client = glace_manager_capture_client; + glace_manager_signals[GLACE_MANAGER_SIGNAL_CHANGED] = g_signal_new( "changed", GLACE_TYPE_MANAGER, diff --git a/protocol/hyprland-toplevel-mapping-v1.xml b/protocol/hyprland-toplevel-mapping-v1.xml new file mode 100644 index 0000000..a8da876 --- /dev/null +++ b/protocol/hyprland-toplevel-mapping-v1.xml @@ -0,0 +1,124 @@ + + + + Copyright © 2025 WhySoBad + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + This protocol allows clients to retrieve the mapping of toplevels to hyprland window addresses. + + + + + This object is a manager which offers requests to retrieve a window address + for a toplevel. + + + + + Get the window address for a toplevel. + + + + + + + + Get the window address for a wlr toplevel. + + + + + + + + All objects created by the manager will still remain valid, until their appropriate destroy + request has been called. + + + + + + + This object represents a mapping of a (wlr) toplevel to a window address. + + Once created, the `window_address` event will be sent containing the address of the window + associated with the toplevel. + Should the mapping fail, the `failed` event will be sent. + + + + + The full 64bit window address. The `address` field contains the lower 32 bits whilst the + `address_hi` contains the upper 32 bits + + + + + + + + The mapping of the toplevel to a window address failed. Most likely the window does not + exist (anymore). + + + + + + Destroy the handle. This request can be sent at any time by the client. + + + + \ No newline at end of file diff --git a/protocol/meson.build b/protocol/meson.build index ec87aff..30fb6b4 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -2,6 +2,7 @@ wayland_dep = dependency('wayland-client') protocol_names = [ 'wlr-foreign-toplevel-management-unstable-v1', + 'hyprland-toplevel-mapping-v1', 'hyprland-toplevel-export-v1', ] From 25402cdd520550a729f7f21d7480d62bf41b432e Mon Sep 17 00:00:00 2001 From: The Darsh <129121284+its-darsh@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:41:36 +0300 Subject: [PATCH 4/4] feat: toplevel mapping (BABY!) and optimized colorspace conversions this commit also changes the name of the library from `libglace` to just `glace` for those who use the library in C this is the pursuit of optimizations, both the processor and the user are now happy, future maintainers can pray though. --- glace/buffer-utils.h | 100 ++++++++++++++++++++++ glace/glace-client-private.h | 10 +-- glace/glace-client.c | 75 ++++++++++++++++ glace/glace-client.h | 4 + glace/glace-manager-private.h | 15 +++- glace/glace-manager.c | 69 +++++++++------ glace/glace-manager.h | 28 ++++-- glace/glace-private.h | 5 +- glace/meson.build | 16 ++-- meson.build | 2 +- protocol/hyprland-toplevel-mapping-v1.xml | 2 +- 11 files changed, 270 insertions(+), 56 deletions(-) create mode 100644 glace/buffer-utils.h diff --git a/glace/buffer-utils.h b/glace/buffer-utils.h new file mode 100644 index 0000000..67322d5 --- /dev/null +++ b/glace/buffer-utils.h @@ -0,0 +1,100 @@ +#include +#include +#include + +#if defined(__SSSE3__) +#include +#elif defined(__SSE2__) +#include +#endif +#if defined(__AVX2__) +#include +#endif + +static inline void buffer_utils_bgrx_to_rgbx(unsigned char* restrict data, long pixels) { +#if defined(__AVX2__) + // 8 pixels (32 bytes) per iteration + long i = 0; + long n = pixels & ~7UL; + + const __m256i shuffle_mask = _mm256_setr_epi8( + 2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15, 18, 17, 16, 19, 22, 21, 20, 23, 26, 25, 24, 27, 30, 29, 28, 31 + ); + + for (; i < n; i += 8) { + __m256i px = _mm256_loadu_si256((__m256i*)(data + i * 4)); + px = _mm256_shuffle_epi8(px, shuffle_mask); + _mm256_storeu_si256((__m256i*)(data + i * 4), px); + } + + // leftovers + for (; i < pixels; i++) { + unsigned char pixel = data[i]; + data[i] = (pixel & 0xFF00FF00) | ((pixel << 16) & 0x00FF0000) | ((pixel >> 16) & 0xFF); + } +#elif defined(__SSSE3__) + // 4 pixels (16 bytes) per iteration + long i = 0; + long n = pixels & ~3UL; + + const __m128i shuffle_mask = _mm_setr_epi8( + 2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15 + ); + + for (; i < n; i += 4) { + __m128i px = _mm_loadu_si128((__m128i*)(data + i * 4)); + px = _mm_shuffle_epi8(px, shuffle_mask); + _mm_storeu_si128((__m128i*)(data + i * 4), px); + } + + for (; i < pixels; i++) { + unsigned char pixel = data[i]; + data[i] = (pixel & 0xFF00FF00) | ((pixel << 16) & 0x00FF0000) | ((pixel >> 16) & 0xFF); + } +#elif defined(__SSE2__) + // 4 pixels (16 bytes) per iteration (shift & mask) + long i = 0; + long n = pixels & ~3UL; + + __m128i mask_rb = _mm_set1_epi32(0x00FF00FF); // R and B + __m128i mask_gx = _mm_set1_epi32(0xFF00FF00); // G and X + + for (; i < n; i += 4) { + __m128i px = _mm_loadu_si128((__m128i*)(data + i * 4)); + + __m128i rb = _mm_and_si128(px, mask_rb); + __m128i gx = _mm_and_si128(px, mask_gx); + + __m128i r = _mm_slli_epi32(rb, 16); + __m128i b = _mm_srli_epi32(rb, 16); + + __m128i swapped = _mm_or_si128(gx, _mm_or_si128(r, b)); + _mm_storeu_si128((__m128i*)(data + i * 4), swapped); + } + + for (; i < pixels; i++) { + unsigned char pixel = data[i]; + data[i] = (pixel & 0xFF00FF00) | ((pixel << 16) & 0x00FF0000) | ((pixel >> 16) & 0xFF); + } + +#else + for (long i = 0; i < pixels; i++) { + unsigned char pixel = data[i]; + + data[i] = (pixel & 0xFF00FF00) | ((pixel << 16) & 0x00FF0000) | ((pixel >> 16) & 0xFF); + } +#endif +} + +static void buffer_utils_log_available_accelerator() { + const char* prefix = "[INFO][BUFFER]"; +#if defined(__AVX2__) + g_debug("%s using AVX2 for buffer format conversion acceleration", prefix); +#elif defined(__SSSE3__) + g_debug("%s using SSSE3 for buffer format conversion acceleration", prefix); +#elif defined(__SSE2__) + g_debug("%s using SSE2 for buffer format conversion acceleration", prefix); +#else + g_debug("%s compiled with no buffer conversion acceleration", prefix); +#endif +} \ No newline at end of file diff --git a/glace/glace-client-private.h b/glace/glace-client-private.h index 1d39967..a44a898 100644 --- a/glace/glace-client-private.h +++ b/glace/glace-client-private.h @@ -51,13 +51,13 @@ } while (0) // signal emitters -static void glace_client_signal_changed_emit(GlaceClient* self); -static void glace_client_signal_close_emit(GlaceClient* self); +// static void glace_client_signal_changed_emit(GlaceClient* self); +// static void glace_client_signal_close_emit(GlaceClient* self); // methods -static void glace_client_init(GlaceClient* self); -static void glace_client_class_init(GlaceClientClass* klass); +// static void glace_client_init(GlaceClient* self); +// static void glace_client_class_init(GlaceClientClass* klass); -GlaceClient* glace_client_new(struct zwlr_foreign_toplevel_handle_v1* wlr_handle, GdkWaylandDisplay* gdk_display); +GlaceClient* glace_client_new(struct zwlr_foreign_toplevel_handle_v1* wlr_handle, struct hyprland_toplevel_mapping_manager_v1* hl_mapping_manager, GdkWaylandDisplay* gdk_display); #endif /* __LIBGLACE_CLIENT_PRIVATE_H__ */ diff --git a/glace/glace-client.c b/glace/glace-client.c index 252b87c..91af9de 100644 --- a/glace/glace-client.c +++ b/glace/glace-client.c @@ -5,6 +5,14 @@ static GParamSpec* glace_client_properties[GLACE_CLIENT_N_PROPERTIES] = { NULL, }; +static void on_mapping_handle_window_address(void* user_data, struct hyprland_toplevel_window_mapping_handle_v1* handle, uint32_t address_hi, uint32_t address); +static void on_mapping_handle_failed(void* user_data, struct hyprland_toplevel_window_mapping_handle_v1* handle); + +static const struct hyprland_toplevel_window_mapping_handle_v1_listener mapping_handle_listener = { + .window_address = &on_mapping_handle_window_address, + .failed = &on_mapping_handle_failed, +}; + static void glace_client_signal_changed_emit(GlaceClient* self) { g_signal_emit( self, @@ -54,6 +62,9 @@ static void glace_client_get_property( case GLACE_CLIENT_PROPERTY_CLOSED: g_value_set_boolean(value, self->priv->closed); break; + case GLACE_CLIENT_PROPERTY_HYPRLAND_ADDRESS: + g_value_set_uint64(value, self->priv->hyprland_address); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -285,6 +296,16 @@ static void glace_client_class_init(GlaceClientClass* klass) { G_PARAM_READABLE ); + glace_client_properties[GLACE_CLIENT_PROPERTY_HYPRLAND_ADDRESS] = g_param_spec_uint64( + "hyprland-address", + "hyprland-address", + "the internal window address for hyprland in 64bits", + 0, + G_MAXUINT64, + 0, + G_PARAM_READABLE + ); + g_object_class_install_properties( parent_class, GLACE_CLIENT_N_PROPERTIES, @@ -300,6 +321,7 @@ static void glace_client_init(GlaceClient* self) { ); self->priv->closed = false; + self->priv->hyprland_address = 0; CLIENT_SET_CURRENT_PROP(self, app_id, false); CLIENT_SET_CURRENT_PROP(self, title, false); CLIENT_SET_CURRENT_PROP(self, maximized, false); @@ -308,8 +330,41 @@ static void glace_client_init(GlaceClient* self) { CLIENT_SET_CURRENT_PROP(self, fullscreen, false); } +static void on_mapping_handle_window_address( + void* user_data, + struct hyprland_toplevel_window_mapping_handle_v1* handle, + // that's how you know a protocol is good + uint32_t address_hi, + uint32_t address_lo +) { + GlaceClient* self = GLACE_CLIENT(user_data); + RETURN_IF_INVALID_CLIENT(self, hyprland_toplevel_window_mapping_handle_v1_destroy(handle)); + + self->priv->hyprland_address = ((guint64)address_hi << 32) | address_lo; + + // we have a proper address now, notify the user... + g_object_notify_by_pspec( + G_OBJECT(self), + glace_client_properties[GLACE_CLIENT_PROPERTY_HYPRLAND_ADDRESS] + ); + + glace_client_signal_changed_emit(self); + + hyprland_toplevel_window_mapping_handle_v1_destroy(handle); +} + +static void on_mapping_handle_failed( + void* user_data, + struct hyprland_toplevel_window_mapping_handle_v1* handle +) { + // something's fucked up, cleanup meanwhile + hyprland_toplevel_window_mapping_handle_v1_destroy(handle); + return; +} + GlaceClient* glace_client_new( struct zwlr_foreign_toplevel_handle_v1* wlr_handle, + struct hyprland_toplevel_mapping_manager_v1* hl_mapping_manager, GdkWaylandDisplay* gdk_display ) { GlaceClient* self = g_object_new(GLACE_TYPE_CLIENT, NULL); @@ -321,6 +376,22 @@ GlaceClient* glace_client_new( &toplevel_handle_listener, self ); + + if (!hl_mapping_manager) + return self; + + struct hyprland_toplevel_window_mapping_handle_v1* + handle = hyprland_toplevel_mapping_manager_v1_get_window_for_toplevel_wlr( + hl_mapping_manager, + self->priv->wlr_handle + ); + + hyprland_toplevel_window_mapping_handle_v1_add_listener( + handle, + &mapping_handle_listener, + self // user_data + ); + return self; } @@ -329,6 +400,10 @@ guint glace_client_get_id(GlaceClient* self) { return (guint)self->priv->id; } +guint64 glace_client_get_hyprland_address(GlaceClient* self) { + return (guint64)self->priv->hyprland_address; +} + const gchar* glace_client_get_app_id(GlaceClient* self) { return CLIENT_GET_CURRENT_PROP(self, app_id); } diff --git a/glace/glace-client.h b/glace/glace-client.h index 280764f..13f602e 100644 --- a/glace/glace-client.h +++ b/glace/glace-client.h @@ -69,6 +69,8 @@ struct _GlaceClientPrivate { GdkWaylandDisplay* gdk_display; GlaceClientProperties current_properties; GlaceClientProperties pending_properties; + + uint64_t hyprland_address; }; enum { @@ -87,6 +89,7 @@ enum { GLACE_CLIENT_PROPERTY_ACTIVATED, GLACE_CLIENT_PROPERTY_FULLSCREEN, GLACE_CLIENT_PROPERTY_CLOSED, + GLACE_CLIENT_PROPERTY_HYPRLAND_ADDRESS, GLACE_CLIENT_N_PROPERTIES }; @@ -99,6 +102,7 @@ gboolean glace_client_get_minimized(GlaceClient* self); gboolean glace_client_get_activated(GlaceClient* self); gboolean glace_client_get_fullscreen(GlaceClient* self); gboolean glace_client_get_closed(GlaceClient* self); +guint64 glace_client_get_hyprland_address(GlaceClient* self); // methods GType glace_client_get_type(); diff --git a/glace/glace-manager-private.h b/glace/glace-manager-private.h index 33713ca..6ad8ad3 100644 --- a/glace/glace-manager-private.h +++ b/glace/glace-manager-private.h @@ -26,6 +26,15 @@ typedef struct _GlaceFrameData GlaceFrameData; typedef struct _GlaceFrameBuffer GlaceFrameBuffer; +struct _GlaceManagerPrivate { + GdkWaylandDisplay* gdk_display; + struct wl_display* display; + struct wl_shm* wl_shm; + struct zwlr_foreign_toplevel_manager_v1* wlr_manager; + struct hyprland_toplevel_export_manager_v1* hl_export_manager; + struct hyprland_toplevel_mapping_manager_v1* hl_mapping_manager; +}; + struct _GlaceFrameBuffer { struct wl_buffer* wl_buffer; void* raw_buffer; @@ -52,8 +61,8 @@ const static struct hyprland_toplevel_export_frame_v1_listener export_manager_fr // static void glace_manager_init(GlaceManager* self); // static void glace_manager_class_init(GlaceManagerClass* klass); -static void glace_manager_signal_changed_emit(GlaceManager* self); -static void glace_manager_signal_client_added_emit(GlaceManager* self, GlaceClient* client); -static void glace_manager_signal_client_removed_emit(GlaceManager* self, GlaceClient* client); +// static void glace_manager_signal_changed_emit(GlaceManager* self); +// static void glace_manager_signal_client_added_emit(GlaceManager* self, GlaceClient* client); +// static void glace_manager_signal_client_removed_emit(GlaceManager* self, GlaceClient* client); #endif /* __LIBGLACE_MANAGER_PRIVATE_H__ */ diff --git a/glace/glace-manager.c b/glace/glace-manager.c index fc64a88..525f1d2 100644 --- a/glace/glace-manager.c +++ b/glace/glace-manager.c @@ -1,3 +1,4 @@ +#include "buffer-utils.h" #include "glace-private.h" static guint glace_manager_signals[GLACE_MANAGER_N_SIGNALS] = {0}; @@ -45,7 +46,7 @@ static void on_manager_toplevel( return; } - GlaceClient* client = glace_client_new(handle, self->priv->gdk_display); + GlaceClient* client = glace_client_new(handle, self->priv->hl_mapping_manager, self->priv->gdk_display); RETURN_IF_INVALID_CLIENT(client, EMPTY_TOKEN); g_signal_connect(client, "close", G_CALLBACK(on_client_closed_cleanup), self); @@ -101,6 +102,14 @@ static void on_registry_global( max(version, 1) ); self->priv->hl_export_manager = export_manager; + } else if (strcmp(interface, hyprland_toplevel_mapping_manager_v1_interface.name) == 0) { + g_debug("[INFO][PROTOCOL] connecting to hyprland_toplevel_mapping_manager_v1\n"); + self->priv->hl_mapping_manager = wl_registry_bind( + registry, + name, + &hyprland_toplevel_mapping_manager_v1_interface, + max(version, 1) + ); } else if (strcmp(interface, wl_shm_interface.name) == 0) { g_debug("[INFO][PROTOCOL] getting a shared memory buffer\n"); @@ -118,7 +127,7 @@ static const struct wl_registry_listener registry_listener = { }; static void glace_manager_class_init(GlaceManagerClass* klass) { - GObjectClass* parent_class = G_OBJECT_CLASS(klass); + // GObjectClass* parent_class = G_OBJECT_CLASS(klass); g_type_class_add_private(klass, sizeof(GlaceManagerPrivate)); @@ -188,6 +197,7 @@ static void glace_manager_init(GlaceManager* self) { self->priv->display = display; self->priv->gdk_display = gdk_display; + self->priv->hl_mapping_manager = NULL; // all aboard... struct wl_registry* registry = wl_display_get_registry(self->priv->display); @@ -199,9 +209,11 @@ static void glace_manager_init(GlaceManager* self) { g_warning( "[WARNING][MANAGER] your compositor does not support the wlr-foreign-toplevel-management protocol, Glace will not work if the protocol support is missing!" ); + + buffer_utils_log_available_accelerator(); } -static int anonymous_shm_open() { +static inline int anonymous_shm_open() { char* name; int retries = 100; do { @@ -219,7 +231,7 @@ static int anonymous_shm_open() { return -1; } -static int create_shm_file(off_t size) { +static inline int create_shm_file(off_t size) { int fd = anonymous_shm_open(); if (fd < 0) { return fd; @@ -276,16 +288,6 @@ static GlaceFrameBuffer* glace_frame_buffer_new(struct wl_shm* shm, enum wl_shm_ return buffer; } -static inline void frame_buffer_correct_format(GlaceFrameBuffer* buffer) { - // BGRA -> RGBA - uint32_t* agbr = (uint32_t*)buffer->raw_buffer; - for (size_t i = 0; i < buffer->width * buffer->height; i++) { - uint32_t pixel = agbr[i]; - - agbr[i] = (pixel & 0xFF00FF00) | ((pixel << 16) & 0x00FF0000) | ((pixel >> 16) & 0xFF); - } -} - static void glace_frame_buffer_destroy(GlaceFrameBuffer* buffer) { if (buffer == NULL) { return; @@ -315,26 +317,30 @@ static void glace_frame_data_destroy(GlaceFrameData* data) { return; } +static void do_release_pixbuf(guchar* pixels, gpointer user_data) { + GlaceFrameData* data = user_data; + // we'll miss you + glace_frame_data_destroy(data); +} + static void on_export_manager_frame_buffer(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { GlaceFrameData* data = user_data; data->buffer = glace_frame_buffer_new(data->manager->priv->wl_shm, format, width, height, stride); return; } -static void on_export_manager_frame_damage(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {} - -static void on_export_manager_frame_flags(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t flags) {} - static void on_export_manager_frame_ready(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { GlaceFrameData* data = user_data; - if (!data || !data->buffer) { + if (!data) + goto finalize; + if (!data->buffer) { data->callback(NULL, data->callback_data); - return; + glace_frame_data_destroy(data); + goto finalize; } - frame_buffer_correct_format(data->buffer); - + buffer_utils_bgrx_to_rgbx(data->buffer->raw_buffer, data->buffer->width * data->buffer->height); GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( data->buffer->raw_buffer, GDK_COLORSPACE_RGB, @@ -343,15 +349,15 @@ static void on_export_manager_frame_ready(void* user_data, struct hyprland_tople data->buffer->width, data->buffer->height, data->buffer->stride, - NULL, - NULL + do_release_pixbuf, + data ); data->callback(pixbuf, data->callback_data); - glace_frame_data_destroy(data); + // gdk_pixbuf_unref(pixbuf); // !FIXME: unsafe, add a function annotation for giving ownership to the caller +finalize: hyprland_toplevel_export_frame_v1_destroy(export_frame); - return; } @@ -366,8 +372,6 @@ static void on_export_manager_frame_failed(void* user_data, struct hyprland_topl return; } -static void on_export_manager_frame_linux_dmabuf(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t format, uint32_t width, uint32_t height) {} - static void on_export_manager_frame_buffer_done(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame) { GlaceFrameData* data = user_data; @@ -377,6 +381,10 @@ static void on_export_manager_frame_buffer_done(void* user_data, struct hyprland return; } +static void on_export_manager_frame_damage(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {} +static void on_export_manager_frame_flags(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t flags) {} +static void on_export_manager_frame_linux_dmabuf(void* user_data, struct hyprland_toplevel_export_frame_v1* export_frame, uint32_t format, uint32_t width, uint32_t height) {} + static const struct hyprland_toplevel_export_frame_v1_listener export_manager_frame_listener = { .buffer = &on_export_manager_frame_buffer, .damage = &on_export_manager_frame_damage, @@ -387,6 +395,11 @@ static const struct hyprland_toplevel_export_frame_v1_listener export_manager_fr .buffer_done = &on_export_manager_frame_buffer_done }; +void glace_manager_finalize(GObject* object) { + GlaceManager* self = GLACE_MANAGER(object); + G_OBJECT_CLASS(g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)))->finalize(object); +} + // public methods GlaceManager* glace_manager_new() { return g_object_new(GLACE_TYPE_MANAGER, NULL); diff --git a/glace/glace-manager.h b/glace/glace-manager.h index 0f34665..5ab4ab1 100644 --- a/glace/glace-manager.h +++ b/glace/glace-manager.h @@ -19,6 +19,14 @@ G_BEGIN_DECLS typedef struct _GlaceManager GlaceManager; typedef struct _GlaceManagerPrivate GlaceManagerPrivate; typedef struct _GlaceManagerClass GlaceManagerClass; + +/** + * GlaceManagerCaptureClientCallback: + * @pixbuf: (transfer full): the captured pixbuf, ownership is transferred to the caller + * + * called when a client capture operation completes. + * the caller must unref the @pixbuf once uneeded to avoid leaks. + */ typedef void (*GlaceManagerCaptureClientCallback)(GdkPixbuf* pixbuf, gpointer user_data); struct _GlaceManager { @@ -29,18 +37,10 @@ struct _GlaceManager { struct _GlaceManagerClass { GObjectClass parent_class; - // methods + // public methods void (*capture_client)(GlaceManager* self, GlaceClient* client, gboolean overlay_cursor, GlaceManagerCaptureClientCallback callback, gpointer user_data, GDestroyNotify notify); }; -struct _GlaceManagerPrivate { - GdkWaylandDisplay* gdk_display; - struct wl_display* display; - struct wl_shm* wl_shm; - struct zwlr_foreign_toplevel_manager_v1* wlr_manager; - struct hyprland_toplevel_export_manager_v1* hl_export_manager; -}; - enum { GLACE_MANAGER_SIGNAL_CHANGED, GLACE_MANAGER_SIGNAL_CLIENT_ADDED, @@ -51,6 +51,16 @@ enum { // methods GType glace_manager_get_type(); GlaceManager* glace_manager_new(); + +/** + * glace_manager_capture_client: + * @self: a #GlaceManager + * @client: the #GlaceClient instance to capture + * @overlay_cursor: whether or not to render the cursor on the client, optional and defaults to false + * @callback: a callback for receiving the rendered snapshot, this callback should be able of receiving a GdkPixbuf where the data resigns + * + * try and get a snapshot capture of a client, this only works on hyprland currently. + */ void glace_manager_capture_client(GlaceManager* self, GlaceClient* client, gboolean overlay_cursor, GlaceManagerCaptureClientCallback callback, gpointer user_data, GDestroyNotify notify); G_END_DECLS diff --git a/glace/glace-private.h b/glace/glace-private.h index ae5dee0..1a6eedc 100644 --- a/glace/glace-private.h +++ b/glace/glace-private.h @@ -3,7 +3,8 @@ #include #include -#include "hyprland-toplevel-export-v1.h" #include "glace-client-private.h" #include "glace-manager-private.h" -#include "wlr-foreign-toplevel-management-unstable-v1.h" \ No newline at end of file +#include "hyprland-toplevel-export-v1.h" +#include "hyprland-toplevel-mapping-v1.h" +#include "wlr-foreign-toplevel-management-unstable-v1.h" diff --git a/glace/meson.build b/glace/meson.build index c354f5a..04ce575 100644 --- a/glace/meson.build +++ b/glace/meson.build @@ -6,7 +6,7 @@ glace_version = meson.project_version() glace_description = 'a library for managing wayland clients' glace_url = 'https://github.com/Fabric-Development/glace' -glace_library_name = 'lib' + glace_name +glace_library_name = glace_name glace_namespace = 'Glace' glace_package_name = glace_namespace + '-' + glace_version @@ -23,6 +23,7 @@ glace_public_headers = files( ) glace_private_headers = files( + 'buffer-utils.h', 'glace-manager-private.h', 'glace-client-private.h', 'glace-private.h' @@ -45,20 +46,21 @@ all_dependencies = [ libglace = library( glace_library_name, glace_sources, + c_args: ['-O3', '-march=native'], dependencies: all_dependencies, install: true, ) glace_gir = gnome.generate_gir( libglace, - header: 'libglace.h', + header: 'glace.h', sources: glace_sources + glace_public_headers, namespace: glace_namespace, nsversion: glace_version, identifier_prefix: glace_namespace, - includes: ['GObject-2.0', 'Gtk-3.0'], + includes: ['GObject-2.0', 'GLib-2.0', 'Gtk-3.0'], dependencies: dependencies, - header: 'libglace/libglace.h', + header: 'glace/glace.h', symbol_prefix: 'glace', install: true, ) @@ -67,13 +69,13 @@ glace_gir = gnome.generate_gir( libgpaste_vapi = gnome.generate_vapi( glace_package_name, sources: [glace_gir[0]], - packages: [ 'gtk+-3.0', 'gio-2.0', 'glib-2.0', 'gobject-2.0' ], + packages: ['gtk+-3.0', 'gio-2.0', 'glib-2.0', 'gobject-2.0'], install: true, ) install_headers( - glace_headers , - subdir: glace_name, + glace_public_headers, + subdir: glace_name, ) pkg_config.generate( diff --git a/meson.build b/meson.build index 87a0964..0e575a9 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project('glace', 'c', version: '0.1', license: 'AGPL-3.0-or-later') if build_machine.system() == 'windows' - error('libglace is not meant for windows users, consider moving to linux.') + error('glace is not meant for windows users, consider moving to linux.') endif subdir('protocol') diff --git a/protocol/hyprland-toplevel-mapping-v1.xml b/protocol/hyprland-toplevel-mapping-v1.xml index a8da876..8fc3174 100644 --- a/protocol/hyprland-toplevel-mapping-v1.xml +++ b/protocol/hyprland-toplevel-mapping-v1.xml @@ -52,7 +52,7 @@