From 00be66dad9d27fb9f271e7a645ef90a343753819 Mon Sep 17 00:00:00 2001 From: Hannes Schulze Date: Sun, 26 Apr 2026 01:22:34 +0200 Subject: sway workspace service skeleton --- .../workspaces/hyprland-workspace-service.c | 58 +-- src/services/workspaces/meson.build | 1 + src/services/workspaces/sway-workspace-service.c | 450 +++++++++++++++++++++ src/services/workspaces/sway-workspace-service.h | 13 + 4 files changed, 465 insertions(+), 57 deletions(-) create mode 100644 src/services/workspaces/sway-workspace-service.c create mode 100644 src/services/workspaces/sway-workspace-service.h (limited to 'src/services/workspaces') diff --git a/src/services/workspaces/hyprland-workspace-service.c b/src/services/workspaces/hyprland-workspace-service.c index 27a01b7..3abe152 100644 --- a/src/services/workspaces/hyprland-workspace-service.c +++ b/src/services/workspaces/hyprland-workspace-service.c @@ -1,6 +1,4 @@ #include "services/workspaces/hyprland-workspace-service.h" -#include "glib-object.h" -#include "services/workspace-service.h" #include #include #include @@ -135,9 +133,6 @@ static gint64 foobar_hyprland_workspace_service_parse_id static gchar* foobar_hyprland_workspace_service_get_hyprland_base_path ( void ); static gint foobar_hyprland_workspace_service_compare_ids ( gconstpointer a, gconstpointer b ); -static gint foobar_hyprland_workspace_service_sort_func ( gconstpointer item_a, - gconstpointer item_b, - gpointer userdata ); G_DEFINE_TYPE_WITH_CODE( @@ -457,7 +452,7 @@ void foobar_hyprland_workspace_service_init( FoobarHyprlandWorkspaceService* sel { self->workspaces = g_list_store_new( FOOBAR_TYPE_WORKSPACE ); - GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_hyprland_workspace_service_sort_func, NULL, NULL ); + GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_workspace_sort_func, NULL, NULL ); self->sorted_workspaces = gtk_sort_list_model_new( G_LIST_MODEL( g_object_ref( self->workspaces ) ), GTK_SORTER( sorter ) ); @@ -1287,57 +1282,6 @@ gint foobar_hyprland_workspace_service_compare_ids( return 0; } -// -// Sorting callback for workspaces. Items are sorted to be in the following order: -// 1. normal -// 2. named persistent -// 3. named -// 4. special -// 5. named special -// -gint foobar_hyprland_workspace_service_sort_func( - gconstpointer item_a, - gconstpointer item_b, - gpointer userdata ) -{ - (void)userdata; - - FoobarWorkspace* workspace_a = (FoobarWorkspace*)item_a; - FoobarWorkspace* workspace_b = (FoobarWorkspace*)item_b; - gint64 id_a = foobar_workspace_get_id( workspace_a ); - gint64 id_b = foobar_workspace_get_id( workspace_b ); - gboolean special_a = ( foobar_workspace_get_flags( workspace_a ) & FOOBAR_WORKSPACE_FLAGS_SPECIAL ) != 0; - gboolean special_b = ( foobar_workspace_get_flags( workspace_b ) & FOOBAR_WORKSPACE_FLAGS_SPECIAL ) != 0; - gboolean named_a = id_a < 0; - gboolean named_b = id_b < 0; - - // both normal (includes numbered persistent) => sort by ID - if ( !named_a && !named_b ) - { - if ( id_a < id_b ) { return -1; } - if ( id_a > id_b ) { return 1; } - return 0; - } - - // one normal, one special => normal first - if ( special_a && !special_b ) { return 1; } - if ( !special_a && special_b ) { return -1; } - - // only one normal, one named - if ( named_a && !named_b ) { return -1; } - if ( !named_a && named_b ) { return 1; } - - // both special - if ( special_a && special_b ) - { - // if one is -99 => put it last - if ( id_a == -99 && id_b != -99 ) { return 1; } - if ( id_a != -99 && id_b == -99 ) { return -1; } - } - - return g_strcmp0( foobar_workspace_get_name( workspace_a ), foobar_workspace_get_name( workspace_b ) ); -} - // // Create a new EventData structure. // diff --git a/src/services/workspaces/meson.build b/src/services/workspaces/meson.build index 7de31dd..5c96af4 100644 --- a/src/services/workspaces/meson.build +++ b/src/services/workspaces/meson.build @@ -1,3 +1,4 @@ foobar_sources += files( 'hyprland-workspace-service.c', + 'sway-workspace-service.c', ) diff --git a/src/services/workspaces/sway-workspace-service.c b/src/services/workspaces/sway-workspace-service.c new file mode 100644 index 0000000..5ab9cc4 --- /dev/null +++ b/src/services/workspaces/sway-workspace-service.c @@ -0,0 +1,450 @@ +#include "services/workspaces/sway-workspace-service.h" +#include +#include + +// +// FoobarSwayWorkspace: +// +// Representation of a workspace in Sway. +// + +#define FOOBAR_TYPE_SWAY_WORKSPACE foobar_sway_workspace_get_type( ) + +G_DECLARE_FINAL_TYPE( FoobarSwayWorkspace, foobar_sway_workspace, FOOBAR, SWAY_WORKSPACE, GObject ) + +struct _FoobarSwayWorkspace +{ + GObject parent_instance; + FoobarSwayWorkspaceService* service; + gint64 id; + gchar* name; + gchar* monitor; + FoobarWorkspaceFlags flags; +}; + +enum +{ + WORKSPACE_PROP_ID = 1, + WORKSPACE_PROP_NAME, + WORKSPACE_PROP_MONITOR, + WORKSPACE_PROP_FLAGS, + N_WORKSPACE_PROPS, +}; + +static void foobar_sway_workspace_class_init ( FoobarSwayWorkspaceClass* klass ); +static void foobar_sway_workspace_interface_init( FoobarWorkspaceInterface* iface ); +static void foobar_sway_workspace_init ( FoobarSwayWorkspace* self ); +static void foobar_sway_workspace_get_property ( GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec ); +static void foobar_sway_workspace_finalize ( GObject* object ); +static FoobarSwayWorkspace* foobar_sway_workspace_new ( FoobarSwayWorkspaceService* service ); +static gint64 foobar_sway_workspace_get_id ( FoobarWorkspace* self ); +static gchar const* foobar_sway_workspace_get_name ( FoobarWorkspace* workspace ); +static gchar const* foobar_sway_workspace_get_monitor ( FoobarWorkspace* workspace ); +static FoobarWorkspaceFlags foobar_sway_workspace_get_flags ( FoobarWorkspace* workspace ); +static void foobar_sway_workspace_activate ( FoobarWorkspace* workspace ); +static void foobar_sway_workspace_set_id ( FoobarSwayWorkspace* self, + gint64 value ); +static void foobar_sway_workspace_set_name ( FoobarSwayWorkspace* self, + gchar const* value ); +static void foobar_sway_workspace_set_monitor ( FoobarSwayWorkspace* self, + gchar const* value ); +static void foobar_sway_workspace_set_flags ( FoobarSwayWorkspace* self, + FoobarWorkspaceFlags value ); + +G_DEFINE_TYPE_WITH_CODE( + FoobarSwayWorkspace, foobar_sway_workspace, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE( FOOBAR_TYPE_WORKSPACE, foobar_sway_workspace_interface_init ) ); + +// +// FoobarSwayWorkspaceService: +// +// Service monitoring the workspaces provided by the window manager. This is implemented by communicating directly with +// Sway through its IPC socket. +// + +struct _FoobarSwayWorkspaceService +{ + GObject parent_instance; + GListStore* workspaces; + GtkSortListModel* sorted_workspaces; + gchar* socket_path; +}; + +enum +{ + PROP_WORKSPACES = 1, + N_PROPS, +}; + +static void foobar_sway_workspace_service_class_init ( FoobarSwayWorkspaceServiceClass* klass ); +static void foobar_sway_workspace_service_interface_init ( FoobarWorkspaceServiceInterface* iface ); +static void foobar_sway_workspace_service_init ( FoobarSwayWorkspaceService* self ); +static void foobar_sway_workspace_service_get_property ( GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec ); +static void foobar_sway_workspace_service_dispose ( GObject* object ); +static void foobar_sway_workspace_service_finalize ( GObject* object ); +static GListModel* foobar_sway_workspace_service_get_workspaces ( FoobarWorkspaceService* service ); +static gboolean foobar_sway_workspace_service_is_available ( FoobarWorkspaceService* service ); +static gchar* foobar_sway_workspace_service_get_socket_path( void ); + +G_DEFINE_TYPE_WITH_CODE( + FoobarSwayWorkspaceService, foobar_sway_workspace_service, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE( FOOBAR_TYPE_WORKSPACE_SERVICE, foobar_sway_workspace_service_interface_init ) ); + +// --------------------------------------------------------------------------------------------------------------------- +// Workspace +// --------------------------------------------------------------------------------------------------------------------- + +// +// Static initialization for workspaces. +// +void foobar_sway_workspace_class_init( FoobarSwayWorkspaceClass* klass ) +{ + GObjectClass* object_klass = G_OBJECT_CLASS( klass ); + object_klass->get_property = foobar_sway_workspace_get_property; + object_klass->finalize = foobar_sway_workspace_finalize; + + g_object_class_override_property( object_klass, WORKSPACE_PROP_ID, "id" ); + g_object_class_override_property( object_klass, WORKSPACE_PROP_NAME, "name" ); + g_object_class_override_property( object_klass, WORKSPACE_PROP_MONITOR, "monitor" ); + g_object_class_override_property( object_klass, WORKSPACE_PROP_FLAGS, "flags" ); +} + +// +// Interface initialization for workspaces. +// +void foobar_sway_workspace_interface_init( FoobarWorkspaceInterface* iface ) +{ + iface->get_id = foobar_sway_workspace_get_id; + iface->get_name = foobar_sway_workspace_get_name; + iface->get_monitor = foobar_sway_workspace_get_monitor; + iface->get_flags = foobar_sway_workspace_get_flags; + iface->activate = foobar_sway_workspace_activate; +} + +// +// Instance initialization for workspaces. +// +void foobar_sway_workspace_init( FoobarSwayWorkspace* self ) +{ + (void)self; +} + +// +// Property getter implementation, mapping a property id to a method. +// +void foobar_sway_workspace_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec ) +{ + FoobarSwayWorkspace* self = (FoobarSwayWorkspace*)object; + + switch ( prop_id ) + { + case WORKSPACE_PROP_ID: + g_value_set_int64( value, foobar_workspace_get_id( FOOBAR_WORKSPACE( self ) ) ); + break; + case WORKSPACE_PROP_NAME: + g_value_set_string( value, foobar_workspace_get_name( FOOBAR_WORKSPACE( self ) ) ); + break; + case WORKSPACE_PROP_MONITOR: + g_value_set_string( value, foobar_workspace_get_monitor( FOOBAR_WORKSPACE( self ) ) ); + break; + case WORKSPACE_PROP_FLAGS: + g_value_set_flags( value, foobar_workspace_get_flags( FOOBAR_WORKSPACE( self ) ) ); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec ); + break; + } +} + +// +// Instance cleanup for workspaces. +// +void foobar_sway_workspace_finalize( GObject* object ) +{ + FoobarSwayWorkspace* self = (FoobarSwayWorkspace*)object; + + g_clear_pointer( &self->name, g_free ); + g_clear_pointer( &self->monitor, g_free ); + + G_OBJECT_CLASS( foobar_sway_workspace_parent_class )->finalize( object ); +} + +// +// Create a new workspace that is owned by the given service (captured as an unowned reference). +// +FoobarSwayWorkspace* foobar_sway_workspace_new( FoobarSwayWorkspaceService* service ) +{ + FoobarSwayWorkspace* self = g_object_new( FOOBAR_TYPE_SWAY_WORKSPACE, NULL ); + self->service = service; + return self; +} + +// +// Numeric ID of the workspace. +// +gint64 foobar_sway_workspace_get_id( FoobarWorkspace* workspace ) +{ + FoobarSwayWorkspace* self = (FoobarSwayWorkspace*)workspace; + return self->id; +} + +// +// Optional human-readable workspace name. +// +gchar const* foobar_sway_workspace_get_name( FoobarWorkspace* workspace ) +{ + FoobarSwayWorkspace* self = (FoobarSwayWorkspace*)workspace; + return self->name; +} + +// +// Connector ID of the workspace's monitor. +// +gchar const* foobar_sway_workspace_get_monitor( FoobarWorkspace* workspace ) +{ + FoobarSwayWorkspace* self = (FoobarSwayWorkspace*)workspace; + return self->monitor; +} + +// +// Flags indicating the current state of the workspace. +// +FoobarWorkspaceFlags foobar_sway_workspace_get_flags( FoobarWorkspace* workspace ) +{ + FoobarSwayWorkspace* self = (FoobarSwayWorkspace*)workspace; + return self->flags; +} + +// +// Let this workspace become the active one. +// +void foobar_sway_workspace_activate( FoobarWorkspace* workspace ) +{ + FoobarSwayWorkspace* self = (FoobarSwayWorkspace*)workspace; + + if ( !self->service ) { return; } +} + +// +// Numeric ID of the workspace. +// +void foobar_sway_workspace_set_id( + FoobarSwayWorkspace* self, + gint64 value ) +{ + g_return_if_fail( FOOBAR_IS_SWAY_WORKSPACE( self ) ); + + if ( self->id != value ) + { + self->id = value; + g_object_notify( G_OBJECT( self ), "id" ); + } +} + +// +// Optional human-readable workspace name. +// +void foobar_sway_workspace_set_name( + FoobarSwayWorkspace* self, + gchar const* value ) +{ + g_return_if_fail( FOOBAR_IS_SWAY_WORKSPACE( self ) ); + + if ( g_strcmp0( self->name, value ) ) + { + g_clear_pointer( &self->name, g_free ); + self->name = g_strdup( value ); + g_object_notify( G_OBJECT( self ), "name" ); + } +} + +// +// Connector ID of the workspace's monitor. +// +void foobar_sway_workspace_set_monitor( + FoobarSwayWorkspace* self, + gchar const* value ) +{ + g_return_if_fail( FOOBAR_IS_SWAY_WORKSPACE( self ) ); + + if ( g_strcmp0( self->monitor, value ) ) + { + g_clear_pointer( &self->monitor, g_free ); + self->monitor = g_strdup( value ); + g_object_notify( G_OBJECT( self ), "monitor" ); + } +} + +// +// Flags indicating the current state of the workspace. +// +void foobar_sway_workspace_set_flags( + FoobarSwayWorkspace* self, + FoobarWorkspaceFlags value ) +{ + g_return_if_fail( FOOBAR_IS_SWAY_WORKSPACE( self ) ); + + if ( self->flags != value ) + { + self->flags = value; + g_object_notify( G_OBJECT( self ), "flags" ); + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// Service Implementation +// --------------------------------------------------------------------------------------------------------------------- + +// +// Static initialization for the workspace service. +// +void foobar_sway_workspace_service_class_init( FoobarSwayWorkspaceServiceClass* klass ) +{ + GObjectClass* object_klass = G_OBJECT_CLASS( klass ); + object_klass->get_property = foobar_sway_workspace_service_get_property; + object_klass->dispose = foobar_sway_workspace_service_dispose; + object_klass->finalize = foobar_sway_workspace_service_finalize; + + g_object_class_override_property( object_klass, PROP_WORKSPACES, "workspaces" ); +} + +// +// Interface initialization for the workspace service. +// +void foobar_sway_workspace_service_interface_init( FoobarWorkspaceServiceInterface* iface ) +{ + iface->get_workspaces = foobar_sway_workspace_service_get_workspaces; + iface->is_available = foobar_sway_workspace_service_is_available; +} + +// +// Instance initialization for the workspace service. +// +void foobar_sway_workspace_service_init( FoobarSwayWorkspaceService* self ) +{ + self->workspaces = g_list_store_new( FOOBAR_TYPE_WORKSPACE ); + + GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_workspace_sort_func, NULL, NULL ); + self->sorted_workspaces = gtk_sort_list_model_new( + G_LIST_MODEL( g_object_ref( self->workspaces ) ), + GTK_SORTER( sorter ) ); + + self->socket_path = foobar_sway_workspace_service_get_socket_path( ); + if ( !self->socket_path ) + { + return; + } + + (void)foobar_sway_workspace_set_id; + (void)foobar_sway_workspace_set_name; + (void)foobar_sway_workspace_set_monitor; + (void)foobar_sway_workspace_set_flags; + (void)foobar_sway_workspace_new; +} + +// +// Property getter implementation, mapping a property id to a method. +// +void foobar_sway_workspace_service_get_property( GObject* object, guint prop_id, GValue* value, GParamSpec* pspec ) +{ + FoobarSwayWorkspaceService* self = (FoobarSwayWorkspaceService*)object; + + switch ( prop_id ) + { + case PROP_WORKSPACES: + g_value_set_object( value, foobar_workspace_service_get_workspaces( FOOBAR_WORKSPACE_SERVICE( self ) ) ); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec ); + break; + } +} + +// +// Instance de-initialization for the workspace service. +// +// This is where the event thread is joined because it might still create new object references which is a bad thing to +// do in finalize. +// +void foobar_sway_workspace_service_dispose( GObject* object ) +{ + FoobarSwayWorkspaceService* self = (FoobarSwayWorkspaceService*)object; + + (void)self; + + G_OBJECT_CLASS( foobar_sway_workspace_service_parent_class )->dispose( object ); +} + +// +// Instance cleanup for the workspace service. +// +void foobar_sway_workspace_service_finalize( GObject* object ) +{ + FoobarSwayWorkspaceService* self = (FoobarSwayWorkspaceService*)object; + + for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->workspaces ) ); ++i ) + { + FoobarSwayWorkspace* workspace = g_list_model_get_item( G_LIST_MODEL( self->workspaces ), i ); + workspace->service = NULL; + } + // g_clear_object( &self->event_cancellable ); + g_clear_object( &self->sorted_workspaces ); + g_clear_object( &self->workspaces ); + g_clear_pointer( &self->socket_path, g_free ); + + G_OBJECT_CLASS( foobar_sway_workspace_service_parent_class )->finalize( object ); +} + +// +// Get a sorted list of all workspaces. +// +GListModel* foobar_sway_workspace_service_get_workspaces( FoobarWorkspaceService* service ) +{ + FoobarSwayWorkspaceService* self = (FoobarSwayWorkspaceService*)service; + return G_LIST_MODEL( self->sorted_workspaces ); +} + +// +// Check whether this service is connected to Sway. +// +gboolean foobar_sway_workspace_service_is_available( FoobarWorkspaceService* service ) +{ + FoobarSwayWorkspaceService* self = (FoobarSwayWorkspaceService*)service; + return self->socket_path != NULL; +} + +// --------------------------------------------------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------------------------------------------------- + +// +// Create a new Sway workspace service instance. +// +FoobarWorkspaceService* foobar_sway_workspace_service_new( void ) +{ + return g_object_new( FOOBAR_TYPE_SWAY_WORKSPACE_SERVICE, NULL ); +} + +// +// Get the sway socket path. +// +gchar* foobar_sway_workspace_service_get_socket_path( void ) +{ + gchar const* sock = g_getenv( "SWAYSOCK" ); + if ( !sock ) + { + return NULL; + } + + return g_strdup( sock ); +} diff --git a/src/services/workspaces/sway-workspace-service.h b/src/services/workspaces/sway-workspace-service.h new file mode 100644 index 0000000..8d078eb --- /dev/null +++ b/src/services/workspaces/sway-workspace-service.h @@ -0,0 +1,13 @@ +#pragma once + +#include "services/workspace-service.h" + +G_BEGIN_DECLS + +#define FOOBAR_TYPE_SWAY_WORKSPACE_SERVICE foobar_sway_workspace_service_get_type( ) + +G_DECLARE_FINAL_TYPE( FoobarSwayWorkspaceService, foobar_sway_workspace_service, FOOBAR, SWAY_WORKSPACE_SERVICE, GObject ) + +FoobarWorkspaceService* foobar_sway_workspace_service_new( void ); + +G_END_DECLS -- cgit v1.3.1