#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 ); }