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/2] 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/2] 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: [