summaryrefslogtreecommitdiff
path: root/src/services
diff options
context:
space:
mode:
Diffstat (limited to 'src/services')
-rw-r--r--src/services/application-service.c746
-rw-r--r--src/services/application-service.h25
-rw-r--r--src/services/audio-service.c930
-rw-r--r--src/services/audio-service.h44
-rw-r--r--src/services/battery-service.c378
-rw-r--r--src/services/battery-service.h36
-rw-r--r--src/services/bluetooth-service.c949
-rw-r--r--src/services/bluetooth-service.h41
-rw-r--r--src/services/brightness-service.c391
-rw-r--r--src/services/brightness-service.h16
-rw-r--r--src/services/clock-service.c141
-rw-r--r--src/services/clock-service.h14
-rw-r--r--src/services/configuration-service.c3248
-rw-r--r--src/services/configuration-service.h305
-rw-r--r--src/services/meson.build15
-rw-r--r--src/services/network-service.c1577
-rw-r--r--src/services/network-service.h62
-rw-r--r--src/services/notification-service.c1833
-rw-r--r--src/services/notification-service.h56
-rw-r--r--src/services/quick-answer-service.c360
-rw-r--r--src/services/quick-answer-service.h21
-rw-r--r--src/services/quick-answers/math-lexer.c248
-rw-r--r--src/services/quick-answers/math-parser.c414
-rw-r--r--src/services/quick-answers/math.c851
-rw-r--r--src/services/quick-answers/math.h148
-rw-r--r--src/services/quick-answers/math.test.c153
-rw-r--r--src/services/quick-answers/meson.build9
-rw-r--r--src/services/workspace-service.c1386
-rw-r--r--src/services/workspace-service.h42
29 files changed, 0 insertions, 14439 deletions
diff --git a/src/services/application-service.c b/src/services/application-service.c
deleted file mode 100644
index 42835ac..0000000
--- a/src/services/application-service.c
+++ /dev/null
@@ -1,746 +0,0 @@
-#include "services/application-service.h"
-#include "launcher-item.h"
-#include "utils.h"
-#include <gtk/gtk.h>
-#include <gio/gdesktopappinfo.h>
-#include <json-glib/json-glib.h>
-#include <string.h>
-
-//
-// FoobarApplicationItem:
-//
-// Represents a single application/desktop file.
-//
-
-struct _FoobarApplicationItem
-{
- GObject parent_instance;
- FoobarApplicationService* service;
- GAppInfo* info;
-};
-
-enum
-{
- APP_PROP_ID = 1,
- APP_PROP_EXECUTABLE,
- APP_PROP_CATEGORIES,
- APP_PROP_FREQUENCY,
- APP_PROP_TITLE,
- APP_PROP_DESCRIPTION,
- APP_PROP_ICON,
- N_APP_PROPS,
-};
-
-static GParamSpec* app_props[N_APP_PROPS] = { 0 };
-
-static void foobar_application_item_class_init ( FoobarApplicationItemClass* klass );
-static void foobar_application_item_launcher_item_interface_init( FoobarLauncherItemInterface* iface );
-static void foobar_application_item_init ( FoobarApplicationItem* self );
-static void foobar_application_item_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_application_item_finalize ( GObject* object );
-static FoobarApplicationItem* foobar_application_item_new ( FoobarApplicationService* service );
-static gchar const* foobar_application_item_get_title ( FoobarLauncherItem* self );
-static gchar const* foobar_application_item_get_description ( FoobarLauncherItem* self );
-static GIcon* foobar_application_item_get_icon ( FoobarLauncherItem* self );
-static void foobar_application_item_activate ( FoobarLauncherItem* self );
-static void foobar_application_item_set_info ( FoobarApplicationItem* self,
- GAppInfo* value );
-
-G_DEFINE_FINAL_TYPE_WITH_CODE(
- FoobarApplicationItem,
- foobar_application_item,
- G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE( FOOBAR_TYPE_LAUNCHER_ITEM, foobar_application_item_launcher_item_interface_init ) )
-
-//
-// FoobarApplicationService:
-//
-// Service providing a list of all installed applications. This implemented using GLib's AppInfo API.
-//
-
-struct _FoobarApplicationService
-{
- GObject parent_instance;
- GListStore* items;
- GtkSortListModel* sorted_items;
- GAppInfoMonitor* monitor;
- GHashTable* frequencies; // only modified on the main thread, but possibly read on a background thread
- GMutex frequencies_mutex;
- GMutex write_cache_mutex;
- gchar* cache_path;
- gulong changed_handler_id;
-};
-
-enum
-{
- PROP_ITEMS = 1,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_application_service_class_init ( FoobarApplicationServiceClass* klass );
-static void foobar_application_service_init ( FoobarApplicationService* self );
-static void foobar_application_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_application_service_finalize ( GObject* object );
-static void foobar_application_service_handle_changed ( GAppInfoMonitor* monitor,
- gpointer userdata );
-static void foobar_application_service_update ( FoobarApplicationService* self );
-static void foobar_application_service_read_cache ( FoobarApplicationService* self );
-static void foobar_application_service_read_cache_foreach_cb ( JsonObject* object,
- gchar const* member_name,
- JsonNode* member_node,
- gpointer userdata );
-static void foobar_application_service_write_cache ( FoobarApplicationService* self );
-static void foobar_application_service_write_cache_cb ( GObject* object,
- GAsyncResult* result,
- gpointer userdata );
-static void foobar_application_service_write_cache_async ( FoobarApplicationService* self,
- GCancellable* cancellable,
- GAsyncReadyCallback callback,
- gpointer userdata );
-static gboolean foobar_application_service_write_cache_finish ( FoobarApplicationService* self,
- GAsyncResult* result,
- GError** error );
-static void foobar_application_service_write_cache_thread ( GTask* task,
- gpointer source_object,
- gpointer task_data,
- GCancellable* cancellable );
-static void foobar_application_service_write_cache_foreach_cb( gpointer key,
- gpointer value,
- gpointer userdata );
-static gint foobar_application_service_sort_func ( gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarApplicationService, foobar_application_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Item Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for application items.
-//
-void foobar_application_item_class_init( FoobarApplicationItemClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_application_item_get_property;
- object_klass->finalize = foobar_application_item_finalize;
-
- gpointer launcher_item_iface = g_type_default_interface_peek( FOOBAR_TYPE_LAUNCHER_ITEM );
- app_props[APP_PROP_ID] = g_param_spec_string(
- "id",
- "ID",
- "Application identifier string.",
- NULL,
- G_PARAM_READABLE );
- app_props[APP_PROP_EXECUTABLE] = g_param_spec_string(
- "executable",
- "Executable",
- "The executable name of the application.",
- NULL,
- G_PARAM_READABLE );
- app_props[APP_PROP_CATEGORIES] = g_param_spec_string(
- "categories",
- "Categories",
- "Semicolon-separated list of application categories.",
- NULL,
- G_PARAM_READABLE );
- app_props[APP_PROP_FREQUENCY] = g_param_spec_int64(
- "frequency",
- "Frequency",
- "The number of times the application was opened.",
- 0,
- INT64_MAX,
- 0,
- G_PARAM_READABLE );
- app_props[APP_PROP_TITLE] = g_param_spec_override(
- "title",
- g_object_interface_find_property( launcher_item_iface, "title" ) );
- app_props[APP_PROP_DESCRIPTION] = g_param_spec_override(
- "description",
- g_object_interface_find_property( launcher_item_iface, "description" ) );
- app_props[APP_PROP_ICON] = g_param_spec_override(
- "icon",
- g_object_interface_find_property( launcher_item_iface, "icon" ) );
- g_object_class_install_properties( object_klass, N_APP_PROPS, app_props );
-}
-
-//
-// Static initialization of the FoobarLauncherItem interface.
-//
-void foobar_application_item_launcher_item_interface_init( FoobarLauncherItemInterface* iface )
-{
- iface->get_title = foobar_application_item_get_title;
- iface->get_description = foobar_application_item_get_description;
- iface->get_icon = foobar_application_item_get_icon;
- iface->activate = foobar_application_item_activate;
-}
-
-//
-// Instance initialization for application items.
-//
-void foobar_application_item_init( FoobarApplicationItem* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_application_item_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarApplicationItem* self = (FoobarApplicationItem*)object;
-
- switch ( prop_id )
- {
- case APP_PROP_ID:
- g_value_set_string( value, foobar_application_item_get_id( self ) );
- break;
- case APP_PROP_EXECUTABLE:
- g_value_set_string( value, foobar_application_item_get_executable( self ) );
- break;
- case APP_PROP_CATEGORIES:
- g_value_set_string( value, foobar_application_item_get_categories( self ) );
- break;
- case APP_PROP_FREQUENCY:
- g_value_set_int64( value, foobar_application_item_get_frequency( self ) );
- break;
- case APP_PROP_TITLE:
- g_value_set_string( value, foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM ( self ) ) );
- break;
- case APP_PROP_DESCRIPTION:
- g_value_set_string( value, foobar_launcher_item_get_description( FOOBAR_LAUNCHER_ITEM( self ) ) );
- break;
- case APP_PROP_ICON:
- g_value_set_object( value, foobar_launcher_item_get_icon( FOOBAR_LAUNCHER_ITEM( self ) ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for application items.
-//
-void foobar_application_item_finalize( GObject* object )
-{
- FoobarApplicationItem* self = (FoobarApplicationItem*)object;
-
- g_clear_object( &self->info );
-
- G_OBJECT_CLASS( foobar_application_item_parent_class )->finalize( object );
-}
-
-//
-// Create a new application item belonging to the parent service (captured as an unowned reference).
-//
-FoobarApplicationItem* foobar_application_item_new( FoobarApplicationService* service )
-{
- FoobarApplicationItem* self = g_object_new( FOOBAR_TYPE_APPLICATION_ITEM, NULL );
- self->service = service;
- return self;
-}
-
-//
-// Get the application's unique textual identifier.
-//
-gchar const* foobar_application_item_get_id( FoobarApplicationItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), NULL );
- return self->info ? g_app_info_get_id( self->info ) : NULL;
-}
-
-//
-// Get the application's executable name.
-//
-gchar const* foobar_application_item_get_executable( FoobarApplicationItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), NULL );
- return self->info ? g_app_info_get_executable( self->info ) : NULL;
-}
-
-//
-// Get the semicolon-separated list of categories for this application.
-//
-gchar const* foobar_application_item_get_categories( FoobarApplicationItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), NULL );
- return G_IS_DESKTOP_APP_INFO( self->info )
- ? g_desktop_app_info_get_categories( G_DESKTOP_APP_INFO( self->info ) )
- : NULL;
-}
-
-//
-// Get the number of times this application was opened using the launcher.
-//
-gint64 foobar_application_item_get_frequency( FoobarApplicationItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), 0 );
-
- if ( !self->service ) { return 0; }
- gpointer value = g_hash_table_lookup( self->service->frequencies, foobar_application_item_get_id( self ) );
- return GPOINTER_TO_SIZE( value );
-}
-
-//
-// Get the application's human-readable name.
-//
-gchar const* foobar_application_item_get_title( FoobarLauncherItem* item )
-{
- FoobarApplicationItem* self = ( FoobarApplicationItem* )item;
- return self->info ? g_app_info_get_display_name( self->info ) : NULL;
-}
-
-//
-// Get an optional short description for the application.
-//
-gchar const* foobar_application_item_get_description( FoobarLauncherItem* item )
-{
- FoobarApplicationItem* self = ( FoobarApplicationItem* )item;
- return self->info ? g_app_info_get_description( self->info ) : NULL;
-}
-
-//
-// Get an icon representing the application.
-//
-GIcon* foobar_application_item_get_icon( FoobarLauncherItem* item )
-{
- FoobarApplicationItem* self = ( FoobarApplicationItem* )item;
- return self->info ? g_app_info_get_icon( self->info ) : NULL;
-}
-
-//
-// Launch the application, increasing its frequency by one.
-//
-void foobar_application_item_activate( FoobarLauncherItem* item )
-{
- FoobarApplicationItem* self = ( FoobarApplicationItem* )item;
-
- if ( !self->info ) { return; }
-
- g_autoptr( GError ) error = NULL;
- if ( !g_app_info_launch( self->info, NULL, NULL, &error ) )
- {
- g_warning( "Unable to launch application: %s", error->message );
- }
-
- gchar const* id = foobar_application_item_get_id( self );
- if ( !self->service || !id ) { return; }
-
- gpointer value = g_hash_table_lookup( self->service->frequencies, id );
- value = GSIZE_TO_POINTER( GPOINTER_TO_SIZE( value ) + 1 );
- g_mutex_lock( &self->service->frequencies_mutex );
- g_hash_table_insert( self->service->frequencies, g_strdup( id ), value );
- g_mutex_unlock( &self->service->frequencies_mutex );
-
- g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_FREQUENCY] );
- gtk_sorter_changed( gtk_sort_list_model_get_sorter( self->service->sorted_items ), GTK_SORTER_CHANGE_DIFFERENT );
- foobar_application_service_write_cache( self->service );
-}
-
-//
-// Update the backing GAppInfo object for the application.
-//
-void foobar_application_item_set_info(
- FoobarApplicationItem* self,
- GAppInfo* value )
-{
- g_return_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ) );
-
- if ( self->info != value )
- {
- g_clear_object( &self->info );
-
- if ( value ) { self->info = g_object_ref( value ); }
-
- g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_ID] );
- g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_TITLE] );
- g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_DESCRIPTION] );
- g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_EXECUTABLE] );
- g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_ICON] );
- g_object_notify_by_pspec( G_OBJECT( self ), app_props[APP_PROP_FREQUENCY] );
- }
-}
-
-//
-// Match the item against the given search terms.
-//
-gboolean foobar_application_item_match(
- FoobarApplicationItem* self,
- gchar const* const* terms )
-{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_ITEM( self ), FALSE );
- g_return_val_if_fail( terms != NULL, FALSE );
-
- for ( gchar const* const* it = terms; *it; ++it )
- {
- gchar const* term = *it;
- gchar const* name = foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM( self ) );
- gchar const* description = foobar_launcher_item_get_description( FOOBAR_LAUNCHER_ITEM( self ) );
- gchar const* executable = foobar_application_item_get_executable( self );
- gchar const* categories = foobar_application_item_get_categories( self );
- gchar const* id = foobar_application_item_get_id( self );
- if ( name && strcasestr( name, term ) != NULL ) { continue; }
- if ( description && strcasestr( description, term ) != NULL ) { continue; }
- if ( executable && strcasestr( executable, term ) != NULL ) { continue; }
- if ( categories && strcasestr( categories, term ) != NULL ) { continue; }
- if ( id && strcasestr( id, term ) != NULL ) { continue; }
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the application service.
-//
-void foobar_application_service_class_init( FoobarApplicationServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_application_service_get_property;
- object_klass->finalize = foobar_application_service_finalize;
-
- props[PROP_ITEMS] = g_param_spec_object(
- "items",
- "Items",
- "Application items, sorted by their frequency.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the application service.
-//
-void foobar_application_service_init( FoobarApplicationService* self )
-{
- self->items = g_list_store_new( FOOBAR_TYPE_APPLICATION_ITEM );
-
- GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_application_service_sort_func, NULL, NULL );
- self->sorted_items = gtk_sort_list_model_new( G_LIST_MODEL( g_object_ref( self->items ) ), GTK_SORTER( sorter ) );
-
- self->cache_path = foobar_get_cache_path( "application-frequencies.json" );
- self->frequencies = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL );
- g_mutex_init( &self->frequencies_mutex );
- g_mutex_init( &self->write_cache_mutex );
-
- foobar_application_service_read_cache( self );
- foobar_application_service_update( self );
-
- self->monitor = g_app_info_monitor_get( );
- self->changed_handler_id = g_signal_connect(
- self->monitor,
- "changed",
- G_CALLBACK( foobar_application_service_handle_changed ),
- self );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_application_service_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarApplicationService* self = (FoobarApplicationService*)object;
-
- switch ( prop_id )
- {
- case PROP_ITEMS:
- g_value_set_object( value, foobar_application_service_get_items( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the application service.
-//
-void foobar_application_service_finalize( GObject* object )
-{
- FoobarApplicationService* self = (FoobarApplicationService*)object;
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->items ) ); ++i )
- {
- FoobarApplicationItem* item = g_list_model_get_item( G_LIST_MODEL( self->items ), i );
- item->service = NULL;
- g_object_notify_by_pspec( G_OBJECT( item ), app_props[APP_PROP_FREQUENCY] );
- }
- g_clear_signal_handler( &self->changed_handler_id, self->monitor );
- g_clear_object( &self->sorted_items );
- g_clear_object( &self->items );
- g_clear_object( &self->monitor );
- g_clear_pointer( &self->frequencies, g_hash_table_unref );
- g_clear_pointer( &self->cache_path, g_free );
-
- g_mutex_clear( &self->frequencies_mutex );
- g_mutex_clear( &self->write_cache_mutex );
-
- G_OBJECT_CLASS( foobar_application_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new application service instance.
-//
-FoobarApplicationService* foobar_application_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_APPLICATION_SERVICE, NULL );
-}
-
-//
-// Get a sorted list of all available applications.
-//
-GListModel* foobar_application_service_get_items( FoobarApplicationService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_SERVICE( self ), NULL );
-
- return G_LIST_MODEL( self->sorted_items );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called by GLib when the list of applications has changed.
-//
-void foobar_application_service_handle_changed(
- GAppInfoMonitor* monitor,
- gpointer userdata )
-{
- (void)monitor;
- FoobarApplicationService* self = (FoobarApplicationService*)userdata;
-
- foobar_application_service_update( self );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Reload the list of applications.
-//
-void foobar_application_service_update( FoobarApplicationService* self )
-{
- g_list_store_remove_all( self->items );
-
- g_autolist( GAppInfo ) info = g_app_info_get_all( );
- for ( GList* it = info; it; it = it->next )
- {
- if ( g_app_info_should_show( it->data ) )
- {
- g_autoptr( FoobarApplicationItem ) item = foobar_application_item_new( self );
- foobar_application_item_set_info( item, it->data );
- g_list_store_append( self->items, item );
- }
- }
-}
-
-//
-// Synchronously read the frequency cache file at self->cache_path, populating self->frequencies.
-//
-void foobar_application_service_read_cache( FoobarApplicationService* self )
-{
- g_autoptr( GError ) error = NULL;
-
- if ( self->cache_path && g_file_test( self->cache_path, G_FILE_TEST_EXISTS ) )
- {
- g_autoptr( JsonParser ) parser = json_parser_new( );
- if ( !json_parser_load_from_mapped_file( parser, self->cache_path, &error ) )
- {
- g_warning( "Unable to read cached application frequencies: %s", error->message );
- return;
- }
-
- JsonNode* root_node = json_parser_get_root( parser );
- JsonObject* root_object = json_node_get_object( root_node );
- json_object_foreach_member( root_object, foobar_application_service_read_cache_foreach_cb, self );
- }
-}
-
-//
-// Callback used by foobar_application_service_read_cache to read a single entry.
-//
-void foobar_application_service_read_cache_foreach_cb(
- JsonObject* object,
- gchar const* member_name,
- JsonNode* member_node,
- gpointer userdata )
-{
- (void)object;
- FoobarApplicationService* self = (FoobarApplicationService*)userdata;
-
- gsize frequency = json_node_get_int( member_node );
- g_hash_table_insert( self->frequencies, g_strdup( member_name ), GSIZE_TO_POINTER( frequency ) );
-}
-
-//
-// Asynchronously start writing the frequency cache file at self->cache_path.
-//
-void foobar_application_service_write_cache( FoobarApplicationService* self )
-{
- foobar_application_service_write_cache_async( self, NULL, foobar_application_service_write_cache_cb, NULL );
-}
-
-//
-// Callback invoked when the frequency cache was written successfully or failed.
-//
-void foobar_application_service_write_cache_cb(
- GObject* object,
- GAsyncResult* result,
- gpointer userdata )
-{
- (void)userdata;
- FoobarApplicationService* self = (FoobarApplicationService*)object;
-
- g_autoptr( GError ) error = NULL;
- if ( !foobar_application_service_write_cache_finish( self, result, &error ) )
- {
- g_warning( "Unable to write cached application frequencies: %s", error->message );
- }
-}
-
-//
-// Asynchronously write the frequency cache file at self->cache_path.
-//
-void foobar_application_service_write_cache_async(
- FoobarApplicationService* self,
- GCancellable* cancellable,
- GAsyncReadyCallback callback,
- gpointer userdata )
-{
- g_autoptr( GTask ) task = g_task_new( self, cancellable, callback, userdata );
- g_task_set_name( task, "write-application-cache" );
- g_task_run_in_thread( task, foobar_application_service_write_cache_thread );
-}
-
-//
-// Get the asynchronous result for writing the frequency cache file, returning TRUE on success, or FALSE on error.
-//
-gboolean foobar_application_service_write_cache_finish(
- FoobarApplicationService* self,
- GAsyncResult* result,
- GError** error )
-{
- (void)self;
-
- return g_task_propagate_boolean( G_TASK( result ), error );
-}
-
-//
-// Task implementation for foobar_application_service_write_cache_async, invoked on a background thread.
-//
-void foobar_application_service_write_cache_thread(
- GTask* task,
- gpointer source_object,
- gpointer task_data,
- GCancellable* cancellable )
-{
- (void)cancellable;
- (void)task_data;
- FoobarApplicationService* self = (FoobarApplicationService*)source_object;
-
- if ( !self->cache_path )
- {
- g_task_return_boolean( task, TRUE );
- return;
- }
-
- g_autoptr( GError ) error = NULL;
-
- g_autoptr( JsonBuilder ) builder = json_builder_new( );
- json_builder_begin_object( builder );
-
- g_mutex_lock( &self->frequencies_mutex );
- g_hash_table_foreach( self->frequencies, foobar_application_service_write_cache_foreach_cb, builder );
- g_mutex_unlock( &self->frequencies_mutex );
-
- json_builder_end_object( builder );
-
- g_autoptr( JsonNode ) root_node = json_builder_get_root( builder );
- g_autoptr( JsonGenerator ) generator = json_generator_new( );
- json_generator_set_root( generator, root_node );
-
- g_mutex_lock( &self->write_cache_mutex );
- gboolean success = json_generator_to_file( generator, self->cache_path, &error );
- g_mutex_unlock( &self->write_cache_mutex );
- if ( !success )
- {
- g_task_return_error( task, g_steal_pointer( &error ) );
- return;
- }
-
- g_task_return_boolean( task, TRUE );
-}
-
-//
-// Callback used by foobar_application_service_write_cache_thread to build a single entry.
-//
-void foobar_application_service_write_cache_foreach_cb(
- gpointer key,
- gpointer value,
- gpointer userdata )
-{
- JsonBuilder* builder = (JsonBuilder*)userdata;
-
- json_builder_set_member_name( builder, key );
- json_builder_add_int_value( builder, GPOINTER_TO_SIZE( value ) );
-}
-
-//
-// Sorting callback for application items. Items are sorted based on:
-// 1. frequency (descending)
-// 2. name (ascending)
-// 3. id (ascending)
-//
-gint foobar_application_service_sort_func(
- gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarApplicationItem* app_a = (FoobarApplicationItem*)item_a;
- FoobarApplicationItem* app_b = (FoobarApplicationItem*)item_b;
-
- gint64 freq_a = foobar_application_item_get_frequency( app_a );
- gint64 freq_b = foobar_application_item_get_frequency( app_b );
- if ( freq_a > freq_b ) { return -1; }
- if ( freq_a < freq_b ) { return 1; }
-
- gchar const* name_a = foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM( app_a ) );
- gchar const* name_b = foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM( app_b ) );
- gint name_res = g_strcmp0( name_a, name_b );
- if ( name_res ) { return name_res; }
-
- gchar const* id_a = foobar_application_item_get_id( app_a );
- gchar const* id_b = foobar_application_item_get_id( app_b );
- return g_strcmp0( id_a, id_b );
-}
diff --git a/src/services/application-service.h b/src/services/application-service.h
deleted file mode 100644
index dd043fc..0000000
--- a/src/services/application-service.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_APPLICATION_ITEM foobar_application_item_get_type( )
-#define FOOBAR_TYPE_APPLICATION_SERVICE foobar_application_service_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarApplicationItem, foobar_application_item, FOOBAR, APPLICATION_ITEM, GObject )
-
-gchar const* foobar_application_item_get_id ( FoobarApplicationItem* self );
-gchar const* foobar_application_item_get_executable ( FoobarApplicationItem* self );
-gchar const* foobar_application_item_get_categories ( FoobarApplicationItem* self );
-gint64 foobar_application_item_get_frequency ( FoobarApplicationItem* self );
-gboolean foobar_application_item_match ( FoobarApplicationItem* self,
- gchar const* const* terms );
-
-G_DECLARE_FINAL_TYPE( FoobarApplicationService, foobar_application_service, FOOBAR, APPLICATION_SERVICE, GObject )
-
-FoobarApplicationService* foobar_application_service_new ( void );
-GListModel* foobar_application_service_get_items( FoobarApplicationService* self );
-
-G_END_DECLS
diff --git a/src/services/audio-service.c b/src/services/audio-service.c
deleted file mode 100644
index 06345f2..0000000
--- a/src/services/audio-service.c
+++ /dev/null
@@ -1,930 +0,0 @@
-#include "services/audio-service.h"
-#include <gvc-mixer-stream.h>
-#include <gvc-mixer-control.h>
-#include <gvc-mixer-source.h>
-#include <gvc-mixer-sink.h>
-#include <gtk/gtk.h>
-#include <math.h>
-
-#define DEFAULT_VOLUME 25
-
-//
-// FoobarAudioDeviceKind:
-//
-// The type of an audio device (either speaker/output or microphone/input).
-//
-
-G_DEFINE_ENUM_TYPE(
- FoobarAudioDeviceKind,
- foobar_audio_device_kind,
- G_DEFINE_ENUM_VALUE( FOOBAR_AUDIO_DEVICE_INPUT, "input" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_AUDIO_DEVICE_OUTPUT, "output" ) )
-
-//
-// FoobarAudioDevice:
-//
-// Representation of a real input/output device or an abstract device (like the default device which only acts as a
-// proxy for another device).
-//
-
-struct _FoobarAudioDevice
-{
- GObject parent_instance;
- FoobarAudioService* service;
- FoobarAudioDeviceKind kind;
- GvcMixerStream* stream;
- gboolean is_default;
- gulong notify_handler_id;
-};
-
-enum
-{
- DEVICE_PROP_KIND = 1,
- DEVICE_PROP_ID,
- DEVICE_PROP_NAME,
- DEVICE_PROP_DESCRIPTION,
- DEVICE_PROP_VOLUME,
- DEVICE_PROP_IS_MUTED,
- DEVICE_PROP_IS_DEFAULT,
- DEVICE_PROP_IS_AVAILABLE,
- N_DEVICE_PROPS,
-};
-
-static GParamSpec* device_props[N_DEVICE_PROPS] = { 0 };
-
-static void foobar_audio_device_class_init ( FoobarAudioDeviceClass* klass );
-static void foobar_audio_device_init ( FoobarAudioDevice* self );
-static void foobar_audio_device_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_audio_device_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_audio_device_finalize ( GObject* object );
-static FoobarAudioDevice* foobar_audio_device_new ( FoobarAudioService* service,
- FoobarAudioDeviceKind kind );
-static void foobar_audio_device_set_stream ( FoobarAudioDevice* self,
- GvcMixerStream* value );
-static void foobar_audio_device_set_default ( FoobarAudioDevice* self,
- gboolean value );
-static void foobar_audio_device_handle_notify( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarAudioDevice, foobar_audio_device, G_TYPE_OBJECT )
-
-//
-// FoobarAudioService:
-//
-// Service managing the available audio input/output devices. This is implemented using the Gnome Volume Control
-// library.
-//
-
-struct _FoobarAudioService
-{
- GObject parent_instance;
- GvcMixerControl* control;
- GListStore* devices;
- GtkSortListModel* sorted_devices;
- GtkFilterListModel* inputs;
- GtkFilterListModel* outputs;
- FoobarAudioDevice* default_input;
- FoobarAudioDevice* default_output;
- gulong default_sink_handler_id;
- gulong default_source_handler_id;
- gulong stream_added_handler_id;
- gulong stream_removed_handler_id;
-};
-
-enum
-{
- PROP_INPUTS = 1,
- PROP_OUTPUTS,
- PROP_DEFAULT_INPUT,
- PROP_DEFAULT_OUTPUT,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_audio_service_class_init ( FoobarAudioServiceClass* klass );
-static void foobar_audio_service_init ( FoobarAudioService* self );
-static void foobar_audio_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_audio_service_finalize ( GObject* object );
-static void foobar_audio_service_handle_default_sink_changed ( GvcMixerControl* control,
- guint id,
- gpointer userdata );
-static void foobar_audio_service_handle_default_source_changed( GvcMixerControl* control,
- guint id,
- gpointer userdata );
-static void foobar_audio_service_handle_stream_added ( GvcMixerControl* control,
- guint id,
- gpointer userdata );
-static void foobar_audio_service_handle_stream_removed ( GvcMixerControl* control,
- guint id,
- gpointer userdata );
-static gboolean foobar_audio_service_identify_stream ( GvcMixerStream* stream,
- FoobarAudioDeviceKind* out_kind );
-static void foobar_audio_service_update_default ( GListModel* list,
- guint id );
-static gint foobar_audio_service_sort_func ( gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata );
-static gboolean foobar_audio_service_input_filter_func ( gpointer item,
- gpointer userdata );
-static gboolean foobar_audio_service_output_filter_func ( gpointer item,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarAudioService, foobar_audio_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Device
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for audio devices.
-//
-void foobar_audio_device_class_init( FoobarAudioDeviceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_audio_device_get_property;
- object_klass->set_property = foobar_audio_device_set_property;
- object_klass->finalize = foobar_audio_device_finalize;
-
- device_props[DEVICE_PROP_KIND] = g_param_spec_enum(
- "kind",
- "Kind",
- "The kind of audio device.",
- FOOBAR_TYPE_AUDIO_DEVICE_KIND,
- FOOBAR_AUDIO_DEVICE_INPUT,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_ID] = g_param_spec_uint(
- "id",
- "ID",
- "Current numeric ID of the device (may change over time).",
- 0,
- UINT_MAX,
- UINT_MAX,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_NAME] = g_param_spec_string(
- "name",
- "Name",
- "Current name of the device.",
- NULL,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_DESCRIPTION] = g_param_spec_string(
- "description",
- "Description",
- "Short textual description of the device.",
- NULL,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_VOLUME] = g_param_spec_int(
- "volume",
- "Volume",
- "Current volume in percent.",
- 0,
- 100,
- 0,
- G_PARAM_READWRITE );
- device_props[DEVICE_PROP_IS_MUTED] = g_param_spec_int(
- "is-muted",
- "Is Muted",
- "Indicates whether the device is currently muted (i.e. the volume is 0).",
- 0,
- 100,
- 0,
- G_PARAM_READWRITE );
- device_props[DEVICE_PROP_IS_DEFAULT] = g_param_spec_boolean(
- "is-default",
- "Is Default",
- "Indicates whether the device is currently also a default device (either microphone or speaker).",
- FALSE,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_IS_AVAILABLE] = g_param_spec_boolean(
- "is-available",
- "Is Available",
- "Indicates whether the device is currently available to use/plugged in.",
- FALSE,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_DEVICE_PROPS, device_props );
-}
-
-//
-// Instance initialization for audio devices.
-//
-void foobar_audio_device_init( FoobarAudioDevice* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_audio_device_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarAudioDevice* self = (FoobarAudioDevice*)object;
-
- switch ( prop_id )
- {
- case DEVICE_PROP_KIND:
- g_value_set_enum( value, foobar_audio_device_get_kind( self ) );
- break;
- case DEVICE_PROP_ID:
- g_value_set_uint( value, foobar_audio_device_get_id( self ) );
- break;
- case DEVICE_PROP_NAME:
- g_value_set_string( value, foobar_audio_device_get_name( self ) );
- break;
- case DEVICE_PROP_DESCRIPTION:
- g_value_set_string( value, foobar_audio_device_get_description( self ) );
- break;
- case DEVICE_PROP_VOLUME:
- g_value_set_int( value, foobar_audio_device_get_volume( self ) );
- break;
- case DEVICE_PROP_IS_MUTED:
- g_value_set_boolean( value, foobar_audio_device_is_muted( self ) );
- break;
- case DEVICE_PROP_IS_DEFAULT:
- g_value_set_boolean( value, foobar_audio_device_is_default( self ) );
- break;
- case DEVICE_PROP_IS_AVAILABLE:
- g_value_set_boolean( value, foobar_audio_device_is_available( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Property setter implementation, mapping a property id to a method.
-//
-void foobar_audio_device_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarAudioDevice* self = (FoobarAudioDevice*)object;
-
- switch ( prop_id )
- {
- case DEVICE_PROP_VOLUME:
- foobar_audio_device_set_volume( self, g_value_get_int( value ) );
- break;
- case DEVICE_PROP_IS_MUTED:
- foobar_audio_device_set_muted( self, g_value_get_boolean( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for audio devices.
-//
-void foobar_audio_device_finalize( GObject* object )
-{
- FoobarAudioDevice* self = (FoobarAudioDevice*)object;
-
- g_clear_signal_handler( &self->notify_handler_id, self->stream );
- g_clear_object( &self->stream );
-
- G_OBJECT_CLASS( foobar_audio_device_parent_class )->finalize( object );
-}
-
-//
-// Create a new audio device object with the specified parent service (captured as an unowned reference).
-//
-FoobarAudioDevice* foobar_audio_device_new(
- FoobarAudioService* service,
- FoobarAudioDeviceKind kind )
-{
- FoobarAudioDevice* self = g_object_new( FOOBAR_TYPE_AUDIO_DEVICE, NULL );
- self->service = service;
- self->kind = kind;
- return self;
-}
-
-//
-// Get the type of this device.
-//
-FoobarAudioDeviceKind foobar_audio_device_get_kind( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), 0 );
- return self->kind;
-}
-
-//
-// Get a numeric identifier for the device provided by the system.
-//
-guint foobar_audio_device_get_id( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), 0 );
- return self->stream ? gvc_mixer_stream_get_id( self->stream ) : UINT_MAX;
-}
-
-//
-// Get the internal name for the device. This should not be used in the UI.
-//
-gchar const* foobar_audio_device_get_name( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), NULL );
- return self->stream ? gvc_mixer_stream_get_name( self->stream ) : NULL;
-}
-
-//
-// Get a human-readable description for the device.
-//
-gchar const* foobar_audio_device_get_description( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), NULL );
- return self->stream ? gvc_mixer_stream_get_description( self->stream ) : NULL;
-}
-
-//
-// Get the current volume as a percentage value. If muted, this is 0.
-//
-gint foobar_audio_device_get_volume( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), 0 );
-
- if ( !self->service ) { return 0; }
- if ( self->stream && gvc_mixer_stream_get_is_muted( self->stream ) ) { return 0; }
- gdouble max_volume = gvc_mixer_control_get_vol_max_norm( self->service->control );
- gdouble cur_volume = self->stream ? gvc_mixer_stream_get_volume( self->stream ) : 0;
- gint percentage = (gint)round( 100. * cur_volume / max_volume );
- return CLAMP( percentage, 0, 100 );
-}
-
-//
-// Check whether the device is currently muted, i.e. its volume is 0.
-//
-gboolean foobar_audio_device_is_muted( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), FALSE );
- return foobar_audio_device_get_volume( self ) == 0;
-}
-
-//
-// Check whether this device is the system-default for its kind.
-//
-// This will always return TRUE for the static default device instance.
-//
-gboolean foobar_audio_device_is_default( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), FALSE );
- return self->is_default;
-}
-
-//
-// Check whether the device is currently available.
-//
-gboolean foobar_audio_device_is_available( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), FALSE );
- return self->stream != NULL;
-}
-
-//
-// Update the stream backing this device object.
-//
-void foobar_audio_device_set_stream(
- FoobarAudioDevice* self,
- GvcMixerStream* value )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- if ( self->stream != value )
- {
- g_clear_signal_handler( &self->notify_handler_id, self->stream );
- g_clear_object( &self->stream );
-
- if ( value )
- {
- self->stream = g_object_ref( value );
- self->notify_handler_id = g_signal_connect(
- self->stream,
- "notify",
- G_CALLBACK( foobar_audio_device_handle_notify ),
- self );
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_ID] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_NAME] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_DESCRIPTION] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_VOLUME] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_IS_MUTED] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_IS_AVAILABLE] );
- }
-}
-
-//
-// Update the device's "default" flag. This does not actually make it the default device.
-//
-void foobar_audio_device_set_default(
- FoobarAudioDevice* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- value = !!value;
- if ( self->is_default != value )
- {
- self->is_default = value;
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_IS_DEFAULT] );
- }
-}
-
-//
-// Update the device's volume as a percentage value.
-//
-void foobar_audio_device_set_volume(
- FoobarAudioDevice* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- if ( !self->service || !self->stream ) { return; }
-
- value = CLAMP( value, 0, 100 );
- if ( foobar_audio_device_get_volume( self ) != value )
- {
- gdouble max_volume = gvc_mixer_control_get_vol_max_norm( self->service->control );
- gdouble new_volume = value * max_volume / 100.;
- gvc_mixer_stream_set_volume( self->stream, (guint32)new_volume );
- gvc_mixer_stream_push_volume( self->stream );
- gvc_mixer_stream_set_is_muted( self->stream, value == 0 );
- gvc_mixer_stream_change_is_muted( self->stream, value == 0 );
- }
-}
-
-//
-// Update the device's "muted" state.
-//
-// If the device was previously muted, the volume is set to either the previous volume or to a default value.
-//
-void foobar_audio_device_set_muted(
- FoobarAudioDevice* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- if ( foobar_audio_device_is_muted( self ) != value )
- {
- if ( self->stream )
- {
- gvc_mixer_stream_set_is_muted( self->stream, value );
- gvc_mixer_stream_change_is_muted( self->stream, value );
- }
-
- if ( !value && foobar_audio_device_get_volume( self ) == 0 )
- {
- foobar_audio_device_set_volume( self, DEFAULT_VOLUME );
- }
- }
-}
-
-//
-// Let this device become default one for its kind.
-//
-void foobar_audio_device_make_default( FoobarAudioDevice* self )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- if ( !self->service || !self->stream ) { return; }
-
- if ( !foobar_audio_device_is_default( self ) )
- {
- switch ( self->kind )
- {
- case FOOBAR_AUDIO_DEVICE_INPUT:
- gvc_mixer_control_set_default_source( self->service->control, self->stream );
- break;
- case FOOBAR_AUDIO_DEVICE_OUTPUT:
- gvc_mixer_control_set_default_sink( self->service->control, self->stream );
- break;
- default:
- break;
- }
- }
-}
-
-//
-// Called by the underlying stream when one of its properties changes.
-//
-void foobar_audio_device_handle_notify(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- FoobarAudioDevice* self = (FoobarAudioDevice*)userdata;
-
- gchar const* property = g_param_spec_get_name( pspec );
- if ( !g_strcmp0( property, "id" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_ID] );
- }
- else if ( !g_strcmp0( property, "name" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_NAME] );
- }
- else if ( !g_strcmp0( property, "description" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_DESCRIPTION] );
- }
- else if ( !g_strcmp0( property, "volume" ) || !g_strcmp0( property, "is-muted" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_VOLUME] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_IS_MUTED] );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the audio service.
-//
-void foobar_audio_service_class_init( FoobarAudioServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_audio_service_get_property;
- object_klass->finalize = foobar_audio_service_finalize;
-
- props[PROP_INPUTS] = g_param_spec_object(
- "inputs",
- "Inputs",
- "Sorted list of input devices.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- props[PROP_OUTPUTS] = g_param_spec_object(
- "outputs",
- "Outputs",
- "Sorted list of output devices.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- props[PROP_DEFAULT_INPUT] = g_param_spec_object(
- "default-input",
- "Default Input",
- "The default input device (reference remains constant).",
- FOOBAR_TYPE_AUDIO_DEVICE,
- G_PARAM_READABLE );
- props[PROP_DEFAULT_OUTPUT] = g_param_spec_object(
- "default-output",
- "Default Output",
- "The default output device (reference remains constant).",
- FOOBAR_TYPE_AUDIO_DEVICE,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the audio service.
-//
-void foobar_audio_service_init( FoobarAudioService* self )
-{
- self->control = gvc_mixer_control_new( "Foobar mixer control" );
- self->devices = g_list_store_new( FOOBAR_TYPE_AUDIO_DEVICE );
-
- GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_audio_service_sort_func, NULL, NULL );
- self->sorted_devices = gtk_sort_list_model_new(
- G_LIST_MODEL( g_object_ref( self->devices ) ),
- GTK_SORTER( sorter ) );
-
- GtkCustomFilter* input_filter = gtk_custom_filter_new( foobar_audio_service_input_filter_func, NULL, NULL );
- self->inputs = gtk_filter_list_model_new(
- G_LIST_MODEL( g_object_ref( self->sorted_devices ) ),
- GTK_FILTER( input_filter ) );
-
- GtkCustomFilter* output_filter = gtk_custom_filter_new( foobar_audio_service_output_filter_func, NULL, NULL );
- self->outputs = gtk_filter_list_model_new(
- G_LIST_MODEL( g_object_ref( self->sorted_devices ) ),
- GTK_FILTER( output_filter ) );
-
- self->default_input = foobar_audio_device_new( self, FOOBAR_AUDIO_DEVICE_INPUT );
- self->default_output = foobar_audio_device_new( self, FOOBAR_AUDIO_DEVICE_OUTPUT );
- foobar_audio_device_set_default( self->default_input, TRUE );
- foobar_audio_device_set_default( self->default_output, TRUE );
-
- self->default_sink_handler_id = g_signal_connect(
- self->control,
- "default-sink-changed",
- G_CALLBACK( foobar_audio_service_handle_default_sink_changed ),
- self );
- self->default_source_handler_id = g_signal_connect(
- self->control,
- "default-source-changed",
- G_CALLBACK( foobar_audio_service_handle_default_source_changed ),
- self );
- self->stream_added_handler_id = g_signal_connect(
- self->control,
- "stream-added",
- G_CALLBACK( foobar_audio_service_handle_stream_added ),
- self );
- self->stream_removed_handler_id = g_signal_connect(
- self->control,
- "stream-removed",
- G_CALLBACK( foobar_audio_service_handle_stream_removed ),
- self );
-
- gvc_mixer_control_open( self->control );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_audio_service_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarAudioService* self = (FoobarAudioService*)object;
-
- switch ( prop_id )
- {
- case PROP_INPUTS:
- g_value_set_object( value, foobar_audio_service_get_inputs( self ) );
- break;
- case PROP_OUTPUTS:
- g_value_set_object( value, foobar_audio_service_get_outputs( self ) );
- break;
- case PROP_DEFAULT_INPUT:
- g_value_set_object( value, foobar_audio_service_get_default_input( self ) );
- break;
- case PROP_DEFAULT_OUTPUT:
- g_value_set_object( value, foobar_audio_service_get_default_output( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the audio service.
-//
-void foobar_audio_service_finalize( GObject* object )
-{
- FoobarAudioService* self = (FoobarAudioService*)object;
-
- g_clear_signal_handler( &self->default_sink_handler_id, self->control );
- g_clear_signal_handler( &self->default_source_handler_id, self->control );
- g_clear_signal_handler( &self->stream_added_handler_id, self->control );
- g_clear_signal_handler( &self->stream_removed_handler_id, self->control );
-
- gvc_mixer_control_close( self->control );
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->devices ) ); ++i )
- {
- FoobarAudioDevice* device = g_list_model_get_item( G_LIST_MODEL( self->devices ), i );
- device->service = NULL;
- }
-
- self->default_input->service = NULL;
- self->default_output->service = NULL;
-
- g_clear_object( &self->control );
- g_clear_object( &self->inputs );
- g_clear_object( &self->outputs );
- g_clear_object( &self->sorted_devices );
- g_clear_object( &self->devices );
- g_clear_object( &self->default_input );
- g_clear_object( &self->default_output );
-
- G_OBJECT_CLASS( foobar_audio_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new audio service instance.
-//
-FoobarAudioService* foobar_audio_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_AUDIO_SERVICE, NULL );
-}
-
-//
-// Get the default input device. This reference remains constant.
-//
-FoobarAudioDevice* foobar_audio_service_get_default_input( FoobarAudioService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( self ), NULL );
- return self->default_input;
-}
-
-//
-// Get the default output device. This reference remains constant.
-//
-FoobarAudioDevice* foobar_audio_service_get_default_output( FoobarAudioService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( self ), NULL );
- return self->default_output;
-}
-
-//
-// Get a sorted list of input devices.
-//
-GListModel* foobar_audio_service_get_inputs( FoobarAudioService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( self ), NULL );
- return G_LIST_MODEL( self->inputs );
-}
-
-//
-// Get a sorted list of output devices.
-//
-GListModel* foobar_audio_service_get_outputs( FoobarAudioService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( self ), NULL );
- return G_LIST_MODEL( self->outputs );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called by the mixer control when the default output device has changed.
-//
-// This will update the "default" state of the device and set the backing stream for the static default device.
-//
-void foobar_audio_service_handle_default_sink_changed(
- GvcMixerControl* control,
- guint id,
- gpointer userdata )
-{
- (void)control;
- FoobarAudioService* self = (FoobarAudioService*)userdata;
-
- foobar_audio_service_update_default( G_LIST_MODEL( self->outputs ), id );
-
- GvcMixerStream* stream = gvc_mixer_control_lookup_stream_id( self->control, id );
- foobar_audio_device_set_stream( self->default_output, stream );
-}
-
-//
-// Called by the mixer control when the default input device has changed.
-//
-// This will update the "default" state of the device and set the backing stream for the static default device.
-//
-void foobar_audio_service_handle_default_source_changed(
- GvcMixerControl* control,
- guint id,
- gpointer userdata )
-{
- (void)control;
- FoobarAudioService* self = (FoobarAudioService*)userdata;
-
- foobar_audio_service_update_default( G_LIST_MODEL( self->inputs ), id );
-
- GvcMixerStream* stream = gvc_mixer_control_lookup_stream_id( self->control, id );
- foobar_audio_device_set_stream( self->default_input, stream );
-}
-
-//
-// Called when an audio device was added.
-//
-void foobar_audio_service_handle_stream_added(
- GvcMixerControl* control,
- guint id,
- gpointer userdata )
-{
- (void)control;
- FoobarAudioService* self = (FoobarAudioService*)userdata;
-
- GvcMixerStream* stream = gvc_mixer_control_lookup_stream_id( self->control, id );
- FoobarAudioDeviceKind kind;
- if ( foobar_audio_service_identify_stream( stream, &kind ) )
- {
- g_autoptr( FoobarAudioDevice ) device = foobar_audio_device_new( self, kind );
- foobar_audio_device_set_stream( device, stream );
- g_list_store_append( self->devices, device );
- }
-}
-
-//
-// Called when an audio device was removed.
-//
-void foobar_audio_service_handle_stream_removed(
- GvcMixerControl* control,
- guint id,
- gpointer userdata )
-{
- (void)control;
- FoobarAudioService* self = (FoobarAudioService*)userdata;
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->devices ) ); ++i )
- {
- FoobarAudioDevice* device = g_list_model_get_item( G_LIST_MODEL( self->devices ), i );
- if ( foobar_audio_device_get_id( device ) == id )
- {
- g_object_ref( device );
- device->service = NULL;
- g_list_store_remove( self->devices, i );
- foobar_audio_device_set_stream( device, NULL );
- g_object_unref( device );
- break;
- }
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Identify whether a stream is an input device or an output device, returning FALSE if unknown.
-//
-gboolean foobar_audio_service_identify_stream(
- GvcMixerStream* stream,
- FoobarAudioDeviceKind* out_kind )
-{
- if ( GVC_IS_MIXER_SINK( stream ) )
- {
- *out_kind = FOOBAR_AUDIO_DEVICE_OUTPUT;
- return TRUE;
- }
-
- if ( GVC_IS_MIXER_SOURCE( stream ) )
- {
- *out_kind = FOOBAR_AUDIO_DEVICE_INPUT;
- return TRUE;
- }
-
- return FALSE;
-}
-
-//
-// Set the "default" state for the device with the specified ID to TRUE and all other devices to FALSE.
-//
-void foobar_audio_service_update_default(
- GListModel* list,
- guint id )
-{
- for ( guint i = 0; i < g_list_model_get_n_items( list ); ++i )
- {
- FoobarAudioDevice* device = g_list_model_get_item( list, i );
- gboolean is_default = foobar_audio_device_get_id( device ) == id;
- foobar_audio_device_set_default( device, is_default );
- }
-}
-
-//
-// Sorting callback for audio devices.
-//
-gint foobar_audio_service_sort_func(
- gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarAudioDevice* device_a = (FoobarAudioDevice*)item_a;
- FoobarAudioDevice* device_b = (FoobarAudioDevice*)item_b;
- return g_strcmp0(
- foobar_audio_device_get_description( device_a ),
- foobar_audio_device_get_description( device_b ) );
-}
-
-//
-// Filtering callback for the list of input devices.
-//
-gboolean foobar_audio_service_input_filter_func(
- gpointer item,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarAudioDevice* device = item;
- return foobar_audio_device_get_kind( device ) == FOOBAR_AUDIO_DEVICE_INPUT;
-}
-
-//
-// Filtering callback for the list of output devices.
-//
-gboolean foobar_audio_service_output_filter_func(
- gpointer item,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarAudioDevice* device = item;
- return foobar_audio_device_get_kind( device ) == FOOBAR_AUDIO_DEVICE_OUTPUT;
-}
diff --git a/src/services/audio-service.h b/src/services/audio-service.h
deleted file mode 100644
index a833c20..0000000
--- a/src/services/audio-service.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_AUDIO_DEVICE_KIND foobar_audio_device_kind_get_type( )
-#define FOOBAR_TYPE_AUDIO_DEVICE foobar_audio_device_get_type( )
-#define FOOBAR_TYPE_AUDIO_SERVICE foobar_audio_service_get_type( )
-
-typedef enum
-{
- FOOBAR_AUDIO_DEVICE_INPUT = 0,
- FOOBAR_AUDIO_DEVICE_OUTPUT,
-} FoobarAudioDeviceKind;
-
-GType foobar_audio_device_kind_get_type( void );
-
-G_DECLARE_FINAL_TYPE( FoobarAudioDevice, foobar_audio_device, FOOBAR, AUDIO_DEVICE, GObject )
-
-FoobarAudioDeviceKind foobar_audio_device_get_kind ( FoobarAudioDevice* self );
-guint foobar_audio_device_get_id ( FoobarAudioDevice* self );
-gchar const* foobar_audio_device_get_name ( FoobarAudioDevice* self );
-gchar const* foobar_audio_device_get_description( FoobarAudioDevice* self );
-gint foobar_audio_device_get_volume ( FoobarAudioDevice* self );
-gboolean foobar_audio_device_is_muted ( FoobarAudioDevice* self );
-gboolean foobar_audio_device_is_default ( FoobarAudioDevice* self );
-gboolean foobar_audio_device_is_available ( FoobarAudioDevice* self );
-void foobar_audio_device_set_volume ( FoobarAudioDevice* self,
- gint value );
-void foobar_audio_device_set_muted ( FoobarAudioDevice* self,
- gboolean value );
-void foobar_audio_device_make_default ( FoobarAudioDevice* self );
-
-G_DECLARE_FINAL_TYPE( FoobarAudioService, foobar_audio_service, FOOBAR, AUDIO_SERVICE, GObject )
-
-FoobarAudioService* foobar_audio_service_new ( void );
-FoobarAudioDevice* foobar_audio_service_get_default_input ( FoobarAudioService* self );
-FoobarAudioDevice* foobar_audio_service_get_default_output( FoobarAudioService* self );
-GListModel* foobar_audio_service_get_inputs ( FoobarAudioService* self );
-GListModel* foobar_audio_service_get_outputs ( FoobarAudioService* self );
-
-G_END_DECLS
diff --git a/src/services/battery-service.c b/src/services/battery-service.c
deleted file mode 100644
index 0457547..0000000
--- a/src/services/battery-service.c
+++ /dev/null
@@ -1,378 +0,0 @@
-#include "services/battery-service.h"
-#include "dbus/upower.h"
-#include <math.h>
-
-#define DEVICE_STATE_CHARGING 1
-#define DEVICE_STATE_FULLY_CHARGED 4
-
-//
-// FoobarBatteryState:
-//
-// Structure storing the current information about the battery.
-//
-
-struct _FoobarBatteryState
-{
- gboolean is_charging;
- gboolean is_charged;
- gint percentage;
- gint time_remaining;
-};
-
-G_DEFINE_BOXED_TYPE( FoobarBatteryState, foobar_battery_state, foobar_battery_state_copy, foobar_battery_state_free )
-
-//
-// FoobarBatteryService:
-//
-// Service monitoring the state of the battery. This is implemented using the UPower D-Bus API.
-//
-
-struct _FoobarBatteryService
-{
- GObject parent_instance;
- FoobarBatteryState* state;
- FoobarUPowerDevice* proxy;
- gulong changed_handler_id;
-};
-
-enum
-{
- PROP_STATE = 1,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_battery_service_class_init ( FoobarBatteryServiceClass* klass );
-static void foobar_battery_service_init ( FoobarBatteryService* self );
-static void foobar_battery_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_battery_service_finalize ( GObject* object );
-static void foobar_battery_service_handle_connect ( GObject* object,
- GAsyncResult* result,
- gpointer userdata );
-static void foobar_battery_service_handle_properties_changed( GDBusProxy* proxy,
- GVariant* changed_properties,
- GStrv invalidated_properties,
- gpointer userdata );
-static void foobar_battery_service_update ( FoobarBatteryService* self );
-
-G_DEFINE_FINAL_TYPE( FoobarBatteryService, foobar_battery_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// State
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new, default-initialized state structure.
-//
-FoobarBatteryState* foobar_battery_state_new( void )
-{
- return g_new0( FoobarBatteryState, 1 );
-}
-
-//
-// Create a mutable copy of a state structure.
-//
-FoobarBatteryState* foobar_battery_state_copy( FoobarBatteryState const* self )
-{
- if ( !self ) { return NULL; }
-
- FoobarBatteryState* copy = g_new0( FoobarBatteryState, 1 );
- copy->is_charging = self->is_charging;
- copy->is_charged = self->is_charged;
- copy->percentage = self->percentage;
- copy->time_remaining = self->time_remaining;
- return copy;
-}
-
-//
-// Release resources associated with a state structure (NULL is allowed).
-//
-void foobar_battery_state_free( FoobarBatteryState* self )
-{
- g_free( self );
-}
-
-//
-// Compare two battery state structures (NULL is allowed)/
-//
-gboolean foobar_battery_state_equal(
- FoobarBatteryState const* a,
- FoobarBatteryState const* b )
-{
- if ( a == NULL && b == NULL ) { return TRUE; }
- if ( a == NULL || b == NULL ) { return FALSE; }
-
- if ( a->is_charging != b->is_charging ) { return FALSE; }
- if ( a->is_charged != b->is_charged ) { return FALSE; }
- if ( a->percentage != b->percentage ) { return FALSE; }
- if ( a->time_remaining != b->time_remaining ) { return FALSE; }
-
- return TRUE;
-}
-
-//
-// Indicates whether the battery is currently being charged.
-//
-gboolean foobar_battery_state_is_charging( FoobarBatteryState const* self )
-{
- g_return_val_if_fail( self != NULL, FALSE );
- return self->is_charging;
-}
-
-//
-// Indicates whether the battery is currently being charged.
-//
-void foobar_battery_state_set_charging(
- FoobarBatteryState* self,
- gboolean value )
-{
- g_return_if_fail( self != NULL );
- self->is_charging = !!value;
-}
-
-//
-// Indicates whether the battery is fully charged.
-//
-gboolean foobar_battery_state_is_charged( FoobarBatteryState const* self )
-{
- g_return_val_if_fail( self != NULL, FALSE );
- return self->is_charged;
-}
-
-//
-// Indicates whether the battery is fully charged.
-//
-void foobar_battery_state_set_charged(
- FoobarBatteryState* self,
- gboolean value )
-{
- g_return_if_fail( self != NULL );
- self->is_charged = !!value;
-}
-
-//
-// Current battery level as a percentage value.
-//
-gint foobar_battery_state_get_percentage( FoobarBatteryState const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->percentage;
-}
-
-//
-// Current battery level as a percentage value.
-//
-void foobar_battery_state_set_percentage(
- FoobarBatteryState* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->percentage = CLAMP( value, 0, 100 );
-}
-
-//
-// Estimated remaining time until fully charged or empty in seconds.
-//
-gint foobar_battery_state_get_time_remaining( FoobarBatteryState const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->time_remaining;
-}
-
-//
-// Estimated remaining time until fully charged or empty in seconds.
-//
-void foobar_battery_state_set_time_remaining(
- FoobarBatteryState* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->time_remaining = MAX( value, 0 );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the battery service.
-//
-void foobar_battery_service_class_init( FoobarBatteryServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_battery_service_get_property;
- object_klass->finalize = foobar_battery_service_finalize;
-
- props[PROP_STATE] = g_param_spec_boxed(
- "state",
- "State",
- "Current state of the battery, if available.",
- FOOBAR_TYPE_BATTERY_STATE,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the battery service.
-//
-void foobar_battery_service_init( FoobarBatteryService* self )
-{
- foobar_upower_device_proxy_new_for_bus(
- G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_NONE,
- "org.freedesktop.UPower",
- "/org/freedesktop/UPower/devices/DisplayDevice",
- NULL,
- foobar_battery_service_handle_connect,
- g_object_ref( self ) );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_battery_service_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarBatteryService* self = (FoobarBatteryService*)object;
-
- switch ( prop_id )
- {
- case PROP_STATE:
- g_value_set_boxed( value, foobar_battery_service_get_state( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the battery service.
-//
-void foobar_battery_service_finalize( GObject* object )
-{
- FoobarBatteryService* self = (FoobarBatteryService*)object;
-
- g_clear_signal_handler( &self->changed_handler_id, self->proxy );
- g_clear_object( &self->proxy );
- g_clear_pointer( &self->state, foobar_battery_state_free );
-
- G_OBJECT_CLASS( foobar_battery_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new battery service instance.
-//
-FoobarBatteryService* foobar_battery_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_BATTERY_SERVICE, NULL );
-}
-
-//
-// Get the current state of the battery, if available.
-//
-FoobarBatteryState const* foobar_battery_service_get_state( FoobarBatteryService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_BATTERY_SERVICE( self ), NULL );
-
- return self->state;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called after asynchronous initialization of the D-Bus proxy.
-//
-void foobar_battery_service_handle_connect(
- GObject* object,
- GAsyncResult* result,
- gpointer userdata )
-{
- (void)object;
- g_autoptr( FoobarBatteryService ) self = (FoobarBatteryService*)userdata;
-
- g_autoptr( GError ) error = NULL;
- self->proxy = foobar_upower_device_proxy_new_for_bus_finish( result, &error );
- if ( self->proxy )
- {
- self->changed_handler_id = g_signal_connect(
- self->proxy,
- "g-properties-changed",
- G_CALLBACK( foobar_battery_service_handle_properties_changed ),
- self );
- foobar_battery_service_update( self );
- }
- else
- {
- g_warning( "Unable to connect to battery status service: %s", error->message );
- }
-}
-
-//
-// Called by the D-Bus server when the battery state has changed.
-//
-void foobar_battery_service_handle_properties_changed(
- GDBusProxy* proxy,
- GVariant* changed_properties,
- GStrv invalidated_properties,
- gpointer userdata )
-{
- (void)proxy;
- (void)changed_properties;
- (void)invalidated_properties;
- FoobarBatteryService* self = (FoobarBatteryService*)userdata;
-
- foobar_battery_service_update( self );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Reload the battery state.
-//
-void foobar_battery_service_update( FoobarBatteryService* self )
-{
- g_return_if_fail( FOOBAR_IS_BATTERY_SERVICE( self ) );
-
- FoobarBatteryState* new_state = NULL;
- if ( self->proxy && foobar_upower_device_get_is_present( self->proxy ) )
- {
- guint state = foobar_upower_device_get_state( self->proxy );
- gint percent = (gint)round( foobar_upower_device_get_percentage( self->proxy ) );
- gboolean is_charging = state == DEVICE_STATE_CHARGING;
- gboolean is_charged =
- state == DEVICE_STATE_FULLY_CHARGED ||
- ( state == DEVICE_STATE_CHARGING && percent == 100 );
- gint time_remaining = is_charging
- ? (gint)foobar_upower_device_get_time_to_full( self->proxy )
- : (gint)foobar_upower_device_get_time_to_empty( self->proxy );
-
- new_state = foobar_battery_state_new( );
- foobar_battery_state_set_charging( new_state, is_charging );
- foobar_battery_state_set_charged( new_state, is_charged );
- foobar_battery_state_set_percentage( new_state, percent );
- foobar_battery_state_set_time_remaining( new_state, time_remaining );
- }
-
- if ( !foobar_battery_state_equal( self->state, new_state ) )
- {
- g_clear_pointer( &self->state, foobar_battery_state_free );
- self->state = new_state;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_STATE] );
- }
-}
diff --git a/src/services/battery-service.h b/src/services/battery-service.h
deleted file mode 100644
index bf15c4a..0000000
--- a/src/services/battery-service.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_BATTERY_STATE foobar_battery_state_get_type( )
-#define FOOBAR_TYPE_BATTERY_SERVICE foobar_battery_service_get_type( )
-
-typedef struct _FoobarBatteryState FoobarBatteryState;
-
-GType foobar_battery_state_get_type ( void );
-FoobarBatteryState* foobar_battery_state_new ( void );
-FoobarBatteryState* foobar_battery_state_copy ( FoobarBatteryState const* self );
-void foobar_battery_state_free ( FoobarBatteryState* self );
-gboolean foobar_battery_state_equal ( FoobarBatteryState const* a,
- FoobarBatteryState const* b );
-gboolean foobar_battery_state_is_charging ( FoobarBatteryState const* self );
-gboolean foobar_battery_state_is_charged ( FoobarBatteryState const* self );
-gint foobar_battery_state_get_percentage ( FoobarBatteryState const* self );
-gint foobar_battery_state_get_time_remaining( FoobarBatteryState const* self );
-void foobar_battery_state_set_charging ( FoobarBatteryState* self,
- gboolean value );
-void foobar_battery_state_set_charged ( FoobarBatteryState* self,
- gboolean value );
-void foobar_battery_state_set_percentage ( FoobarBatteryState* self,
- gint value );
-void foobar_battery_state_set_time_remaining( FoobarBatteryState* self,
- gint value );
-
-G_DECLARE_FINAL_TYPE( FoobarBatteryService, foobar_battery_service, FOOBAR, BATTERY_SERVICE, GObject )
-
-FoobarBatteryService* foobar_battery_service_new ( void );
-FoobarBatteryState const* foobar_battery_service_get_state( FoobarBatteryService* self );
-
-G_END_DECLS
diff --git a/src/services/bluetooth-service.c b/src/services/bluetooth-service.c
deleted file mode 100644
index eb13968..0000000
--- a/src/services/bluetooth-service.c
+++ /dev/null
@@ -1,949 +0,0 @@
-#include "services/bluetooth-service.h"
-#include "dbus/bluez.h"
-#include <gtk/gtk.h>
-
-//
-// FoobarBluetoothDeviceState:
-//
-// The current connection state of a remote bluetooth device.
-//
-
-G_DEFINE_ENUM_TYPE(
- FoobarBluetoothDeviceState,
- foobar_bluetooth_device_state,
- G_DEFINE_ENUM_VALUE( FOOBAR_BLUETOOTH_DEVICE_STATE_DISCONNECTED, "disconnected" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTING, "connecting" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTED, "connected" ) )
-
-//
-// FoobarBluetoothDevice:
-//
-// Represents an external bluetooth device the client can connect to.
-//
-
-struct _FoobarBluetoothDevice
-{
- GObject parent_instance;
- FoobarBluetoothService* service;
- FoobarBluetoothDeviceState state;
- FoobarBluezDevice* device;
- GCancellable* connect_cancellable;
- gulong notify_handler_id;
-};
-
-enum
-{
- DEVICE_PROP_NAME = 1,
- DEVICE_PROP_STATE,
- N_DEVICE_PROPS,
-};
-
-static GParamSpec* device_props[N_DEVICE_PROPS] = { 0 };
-
-static void foobar_bluetooth_device_class_init ( FoobarBluetoothDeviceClass* klass );
-static void foobar_bluetooth_device_init ( FoobarBluetoothDevice* self );
-static void foobar_bluetooth_device_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_bluetooth_device_finalize ( GObject* object );
-static FoobarBluetoothDevice* foobar_bluetooth_device_new ( FoobarBluetoothService* service,
- FoobarBluezDevice* device );
-static void foobar_bluetooth_device_handle_notify( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_bluetooth_device_connect_cb ( GObject* object,
- GAsyncResult* result,
- gpointer userdata );
-static void foobar_bluetooth_device_disconnect_cb( GObject* object,
- GAsyncResult* result,
- gpointer userdata );
-static void foobar_bluetooth_device_update_state ( FoobarBluetoothDevice* self );
-
-G_DEFINE_FINAL_TYPE( FoobarBluetoothDevice, foobar_bluetooth_device, G_TYPE_OBJECT )
-
-//
-// FoobarBluetoothService:
-//
-// Service managing the state of the bluetooth adapter. This is implemented using the Bluez DBus API.
-//
-
-struct _FoobarBluetoothService
-{
- GObject parent_instance;
- GDBusObjectManager* object_manager;
- GListStore* devices;
- GtkFilterListModel* filtered_devices;
- GtkSortListModel* sorted_devices;
- GtkFilterListModel* connected_devices;
- GPtrArray* adapters; // First adapter is the default one.
- FoobarBluezAdapter* default_adapter;
- gulong interface_added_handler_id;
- gulong interface_removed_handler_id;
- gulong object_added_handler_id;
- gulong object_removed_handler_id;
- gulong adapter_notify_handler_id;
-};
-
-enum
-{
- PROP_DEVICES = 1,
- PROP_CONNECTED_DEVICES,
- PROP_IS_AVAILABLE,
- PROP_IS_ENABLED,
- PROP_IS_SCANNING,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_bluetooth_service_class_init ( FoobarBluetoothServiceClass* klass );
-static void foobar_bluetooth_service_init ( FoobarBluetoothService* self );
-static void foobar_bluetooth_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_bluetooth_service_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_bluetooth_service_finalize ( GObject* object );
-static void foobar_bluetooth_service_handle_interface_added ( GDBusObjectManager* manager,
- GDBusObject* object,
- GDBusInterface* interface,
- gpointer userdata );
-static void foobar_bluetooth_service_handle_interface_removed( GDBusObjectManager* manager,
- GDBusObject* object,
- GDBusInterface* interface,
- gpointer userdata );
-static void foobar_bluetooth_service_handle_object_added ( GDBusObjectManager* manager,
- GDBusObject* object,
- gpointer userdata );
-static void foobar_bluetooth_service_handle_object_removed ( GDBusObjectManager* manager,
- GDBusObject* object,
- gpointer userdata );
-static void foobar_bluetooth_service_handle_adapter_notify ( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_bluetooth_service_update_default_adapter ( FoobarBluetoothService* self );
-static GType foobar_bluetooth_service_dbus_proxy_type_cb ( GDBusObjectManagerClient* manager,
- gchar const* object_path,
- gchar const* interface_name,
- gpointer userdata );
-static gboolean foobar_bluetooth_service_filter_func ( gpointer item,
- gpointer userdata );
-static gboolean foobar_bluetooth_service_connected_filter_func ( gpointer item,
- gpointer userdata );
-static gint foobar_bluetooth_service_sort_func ( gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarBluetoothService, foobar_bluetooth_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Device
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for devices.
-//
-void foobar_bluetooth_device_class_init( FoobarBluetoothDeviceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_bluetooth_device_get_property;
- object_klass->finalize = foobar_bluetooth_device_finalize;
-
- device_props[DEVICE_PROP_NAME] = g_param_spec_string(
- "name",
- "Name",
- "Human-readable name for the device.",
- NULL,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_STATE] = g_param_spec_enum(
- "state",
- "State",
- "Current connection state of the device.",
- FOOBAR_TYPE_BLUETOOTH_DEVICE_STATE,
- 0,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_DEVICE_PROPS, device_props );
-}
-
-//
-// Instance initialization for devices.
-//
-void foobar_bluetooth_device_init( FoobarBluetoothDevice* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_bluetooth_device_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarBluetoothDevice* self = (FoobarBluetoothDevice*)object;
-
- switch ( prop_id )
- {
- case DEVICE_PROP_NAME:
- g_value_set_string( value, foobar_bluetooth_device_get_name( self ) );
- break;
- case DEVICE_PROP_STATE:
- g_value_set_enum( value, foobar_bluetooth_device_get_state( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for devices.
-//
-void foobar_bluetooth_device_finalize( GObject* object )
-{
- FoobarBluetoothDevice* self = (FoobarBluetoothDevice*)object;
-
- g_clear_signal_handler( &self->notify_handler_id, self->device );
- g_clear_object( &self->device );
- g_clear_object( &self->connect_cancellable );
-
- G_OBJECT_CLASS( foobar_bluetooth_device_parent_class )->finalize( object );
-}
-
-//
-// Create a new device object, wrapping a DBus device instance.
-//
-FoobarBluetoothDevice* foobar_bluetooth_device_new(
- FoobarBluetoothService* service,
- FoobarBluezDevice* device )
-{
- FoobarBluetoothDevice* self = g_object_new( FOOBAR_TYPE_BLUETOOTH_DEVICE, NULL );
-
- self->service = service;
- self->device = g_object_ref( device );
- foobar_bluetooth_device_update_state( self );
-
- self->notify_handler_id = g_signal_connect(
- self->device, "notify", G_CALLBACK( foobar_bluetooth_device_handle_notify ), self );
-
- return self;
-}
-
-//
-// Get the name/alias for the device.
-//
-gchar const* foobar_bluetooth_device_get_name( FoobarBluetoothDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_DEVICE( self ), NULL );
- return foobar_bluez_device_get_alias( self->device );
-}
-
-//
-// Get the current connection state for the device.
-//
-FoobarBluetoothDeviceState foobar_bluetooth_device_get_state( FoobarBluetoothDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_DEVICE( self ), 0 );
- return self->state;
-}
-
-//
-// Toggle whether the device is connected:
-//
-// - If the device is currently disconnected, it is connected.
-// - If the device is currently being connected, that process is aborted.
-// - If the device is currently connected, it is disconnected.
-//
-void foobar_bluetooth_device_toggle_connection( FoobarBluetoothDevice* self )
-{
- g_return_if_fail( FOOBAR_IS_BLUETOOTH_DEVICE( self ) );
-
- switch ( self->state )
- {
- case FOOBAR_BLUETOOTH_DEVICE_STATE_DISCONNECTED:
- self->connect_cancellable = g_cancellable_new( );
- foobar_bluetooth_device_update_state( self );
- foobar_bluez_device_call_connect(
- self->device,
- self->connect_cancellable,
- foobar_bluetooth_device_connect_cb,
- g_object_ref( self ) );
- break;
- case FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTING:
- g_cancellable_cancel( self->connect_cancellable );
- break;
- case FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTED:
- foobar_bluez_device_call_disconnect(
- self->device,
- NULL,
- foobar_bluetooth_device_disconnect_cb,
- g_object_ref( self ) );
- break;
- default:
- g_warn_if_reached( );
- }
-}
-
-//
-// Called by the underlying device when one of its properties changes.
-//
-void foobar_bluetooth_device_handle_notify(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- FoobarBluetoothDevice* self = (FoobarBluetoothDevice*)userdata;
-
- gchar const* property = g_param_spec_get_name( pspec );
- if ( !g_strcmp0( property, "alias" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_NAME] );
- }
- else if ( !g_strcmp0( property, "name" ) || !g_strcmp0( property, "adapter" ) )
- {
- if ( self->service )
- {
- gtk_filter_changed(
- gtk_filter_list_model_get_filter( self->service->filtered_devices ),
- GTK_FILTER_CHANGE_DIFFERENT );
- }
- }
- else if ( !g_strcmp0( property, "connected" ) )
- {
- foobar_bluetooth_device_update_state( self );
- }
-}
-
-//
-// Called when a request to connect the device is completed.
-//
-// This is mainly used to update the device's state.
-//
-void foobar_bluetooth_device_connect_cb(
- GObject* object,
- GAsyncResult* result,
- gpointer userdata )
-{
- (void)object;
- g_autoptr( FoobarBluetoothDevice ) self = (FoobarBluetoothDevice*)userdata;
-
- g_autoptr( GError ) error = NULL;
- if ( !foobar_bluez_device_call_connect_finish( self->device, result, &error ) )
- {
- g_warning( "Unable to connect bluetooth device: %s", error->message );
- }
-
- g_clear_object( &self->connect_cancellable );
- foobar_bluetooth_device_update_state( self );
-}
-
-//
-// Called when a request to disconnect the device is completed.
-//
-void foobar_bluetooth_device_disconnect_cb(
- GObject* object,
- GAsyncResult* result,
- gpointer userdata )
-{
- (void)object;
- g_autoptr( FoobarBluetoothDevice ) self = (FoobarBluetoothDevice*)userdata;
-
- g_autoptr( GError ) error = NULL;
- if ( !foobar_bluez_device_call_disconnect_finish( self->device, result, &error ) )
- {
- g_warning( "Unable to disconnect bluetooth device: %s", error->message );
- }
-}
-
-//
-// Update the current device state.
-//
-// The connect_cancellable member variable is used to indicate an ongoing connection attempt. Otherwise, the value of
-// is_connected is used.
-//
-void foobar_bluetooth_device_update_state( FoobarBluetoothDevice* self )
-{
- FoobarBluetoothDeviceState new_state = foobar_bluez_device_get_connected( self->device )
- ? FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTED
- : FOOBAR_BLUETOOTH_DEVICE_STATE_DISCONNECTED;
- if ( self->connect_cancellable )
- {
- new_state = FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTING;
- }
-
- if ( self->state != new_state )
- {
- self->state = new_state;
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_STATE] );
-
- if ( self->service )
- {
- gtk_filter_changed(
- gtk_filter_list_model_get_filter( self->service->connected_devices ),
- GTK_FILTER_CHANGE_DIFFERENT );
- }
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the bluetooth service.
-//
-void foobar_bluetooth_service_class_init( FoobarBluetoothServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_bluetooth_service_get_property;
- object_klass->set_property = foobar_bluetooth_service_set_property;
- object_klass->finalize = foobar_bluetooth_service_finalize;
-
- props[PROP_DEVICES] = g_param_spec_object(
- "devices",
- "Devices",
- "Sorted list of available bluetooth devices.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- props[PROP_CONNECTED_DEVICES] = g_param_spec_object(
- "connected-devices",
- "Connected Devices",
- "Sorted list of currently connected bluetooth devices.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- props[PROP_IS_AVAILABLE] = g_param_spec_boolean(
- "is-available",
- "Is Available",
- "Indicates whether the system has a bluetooth adapter.",
- FALSE,
- G_PARAM_READABLE );
- props[PROP_IS_ENABLED] = g_param_spec_boolean(
- "is-enabled",
- "Is Enabled",
- "Indicates whether the bluetooth adapter is currently enabled.",
- FALSE,
- G_PARAM_READWRITE );
- props[PROP_IS_SCANNING] = g_param_spec_boolean(
- "is-scanning",
- "Is Scanning",
- "Indicates whether the bluetooth adapter is currently scanning for devices.",
- FALSE,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the bluetooth service.
-//
-void foobar_bluetooth_service_init( FoobarBluetoothService* self )
-{
- self->devices = g_list_store_new( FOOBAR_TYPE_BLUETOOTH_DEVICE );
-
- GtkCustomFilter* filter = gtk_custom_filter_new( foobar_bluetooth_service_filter_func, self, NULL );
- self->filtered_devices = gtk_filter_list_model_new(
- G_LIST_MODEL( g_object_ref( self->devices ) ),
- GTK_FILTER( filter ) );
-
- GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_bluetooth_service_sort_func, NULL, NULL );
- self->sorted_devices = gtk_sort_list_model_new(
- G_LIST_MODEL( g_object_ref( self->filtered_devices ) ),
- GTK_SORTER( sorter ) );
-
- GtkCustomFilter* connected_filter = gtk_custom_filter_new( foobar_bluetooth_service_connected_filter_func, NULL, NULL );
- self->connected_devices = gtk_filter_list_model_new(
- G_LIST_MODEL( g_object_ref( self->sorted_devices ) ),
- GTK_FILTER( connected_filter ) );
-
- self->adapters = g_ptr_array_new_with_free_func( g_object_unref );
-
- g_autoptr( GError ) error = NULL;
- self->object_manager = g_dbus_object_manager_client_new_for_bus_sync(
- G_BUS_TYPE_SYSTEM,
- G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
- "org.bluez",
- "/",
- foobar_bluetooth_service_dbus_proxy_type_cb,
- NULL,
- NULL,
- NULL,
- &error );
-
- if ( self->object_manager )
- {
- self->interface_added_handler_id = g_signal_connect(
- self->object_manager,
- "interface-added",
- G_CALLBACK( foobar_bluetooth_service_handle_interface_added ),
- self );
- self->interface_removed_handler_id = g_signal_connect(
- self->object_manager,
- "interface-removed",
- G_CALLBACK( foobar_bluetooth_service_handle_interface_removed ),
- self );
- self->object_added_handler_id = g_signal_connect(
- self->object_manager,
- "object-added",
- G_CALLBACK( foobar_bluetooth_service_handle_object_added ),
- self );
- self->object_removed_handler_id = g_signal_connect(
- self->object_manager,
- "object-removed",
- G_CALLBACK( foobar_bluetooth_service_handle_object_removed ),
- self );
-
- g_autolist( GDBusObject ) objects = g_dbus_object_manager_get_objects( self->object_manager );
- for ( GList* it = objects; it; it = it->next )
- {
- foobar_bluetooth_service_handle_object_added( self->object_manager, it->data, self );
- }
- }
- else
- {
- g_warning( "Unable to create a DBus object manager for communication with Bluez: %s", error->message );
- }
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_bluetooth_service_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarBluetoothService* self = (FoobarBluetoothService*)object;
-
- switch ( prop_id )
- {
- case PROP_DEVICES:
- g_value_set_object( value, foobar_bluetooth_service_get_devices( self ) );
- break;
- case PROP_CONNECTED_DEVICES:
- g_value_set_object( value, foobar_bluetooth_service_get_connected_devices( self ) );
- break;
- case PROP_IS_AVAILABLE:
- g_value_set_boolean( value, foobar_bluetooth_service_is_available( self ) );
- break;
- case PROP_IS_ENABLED:
- g_value_set_boolean( value, foobar_bluetooth_service_is_enabled( self ) );
- break;
- case PROP_IS_SCANNING:
- g_value_set_boolean( value, foobar_bluetooth_service_is_scanning( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Property setter implementation, mapping a property id to a method.
-//
-void foobar_bluetooth_service_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarBluetoothService* self = (FoobarBluetoothService*)object;
-
- switch ( prop_id )
- {
- case PROP_IS_ENABLED:
- foobar_bluetooth_service_set_enabled( self, g_value_get_boolean( value ) );
- break;
- case PROP_IS_SCANNING:
- foobar_bluetooth_service_set_scanning( self, g_value_get_boolean( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the bluetooth service.
-//
-void foobar_bluetooth_service_finalize( GObject* object )
-{
- FoobarBluetoothService* self = (FoobarBluetoothService*)object;
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->devices ) ); ++i )
- {
- FoobarBluetoothDevice* device = g_list_model_get_item( G_LIST_MODEL( self->devices ), i );
- device->service = NULL;
- }
-
- g_clear_signal_handler( &self->adapter_notify_handler_id, self->default_adapter );
- g_clear_signal_handler( &self->interface_added_handler_id, self->object_manager );
- g_clear_signal_handler( &self->interface_removed_handler_id, self->object_manager );
- g_clear_signal_handler( &self->object_added_handler_id, self->object_manager );
- g_clear_signal_handler( &self->object_removed_handler_id, self->object_manager );
- g_clear_object( &self->object_manager );
- g_clear_object( &self->connected_devices );
- g_clear_object( &self->sorted_devices );
- g_clear_object( &self->filtered_devices );
- g_clear_object( &self->devices );
- g_clear_object( &self->default_adapter );
- g_clear_pointer( &self->adapters, g_ptr_array_unref );
-
- G_OBJECT_CLASS( foobar_bluetooth_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new bluetooth service instance.
-//
-FoobarBluetoothService* foobar_bluetooth_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_BLUETOOTH_SERVICE, NULL );
-}
-
-//
-// Get the sorted list of available bluetooth devices.
-//
-GListModel* foobar_bluetooth_service_get_devices( FoobarBluetoothService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( self ), NULL );
- return G_LIST_MODEL( self->sorted_devices );
-}
-
-//
-// Get the sorted list of currently connected bluetooth devices.
-//
-GListModel* foobar_bluetooth_service_get_connected_devices( FoobarBluetoothService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( self ), NULL );
- return G_LIST_MODEL( self->connected_devices );
-}
-
-//
-// Check whether the system has a bluetooth adapter.
-//
-gboolean foobar_bluetooth_service_is_available( FoobarBluetoothService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( self ), FALSE );
- return self->default_adapter != NULL;
-}
-
-//
-// Check whether the bluetooth adapter is currently enabled.
-//
-gboolean foobar_bluetooth_service_is_enabled( FoobarBluetoothService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( self ), FALSE );
- return self->default_adapter ? foobar_bluez_adapter_get_powered( self->default_adapter ) : FALSE;
-}
-
-//
-// Check whether the bluetooth adapter is currently scanning for devices.
-//
-gboolean foobar_bluetooth_service_is_scanning( FoobarBluetoothService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( self ), FALSE );
- return self->default_adapter ? foobar_bluez_adapter_get_discovering( self->default_adapter ) : FALSE;
-}
-
-//
-// Update whether the bluetooth adapter is currently enabled.
-//
-void foobar_bluetooth_service_set_enabled(
- FoobarBluetoothService* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( self ) );
-
- if (self->default_adapter)
- {
- foobar_bluez_adapter_set_powered( self->default_adapter, value );
- }
-}
-
-//
-// Update whether the bluetooth adapter is currently scanning for devices.
-//
-void foobar_bluetooth_service_set_scanning(
- FoobarBluetoothService* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( self ) );
-
- g_autoptr( GError ) error = NULL;
-
- if (self->default_adapter && foobar_bluetooth_service_is_scanning( self ) != value)
- {
- if (value)
- {
- // Make this device discoverable while discovery is active.
-
- GVariantBuilder filter_builder;
- g_variant_builder_init( &filter_builder, G_VARIANT_TYPE_VARDICT );
- g_variant_builder_add( &filter_builder, "{sv}", "Discoverable", g_variant_new_boolean( TRUE ) );
- GVariant* filter = g_variant_builder_end( &filter_builder );
- if ( !foobar_bluez_adapter_call_set_discovery_filter_sync( self->default_adapter, filter, NULL, &error ) )
- {
- g_warning( "Failed to set discovery filter: %s", error->message );
- return;
- }
- if ( !foobar_bluez_adapter_call_start_discovery_sync( self->default_adapter, NULL, &error ) )
- {
- g_warning( "Failed to start discovery: %s", error->message );
- }
- }
- else
- {
- if ( !foobar_bluez_adapter_call_stop_discovery_sync( self->default_adapter, NULL, &error ) )
- {
- g_warning( "Failed to stop discovery: %s", error->message );
- }
- }
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when an interface was added to an existing DBus object (either adapter or device).
-//
-void foobar_bluetooth_service_handle_interface_added(
- GDBusObjectManager* manager,
- GDBusObject* object,
- GDBusInterface* interface,
- gpointer userdata )
-{
- (void)manager;
- (void)object;
- FoobarBluetoothService* self = (FoobarBluetoothService*)userdata;
-
- if ( FOOBAR_IS_BLUEZ_DEVICE( interface ) )
- {
- g_autoptr( FoobarBluetoothDevice ) device = foobar_bluetooth_device_new(
- self,
- FOOBAR_BLUEZ_DEVICE( interface ) );
- g_list_store_append( self->devices, device );
- }
- else if ( FOOBAR_IS_BLUEZ_ADAPTER( interface ) )
- {
- g_ptr_array_add( self->adapters, g_object_ref( interface ) );
- foobar_bluetooth_service_update_default_adapter( self );
- }
-}
-
-//
-// Called when an interface was removed from an existing DBus object (either adapter or device).
-//
-void foobar_bluetooth_service_handle_interface_removed(
- GDBusObjectManager* manager,
- GDBusObject* object,
- GDBusInterface* interface,
- gpointer userdata )
-{
- (void)manager;
- FoobarBluetoothService* self = (FoobarBluetoothService*)userdata;
-
- gchar const* removed_path = g_dbus_object_get_object_path( object );
- if ( FOOBAR_IS_BLUEZ_DEVICE( interface ) )
- {
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->devices ) ); ++i )
- {
- FoobarBluetoothDevice* device = g_list_model_get_item( G_LIST_MODEL( self->devices ), i );
- gchar const* path = g_dbus_proxy_get_object_path( G_DBUS_PROXY( device->device ) );
- if ( !g_strcmp0( path, removed_path ) )
- {
- device->service = NULL;
- g_list_store_remove( self->devices, i );
- break;
- }
- }
- }
- else if ( FOOBAR_IS_BLUEZ_ADAPTER( interface ) )
- {
- for ( guint i = 0; i < self->adapters->len; ++i )
- {
- FoobarBluezAdapter* adapter = g_ptr_array_index( self->adapters, i );
- gchar const* path = g_dbus_proxy_get_object_path( G_DBUS_PROXY( adapter ) );
- if ( !g_strcmp0( path, removed_path ) )
- {
- g_ptr_array_remove_index( self->adapters, i );
- break;
- }
- }
- foobar_bluetooth_service_update_default_adapter( self );
- }
-}
-
-//
-// Called when a DBus object was added (either adapter or device).
-//
-void foobar_bluetooth_service_handle_object_added(
- GDBusObjectManager* manager,
- GDBusObject* object,
- gpointer userdata )
-{
- g_autolist( GDBusInterface ) interfaces = g_dbus_object_get_interfaces( object );
- for ( GList* it = interfaces; it; it = it->next )
- {
- foobar_bluetooth_service_handle_interface_added( manager, object, it->data, userdata );
- }
-}
-
-//
-// Called when a DBus object was removed (either adapter or device).
-//
-void foobar_bluetooth_service_handle_object_removed(
- GDBusObjectManager* manager,
- GDBusObject* object,
- gpointer userdata )
-{
- g_autolist( GDBusInterface ) interfaces = g_dbus_object_get_interfaces( object );
- for ( GList* it = interfaces; it; it = it->next )
- {
- foobar_bluetooth_service_handle_interface_removed( manager, object, it->data, userdata );
- }
-}
-
-//
-// Called when a property of the default adapter has changed.
-//
-void foobar_bluetooth_service_handle_adapter_notify(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- FoobarBluetoothService* self = (FoobarBluetoothService*)userdata;
-
- gchar const* property = g_param_spec_get_name( pspec );
- if ( !g_strcmp0( property, "powered" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_ENABLED] );
- }
- else if ( !g_strcmp0( property, "discovering" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_SCANNING] );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Update the default_adapter to match the first item in the adapters array.
-//
-void foobar_bluetooth_service_update_default_adapter( FoobarBluetoothService* self )
-{
- FoobarBluezAdapter* new_adapter = ( self->adapters->len > 0 ) ? g_ptr_array_index( self->adapters, 0 ) : NULL;
-
- if ( self->default_adapter != new_adapter )
- {
- g_clear_signal_handler( &self->adapter_notify_handler_id, self->default_adapter );
- g_clear_object( &self->default_adapter );
-
- if ( new_adapter )
- {
- self->default_adapter = g_object_ref( new_adapter );
- self->adapter_notify_handler_id = g_signal_connect(
- self->default_adapter,
- "notify",
- G_CALLBACK( foobar_bluetooth_service_handle_adapter_notify ),
- self );
- }
-
- gtk_filter_changed( gtk_filter_list_model_get_filter( self->filtered_devices ), GTK_FILTER_CHANGE_DIFFERENT );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_AVAILABLE] );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_ENABLED] );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_SCANNING] );
- }
-}
-
-//
-// Get the type for creating a DBus proxy object.
-//
-// This is used because the DBus object manager should give us instances of the FoobarBluezAdapterProxy and
-// FoobarBluezDeviceProxy types.
-//
-GType foobar_bluetooth_service_dbus_proxy_type_cb(
- GDBusObjectManagerClient* manager,
- gchar const* object_path,
- gchar const* interface_name,
- gpointer userdata )
-{
- if ( !interface_name )
- {
- return G_TYPE_DBUS_OBJECT_PROXY;
- }
-
- if ( !g_strcmp0( interface_name, "org.bluez.Device1" ) )
- {
- return FOOBAR_TYPE_BLUEZ_DEVICE_PROXY;
- }
-
- if ( !g_strcmp0( interface_name, "org.bluez.Adapter1" ) )
- {
- return FOOBAR_TYPE_BLUEZ_ADAPTER_PROXY;
- }
-
- return G_TYPE_DBUS_PROXY;
-}
-
-//
-// Filtering callback for the list of devices, only showing devices from the default adapter.
-//
-gboolean foobar_bluetooth_service_filter_func(
- gpointer item,
- gpointer userdata )
-{
- FoobarBluetoothService* self = (FoobarBluetoothService*)userdata;
- FoobarBluetoothDevice* device = item;
-
- gchar const* device_adapter = foobar_bluez_device_get_adapter( device->device );
- gchar const* default_adapter = self->default_adapter
- ? g_dbus_proxy_get_object_path( G_DBUS_PROXY( self->default_adapter ) )
- : NULL;
- gchar const* name = foobar_bluez_device_get_name( device->device );
- return name != NULL && !g_strcmp0( device_adapter, default_adapter );
-}
-
-//
-// Filtering callback for the list of connected devices.
-//
-gboolean foobar_bluetooth_service_connected_filter_func(
- gpointer item,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarBluetoothDevice* device = item;
- return foobar_bluetooth_device_get_state( device ) == FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTED;
-}
-
-//
-// Sorting callback for the list of devices.
-//
-// Devices are sorted by their names.
-//
-gint foobar_bluetooth_service_sort_func(
- gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarBluetoothDevice* device_a = (FoobarBluetoothDevice*)item_a;
- FoobarBluetoothDevice* device_b = (FoobarBluetoothDevice*)item_b;
- return g_strcmp0( foobar_bluetooth_device_get_name( device_a ), foobar_bluetooth_device_get_name( device_b ) );
-}
diff --git a/src/services/bluetooth-service.h b/src/services/bluetooth-service.h
deleted file mode 100644
index 684fd45..0000000
--- a/src/services/bluetooth-service.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_BLUETOOTH_DEVICE_STATE foobar_bluetooth_device_state_get_type( )
-#define FOOBAR_TYPE_BLUETOOTH_DEVICE foobar_bluetooth_device_get_type( )
-#define FOOBAR_TYPE_BLUETOOTH_ADAPTER foobar_bluetooth_adapter_get_type( )
-#define FOOBAR_TYPE_BLUETOOTH_SERVICE foobar_bluetooth_service_get_type( )
-
-typedef enum
-{
- FOOBAR_BLUETOOTH_DEVICE_STATE_DISCONNECTED,
- FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTING,
- FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTED,
-} FoobarBluetoothDeviceState;
-
-GType foobar_bluetooth_device_state_get_type( void );
-
-G_DECLARE_FINAL_TYPE( FoobarBluetoothDevice, foobar_bluetooth_device, FOOBAR, BLUETOOTH_DEVICE, GObject )
-
-gchar const* foobar_bluetooth_device_get_name ( FoobarBluetoothDevice* self );
-FoobarBluetoothDeviceState foobar_bluetooth_device_get_state ( FoobarBluetoothDevice* self );
-void foobar_bluetooth_device_toggle_connection( FoobarBluetoothDevice* self );
-
-G_DECLARE_FINAL_TYPE( FoobarBluetoothService, foobar_bluetooth_service, FOOBAR, BLUETOOTH_SERVICE, GObject )
-
-FoobarBluetoothService* foobar_bluetooth_service_new ( void );
-GListModel* foobar_bluetooth_service_get_devices ( FoobarBluetoothService* self );
-GListModel* foobar_bluetooth_service_get_connected_devices( FoobarBluetoothService* self );
-gboolean foobar_bluetooth_service_is_available ( FoobarBluetoothService* self );
-gboolean foobar_bluetooth_service_is_enabled ( FoobarBluetoothService* self );
-gboolean foobar_bluetooth_service_is_scanning ( FoobarBluetoothService* self );
-void foobar_bluetooth_service_set_enabled ( FoobarBluetoothService* self,
- gboolean value );
-void foobar_bluetooth_service_set_scanning ( FoobarBluetoothService* self,
- gboolean value );
-
-G_END_DECLS
diff --git a/src/services/brightness-service.c b/src/services/brightness-service.c
deleted file mode 100644
index 32f0501..0000000
--- a/src/services/brightness-service.c
+++ /dev/null
@@ -1,391 +0,0 @@
-#include "services/brightness-service.h"
-#include <gio/gio.h>
-#include <math.h>
-
-//
-// FoobarBrightnessService:
-//
-// Service managing the brightness level. This is implemented by
-// - for read access: monitoring the "brightness" file in a "/sys/class/backlight" subdirectory,
-// - for write access: invoking the "brightnessctl" command.
-//
-
-struct _FoobarBrightnessService
-{
- GObject parent_instance;
- gint percentage;
- GFileMonitor* file_monitor;
- gulong file_monitor_handler_id;
- gchar* device_name;
- gchar* file_path;
- gint max_brightness;
-};
-
-enum
-{
- PROP_PERCENTAGE = 1,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_brightness_service_class_init ( FoobarBrightnessServiceClass* klass );
-static void foobar_brightness_service_init ( FoobarBrightnessService* self );
-static void foobar_brightness_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_brightness_service_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_brightness_service_finalize ( GObject* object );
-static void foobar_brightness_service_handle_changed ( GFileMonitor* monitor,
- GFile* file,
- GFile* other_file,
- GFileMonitorEvent event_type,
- gpointer userdata );
-static gint foobar_brightness_service_load_percentage ( FoobarBrightnessService* self );
-static void foobar_brightness_service_write_percentage ( FoobarBrightnessService* self );
-static void foobar_brightness_service_write_percentage_watch_cb( GPid pid,
- gint status,
- gpointer userdata );
-static void foobar_brightness_service_get_info ( gchar** out_device_name,
- gchar** out_file_path,
- gint* out_max_brightness );
-static gint foobar_brightness_service_read_value ( gchar const* path );
-static GFileMonitor* foobar_brightness_service_monitor ( gchar const* path );
-
-G_DEFINE_FINAL_TYPE( FoobarBrightnessService, foobar_brightness_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the brightness service.
-//
-void foobar_brightness_service_class_init( FoobarBrightnessServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_brightness_service_get_property;
- object_klass->set_property = foobar_brightness_service_set_property;
- object_klass->finalize = foobar_brightness_service_finalize;
-
- props[PROP_PERCENTAGE] = g_param_spec_int(
- "percentage",
- "Percentage",
- "Current brightness percentage.",
- 0,
- 100,
- 100,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the brightness service.
-//
-void foobar_brightness_service_init( FoobarBrightnessService* self )
-{
- foobar_brightness_service_get_info( &self->device_name, &self->file_path, &self->max_brightness );
- self->percentage = foobar_brightness_service_load_percentage( self );
- if ( self->file_path )
- {
- self->file_monitor = foobar_brightness_service_monitor( self->file_path );
- }
- if ( self->file_monitor )
- {
- self->file_monitor_handler_id = g_signal_connect(
- self->file_monitor,
- "changed",
- G_CALLBACK( foobar_brightness_service_handle_changed ),
- self );
- }
- else
- {
- self->file_monitor_handler_id = 0;
- }
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_brightness_service_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarBrightnessService* self = (FoobarBrightnessService*)object;
-
- switch ( prop_id )
- {
- case PROP_PERCENTAGE:
- g_value_set_int( value, foobar_brightness_service_get_percentage( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Property setter implementation, mapping a property id to a method.
-//
-void foobar_brightness_service_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarBrightnessService* self = (FoobarBrightnessService*)object;
-
- switch ( prop_id )
- {
- case PROP_PERCENTAGE:
- foobar_brightness_service_set_percentage( self, g_value_get_int( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the brightness service.
-//
-void foobar_brightness_service_finalize( GObject* object )
-{
- FoobarBrightnessService* self = (FoobarBrightnessService*)object;
-
- g_clear_signal_handler( &self->file_monitor_handler_id, self->file_monitor );
- g_clear_object( &self->file_monitor );
- g_clear_pointer( &self->device_name, g_free );
- g_clear_pointer( &self->file_path, g_free );
-
- G_OBJECT_CLASS( foobar_brightness_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new brightness service instance.
-//
-FoobarBrightnessService* foobar_brightness_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_BRIGHTNESS_SERVICE, NULL );
-}
-
-//
-// Get the current brightness percentage value.
-//
-gint foobar_brightness_service_get_percentage( FoobarBrightnessService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_BRIGHTNESS_SERVICE( self ), 0 );
-
- return self->percentage;
-}
-
-//
-// Update the current brightness percentage value.
-//
-void foobar_brightness_service_set_percentage(
- FoobarBrightnessService* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_BRIGHTNESS_SERVICE( self ) );
-
- value = CLAMP( value, 0, 100 );
- if ( self->percentage != value )
- {
- self->percentage = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_PERCENTAGE] );
- foobar_brightness_service_write_percentage( self );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called by the file monitor when the contents of the "brightness" file have changed.
-//
-void foobar_brightness_service_handle_changed(
- GFileMonitor* monitor,
- GFile* file,
- GFile* other_file,
- GFileMonitorEvent event_type,
- gpointer userdata )
-{
- (void)monitor;
- (void)file;
- (void)other_file;
- (void)event_type;
- FoobarBrightnessService* self = (FoobarBrightnessService*)userdata;
-
- gint new_percentage = foobar_brightness_service_load_percentage( self );
- if ( self->percentage != new_percentage )
- {
- self->percentage = new_percentage;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_PERCENTAGE] );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Load the current percentage value from the file at self->file_path.
-//
-// This requires file_path and max_brightness to be initialized, otherwise the brightness is assumed to be 100%.
-//
-gint foobar_brightness_service_load_percentage( FoobarBrightnessService* self )
-{
- if ( !self->file_path || self->max_brightness == -1 ) { return 100; }
-
- gint value = foobar_brightness_service_read_value( self->file_path );
- if ( value == -1 ) { return 100; }
-
- gdouble rel = value / (gdouble)self->max_brightness;
- gint percentage = (gint)round( rel * 100. );
- return CLAMP( percentage, 0, 100 );
-}
-
-//
-// Asynchronously update the current percentage value by invoking brightnessctl.
-//
-// This requires device_name to be initialized.
-//
-void foobar_brightness_service_write_percentage( FoobarBrightnessService* self )
-{
- if ( !self->device_name ) { return; }
-
- g_autofree gchar* percentage_string = g_strdup_printf( "%d%%", self->percentage );
- gchar* args[] = { "brightnessctl", "-d", self->device_name, "s", percentage_string, NULL };
-
- g_autoptr( GError ) error = NULL;
- GPid child_pid;
- if ( !g_spawn_async(
- NULL,
- args,
- NULL,
- G_SPAWN_DO_NOT_REAP_CHILD
- | G_SPAWN_SEARCH_PATH
- | G_SPAWN_STDIN_FROM_DEV_NULL
- | G_SPAWN_STDOUT_TO_DEV_NULL
- | G_SPAWN_STDERR_TO_DEV_NULL,
- NULL,
- NULL,
- &child_pid,
- &error ) )
- {
- g_warning( "Unable to spawn brightnessctl: %s", error->message );
- return;
- }
-
- g_child_watch_add( child_pid, foobar_brightness_service_write_percentage_watch_cb, NULL );
-}
-
-//
-// Called when the brightnessctl child process has finished.
-//
-void foobar_brightness_service_write_percentage_watch_cb(
- GPid pid,
- gint status,
- gpointer userdata )
-{
- (void)userdata;
-
- g_autoptr( GError ) error = NULL;
- if ( !g_spawn_check_wait_status( status, &error ) )
- {
- g_warning( "Unable to set brightness with brightnessctl: %s", error->message );
- }
-
- g_spawn_close_pid( pid );
-}
-
-//
-// Initialize information for the brightness device.
-//
-// This looks for the first directory in the /sys/class/backlight directories and uses it as the device name. It also
-// populates out_file_path to the path of this directory and out_max_brightness to the contents of the corresponding
-// max_brightness file.
-//
-void foobar_brightness_service_get_info(
- gchar** out_device_name,
- gchar** out_file_path,
- gint* out_max_brightness )
-{
- *out_device_name = NULL;
- *out_file_path = NULL;
- *out_max_brightness = -1;
-
- g_autoptr( GFile ) dir = g_file_new_for_path( "/sys/class/backlight" );
- if ( !dir ) { return; }
-
- g_autoptr( GFileEnumerator ) enumerator = g_file_enumerate_children(
- dir,
- "standard::",
- G_FILE_QUERY_INFO_NONE,
- NULL,
- NULL );
- if ( !enumerator ) { return; }
-
- g_autoptr( GFileInfo ) info = g_file_enumerator_next_file( enumerator, NULL, NULL );
- g_file_enumerator_close( enumerator, NULL, NULL );
- if ( !info ) { return; }
-
- gchar* device_name = g_strdup( g_file_info_get_name( info ) );
- gchar* current_file_path = g_strdup_printf( "/sys/class/backlight/%s/brightness", device_name );
- gchar* max_file_path = g_strdup_printf( "/sys/class/backlight/%s/max_brightness", device_name );
- *out_device_name = device_name;
- *out_file_path = current_file_path;
- *out_max_brightness = foobar_brightness_service_read_value( max_file_path );
- g_free( max_file_path );
-}
-
-//
-// Read the current brightness integer (not percentage) from the given file path.
-//
-gint foobar_brightness_service_read_value( gchar const* path )
-{
- g_autofree gchar* contents = NULL;
- g_autoptr( GError ) error = NULL;
- if ( !g_file_get_contents( path, &contents, NULL, &error ) )
- {
- g_warning( "Unable to read %s: %s", path, error->message );
- return -1;
- }
-
- errno = 0;
- char* endptr;
- long result = strtol( contents, &endptr, 10 );
- if ( errno != 0 || endptr == contents || result > INT_MAX || result < 0 )
- {
- g_warning( "Expected integer in %s, but got: %s", path, contents );
- return -1;
- }
-
- return (gint)result;
-}
-
-//
-// Set up a file monitor for the given path.
-//
-GFileMonitor* foobar_brightness_service_monitor( gchar const* path )
-{
- g_autoptr( GFile ) file = g_file_new_for_path( path );
- if ( !file ) { return NULL; }
-
- g_autoptr( GError ) error = NULL;
- GFileMonitor* monitor = g_file_monitor_file( file, G_FILE_MONITOR_NONE, NULL, &error );
- if ( !monitor ) { g_warning( "Unable to monitor brightness: %s", error->message ); }
-
- return monitor;
-}
diff --git a/src/services/brightness-service.h b/src/services/brightness-service.h
deleted file mode 100644
index 2b3799e..0000000
--- a/src/services/brightness-service.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_BRIGHTNESS_SERVICE foobar_brightness_service_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarBrightnessService, foobar_brightness_service, FOOBAR, BRIGHTNESS_SERVICE, GObject )
-
-FoobarBrightnessService* foobar_brightness_service_new ( void );
-gint foobar_brightness_service_get_percentage( FoobarBrightnessService* self );
-void foobar_brightness_service_set_percentage( FoobarBrightnessService* self,
- gint percentage );
-
-G_END_DECLS
diff --git a/src/services/clock-service.c b/src/services/clock-service.c
deleted file mode 100644
index c957011..0000000
--- a/src/services/clock-service.c
+++ /dev/null
@@ -1,141 +0,0 @@
-#include "services/clock-service.h"
-
-//
-// FoobarClockService:
-//
-// Service providing an auto-updating timestamp value which can be used to display the current time.
-//
-
-struct _FoobarClockService
-{
- GObject parent_instance;
- GDateTime* time;
- guint source_id;
-};
-
-enum
-{
- PROP_TIME = 1,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_clock_service_class_init ( FoobarClockServiceClass* klass );
-static void foobar_clock_service_init ( FoobarClockService* self );
-static void foobar_clock_service_get_property( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_clock_service_finalize ( GObject* object );
-static gboolean foobar_clock_service_handle_tick ( gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarClockService, foobar_clock_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the clock service.
-//
-void foobar_clock_service_class_init( FoobarClockServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_clock_service_get_property;
- object_klass->finalize = foobar_clock_service_finalize;
-
- props[PROP_TIME] = g_param_spec_boxed(
- "time",
- "Time",
- "The current time, regularly updated.",
- G_TYPE_DATE_TIME,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the clock service.
-//
-void foobar_clock_service_init( FoobarClockService* self )
-{
- self->time = g_date_time_new_now_local( );
- self->source_id = g_timeout_add_seconds( 1, foobar_clock_service_handle_tick, self );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_clock_service_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarClockService* self = (FoobarClockService*)object;
-
- switch ( prop_id )
- {
- case PROP_TIME:
- g_value_set_boxed( value, foobar_clock_service_get_time( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the clock service.
-//
-void foobar_clock_service_finalize( GObject* object )
-{
- FoobarClockService* self = (FoobarClockService*)object;
-
- g_clear_pointer( &self->time, g_date_time_unref );
- g_clear_handle_id( &self->source_id, g_source_remove );
-
- G_OBJECT_CLASS( foobar_clock_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new clock service instance.
-//
-FoobarClockService* foobar_clock_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_CLOCK_SERVICE, NULL );
-}
-
-//
-// Get the current snapshot of the timestamp.
-//
-// This may be a bit older than the value returned by g_date_time_new_now_local.
-//
-GDateTime* foobar_clock_service_get_time( FoobarClockService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CLOCK_SERVICE( self ), NULL );
-
- return self->time;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called every time the update delay has elapsed to update the current timestamp value.
-//
-gboolean foobar_clock_service_handle_tick( gpointer userdata )
-{
- FoobarClockService* self = (FoobarClockService*)userdata;
-
- g_clear_pointer( &self->time, g_date_time_unref );
- self->time = g_date_time_new_now_local( );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_TIME] );
-
- return G_SOURCE_CONTINUE;
-}
diff --git a/src/services/clock-service.h b/src/services/clock-service.h
deleted file mode 100644
index d435d92..0000000
--- a/src/services/clock-service.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_CLOCK_SERVICE foobar_clock_service_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarClockService, foobar_clock_service, FOOBAR, CLOCK_SERVICE, GObject )
-
-FoobarClockService* foobar_clock_service_new ( void );
-GDateTime* foobar_clock_service_get_time( FoobarClockService* self );
-
-G_END_DECLS
diff --git a/src/services/configuration-service.c b/src/services/configuration-service.c
deleted file mode 100644
index f3ca010..0000000
--- a/src/services/configuration-service.c
+++ /dev/null
@@ -1,3248 +0,0 @@
-#include "services/configuration-service.h"
-#include <gio/gio.h>
-
-#define UPDATE_APPLY_DELAY 250
-#define CONFIGURATION_LOG_DOMAIN "foobar.conf"
-#define CONFIGURATION_WARNING( ... ) g_log( CONFIGURATION_LOG_DOMAIN, G_LOG_LEVEL_WARNING, __VA_ARGS__ )
-
-//
-// FoobarScreenEdge:
-//
-// An edge of the screen.
-//
-
-G_STATIC_ASSERT( sizeof( FoobarScreenEdge ) == sizeof( gint ) );
-G_DEFINE_ENUM_TYPE(
- FoobarScreenEdge,
- foobar_screen_edge,
- G_DEFINE_ENUM_VALUE( FOOBAR_SCREEN_EDGE_LEFT, "left" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_SCREEN_EDGE_RIGHT, "right" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_SCREEN_EDGE_TOP, "top" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_SCREEN_EDGE_BOTTOM, "bottom" ) )
-
-//
-// FoobarOrientation:
-//
-// An orientation value.
-//
-
-G_STATIC_ASSERT( sizeof( FoobarOrientation ) == sizeof( gint ) );
-G_DEFINE_ENUM_TYPE(
- FoobarOrientation,
- foobar_orientation,
- G_DEFINE_ENUM_VALUE( FOOBAR_ORIENTATION_HORIZONTAL, "horizontal" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_ORIENTATION_VERTICAL, "vertical" ) )
-
-//
-// FoobarStatusItem:
-//
-// An item to display in a status panel item.
-//
-
-G_STATIC_ASSERT( sizeof( FoobarStatusItem ) == sizeof( gint ) );
-G_DEFINE_ENUM_TYPE(
- FoobarStatusItem,
- foobar_status_item,
- G_DEFINE_ENUM_VALUE( FOOBAR_STATUS_ITEM_NETWORK, "network" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_STATUS_ITEM_BLUETOOTH, "bluetooth" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_STATUS_ITEM_BATTERY, "battery" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_STATUS_ITEM_BRIGHTNESS, "brightness" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_STATUS_ITEM_AUDIO, "audio" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_STATUS_ITEM_NOTIFICATIONS, "notifications" ) )
-
-//
-// FoobarPanelItemKind:
-//
-// Enum representing the various panel item types.
-//
-
-G_STATIC_ASSERT( sizeof( FoobarPanelItemKind ) == sizeof( gint ) );
-G_DEFINE_ENUM_TYPE(
- FoobarPanelItemKind,
- foobar_panel_item_kind,
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_KIND_ICON, "icon" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_KIND_CLOCK, "clock" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_KIND_WORKSPACES, "workspaces" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_KIND_STATUS, "status" ) )
-
-//
-// FoobarPanelItemAction:
-//
-// The action to be executed when a panel item is clicked (if supported by the item).
-//
-
-G_STATIC_ASSERT( sizeof( FoobarPanelItemAction ) == sizeof( gint ) );
-G_DEFINE_ENUM_TYPE(
- FoobarPanelItemAction,
- foobar_panel_item_action,
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_ACTION_NONE, "none" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_ACTION_LAUNCHER, "launcher" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_ACTION_CONTROL_CENTER, "control-center" ) )
-
-//
-// FoobarPanelItemPosition:
-//
-// Position of a panel item in the panel.
-//
-
-G_STATIC_ASSERT( sizeof( FoobarPanelItemPosition ) == sizeof( gint ) );
-G_DEFINE_ENUM_TYPE(
- FoobarPanelItemPosition,
- foobar_panel_item_position,
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_POSITION_START, "start" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_POSITION_CENTER, "center" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_PANEL_ITEM_POSITION_END, "end" ) )
-
-//
-// FoobarControlCenterRow:
-//
-// A row in the "controls" section of the control center.
-//
-
-G_STATIC_ASSERT( sizeof( FoobarControlCenterRow ) == sizeof( gint ) );
-G_DEFINE_ENUM_TYPE(
- FoobarControlCenterRow,
- foobar_control_center_row,
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_CENTER_ROW_CONNECTIVITY, "connectivity" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_CENTER_ROW_AUDIO_OUTPUT, "audio-output" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_CENTER_ROW_AUDIO_INPUT, "audio-input" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_CENTER_ROW_BRIGHTNESS, "brightness" ) )
-
-//
-// FoobarControlCenterAlignment:
-//
-// Alignment of the control center along its screen edge.
-//
-
-G_STATIC_ASSERT( sizeof( FoobarControlCenterAlignment ) == sizeof( gint ) );
-G_DEFINE_ENUM_TYPE(
- FoobarControlCenterAlignment,
- foobar_control_center_alignment,
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_CENTER_ALIGNMENT_START, "start" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_CENTER_ALIGNMENT_CENTER, "center" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_CENTER_ALIGNMENT_END, "end" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL, "fill" ) )
-
-//
-// FoobarGeneralConfiguration:
-//
-// General application settings not specific to a single component.
-//
-
-struct _FoobarGeneralConfiguration
-{
- gchar* stylesheet;
-};
-
-static void foobar_general_configuration_load ( FoobarGeneralConfiguration* self,
- GKeyFile* file );
-static void foobar_general_configuration_store( FoobarGeneralConfiguration const* self,
- GKeyFile* file );
-
-G_DEFINE_BOXED_TYPE(
- FoobarGeneralConfiguration,
- foobar_general_configuration,
- foobar_general_configuration_copy,
- foobar_general_configuration_free )
-
-//
-// FoobarPanelItemConfiguration:
-//
-// Configuration for an item in the panel. Structures of this type have a specific item kind with more properties.
-//
-
-struct _FoobarPanelItemConfiguration
-{
- FoobarPanelItemKind kind;
- gchar* name;
- FoobarPanelItemPosition position;
- FoobarPanelItemAction action;
- union
- {
- struct
- {
- gchar* icon_name;
- } icon;
-
- struct
- {
- gchar* format;
- } clock;
-
- struct
- {
- gint button_size;
- gint spacing;
- } workspaces;
-
- struct
- {
- FoobarStatusItem* items;
- gsize items_count;
- gint spacing;
- gboolean show_labels;
- gboolean enable_scrolling;
- } status;
- };
-};
-
-static FoobarPanelItemConfiguration* foobar_panel_item_configuration_load ( GKeyFile* file,
- gchar const* group );
-static void foobar_panel_item_configuration_store( FoobarPanelItemConfiguration const* self,
- GKeyFile* file );
-
-G_DEFINE_BOXED_TYPE(
- FoobarPanelItemConfiguration,
- foobar_panel_item_configuration,
- foobar_panel_item_configuration_copy,
- foobar_panel_item_configuration_free )
-
-//
-// FoobarPanelConfiguration:
-//
-// Configuration for the panel shown at the edge of a screen.
-//
-
-struct _FoobarPanelConfiguration
-{
- FoobarScreenEdge position;
- gint margin;
- gint padding;
- gint size;
- gint spacing;
- gboolean multi_monitor;
- FoobarPanelItemConfiguration** items;
- gsize items_count;
-};
-
-static void foobar_panel_configuration_load ( FoobarPanelConfiguration* self,
- GKeyFile* file );
-static void foobar_panel_configuration_store( FoobarPanelConfiguration const* self,
- GKeyFile* file );
-
-G_DEFINE_BOXED_TYPE(
- FoobarPanelConfiguration,
- foobar_panel_configuration,
- foobar_panel_configuration_copy,
- foobar_panel_configuration_free )
-
-//
-// FoobarLauncherConfiguration:
-//
-// Configuration for the application launcher.
-//
-
-struct _FoobarLauncherConfiguration
-{
- gint width;
- gint position;
- gint max_height;
-};
-
-static void foobar_launcher_configuration_load ( FoobarLauncherConfiguration* self,
- GKeyFile* file );
-static void foobar_launcher_configuration_store( FoobarLauncherConfiguration const* self,
- GKeyFile* file );
-
-G_DEFINE_BOXED_TYPE(
- FoobarLauncherConfiguration,
- foobar_launcher_configuration,
- foobar_launcher_configuration_copy,
- foobar_launcher_configuration_free )
-
-//
-// FoobarControlCenterConfiguration:
-//
-// Configuration for the control center.
-//
-
-struct _FoobarControlCenterConfiguration
-{
- gint width;
- gint height;
- FoobarScreenEdge position;
- gint offset;
- gint padding;
- gint spacing;
- FoobarOrientation orientation;
- FoobarControlCenterAlignment alignment;
- FoobarControlCenterRow* rows;
- gsize rows_count;
-};
-
-static void foobar_control_center_configuration_load ( FoobarControlCenterConfiguration* self,
- GKeyFile* file );
-static void foobar_control_center_configuration_store( FoobarControlCenterConfiguration const* self,
- GKeyFile* file );
-
-G_DEFINE_BOXED_TYPE(
- FoobarControlCenterConfiguration,
- foobar_control_center_configuration,
- foobar_control_center_configuration_copy,
- foobar_control_center_configuration_free )
-
-//
-// FoobarNotificationConfiguration:
-//
-// Configuration for notifications and the notification area shown in the corner of the screen.
-//
-
-struct _FoobarNotificationConfiguration
-{
- gint width;
- gint min_height;
- gint spacing;
- gint close_button_inset;
- gchar* time_format;
-};
-
-static void foobar_notification_configuration_load ( FoobarNotificationConfiguration* self,
- GKeyFile* file );
-static void foobar_notification_configuration_store( FoobarNotificationConfiguration const* self,
- GKeyFile* file );
-
-G_DEFINE_BOXED_TYPE(
- FoobarNotificationConfiguration,
- foobar_notification_configuration,
- foobar_notification_configuration_copy,
- foobar_notification_configuration_free )
-
-//
-// FoobarConfiguration:
-//
-// Main configuration structure containing the configuration state for all components of the application.
-//
-
-struct _FoobarConfiguration
-{
- FoobarGeneralConfiguration* general;
- FoobarPanelConfiguration* panel;
- FoobarLauncherConfiguration* launcher;
- FoobarControlCenterConfiguration* control_center;
- FoobarNotificationConfiguration* notifications;
-};
-
-static void foobar_configuration_load ( FoobarConfiguration* self,
- GKeyFile* file );
-static void foobar_configuration_load_from_file( FoobarConfiguration* self,
- gchar const* path );
-static void foobar_configuration_store ( FoobarConfiguration const* self,
- GKeyFile* file );
-
-G_DEFINE_BOXED_TYPE( FoobarConfiguration, foobar_configuration, foobar_configuration_copy, foobar_configuration_free )
-
-//
-// FoobarConfigurationService:
-//
-// Service monitoring the configuration state of the application specified in its config file.
-//
-
-struct _FoobarConfigurationService
-{
- GObject parent_instance;
- gchar* path;
- char* actual_path;
- FoobarConfiguration* current;
- GFileMonitor* monitor;
- GFileMonitor* actual_monitor;
- gulong changed_handler_id;
- gulong actual_changed_handler_id;
- guint update_source_id;
-};
-
-enum
-{
- PROP_CURRENT = 1,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_configuration_service_class_init ( FoobarConfigurationServiceClass* klass );
-static void foobar_configuration_service_init ( FoobarConfigurationService* self );
-static void foobar_configuration_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_configuration_service_finalize ( GObject* object );
-static void foobar_configuration_service_handle_changed ( GFileMonitor* monitor,
- GFile* file,
- GFile* other_file,
- GFileMonitorEvent event_type,
- gpointer userdata );
-static void foobar_configuration_service_handle_actual_changed( GFileMonitor* monitor,
- GFile* file,
- GFile* other_file,
- GFileMonitorEvent event_type,
- gpointer userdata );
-static void foobar_configuration_service_update ( FoobarConfigurationService* self );
-static gboolean foobar_configuration_service_update_cb ( gpointer userdata );
-static GFileMonitor* foobar_configuration_service_create_monitor ( gchar const* path );
-
-G_DEFINE_FINAL_TYPE( FoobarConfigurationService, foobar_configuration_service, G_TYPE_OBJECT )
-
-//
-// Parsing/Serialization Helpers:
-//
-// These functions make it easier to parse, validate and serialize various data types.
-//
-
-typedef enum
-{
- VALIDATE_NONE = 0,
- VALIDATE_NON_NEGATIVE = 1 << 0,
- VALIDATE_NON_ZERO = 1 << 1,
- VALIDATE_POSITIVE = VALIDATE_NON_NEGATIVE | VALIDATE_NON_ZERO,
- VALIDATE_FILE_URL = 1 << 2,
- VALIDATE_DISTINCT = 1 << 3,
-} ValidationFlags;
-
-static gboolean try_get_string_value ( GKeyFile* file,
- gchar const* group,
- gchar const* key,
- ValidationFlags validation,
- gchar** out );
-static gboolean try_get_string_list_value( GKeyFile* file,
- gchar const* group,
- gchar const* key,
- ValidationFlags validation,
- GStrv* out,
- gsize* out_count );
-static gboolean try_get_int_value ( GKeyFile* file,
- gchar const* group,
- gchar const* key,
- ValidationFlags validation,
- gint* out );
-static gboolean try_get_enum_value ( GKeyFile* file,
- gchar const* group,
- gchar const* key,
- GType enum_type,
- ValidationFlags validation,
- gint* out );
-static gboolean try_get_enum_list_value ( GKeyFile* file,
- gchar const* group,
- gchar const* key,
- GType enum_type,
- ValidationFlags validation,
- gpointer* out,
- gsize* out_count );
-static gboolean try_get_boolean_value ( GKeyFile* file,
- gchar const* group,
- gchar const* key,
- ValidationFlags validation,
- gboolean* out );
-
-static gchar const* enum_value_to_string ( GType enum_type,
- gint value );
-static gchar const** enum_list_to_string ( GType enum_type,
- gconstpointer values,
- gsize values_count );
-static gchar* enum_format_help_text( GType enum_type );
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Default Configuration
-// ---------------------------------------------------------------------------------------------------------------------
-
-static FoobarGeneralConfiguration default_general_configuration =
- {
- .stylesheet = "resource:///foobar/styles/default.css",
- };
-
-static FoobarPanelItemConfiguration default_panel_item_icon_configuration =
- {
- .kind = FOOBAR_PANEL_ITEM_KIND_ICON,
- .name = "launcher",
- .position = FOOBAR_PANEL_ITEM_POSITION_START,
- .action = FOOBAR_PANEL_ITEM_ACTION_LAUNCHER,
- .icon.icon_name = "fluent-grid-dots-symbolic",
- };
-
-static FoobarPanelItemConfiguration default_panel_item_clock_configuration =
- {
- .kind = FOOBAR_PANEL_ITEM_KIND_CLOCK,
- .name = "clock",
- .position = FOOBAR_PANEL_ITEM_POSITION_CENTER,
- .action = FOOBAR_PANEL_ITEM_ACTION_NONE,
- .clock.format = "%H\n%M",
- };
-
-static FoobarPanelItemConfiguration default_panel_item_workspaces_configuration =
- {
- .kind = FOOBAR_PANEL_ITEM_KIND_WORKSPACES,
- .name = "workspaces",
- .position = FOOBAR_PANEL_ITEM_POSITION_START,
- .action = FOOBAR_PANEL_ITEM_ACTION_NONE,
- .workspaces.button_size = 20,
- .workspaces.spacing = 6,
- };
-
-static FoobarStatusItem default_panel_item_status_configuration_items[] =
- {
- FOOBAR_STATUS_ITEM_BATTERY,
- FOOBAR_STATUS_ITEM_BRIGHTNESS,
- FOOBAR_STATUS_ITEM_AUDIO,
- FOOBAR_STATUS_ITEM_NETWORK,
- FOOBAR_STATUS_ITEM_BLUETOOTH,
- FOOBAR_STATUS_ITEM_NOTIFICATIONS
- };
-
-static FoobarPanelItemConfiguration default_panel_item_status_configuration =
- {
- .kind = FOOBAR_PANEL_ITEM_KIND_STATUS,
- .name = "status",
- .position = FOOBAR_PANEL_ITEM_POSITION_END,
- .action = FOOBAR_PANEL_ITEM_ACTION_CONTROL_CENTER,
- .status.items = default_panel_item_status_configuration_items,
- .status.items_count = G_N_ELEMENTS( default_panel_item_status_configuration_items ),
- .status.spacing = 6,
- .status.show_labels = FALSE,
- .status.enable_scrolling = FALSE,
- };
-
-static FoobarPanelItemConfiguration *default_panel_configuration_items[] =
- {
- &default_panel_item_icon_configuration,
- &default_panel_item_workspaces_configuration,
- &default_panel_item_clock_configuration,
- &default_panel_item_status_configuration,
- NULL,
- };
-
-static FoobarPanelConfiguration default_panel_configuration =
- {
- .position = FOOBAR_SCREEN_EDGE_LEFT,
- .margin = 16,
- .padding = 12,
- .size = 48,
- .spacing = 12,
- .multi_monitor = TRUE,
- .items = default_panel_configuration_items,
- .items_count = G_N_ELEMENTS( default_panel_configuration_items ) - 1,
- };
-
-static FoobarLauncherConfiguration default_launcher_configuration =
- {
- .width = 600,
- .position = 300,
- .max_height = 400,
- };
-
-static FoobarControlCenterRow default_control_center_configuration_rows[] =
- {
- FOOBAR_CONTROL_CENTER_ROW_CONNECTIVITY,
- FOOBAR_CONTROL_CENTER_ROW_AUDIO_OUTPUT,
- FOOBAR_CONTROL_CENTER_ROW_BRIGHTNESS,
- };
-
-static FoobarControlCenterConfiguration default_control_center_configuration =
- {
- .position = FOOBAR_SCREEN_EDGE_LEFT,
- .width = 400,
- .height = 600,
- .offset = 16,
- .padding = 24,
- .spacing = 12,
- .orientation = FOOBAR_ORIENTATION_VERTICAL,
- .alignment = FOOBAR_CONTROL_CENTER_ALIGNMENT_CENTER,
- .rows = default_control_center_configuration_rows,
- .rows_count = G_N_ELEMENTS( default_control_center_configuration_rows ),
- };
-
-static FoobarNotificationConfiguration default_notification_configuration =
- {
- .width = 400,
- .min_height = 48,
- .spacing = 16,
- .close_button_inset = -6,
- .time_format = "%H:%M",
- };
-
-static FoobarConfiguration default_configuration =
- {
- .general = &default_general_configuration,
- .panel = &default_panel_configuration,
- .launcher = &default_launcher_configuration,
- .control_center = &default_control_center_configuration,
- .notifications = &default_notification_configuration,
- };
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Default Configuration
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a default general configuration structure.
-//
-FoobarGeneralConfiguration* foobar_general_configuration_new( void )
-{
- return foobar_general_configuration_copy( &default_general_configuration );
-}
-
-//
-// Create a mutable copy of another general configuration structure.
-//
-FoobarGeneralConfiguration* foobar_general_configuration_copy( FoobarGeneralConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
-
- FoobarGeneralConfiguration* copy = g_new0( FoobarGeneralConfiguration, 1 );
- copy->stylesheet = g_strdup( self->stylesheet );
- return copy;
-}
-
-//
-// Release resources associated with a general configuration structure.
-//
-void foobar_general_configuration_free( FoobarGeneralConfiguration* self )
-{
- g_return_if_fail( self != NULL );
-
- g_free( self->stylesheet );
- g_free( self );
-}
-
-//
-// Check if two general configuration structures are equal.
-//
-gboolean foobar_general_configuration_equal(
- FoobarGeneralConfiguration const* a,
- FoobarGeneralConfiguration const* b )
-{
- g_return_val_if_fail( a != NULL, FALSE );
- g_return_val_if_fail( b != NULL, FALSE );
-
- if ( g_strcmp0( a->stylesheet, b->stylesheet ) ) { return FALSE; }
-
- return TRUE;
-}
-
-//
-// URI to the CSS stylesheet to use -- this may be a resource that's bundled with Foobar or a "file:" URI.
-//
-gchar const* foobar_general_configuration_get_stylesheet( FoobarGeneralConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->stylesheet;
-}
-
-//
-// URI to the CSS stylesheet to use -- this may be a resource that's bundled with Foobar or a "file:" URI.
-//
-void foobar_general_configuration_set_stylesheet(
- FoobarGeneralConfiguration* self,
- gchar const* value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( value != NULL );
-
- g_free( self->stylesheet );
- self->stylesheet = g_strdup( value );
-}
-
-//
-// Populate a general configuration structure from the "general" section of a keyfile.
-//
-void foobar_general_configuration_load(
- FoobarGeneralConfiguration* self,
- GKeyFile* file )
-{
- g_autofree gchar* stylesheet = NULL;
- if ( try_get_string_value( file, "general", "stylesheet", VALIDATE_FILE_URL, &stylesheet ) )
- {
- foobar_general_configuration_set_stylesheet( self, stylesheet );
- }
-}
-
-//
-// Store a general configuration structure in the "general" section of a keyfile.
-//
-void foobar_general_configuration_store(
- FoobarGeneralConfiguration const* self,
- GKeyFile* file )
-{
- gchar const* stylesheet = foobar_general_configuration_get_stylesheet( self );
- g_key_file_set_string( file, "general", "stylesheet", stylesheet );
- g_key_file_set_comment(
- file,
- "general",
- "stylesheet",
- " URI to the CSS stylesheet to use -- this may be a resource that's bundled with Foobar or a \"file:\" URI.",
- NULL );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Panel Item Configuration
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a default icon panel item configuration structure.
-//
-FoobarPanelItemConfiguration* foobar_panel_item_icon_configuration_new( void )
-{
- return foobar_panel_item_configuration_copy( &default_panel_item_icon_configuration );
-}
-
-//
-// Create a default clock panel item configuration structure.
-//
-FoobarPanelItemConfiguration* foobar_panel_item_clock_configuration_new( void )
-{
- return foobar_panel_item_configuration_copy( &default_panel_item_clock_configuration );
-}
-
-//
-// Create a default workspaces panel item configuration structure.
-//
-FoobarPanelItemConfiguration* foobar_panel_item_workspaces_configuration_new( void )
-{
- return foobar_panel_item_configuration_copy( &default_panel_item_workspaces_configuration );
-}
-
-//
-// Create a default status panel item configuration structure.
-//
-FoobarPanelItemConfiguration* foobar_panel_item_status_configuration_new( void )
-{
- return foobar_panel_item_configuration_copy( &default_panel_item_status_configuration );
-}
-
-//
-// Create a mutable copy of another panel item configuration structure, preserving its item type.
-//
-FoobarPanelItemConfiguration* foobar_panel_item_configuration_copy( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
-
- FoobarPanelItemConfiguration* copy = g_new0( FoobarPanelItemConfiguration, 1 );
- copy->kind = self->kind;
- copy->name = g_strdup( self->name );
- copy->position = self->position;
- copy->action = self->action;
- switch ( copy->kind )
- {
- case FOOBAR_PANEL_ITEM_KIND_ICON:
- copy->icon.icon_name = g_strdup( self->icon.icon_name );
- break;
- case FOOBAR_PANEL_ITEM_KIND_CLOCK:
- copy->clock.format = g_strdup( self->clock.format );
- break;
- case FOOBAR_PANEL_ITEM_KIND_WORKSPACES:
- copy->workspaces.button_size = self->workspaces.button_size;
- copy->workspaces.spacing = self->workspaces.spacing;
- break;
- case FOOBAR_PANEL_ITEM_KIND_STATUS:
- copy->status.items = g_new( FoobarStatusItem, self->status.items_count );
- copy->status.items_count = self->status.items_count;
- memcpy( copy->status.items, self->status.items, sizeof( *copy->status.items ) * copy->status.items_count );
- copy->status.spacing = self->status.spacing;
- copy->status.show_labels = self->status.show_labels;
- copy->status.enable_scrolling = self->status.enable_scrolling;
- break;
- default:
- g_warn_if_reached( );
- }
- return copy;
-}
-
-//
-// Release resources associated with a panel item configuration structure.
-//
-void foobar_panel_item_configuration_free( FoobarPanelItemConfiguration* self )
-{
- g_return_if_fail( self != NULL );
-
- switch ( self->kind )
- {
- case FOOBAR_PANEL_ITEM_KIND_ICON:
- g_free( self->icon.icon_name );
- break;
- case FOOBAR_PANEL_ITEM_KIND_CLOCK:
- g_free( self->clock.format );
- break;
- case FOOBAR_PANEL_ITEM_KIND_WORKSPACES:
- break;
- case FOOBAR_PANEL_ITEM_KIND_STATUS:
- g_free( self->status.items );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-
- g_free( self->name );
- g_free( self );
-}
-
-//
-// Check if two panel item configuration structures are equal.
-//
-gboolean foobar_panel_item_configuration_equal(
- FoobarPanelItemConfiguration const* a,
- FoobarPanelItemConfiguration const* b )
-{
- g_return_val_if_fail( a != NULL, FALSE );
- g_return_val_if_fail( b != NULL, FALSE );
-
- if ( a->kind != b->kind ) { return FALSE; }
- if ( a->position != b->position ) { return FALSE; }
- if ( a->action != b->action ) { return FALSE; }
- if ( g_strcmp0( a->name, b->name ) ) { return FALSE; }
-
- switch ( a->kind )
- {
- case FOOBAR_PANEL_ITEM_KIND_ICON:
- if ( g_strcmp0( a->icon.icon_name, b->icon.icon_name ) ) { return FALSE; }
- break;
- case FOOBAR_PANEL_ITEM_KIND_CLOCK:
- if ( g_strcmp0( a->clock.format, b->clock.format ) ) { return FALSE; }
- break;
- case FOOBAR_PANEL_ITEM_KIND_WORKSPACES:
- if ( a->workspaces.button_size != b->workspaces.button_size ) { return FALSE; }
- if ( a->workspaces.spacing != b->workspaces.spacing ) { return FALSE; }
- break;
- case FOOBAR_PANEL_ITEM_KIND_STATUS:
- if ( a->status.items_count != b->status.items_count ) { return FALSE; }
- if ( memcmp( a->status.items, b->status.items, sizeof( *a->status.items ) * a->status.items_count ) ) { return FALSE; }
- if ( a->status.spacing != b->status.spacing ) { return FALSE; }
- if ( a->status.show_labels != b->status.show_labels ) { return FALSE; }
- if ( a->status.enable_scrolling != b->status.enable_scrolling ) { return FALSE; }
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-
- return TRUE;
-}
-
-//
-// The type of panel item to be configured by this structure.
-//
-FoobarPanelItemKind foobar_panel_item_configuration_get_kind( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->kind;
-}
-
-//
-// Name of the panel item (this is derived from the section name, which has the form "panel.[name]").
-//
-gchar const* foobar_panel_item_configuration_get_name( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->name;
-}
-
-//
-// Position where the item should be placed item within the panel.
-//
-FoobarPanelItemPosition foobar_panel_item_configuration_get_position( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->position;
-}
-
-//
-// Action invoked when the user clicks the item (if supported by the item).
-//
-FoobarPanelItemAction foobar_panel_item_configuration_get_action( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->action;
-}
-
-//
-// The GTK icon to use for the item.
-//
-gchar const* foobar_panel_item_icon_configuration_get_icon_name( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- g_return_val_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_ICON, NULL );
-
- return self->icon.icon_name;
-}
-
-//
-// The time format string as used by g_date_time_format.
-//
-gchar const* foobar_panel_item_clock_configuration_get_format( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- g_return_val_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_CLOCK, NULL );
-
- return self->clock.format;
-}
-
-//
-// Size of each workspace button.
-//
-gint foobar_panel_item_workspaces_configuration_get_button_size( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- g_return_val_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_WORKSPACES, 0 );
-
- return self->workspaces.button_size;
-}
-
-//
-// Inner spacing between the workspace buttons.
-//
-gint foobar_panel_item_workspaces_configuration_get_spacing( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- g_return_val_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_WORKSPACES, 0 );
-
- return self->workspaces.spacing;
-}
-
-//
-// Ordered list of status items to display in the item.
-//
-FoobarStatusItem const* foobar_panel_item_status_configuration_get_items(
- FoobarPanelItemConfiguration const* self,
- gsize* out_count )
-{
- g_return_val_if_fail( self != NULL, NULL );
- g_return_val_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_STATUS, NULL );
- g_return_val_if_fail( out_count != NULL, NULL );
-
- *out_count = self->status.items_count;
- return self->status.items;
-}
-
-//
-// Inner spacing between the status items.
-//
-gint foobar_panel_item_status_configuration_get_spacing( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- g_return_val_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_STATUS, 0 );
-
- return self->status.spacing;
-}
-
-//
-// Indicates whether text labels should be shown next to the status icons.
-//
-gboolean foobar_panel_item_status_configuration_get_show_labels( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, FALSE );
- g_return_val_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_STATUS, FALSE );
-
- return self->status.show_labels;
-}
-
-//
-// If set to true, some settings like volume or brightness can be adjusted by scrolling while hovering over the status
-// item.
-//
-gboolean foobar_panel_item_status_configuration_get_enable_scrolling( FoobarPanelItemConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, FALSE );
- g_return_val_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_STATUS, FALSE );
-
- return self->status.enable_scrolling;
-}
-
-//
-// Name of the panel item (this is derived from the section name, which has the form "panel.[name]").
-//
-void foobar_panel_item_configuration_set_name(
- FoobarPanelItemConfiguration* self,
- gchar const* value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( value != NULL );
-
- g_free( self->name );
- self->name = g_strdup( value );
-}
-
-//
-// Position where the item should be placed item within the panel.
-//
-void foobar_panel_item_configuration_set_position(
- FoobarPanelItemConfiguration* self,
- FoobarPanelItemPosition value )
-{
- g_return_if_fail( self != NULL );
- self->position = value;
-}
-
-//
-// Action invoked when the user clicks the item (if supported by the item).
-//
-void foobar_panel_item_configuration_set_action(
- FoobarPanelItemConfiguration* self,
- FoobarPanelItemAction value )
-{
- g_return_if_fail( self != NULL );
- self->action = value;
-}
-
-//
-// The GTK icon to use for the item.
-//
-void foobar_panel_item_icon_configuration_set_icon_name(
- FoobarPanelItemConfiguration* self,
- gchar const* value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_ICON );
- g_return_if_fail( value != NULL );
-
- g_free( self->icon.icon_name );
- self->icon.icon_name = g_strdup( value );
-}
-
-//
-// The time format string as used by g_date_time_format.
-//
-void foobar_panel_item_clock_configuration_set_format(
- FoobarPanelItemConfiguration* self,
- gchar const* value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_CLOCK );
- g_return_if_fail( value != NULL );
-
- g_free( self->clock.format );
- self->clock.format = g_strdup( value );
-}
-
-//
-// Size of each workspace button.
-//
-void foobar_panel_item_workspaces_configuration_set_button_size(
- FoobarPanelItemConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_WORKSPACES );
-
- self->workspaces.button_size = value;
-}
-
-//
-// Inner spacing between the workspace buttons.
-//
-void foobar_panel_item_workspaces_configuration_set_spacing(
- FoobarPanelItemConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_WORKSPACES );
-
- self->workspaces.spacing = value;
-}
-
-//
-// Ordered list of status items to display in the item.
-//
-void foobar_panel_item_status_configuration_set_items(
- FoobarPanelItemConfiguration* self,
- FoobarStatusItem const* value,
- gsize value_count )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_STATUS );
- g_return_if_fail( value != NULL || value_count == 0 );
-
- g_free( self->status.items );
- self->status.items = g_new( FoobarStatusItem, value_count );
- self->status.items_count = value_count;
- memcpy( self->status.items, value, sizeof( *self->status.items ) * self->status.items_count );
-}
-
-//
-// Inner spacing between the status items.
-//
-void foobar_panel_item_status_configuration_set_spacing(
- FoobarPanelItemConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_STATUS );
-
- self->status.spacing = value;
-}
-
-//
-// Indicates whether text labels should be shown next to the status icons.
-//
-void foobar_panel_item_status_configuration_set_show_labels(
- FoobarPanelItemConfiguration* self,
- gboolean value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_STATUS );
-
- self->status.show_labels = value;
-}
-
-//
-// If set to true, some settings like volume or brightness can be adjusted by scrolling while hovering over the status
-// item.
-//
-void foobar_panel_item_status_configuration_set_enable_scrolling(
- FoobarPanelItemConfiguration* self,
- gboolean value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( self->kind == FOOBAR_PANEL_ITEM_KIND_STATUS );
-
- self->status.enable_scrolling = value;
-}
-
-//
-// Create a panel item configuration structure from the given section of a keyfile, returning NULL if it is not a panel
-// item configuration section.
-//
-FoobarPanelItemConfiguration* foobar_panel_item_configuration_load(
- GKeyFile* file,
- gchar const* group )
-{
- if ( !g_str_has_prefix( group, "panel." ) )
- {
- return NULL;
- }
-
- gint kind;
- if ( !try_get_enum_value( file, group, "kind", FOOBAR_TYPE_PANEL_ITEM_KIND, VALIDATE_NONE, &kind ) )
- {
- return NULL;
- }
-
- FoobarPanelItemConfiguration* result;
- switch ( (FoobarPanelItemKind)kind )
- {
- case FOOBAR_PANEL_ITEM_KIND_ICON:
- {
- result = foobar_panel_item_icon_configuration_new( );
-
- g_autofree gchar* icon_name = NULL;
- if ( try_get_string_value( file, group, "icon-name", VALIDATE_NONE, &icon_name ) )
- {
- foobar_panel_item_icon_configuration_set_icon_name( result, icon_name );
- }
-
- break;
- }
- case FOOBAR_PANEL_ITEM_KIND_CLOCK:
- {
- result = foobar_panel_item_clock_configuration_new( );
-
- g_autofree gchar* format = NULL;
- if ( try_get_string_value( file, group, "format", VALIDATE_NONE, &format ) )
- {
- foobar_panel_item_clock_configuration_set_format( result, format );
- }
-
- break;
- }
- case FOOBAR_PANEL_ITEM_KIND_WORKSPACES:
- {
- result = foobar_panel_item_workspaces_configuration_new( );
-
- gint button_size;
- if ( try_get_int_value( file, group, "button-size", VALIDATE_POSITIVE, &button_size ) )
- {
- foobar_panel_item_workspaces_configuration_set_button_size( result, button_size );
- }
-
- gint spacing;
- if ( try_get_int_value( file, group, "spacing", VALIDATE_NON_NEGATIVE, &spacing ) )
- {
- foobar_panel_item_workspaces_configuration_set_spacing( result, spacing );
- }
-
- break;
- }
- case FOOBAR_PANEL_ITEM_KIND_STATUS:
- {
- result = foobar_panel_item_status_configuration_new( );
-
- g_autofree gpointer items = NULL;
- gsize items_count;
- if ( try_get_enum_list_value( file, group, "items", FOOBAR_TYPE_STATUS_ITEM, VALIDATE_DISTINCT, &items, &items_count ) )
- {
- foobar_panel_item_status_configuration_set_items( result, items, items_count );
- }
-
- gint spacing;
- if ( try_get_int_value( file, group, "spacing", VALIDATE_NON_NEGATIVE, &spacing ) )
- {
- foobar_panel_item_status_configuration_set_spacing( result, spacing );
- }
-
- gboolean show_labels;
- if ( try_get_boolean_value( file, group, "show-labels", VALIDATE_NONE, &show_labels ) )
- {
- foobar_panel_item_status_configuration_set_show_labels( result, show_labels );
- }
-
- gboolean enable_scrolling;
- if ( try_get_boolean_value( file, group, "enable-scrolling", VALIDATE_NONE, &enable_scrolling ) )
- {
- foobar_panel_item_status_configuration_set_enable_scrolling( result, enable_scrolling );
- }
-
- break;
- }
- default:
- {
- g_warn_if_reached( );
- return NULL;
- }
- }
-
- gchar const* name = group + strlen( "panel." );
- foobar_panel_item_configuration_set_name( result, name );
-
- gint position;
- if ( try_get_enum_value( file, group, "position", FOOBAR_TYPE_PANEL_ITEM_POSITION, VALIDATE_NONE, &position ) )
- {
- foobar_panel_item_configuration_set_position( result, position );
- }
-
- gint action;
- if ( try_get_enum_value( file, group, "action", FOOBAR_TYPE_PANEL_ITEM_ACTION, VALIDATE_NONE, &action ) )
- {
- foobar_panel_item_configuration_set_action( result, action );
- }
-
- return result;
-}
-
-//
-// Store a panel item configuration structure in the "panel.[name]" section of a keyfile.
-//
-void foobar_panel_item_configuration_store(
- FoobarPanelItemConfiguration const* self,
- GKeyFile* file )
-{
- gchar const* name = foobar_panel_item_configuration_get_name( self );
- g_autofree gchar* group = g_strdup_printf( "panel.%s", name );
-
- FoobarPanelItemKind kind = foobar_panel_item_configuration_get_kind( self );
- g_key_file_set_string( file, group, "kind", enum_value_to_string( FOOBAR_TYPE_PANEL_ITEM_KIND, kind ) );
-
- FoobarPanelItemPosition position = foobar_panel_item_configuration_get_position( self );
- g_key_file_set_string( file, group, "position", enum_value_to_string( FOOBAR_TYPE_PANEL_ITEM_POSITION, position ) );
-
- FoobarPanelItemAction action = foobar_panel_item_configuration_get_action( self );
- g_key_file_set_string( file, group, "action", enum_value_to_string( FOOBAR_TYPE_PANEL_ITEM_ACTION, action ) );
-
- switch ( kind )
- {
- case FOOBAR_PANEL_ITEM_KIND_ICON:
- {
- gchar const* icon_name = foobar_panel_item_icon_configuration_get_icon_name( self );
- g_key_file_set_string( file, group, "icon-name", icon_name );
- g_key_file_set_comment(
- file,
- group,
- "icon-name",
- " The GTK icon to use for the item.",
- NULL );
-
- break;
- }
- case FOOBAR_PANEL_ITEM_KIND_CLOCK:
- {
- gchar const* format = foobar_panel_item_clock_configuration_get_format( self );
- g_key_file_set_string( file, group, "format", format );
- g_key_file_set_comment(
- file,
- group,
- "format",
- " The time format string as used by g_date_time_format.",
- NULL );
-
- break;
- }
- case FOOBAR_PANEL_ITEM_KIND_WORKSPACES:
- {
- gint button_size = foobar_panel_item_workspaces_configuration_get_button_size( self );
- g_key_file_set_integer( file, group, "button-size", button_size );
- g_key_file_set_comment(
- file,
- group,
- "button-size",
- " Size of each workspace button.",
- NULL );
-
- gint spacing = foobar_panel_item_workspaces_configuration_get_spacing( self );
- g_key_file_set_integer( file, group, "spacing", spacing );
- g_key_file_set_comment(
- file,
- group,
- "spacing",
- " Inner spacing between the workspace buttons.",
- NULL );
-
- break;
- }
- case FOOBAR_PANEL_ITEM_KIND_STATUS:
- {
- gsize items_count;
- FoobarStatusItem const* items = foobar_panel_item_status_configuration_get_items( self, &items_count );
- g_autofree gchar const** items_str = enum_list_to_string( FOOBAR_TYPE_STATUS_ITEM, items, items_count );
- g_key_file_set_string_list( file, group, "items", items_str, items_count );
- g_key_file_set_comment(
- file,
- group,
- "items",
- " Ordered list of status items to display in the item.",
- NULL );
-
- gint spacing = foobar_panel_item_status_configuration_get_spacing( self );
- g_key_file_set_integer( file, group, "spacing", spacing );
- g_key_file_set_comment(
- file,
- group,
- "spacing",
- " Inner spacing between the status items.",
- NULL );
-
- gboolean show_labels = foobar_panel_item_status_configuration_get_show_labels( self );
- g_key_file_set_boolean( file, group, "show-labels", show_labels );
- g_key_file_set_comment(
- file,
- group,
- "show-labels",
- " Indicates whether text labels should be shown next to the status icons.",
- NULL );
-
- gboolean enable_scrolling = foobar_panel_item_status_configuration_get_enable_scrolling( self );
- g_key_file_set_boolean( file, group, "enable-scrolling", enable_scrolling );
- g_key_file_set_comment(
- file,
- group,
- "enable-scrolling",
- " If set to true, some settings like volume or brightness can be adjusted by scrolling while hovering"
- " over the status item.",
- NULL );
-
- break;
- }
- default:
- {
- g_warn_if_reached( );
- break;
- }
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Panel Configuration
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a default panel configuration structure.
-//
-FoobarPanelConfiguration* foobar_panel_configuration_new( void )
-{
- return foobar_panel_configuration_copy( &default_panel_configuration );
-}
-
-//
-// Create a mutable copy of another panel configuration structure.
-//
-FoobarPanelConfiguration* foobar_panel_configuration_copy( FoobarPanelConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
-
- FoobarPanelConfiguration* copy = g_new0( FoobarPanelConfiguration, 1 );
- copy->position = self->position;
- copy->margin = self->margin;
- copy->padding = self->padding;
- copy->size = self->size;
- copy->spacing = self->spacing;
- copy->multi_monitor = self->multi_monitor;
- copy->items = g_new( FoobarPanelItemConfiguration*, self->items_count + 1 );
- copy->items_count = self->items_count;
- for ( gsize i = 0; i < copy->items_count; ++i )
- {
- copy->items[i] = foobar_panel_item_configuration_copy( self->items[i] );
- }
- return copy;
-}
-
-//
-// Release resources associated with a panel configuration structure.
-//
-void foobar_panel_configuration_free( FoobarPanelConfiguration* self )
-{
- g_return_if_fail( self != NULL );
-
- for ( gsize i = 0; i < self->items_count; ++i )
- {
- foobar_panel_item_configuration_free( self->items[i] );
- }
- g_free( self->items );
- g_free( self );
-}
-
-//
-// Check if two panel configuration structures are equal.
-//
-gboolean foobar_panel_configuration_equal(
- FoobarPanelConfiguration const* a,
- FoobarPanelConfiguration const* b )
-{
- g_return_val_if_fail( a != NULL, FALSE );
- g_return_val_if_fail( b != NULL, FALSE );
-
- if ( a->position != b->position ) { return FALSE; }
- if ( a->margin != b->margin ) { return FALSE; }
- if ( a->padding != b->padding ) { return FALSE; }
- if ( a->size != b->size ) { return FALSE; }
- if ( a->spacing != b->spacing ) { return FALSE; }
- if ( a->multi_monitor != b->multi_monitor ) { return FALSE; }
- if ( a->items_count != b->items_count ) { return FALSE; }
-
- for ( gsize i = 0; i < a->items_count; ++i )
- {
- if ( !foobar_panel_item_configuration_equal( a->items[i], b->items[i] ) ) { return FALSE; }
- }
-
- return TRUE;
-}
-
-//
-// The screen edge which the panel should appear on.
-//
-FoobarScreenEdge foobar_panel_configuration_get_position( FoobarPanelConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->position;
-}
-
-//
-// Offset of the panel from all screen edges.
-//
-gint foobar_panel_configuration_get_margin( FoobarPanelConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->margin;
-}
-
-//
-// Inset of the panel along its orientation.
-//
-gint foobar_panel_configuration_get_padding( FoobarPanelConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->padding;
-}
-
-//
-// Size of the panel (depending on the orientation, this can be either the width or the height).
-//
-gint foobar_panel_configuration_get_size( FoobarPanelConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->size;
-}
-
-//
-// Spacing between panel items.
-//
-gint foobar_panel_configuration_get_spacing( FoobarPanelConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->spacing;
-}
-
-//
-// Flag to enable the panel on all monitors.
-//
-gboolean foobar_panel_configuration_get_multi_monitor( FoobarPanelConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, FALSE );
- return self->multi_monitor;
-}
-
-//
-// Items configured to be displayed in the panel.
-//
-FoobarPanelItemConfiguration const* const* foobar_panel_configuration_get_items(
- FoobarPanelConfiguration const* self,
- gsize* out_count )
-{
- g_return_val_if_fail( self != NULL, NULL );
-
- if ( out_count ) { *out_count = self->items_count; }
- return (FoobarPanelItemConfiguration const* const*)self->items;
-}
-
-//
-// Mutable items configured to be displayed in the panel.
-//
-FoobarPanelItemConfiguration* const* foobar_panel_configuration_get_items_mut(
- FoobarPanelConfiguration* self,
- gsize* out_count )
-{
- g_return_val_if_fail( self != NULL, NULL );
-
- if ( out_count ) { *out_count = self->items_count; }
- return self->items;
-}
-
-//
-// The screen edge which the panel should appear on.
-//
-void foobar_panel_configuration_set_position(
- FoobarPanelConfiguration* self,
- FoobarScreenEdge value )
-{
- g_return_if_fail( self != NULL );
- self->position = value;
-}
-
-//
-// Offset of the panel from all screen edges.
-//
-void foobar_panel_configuration_set_margin(
- FoobarPanelConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->margin = value;
-}
-
-//
-// Inset of the panel along its orientation.
-//
-void foobar_panel_configuration_set_padding(
- FoobarPanelConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->padding = value;
-}
-
-//
-// Size of the panel (depending on the orientation, this can be either the width or the height).
-//
-void foobar_panel_configuration_set_size(
- FoobarPanelConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->size = value;
-}
-
-//
-// Spacing between panel items.
-//
-void foobar_panel_configuration_set_spacing(
- FoobarPanelConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->spacing = value;
-}
-
-//
-// Flag to enable the panel on all monitors.
-//
-void foobar_panel_configuration_set_multi_monitor(
- FoobarPanelConfiguration* self,
- gboolean value )
-{
- g_return_if_fail( self != NULL );
- self->multi_monitor = value;
-}
-
-//
-// Items configured to be displayed in the panel.
-//
-void foobar_panel_configuration_set_items(
- FoobarPanelConfiguration* self,
- FoobarPanelItemConfiguration const* const* value,
- gsize value_count )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( value != NULL || value_count == 0 );
-
- for ( gsize i = 0; i < self->items_count; ++i )
- {
- foobar_panel_item_configuration_free( self->items[i] );
- }
- g_free( self->items );
- self->items = g_new0( FoobarPanelItemConfiguration*, value_count + 1 );
- self->items_count = 0;
- for ( gsize i = 0; i < value_count; ++i )
- {
- g_warn_if_fail( value[i] != NULL );
- if ( value[i] )
- {
- self->items[self->items_count++] = foobar_panel_item_configuration_copy( value[i] );
- }
- }
-}
-
-//
-// Populate a panel configuration structure from the "panel" section of a keyfile.
-//
-void foobar_panel_configuration_load(
- FoobarPanelConfiguration* self,
- GKeyFile* file )
-{
- gint position;
- if ( try_get_enum_value( file, "panel", "position", FOOBAR_TYPE_SCREEN_EDGE, VALIDATE_NONE, &position ) )
- {
- foobar_panel_configuration_set_position( self, position );
- }
-
- gint margin;
- if ( try_get_int_value( file, "panel", "margin", VALIDATE_NON_NEGATIVE, &margin ) )
- {
- foobar_panel_configuration_set_margin( self, margin );
- }
-
- gint padding;
- if ( try_get_int_value( file, "panel", "padding", VALIDATE_NON_NEGATIVE, &padding ) )
- {
- foobar_panel_configuration_set_padding( self, padding );
- }
-
- gint size;
- if ( try_get_int_value( file, "panel", "size", VALIDATE_POSITIVE, &size ) )
- {
- foobar_panel_configuration_set_size( self, size );
- }
-
- gint spacing;
- if ( try_get_int_value( file, "panel", "spacing", VALIDATE_NON_NEGATIVE, &spacing ) )
- {
- foobar_panel_configuration_set_spacing( self, spacing );
- }
-
- gboolean multi_monitor;
- if ( try_get_boolean_value( file, "panel", "multi-monitor", VALIDATE_NONE, &multi_monitor ) )
- {
- foobar_panel_configuration_set_multi_monitor( self, multi_monitor );
- }
-
- g_autoptr( GPtrArray ) items = g_ptr_array_new_with_free_func( (GDestroyNotify)foobar_panel_item_configuration_free );
- g_auto( GStrv ) groups = g_key_file_get_groups( file, NULL );
- for ( gchar** it = groups; *it; ++it )
- {
- FoobarPanelItemConfiguration* item = foobar_panel_item_configuration_load( file, *it );
- if ( item ) { g_ptr_array_add( items, item ); }
- }
- foobar_panel_configuration_set_items( self, (FoobarPanelItemConfiguration const* const*)items->pdata, items->len );
-}
-
-//
-// Store a panel configuration structure in the "panel" section of a keyfile.
-//
-void foobar_panel_configuration_store(
- FoobarPanelConfiguration const* self,
- GKeyFile* file )
-{
- FoobarScreenEdge position = foobar_panel_configuration_get_position( self );
- g_key_file_set_string( file, "panel", "position", enum_value_to_string( FOOBAR_TYPE_SCREEN_EDGE, position ) );
- g_key_file_set_comment(
- file,
- "panel",
- "position",
- " The screen edge which the panel should appear on.",
- NULL );
-
- gint margin = foobar_panel_configuration_get_margin( self );
- g_key_file_set_integer( file, "panel", "margin", margin );
- g_key_file_set_comment(
- file,
- "panel",
- "margin",
- " Offset of the panel from all screen edges.",
- NULL );
-
- gint padding = foobar_panel_configuration_get_padding( self );
- g_key_file_set_integer( file, "panel", "padding", padding );
- g_key_file_set_comment(
- file,
- "panel",
- "padding",
- " Inset of the panel along its orientation.",
- NULL );
-
- gint size = foobar_panel_configuration_get_size( self );
- g_key_file_set_integer( file, "panel", "size", size );
- g_key_file_set_comment(
- file,
- "panel",
- "size",
- " Size of the panel (depending on the orientation, this can be either the width or the height).",
- NULL );
-
- gint spacing = foobar_panel_configuration_get_spacing( self );
- g_key_file_set_integer( file, "panel", "spacing", spacing );
- g_key_file_set_comment(
- file,
- "panel",
- "spacing",
- " Spacing between panel items.",
- NULL );
-
- gboolean multi_monitor = foobar_panel_configuration_get_multi_monitor( self );
- g_key_file_set_boolean( file, "panel", "multi-monitor", multi_monitor );
- g_key_file_set_comment(
- file,
- "panel",
- "multi-monitor",
- " Flag to enable the panel on all monitors.",
- NULL );
-
- gsize items_count;
- FoobarPanelItemConfiguration const* const* items = foobar_panel_configuration_get_items( self, &items_count );
- for ( gsize i = 0; i < items_count; ++i )
- {
- foobar_panel_item_configuration_store( items[i], file );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Launcher Configuration
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a default launcher configuration structure.
-//
-FoobarLauncherConfiguration* foobar_launcher_configuration_new( void )
-{
- return foobar_launcher_configuration_copy( &default_launcher_configuration );
-}
-
-//
-// Create a mutable copy of another launcher configuration structure.
-//
-FoobarLauncherConfiguration* foobar_launcher_configuration_copy( FoobarLauncherConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
-
- FoobarLauncherConfiguration* copy = g_new0( FoobarLauncherConfiguration, 1 );
- copy->width = self->width;
- copy->position = self->position;
- copy->max_height = self->max_height;
- return copy;
-}
-
-//
-// Release resources associated with a launcher configuration structure.
-//
-void foobar_launcher_configuration_free( FoobarLauncherConfiguration* self )
-{
- g_return_if_fail( self != NULL );
-
- g_free( self );
-}
-
-//
-// Check if two launcher configuration structures are equal.
-//
-gboolean foobar_launcher_configuration_equal(
- FoobarLauncherConfiguration const* a,
- FoobarLauncherConfiguration const* b )
-{
- g_return_val_if_fail( a != NULL, FALSE );
- g_return_val_if_fail( b != NULL, FALSE );
-
- if ( a->width != b->width ) { return FALSE; }
- if ( a->position != b->position ) { return FALSE; }
- if ( a->max_height != b->max_height ) { return FALSE; }
-
- return TRUE;
-}
-
-//
-// Horizontal size of the launcher.
-//
-gint foobar_launcher_configuration_get_width( FoobarLauncherConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->width;
-}
-
-//
-// Offset from the top of the screen.
-//
-gint foobar_launcher_configuration_get_position( FoobarLauncherConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->position;
-}
-
-//
-// Maximum allowed height for the launcher before scrolling is enabled.
-//
-gint foobar_launcher_configuration_get_max_height( FoobarLauncherConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->max_height;
-}
-
-//
-// Horizontal size of the launcher.
-//
-void foobar_launcher_configuration_set_width(
- FoobarLauncherConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->width = value;
-}
-
-//
-// Offset from the top of the screen.
-//
-void foobar_launcher_configuration_set_position(
- FoobarLauncherConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->position = value;
-}
-
-//
-// Maximum allowed height for the launcher before scrolling is enabled.
-//
-void foobar_launcher_configuration_set_max_height(
- FoobarLauncherConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->max_height = value;
-}
-
-//
-// Populate a launcher configuration structure from the "launcher" section of a keyfile.
-//
-void foobar_launcher_configuration_load(
- FoobarLauncherConfiguration* self,
- GKeyFile* file )
-{
- gint width;
- if ( try_get_int_value( file, "launcher", "width", VALIDATE_POSITIVE, &width ) )
- {
- foobar_launcher_configuration_set_width( self, width );
- }
-
- gint position;
- if ( try_get_int_value( file, "launcher", "position", VALIDATE_NON_NEGATIVE, &position ) )
- {
- foobar_launcher_configuration_set_position( self, position );
- }
-
- gint max_height;
- if ( try_get_int_value( file, "launcher", "max-height", VALIDATE_POSITIVE, &max_height ) )
- {
- foobar_launcher_configuration_set_position( self, max_height );
- }
-}
-
-//
-// Store a launcher configuration structure in the "launcher" section of a keyfile.
-//
-void foobar_launcher_configuration_store(
- FoobarLauncherConfiguration const* self,
- GKeyFile* file )
-{
- gint width = foobar_launcher_configuration_get_width( self );
- g_key_file_set_integer( file, "launcher", "width", width );
-
- gint position = foobar_launcher_configuration_get_position( self );
- g_key_file_set_integer( file, "launcher", "position", position );
- g_key_file_set_comment(
- file,
- "launcher",
- "position",
- " Offset from the top of the screen.",
- NULL );
-
- gint max_height = foobar_launcher_configuration_get_max_height( self );
- g_key_file_set_integer( file, "launcher", "max-height", max_height );
- g_key_file_set_comment(
- file,
- "launcher",
- "max-height",
- " Maximum allowed height for the launcher before scrolling is enabled.",
- NULL );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Control Center Configuration
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a default control center configuration structure.
-//
-FoobarControlCenterConfiguration* foobar_control_center_configuration_new( void )
-{
- return foobar_control_center_configuration_copy( &default_control_center_configuration );
-}
-
-//
-// Create a mutable copy of another control center configuration structure.
-//
-FoobarControlCenterConfiguration* foobar_control_center_configuration_copy( FoobarControlCenterConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
-
- FoobarControlCenterConfiguration* copy = g_new0( FoobarControlCenterConfiguration, 1 );
- copy->width = self->width;
- copy->height = self->height;
- copy->position = self->position;
- copy->offset = self->offset;
- copy->padding = self->padding;
- copy->spacing = self->spacing;
- copy->orientation = self->orientation;
- copy->alignment = self->alignment;
- copy->rows = g_new( FoobarControlCenterRow, self->rows_count );
- copy->rows_count = self->rows_count;
- memcpy( copy->rows, self->rows, sizeof( *copy->rows ) * copy->rows_count );
- return copy;
-}
-
-//
-// Release resources associated with a control center configuration structure.
-//
-void foobar_control_center_configuration_free( FoobarControlCenterConfiguration* self )
-{
- g_return_if_fail( self != NULL );
-
- g_free( self->rows );
- g_free( self );
-}
-
-//
-// Check if two control center configuration structures are equal.
-//
-gboolean foobar_control_center_configuration_equal(
- FoobarControlCenterConfiguration const* a,
- FoobarControlCenterConfiguration const* b )
-{
- g_return_val_if_fail( a != NULL, FALSE );
- g_return_val_if_fail( b != NULL, FALSE );
-
- if ( a->width != b->width ) { return FALSE; }
- if ( a->height != b->height ) { return FALSE; }
- if ( a->position != b->position ) { return FALSE; }
- if ( a->offset != b->offset ) { return FALSE; }
- if ( a->padding != b->padding ) { return FALSE; }
- if ( a->spacing != b->spacing ) { return FALSE; }
- if ( a->orientation != b->orientation ) { return FALSE; }
- if ( a->alignment != b->alignment ) { return FALSE; }
- if ( a->rows_count != b->rows_count ) { return FALSE; }
- if ( memcmp( a->rows, b->rows, sizeof( *a->rows ) * a->rows_count ) ) { return FALSE; }
-
- return TRUE;
-}
-
-//
-// Horizontal size of the control center.
-//
-gint foobar_control_center_configuration_get_width( FoobarControlCenterConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->width;
-}
-
-//
-// Vertical size of the control center.
-//
-gint foobar_control_center_configuration_get_height( FoobarControlCenterConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->height;
-}
-
-//
-// The screen edge which the control center should be attached to.
-//
-FoobarScreenEdge foobar_control_center_configuration_get_position( FoobarControlCenterConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->position;
-}
-
-//
-// Offset from the attached screen edge.
-//
-gint foobar_control_center_configuration_get_offset( FoobarControlCenterConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->offset;
-}
-
-//
-// Spacing between the edges of the control center and its items.
-//
-gint foobar_control_center_configuration_get_padding( FoobarControlCenterConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->padding;
-}
-
-//
-// Spacing between the items.
-//
-gint foobar_control_center_configuration_get_spacing( FoobarControlCenterConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->spacing;
-}
-
-//
-// The orientation used to arrange the controls and notifications sections.
-//
-FoobarOrientation foobar_control_center_configuration_get_orientation( FoobarControlCenterConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->orientation;
-}
-
-//
-// Alignment along the attached screen edge, or "fill".
-//
-FoobarControlCenterAlignment foobar_control_center_configuration_get_alignment( FoobarControlCenterConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->alignment;
-}
-
-//
-// Ordered list of rows to display in the controls section.
-//
-FoobarControlCenterRow const* foobar_control_center_configuration_get_rows(
- FoobarControlCenterConfiguration const* self,
- gsize* out_count )
-{
- g_return_val_if_fail( self != NULL, 0 );
- g_return_val_if_fail( out_count != NULL, NULL );
-
- *out_count = self->rows_count;
- return self->rows;
-}
-
-//
-// Horizontal size of the control center.
-//
-void foobar_control_center_configuration_set_width(
- FoobarControlCenterConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->width = value;
-}
-
-//
-// Vertical size of the control center.
-//
-void foobar_control_center_configuration_set_height(
- FoobarControlCenterConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->height = value;
-}
-
-//
-// The screen edge which the control center should be attached to.
-//
-void foobar_control_center_configuration_set_position(
- FoobarControlCenterConfiguration* self,
- FoobarScreenEdge value )
-{
- g_return_if_fail( self != NULL );
- self->position = value;
-}
-
-//
-// Offset from the attached screen edge.
-//
-void foobar_control_center_configuration_set_offset(
- FoobarControlCenterConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->offset = value;
-}
-
-//
-// Spacing between the edges of the control center and its items.
-//
-void foobar_control_center_configuration_set_padding(
- FoobarControlCenterConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->padding = value;
-}
-
-//
-// Spacing between the items.
-//
-void foobar_control_center_configuration_set_spacing(
- FoobarControlCenterConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->spacing = value;
-}
-
-//
-// The orientation used to arrange the controls and notifications sections.
-//
-void foobar_control_center_configuration_set_orientation(
- FoobarControlCenterConfiguration* self,
- FoobarOrientation value )
-{
- g_return_if_fail( self != NULL );
- self->orientation = value;
-}
-
-//
-// Alignment along the attached screen edge, or "fill".
-//
-void foobar_control_center_configuration_set_alignment(
- FoobarControlCenterConfiguration* self,
- FoobarControlCenterAlignment value )
-{
- g_return_if_fail( self != NULL );
- self->alignment = value;
-}
-
-//
-// Ordered list of rows to display in the controls section.
-//
-void foobar_control_center_configuration_set_rows(
- FoobarControlCenterConfiguration* self,
- FoobarControlCenterRow const* value,
- gsize value_count )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( value != NULL || value_count == 0 );
-
- g_free( self->rows );
- self->rows = g_new( FoobarControlCenterRow, value_count );
- self->rows_count = value_count;
- memcpy( self->rows, value, sizeof( *self->rows ) * self->rows_count );
-}
-
-//
-// Populate a control center configuration structure from the "control-center" section of a keyfile.
-//
-void foobar_control_center_configuration_load(
- FoobarControlCenterConfiguration* self,
- GKeyFile* file )
-{
- gint width;
- if ( try_get_int_value( file, "control-center", "width", VALIDATE_POSITIVE, &width ) )
- {
- foobar_control_center_configuration_set_width( self, width );
- }
-
- gint height;
- if ( try_get_int_value( file, "control-center", "height", VALIDATE_POSITIVE, &height ) )
- {
- foobar_control_center_configuration_set_height( self, height );
- }
-
- gint position;
- if ( try_get_enum_value( file, "control-center", "position", FOOBAR_TYPE_SCREEN_EDGE, VALIDATE_NONE, &position ) )
- {
- foobar_control_center_configuration_set_position( self, position );
- }
-
- gint offset;
- if ( try_get_int_value( file, "control-center", "offset", VALIDATE_NON_NEGATIVE, &offset ) )
- {
- foobar_control_center_configuration_set_offset( self, offset );
- }
-
- gint padding;
- if ( try_get_int_value( file, "control-center", "padding", VALIDATE_NON_NEGATIVE, &padding ) )
- {
- foobar_control_center_configuration_set_padding( self, padding );
- }
-
- gint spacing;
- if ( try_get_int_value( file, "control-center", "spacing", VALIDATE_NON_NEGATIVE, &spacing ) )
- {
- foobar_control_center_configuration_set_spacing( self, spacing );
- }
-
- gint orientation;
- if ( try_get_enum_value( file, "control-center", "orientation", FOOBAR_TYPE_ORIENTATION, VALIDATE_NONE, &orientation ) )
- {
- foobar_control_center_configuration_set_orientation( self, orientation );
- }
-
- gint alignment;
- if ( try_get_enum_value( file, "control-center", "alignment", FOOBAR_TYPE_CONTROL_CENTER_ALIGNMENT, VALIDATE_NONE, &alignment ) )
- {
- foobar_control_center_configuration_set_alignment( self, alignment );
- }
-
- g_autofree gpointer rows = NULL;
- gsize rows_count;
- if ( try_get_enum_list_value( file, "control-center", "rows", FOOBAR_TYPE_CONTROL_CENTER_ROW, VALIDATE_DISTINCT, &rows, &rows_count ) )
- {
- foobar_control_center_configuration_set_rows( self, rows, rows_count );
- }
-}
-
-//
-// Store a control center configuration structure in the "control-center" section of a keyfile.
-//
-void foobar_control_center_configuration_store(
- FoobarControlCenterConfiguration const* self,
- GKeyFile* file )
-{
- gint width = foobar_control_center_configuration_get_width( self );
- g_key_file_set_integer( file, "control-center", "width", width );
-
- gint height = foobar_control_center_configuration_get_height( self );
- g_key_file_set_integer( file, "control-center", "height", height );
-
- FoobarScreenEdge position = foobar_control_center_configuration_get_position( self );
- g_key_file_set_string( file, "control-center", "position", enum_value_to_string( FOOBAR_TYPE_SCREEN_EDGE, position ) );
- g_key_file_set_comment(
- file,
- "control-center",
- "position",
- " The screen edge which the control center should be attached to.",
- NULL );
-
- gint offset = foobar_control_center_configuration_get_offset( self );
- g_key_file_set_integer( file, "control-center", "offset", offset );
- g_key_file_set_comment(
- file,
- "control-center",
- "offset",
- " Offset from the attached screen edge.",
- NULL );
-
- gint padding = foobar_control_center_configuration_get_padding( self );
- g_key_file_set_integer( file, "control-center", "padding", padding );
- g_key_file_set_comment(
- file,
- "control-center",
- "padding",
- " Spacing between the edges of the control center and its items.",
- NULL );
-
- gint spacing = foobar_control_center_configuration_get_spacing( self );
- g_key_file_set_integer( file, "control-center", "spacing", spacing );
- g_key_file_set_comment(
- file,
- "control-center",
- "spacing",
- " Spacing between the items.",
- NULL );
-
- FoobarOrientation orientation = foobar_control_center_configuration_get_orientation( self );
- g_key_file_set_string( file, "control-center", "orientation", enum_value_to_string( FOOBAR_TYPE_ORIENTATION, orientation ) );
- g_key_file_set_comment(
- file,
- "control-center",
- "orientation",
- " The orientation used to arrange the controls and notifications sections.",
- NULL );
-
- FoobarControlCenterAlignment alignment = foobar_control_center_configuration_get_alignment( self );
- g_key_file_set_string( file, "control-center", "alignment", enum_value_to_string( FOOBAR_TYPE_CONTROL_CENTER_ALIGNMENT, alignment ) );
- g_key_file_set_comment(
- file,
- "control-center",
- "alignment",
- " Alignment along the attached screen edge, or \"fill\".",
- NULL );
-
- gsize rows_count;
- FoobarControlCenterRow const* rows = foobar_control_center_configuration_get_rows( self, &rows_count );
- g_autofree gchar const** rows_str = enum_list_to_string( FOOBAR_TYPE_CONTROL_CENTER_ROW, rows, rows_count );
- g_key_file_set_string_list( file, "control-center", "rows", rows_str, rows_count );
- g_key_file_set_comment(
- file,
- "control-center",
- "rows",
- " Ordered list of rows to display in the controls section.",
- NULL );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Notification Configuration
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a default notification configuration structure.
-//
-FoobarNotificationConfiguration* foobar_notification_configuration_new( void )
-{
- return foobar_notification_configuration_copy( &default_notification_configuration );
-}
-
-//
-// Create a mutable copy of another notification configuration structure.
-//
-FoobarNotificationConfiguration* foobar_notification_configuration_copy( FoobarNotificationConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
-
- FoobarNotificationConfiguration* copy = g_new0( FoobarNotificationConfiguration, 1 );
- copy->width = self->width;
- copy->min_height = self->min_height;
- copy->spacing = self->spacing;
- copy->close_button_inset = self->close_button_inset;
- copy->time_format = g_strdup( self->time_format );
- return copy;
-}
-
-//
-// Release resources associated with a notification configuration structure.
-//
-void foobar_notification_configuration_free( FoobarNotificationConfiguration* self )
-{
- g_return_if_fail( self != NULL );
-
- g_free( self->time_format );
- g_free( self );
-}
-
-//
-// Check if two notification configuration structures are equal.
-//
-gboolean foobar_notification_configuration_equal(
- FoobarNotificationConfiguration const* a,
- FoobarNotificationConfiguration const* b )
-{
- g_return_val_if_fail( a != NULL, FALSE );
- g_return_val_if_fail( b != NULL, FALSE );
-
- if ( a->width != b->width ) { return FALSE; }
- if ( a->min_height != b->min_height ) { return FALSE; }
- if ( a->spacing != b->spacing ) { return FALSE; }
- if ( a->close_button_inset != b->close_button_inset ) { return FALSE; }
- if ( g_strcmp0( a->time_format, b->time_format ) ) { return FALSE; }
-
- return TRUE;
-}
-
-//
-// Horizontal size of notifications in the notification area.
-//
-gint foobar_notification_configuration_get_width( FoobarNotificationConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->width;
-}
-
-//
-// Minimum height for each notification.
-//
-gint foobar_notification_configuration_get_min_height( FoobarNotificationConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->min_height;
-}
-
-//
-// Spacing between notifications and from the screen edges.
-//
-gint foobar_notification_configuration_get_spacing( FoobarNotificationConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->spacing;
-}
-
-//
-// Inset of the close button within the notification (may be negative).
-//
-gint foobar_notification_configuration_get_close_button_inset( FoobarNotificationConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, 0 );
- return self->close_button_inset;
-}
-
-//
-// The time format string as used by g_date_time_format.
-//
-gchar const* foobar_notification_configuration_get_time_format( FoobarNotificationConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->time_format;
-}
-
-//
-// Horizontal size of notifications in the notification area.
-//
-void foobar_notification_configuration_set_width(
- FoobarNotificationConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->width = value;
-}
-
-//
-// Minimum height for each notification.
-//
-void foobar_notification_configuration_set_min_height(
- FoobarNotificationConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->min_height = value;
-}
-
-//
-// Spacing between notifications and from the screen edges.
-//
-void foobar_notification_configuration_set_spacing(
- FoobarNotificationConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->spacing = value;
-}
-
-//
-// Inset of the close button within the notification (may be negative).
-//
-void foobar_notification_configuration_set_close_button_inset(
- FoobarNotificationConfiguration* self,
- gint value )
-{
- g_return_if_fail( self != NULL );
- self->close_button_inset = value;
-}
-
-//
-// The time format string as used by g_date_time_format.
-//
-void foobar_notification_configuration_set_time_format(
- FoobarNotificationConfiguration* self,
- gchar const* value )
-{
- g_return_if_fail( self != NULL );
- g_return_if_fail( value != NULL );
-
- g_free( self->time_format );
- self->time_format = g_strdup( value );
-}
-
-//
-// Populate a notification configuration structure from the "notifications" section of a keyfile.
-//
-void foobar_notification_configuration_load(
- FoobarNotificationConfiguration* self,
- GKeyFile* file )
-{
- gint width;
- if ( try_get_int_value( file, "notifications", "width", VALIDATE_POSITIVE, &width ) )
- {
- foobar_notification_configuration_set_width( self, width );
- }
-
- gint min_height;
- if ( try_get_int_value( file, "notifications", "min-height", VALIDATE_NON_NEGATIVE, &min_height ) )
- {
- foobar_notification_configuration_set_min_height( self, min_height );
- }
-
- gint spacing;
- if ( try_get_int_value( file, "notifications", "spacing", VALIDATE_NON_NEGATIVE, &spacing ) )
- {
- foobar_notification_configuration_set_spacing( self, spacing );
- }
-
- gint close_button_inset;
- if ( try_get_int_value( file, "notifications", "close-button-inset", VALIDATE_NONE, &close_button_inset ) )
- {
- foobar_notification_configuration_set_close_button_inset( self, close_button_inset );
- }
-
- g_autofree gchar* time_format = NULL;
- if ( try_get_string_value( file, "notifications", "time-format", VALIDATE_NONE, &time_format ) )
- {
- foobar_notification_configuration_set_time_format( self, time_format );
- }
-}
-
-//
-// Store a notification configuration structure in the "notifications" section of a keyfile.
-//
-void foobar_notification_configuration_store(
- FoobarNotificationConfiguration const* self,
- GKeyFile* file )
-{
- gint width = foobar_notification_configuration_get_width( self );
- g_key_file_set_integer( file, "notifications", "width", width );
-
- gint min_height = foobar_notification_configuration_get_min_height( self );
- g_key_file_set_integer( file, "notifications", "min-height", min_height );
- g_key_file_set_comment(
- file,
- "notifications",
- "min-height",
- " Minimum height for each notification.",
- NULL );
-
- gint spacing = foobar_notification_configuration_get_spacing( self );
- g_key_file_set_integer( file, "notifications", "spacing", spacing );
- g_key_file_set_comment(
- file,
- "notifications",
- "spacing",
- " Spacing between notifications and from the screen edges.",
- NULL );
-
- gint close_button_inset = foobar_notification_configuration_get_close_button_inset( self );
- g_key_file_set_integer( file, "notifications", "close-button-inset", close_button_inset );
- g_key_file_set_comment(
- file,
- "notifications",
- "close-button-inset",
- " Inset of the close button within the notification (may be negative).",
- NULL );
-
- gchar const* time_format = foobar_notification_configuration_get_time_format( self );
- g_key_file_set_string( file, "notifications", "time-format", time_format );
- g_key_file_set_comment(
- file,
- "notifications",
- "time-format",
- " The time format string as used by g_date_time_format.",
- NULL );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Configuration
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a default configuration structure.
-//
-FoobarConfiguration* foobar_configuration_new( void )
-{
- return foobar_configuration_copy( &default_configuration );
-}
-
-//
-// Create a mutable copy of another configuration structure.
-//
-FoobarConfiguration* foobar_configuration_copy( FoobarConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
-
- FoobarConfiguration* copy = g_new0( FoobarConfiguration, 1 );
- copy->general = foobar_general_configuration_copy( self->general );
- copy->panel = foobar_panel_configuration_copy( self->panel );
- copy->launcher = foobar_launcher_configuration_copy( self->launcher );
- copy->control_center = foobar_control_center_configuration_copy( self->control_center );
- copy->notifications = foobar_notification_configuration_copy( self->notifications );
- return copy;
-}
-
-//
-// Release resources associated with a configuration structure.
-//
-void foobar_configuration_free( FoobarConfiguration* self )
-{
- g_return_if_fail( self != NULL );
-
- foobar_general_configuration_free( self->general );
- foobar_panel_configuration_free( self->panel );
- foobar_launcher_configuration_free( self->launcher );
- foobar_control_center_configuration_free( self->control_center );
- foobar_notification_configuration_free( self->notifications );
- g_free( self );
-}
-
-//
-// Check if two configuration structures are equal.
-//
-gboolean foobar_configuration_equal(
- FoobarConfiguration const* a,
- FoobarConfiguration const* b )
-{
- g_return_val_if_fail( a != NULL, FALSE );
- g_return_val_if_fail( b != NULL, FALSE );
-
- if ( !foobar_general_configuration_equal( a->general, b->general ) ) { return FALSE; }
- if ( !foobar_panel_configuration_equal( a->panel, b->panel ) ) { return FALSE; }
- if ( !foobar_launcher_configuration_equal( a->launcher, b->launcher ) ) { return FALSE; }
- if ( !foobar_control_center_configuration_equal( a->control_center, b->control_center ) ) { return FALSE; }
- if ( !foobar_notification_configuration_equal( a->notifications, b->notifications ) ) { return FALSE; }
-
- return TRUE;
-}
-
-//
-// Get the general application configuration.
-//
-FoobarGeneralConfiguration const* foobar_configuration_get_general( FoobarConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->general;
-}
-
-//
-// Get a mutable reference to the general application configuration.
-//
-FoobarGeneralConfiguration* foobar_configuration_get_general_mut( FoobarConfiguration* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->general;
-}
-
-//
-// Get the panel configuration.
-//
-FoobarPanelConfiguration const* foobar_configuration_get_panel( FoobarConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->panel;
-}
-
-//
-// Get a mutable reference to the panel configuration.
-//
-FoobarPanelConfiguration* foobar_configuration_get_panel_mut( FoobarConfiguration* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->panel;
-}
-
-//
-// Get the launcher configuration.
-//
-FoobarLauncherConfiguration const* foobar_configuration_get_launcher( FoobarConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->launcher;
-}
-
-//
-// Get a mutable reference to the launcher configuration.
-//
-FoobarLauncherConfiguration* foobar_configuration_get_launcher_mut( FoobarConfiguration* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->launcher;
-}
-
-//
-// Get the control center configuration.
-//
-FoobarControlCenterConfiguration const* foobar_configuration_get_control_center( FoobarConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->control_center;
-}
-
-//
-// Get a mutable reference to the control center configuration.
-//
-FoobarControlCenterConfiguration* foobar_configuration_get_control_center_mut( FoobarConfiguration* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->control_center;
-}
-
-//
-// Get the notification configuration.
-//
-FoobarNotificationConfiguration const* foobar_configuration_get_notifications( FoobarConfiguration const* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->notifications;
-}
-
-//
-// Get a mutable reference to the notification configuration.
-//
-FoobarNotificationConfiguration* foobar_configuration_get_notifications_mut( FoobarConfiguration* self )
-{
- g_return_val_if_fail( self != NULL, NULL );
- return self->notifications;
-}
-
-//
-// Populate a configuration structure from a keyfile.
-//
-void foobar_configuration_load(
- FoobarConfiguration* self,
- GKeyFile* file )
-{
- foobar_general_configuration_load( foobar_configuration_get_general_mut( self ), file );
- foobar_panel_configuration_load( foobar_configuration_get_panel_mut( self ), file );
- foobar_launcher_configuration_load( foobar_configuration_get_launcher_mut( self ), file );
- foobar_control_center_configuration_load( foobar_configuration_get_control_center_mut( self ), file );
- foobar_notification_configuration_load( foobar_configuration_get_notifications_mut( self ), file );
-}
-
-//
-// Store a configuration structure in a keyfile.
-//
-void foobar_configuration_store(
- FoobarConfiguration const* self,
- GKeyFile* file )
-{
- foobar_general_configuration_store( foobar_configuration_get_general( self ), file );
- foobar_panel_configuration_store( foobar_configuration_get_panel( self ), file );
- foobar_launcher_configuration_store( foobar_configuration_get_launcher( self ), file );
- foobar_control_center_configuration_store( foobar_configuration_get_control_center( self ), file );
- foobar_notification_configuration_store( foobar_configuration_get_notifications( self ), file );
-}
-
-//
-// Populate a configuration structure from a keyfile at the given path.
-//
-void foobar_configuration_load_from_file(
- FoobarConfiguration* self,
- gchar const* path )
-{
- g_autoptr( GKeyFile ) file = g_key_file_new( );
- g_autoptr( GError ) error = NULL;
- if ( !g_key_file_load_from_file( file, path, G_KEY_FILE_NONE, &error ) )
- {
- CONFIGURATION_WARNING( "%s", error->message );
- return;
- }
- foobar_configuration_load( self, file );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the configuration service.
-//
-void foobar_configuration_service_class_init( FoobarConfigurationServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_configuration_service_get_property;
- object_klass->finalize = foobar_configuration_service_finalize;
-
- props[PROP_CURRENT] = g_param_spec_boxed(
- "current",
- "Current",
- "The current configuration state.",
- FOOBAR_TYPE_CONFIGURATION,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the configuration service.
-//
-void foobar_configuration_service_init( FoobarConfigurationService* self )
-{
- self->path = g_strdup_printf( "%s/foobar.conf", g_get_user_config_dir( ) );
- self->current = foobar_configuration_new( );
- g_return_if_fail( self->path != NULL );
-
- // Ensure that the file at path exists.
-
- if ( g_file_test( self->path, G_FILE_TEST_EXISTS ) )
- {
- foobar_configuration_load_from_file( self->current, self->path );
- }
- else
- {
- g_info( "Config file does not exist -- falling back to default config and creating a copy of it." );
- g_autoptr( GKeyFile ) file = g_key_file_new( );
- foobar_configuration_store( self->current, file );
- g_key_file_save_to_file( file, self->path, NULL );
- }
-
- // Monitor the config file.
-
- self->monitor = foobar_configuration_service_create_monitor( self->path );
- if ( self->monitor )
- {
- self->changed_handler_id = g_signal_connect(
- self->monitor,
- "changed",
- G_CALLBACK( foobar_configuration_service_handle_changed ),
- self );
- }
-
- // In case the config is symlinked, also watch the actual file for content changes.
-
- self->actual_path = realpath( self->path, NULL );
- if ( self->actual_path && g_strcmp0( self->actual_path, self->path ) )
- {
- self->actual_monitor = foobar_configuration_service_create_monitor( self->actual_path );
- if ( self->actual_monitor )
- {
- self->actual_changed_handler_id = g_signal_connect(
- self->actual_monitor,
- "changed",
- G_CALLBACK( foobar_configuration_service_handle_actual_changed ),
- self );
- }
- }
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_configuration_service_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarConfigurationService* self = (FoobarConfigurationService*)object;
-
- switch ( prop_id )
- {
- case PROP_CURRENT:
- g_value_set_boxed( value, foobar_configuration_service_get_current( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the configuration service.
-//
-void foobar_configuration_service_finalize( GObject* object )
-{
- FoobarConfigurationService* self = (FoobarConfigurationService*)object;
-
- g_clear_handle_id( &self->update_source_id, g_source_remove );
- g_clear_signal_handler( &self->changed_handler_id, self->monitor );
- g_clear_signal_handler( &self->actual_changed_handler_id, self->actual_monitor );
- g_clear_object( &self->monitor );
- g_clear_object( &self->actual_monitor );
- g_clear_pointer( &self->current, foobar_configuration_free );
- g_clear_pointer( &self->path, g_free );
- g_clear_pointer( &self->actual_path, free );
-
- G_OBJECT_CLASS( foobar_configuration_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new configuration service instance.
-//
-FoobarConfigurationService* foobar_configuration_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_CONFIGURATION_SERVICE, NULL );
-}
-
-//
-// Get the current configuration state.
-//
-FoobarConfiguration const* foobar_configuration_service_get_current( FoobarConfigurationService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONFIGURATION_SERVICE( self ), NULL );
- return self->current;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when the file at self->path has changed.
-//
-void foobar_configuration_service_handle_changed(
- GFileMonitor* monitor,
- GFile* file,
- GFile* other_file,
- GFileMonitorEvent event_type,
- gpointer userdata )
-{
- (void)monitor;
- (void)file;
- (void)other_file;
- FoobarConfigurationService* self = (FoobarConfigurationService*)userdata;
-
- // If this file is a symlink, the actual path might have changed.
-
- char* actual_path = realpath( self->path, NULL );
- if ( g_strcmp0( self->actual_path, actual_path ) )
- {
- free(self->actual_path);
- self->actual_path = g_steal_pointer( &actual_path );
-
- // Recreate the file monitor.
-
- g_clear_signal_handler( &self->actual_changed_handler_id, self->actual_monitor );
- g_clear_object( &self->actual_monitor );
-
- if ( self->actual_path && g_strcmp0( self->actual_path, self->path ) )
- {
- self->actual_monitor = foobar_configuration_service_create_monitor( self->actual_path );
- if ( self->actual_monitor )
- {
- self->actual_changed_handler_id = g_signal_connect(
- self->actual_monitor,
- "changed",
- G_CALLBACK( foobar_configuration_service_handle_actual_changed ),
- self );
- }
- }
- }
- free(actual_path);
-
- // Handle actual file changes.
-
- if ( event_type == G_FILE_MONITOR_EVENT_CREATED || event_type == G_FILE_MONITOR_EVENT_CHANGED )
- {
- foobar_configuration_service_update( self );
- }
-}
-
-//
-// Called when the actual file (the one at self->actual_path) behind the symlink has changed.
-//
-void foobar_configuration_service_handle_actual_changed(
- GFileMonitor* monitor,
- GFile* file,
- GFile* other_file,
- GFileMonitorEvent event_type,
- gpointer userdata )
-{
- (void)monitor;
- (void)file;
- (void)other_file;
- FoobarConfigurationService* self = (FoobarConfigurationService*)userdata;
-
- if ( event_type == G_FILE_MONITOR_EVENT_CREATED || event_type == G_FILE_MONITOR_EVENT_CHANGED )
- {
- foobar_configuration_service_update( self );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Schedule a configuration file reload, used to coalesce change events.
-//
-void foobar_configuration_service_update( FoobarConfigurationService* self )
-{
- if ( !self->update_source_id )
- {
- self->update_source_id = g_timeout_add(
- UPDATE_APPLY_DELAY,
- foobar_configuration_service_update_cb,
- self );
- }
-}
-
-//
-// Reload the configuration file.
-//
-gboolean foobar_configuration_service_update_cb( gpointer userdata )
-{
- FoobarConfigurationService* self = (FoobarConfigurationService*)userdata;
-
- FoobarConfiguration* updated = foobar_configuration_new( );
- foobar_configuration_load_from_file( updated, self->path );
- if ( !foobar_configuration_equal( self->current, updated ) )
- {
- foobar_configuration_free( self->current );
- self->current = g_steal_pointer( &updated );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CURRENT] );
- g_info( "Config reloaded." );
- }
- g_clear_pointer( &updated, foobar_configuration_free );
-
- self->update_source_id = 0;
- return G_SOURCE_REMOVE;
-}
-
-//
-// Create a new file monitor, logging a warning on error.
-//
-GFileMonitor* foobar_configuration_service_create_monitor( gchar const* path )
-{
- g_autoptr( GError ) error = NULL;
- g_autoptr( GFile ) file = g_file_new_for_path( path );
- GFileMonitor* monitor = g_file_monitor( file, G_FILE_MONITOR_NONE, NULL, &error );
- if ( !monitor )
- {
- g_warning( "Unable to monitor config file: %s", error->message );
- }
-
- return monitor;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Parsing Helpers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Get and validate a string value from a keyfile. *out should always be freed, even if FALSE is returned.
-//
-gboolean try_get_string_value(
- GKeyFile* file,
- gchar const* group,
- gchar const* key,
- ValidationFlags validation,
- gchar** out )
-{
- if ( !g_key_file_has_key( file, group, key, NULL ) ) { return FALSE; }
-
- g_autoptr( GError ) error = NULL;
- *out = g_key_file_get_string( file, group, key, &error );
- if ( !*out )
- {
- CONFIGURATION_WARNING( "%s:%s: %s", group, key, error->message );
- return FALSE;
- }
-
- if ( validation & VALIDATE_FILE_URL )
- {
- g_autoptr( GFile ) test_file = g_file_new_for_uri( *out );
- if ( !g_file_query_exists( test_file, NULL ) )
- {
- CONFIGURATION_WARNING( "%s:%s: A file with the URL \"%s\" does not exist.", group, key, *out );
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-//
-// Get and validate a list of string values from a keyfile. *out should always be freed, even if FALSE is returned.
-//
-gboolean try_get_string_list_value(
- GKeyFile* file,
- gchar const* group,
- gchar const* key,
- ValidationFlags validation,
- GStrv* out,
- gsize* out_count )
-{
- if ( !g_key_file_has_key( file, group, key, NULL ) ) { return FALSE; }
-
- g_autoptr( GError ) error = NULL;
- *out = g_key_file_get_string_list( file, group, key, out_count, &error );
- if ( !*out )
- {
- CONFIGURATION_WARNING( "%s:%s: %s", group, key, error->message );
- return FALSE;
- }
-
- if ( validation & VALIDATE_DISTINCT )
- {
- g_autoptr( GHashTable ) items = g_hash_table_new( g_str_hash, g_str_equal );
- for ( gchar** it = *out; *it; ++it )
- {
- if ( !g_hash_table_add( items, *it ) )
- {
- CONFIGURATION_WARNING( "%s:%s: Duplicate value \"%s\" not allowed.", group, key, *it );
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
-//
-// Get and validate an integer value from a keyfile.
-//
-gboolean try_get_int_value(
- GKeyFile* file,
- gchar const* group,
- gchar const* key,
- ValidationFlags validation,
- gint* out )
-{
- if ( !g_key_file_has_key( file, group, key, NULL ) ) { return FALSE; }
-
- g_autoptr( GError ) error = NULL;
- *out = g_key_file_get_integer( file, group, key, &error );
- if ( error )
- {
- CONFIGURATION_WARNING( "%s:%s: %s", group, key, error->message );
- return FALSE;
- }
-
- if ( validation & VALIDATE_NON_NEGATIVE )
- {
- if ( *out < 0 )
- {
- CONFIGURATION_WARNING( "%s:%s: Value is not allowed to be negative.", group, key );
- return FALSE;
- }
- }
-
- if ( validation & VALIDATE_NON_ZERO )
- {
- if ( *out == 0 )
- {
- CONFIGURATION_WARNING( "%s:%s: Value is not allowed to be 0.", group, key );
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-//
-// Get and validate an enum value (encoded as a string) from a keyfile.
-//
-gboolean try_get_enum_value(
- GKeyFile* file,
- gchar const* group,
- gchar const* key,
- GType enum_type,
- ValidationFlags validation,
- gint* out )
-{
- (void)validation;
-
- g_autofree gchar* str = NULL;
- if ( !try_get_string_value( file, group, key, VALIDATE_NONE, &str ) ) { return FALSE; }
-
- g_autoptr( GEnumClass ) enum_klass = g_type_class_ref( enum_type );
- GEnumValue* val = g_enum_get_value_by_nick( enum_klass, str );
- if ( !val )
- {
- g_autofree gchar* help_text = enum_format_help_text( enum_type );
- CONFIGURATION_WARNING( "%s:%s: Invalid enum value \"%s\" (allowed values: %s).", group, key, str, help_text );
- return FALSE;
- }
-
- *out = val->value;
- return TRUE;
-}
-
-//
-// Get and validate a list of enum values (encoded as strings) from a keyfile. *out should always be freed, even if
-// FALSE is returned.
-//
-gboolean try_get_enum_list_value(
- GKeyFile* file,
- gchar const* group,
- gchar const* key,
- GType enum_type,
- ValidationFlags validation,
- gpointer* out,
- gsize* out_count )
-{
- g_auto( GStrv ) str_values = NULL;
- if ( !try_get_string_list_value( file, group, key, validation, &str_values, out_count ) ) { return FALSE; }
-
- gint* untyped_out = g_new0( gint, *out_count );
- *out = untyped_out;
-
- g_autoptr( GEnumClass ) enum_klass = g_type_class_ref( enum_type );
- for ( gsize i = 0; i < *out_count; ++i )
- {
- GEnumValue* val = g_enum_get_value_by_nick( enum_klass, str_values[i] );
- if ( !val )
- {
- g_autofree gchar* help_text = enum_format_help_text( enum_type );
- CONFIGURATION_WARNING( "%s:%s: Invalid enum value \"%s\" (allowed values: %s).", group, key, str_values[i], help_text );
- return FALSE;
- }
-
- untyped_out[i] = val->value;
- }
-
- return TRUE;
-}
-
-//
-// Get and validate a boolean value from a keyfile.
-//
-gboolean try_get_boolean_value(
- GKeyFile* file,
- gchar const* group,
- gchar const* key,
- ValidationFlags validation,
- gboolean* out )
-{
- (void)validation;
-
- if ( !g_key_file_has_key( file, group, key, NULL ) ) { return FALSE; }
-
- g_autoptr( GError ) error = NULL;
- *out = g_key_file_get_boolean( file, group, key, &error );
- if ( error )
- {
- CONFIGURATION_WARNING( "%s:%s: %s", group, key, error->message );
- return FALSE;
- }
-
- return TRUE;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Serialization
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Get the string constant for an enum value.
-//
-gchar const* enum_value_to_string(
- GType enum_type,
- gint value )
-{
- g_autoptr( GEnumClass ) enum_klass = g_type_class_ref( enum_type );
- GEnumValue* val = g_enum_get_value( enum_klass, value );
- return val->value_nick;
-}
-
-//
-// Get a list of string constants for a list of enum values. Only the list itself needs to be freed.
-//
-gchar const** enum_list_to_string(
- GType enum_type,
- gconstpointer values,
- gsize values_count )
-{
- gint const* untyped_values = values;
- gchar const** result = g_new0( gchar const*, values_count );
-
- g_autoptr( GEnumClass ) enum_klass = g_type_class_ref( enum_type );
- for ( gsize i = 0; i < values_count; ++i )
- {
- GEnumValue* val = g_enum_get_value( enum_klass, untyped_values[i] );
- result[i] = val->value_nick;
- }
-
- return result;
-}
-
-//
-// Return a comma-separated list of all allowed values for an enum type. The result needs to be freed.
-//
-gchar* enum_format_help_text( GType enum_type )
-{
- GString* result = g_string_new( NULL );
- g_autoptr( GEnumClass ) enum_klass = g_type_class_ref( enum_type );
-
- gboolean is_first = TRUE;
- for ( guint i = 0; i < enum_klass->n_values; ++i )
- {
- g_string_append_printf( result, is_first ? "\"%s\"" : ", \"%s\"", enum_klass->values[i].value_nick );
- is_first = FALSE;
- }
-
- return g_string_free_and_steal( result );
-}
diff --git a/src/services/configuration-service.h b/src/services/configuration-service.h
deleted file mode 100644
index 2c1296b..0000000
--- a/src/services/configuration-service.h
+++ /dev/null
@@ -1,305 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_SCREEN_EDGE foobar_screen_edge_get_type( )
-#define FOOBAR_TYPE_ORIENTATION foobar_orientation_get_type( )
-#define FOOBAR_TYPE_STATUS_ITEM foobar_status_item_get_type( )
-#define FOOBAR_TYPE_PANEL_ITEM_KIND foobar_panel_item_kind_get_type( )
-#define FOOBAR_TYPE_PANEL_ITEM_ACTION foobar_panel_item_action_get_type( )
-#define FOOBAR_TYPE_PANEL_ITEM_POSITION foobar_panel_item_position_get_type( )
-#define FOOBAR_TYPE_CONTROL_CENTER_ROW foobar_control_center_row_get_type( )
-#define FOOBAR_TYPE_CONTROL_CENTER_ALIGNMENT foobar_control_center_alignment_get_type( )
-#define FOOBAR_TYPE_GENERAL_CONFIGURATION foobar_general_configuration_get_type( )
-#define FOOBAR_TYPE_PANEL_ITEM_CONFIGURATION foobar_panel_item_configuration_get_type( )
-#define FOOBAR_TYPE_PANEL_CONFIGURATION foobar_panel_configuration_get_type( )
-#define FOOBAR_TYPE_LAUNCHER_CONFIGURATION foobar_launcher_configuration_get_type( )
-#define FOOBAR_TYPE_CONTROL_CENTER_CONFIGURATION foobar_control_center_configuration_get_type( )
-#define FOOBAR_TYPE_NOTIFICATION_CONFIGURATION foobar_notification_configuration_get_type( )
-#define FOOBAR_TYPE_CONFIGURATION foobar_configuration_get_type( )
-#define FOOBAR_TYPE_CONFIGURATION_SERVICE foobar_configuration_service_get_type( )
-
-typedef enum
-{
- FOOBAR_SCREEN_EDGE_LEFT,
- FOOBAR_SCREEN_EDGE_RIGHT,
- FOOBAR_SCREEN_EDGE_TOP,
- FOOBAR_SCREEN_EDGE_BOTTOM,
-} FoobarScreenEdge;
-
-GType foobar_screen_edge_get_type( void );
-
-typedef enum
-{
- FOOBAR_ORIENTATION_HORIZONTAL,
- FOOBAR_ORIENTATION_VERTICAL,
-} FoobarOrientation;
-
-GType foobar_orientation_get_type( void );
-
-typedef enum
-{
- FOOBAR_STATUS_ITEM_NETWORK,
- FOOBAR_STATUS_ITEM_BLUETOOTH,
- FOOBAR_STATUS_ITEM_BATTERY,
- FOOBAR_STATUS_ITEM_BRIGHTNESS,
- FOOBAR_STATUS_ITEM_AUDIO,
- FOOBAR_STATUS_ITEM_NOTIFICATIONS,
-} FoobarStatusItem;
-
-GType foobar_status_item_get_type( void );
-
-typedef enum
-{
- FOOBAR_PANEL_ITEM_KIND_ICON,
- FOOBAR_PANEL_ITEM_KIND_CLOCK,
- FOOBAR_PANEL_ITEM_KIND_WORKSPACES,
- FOOBAR_PANEL_ITEM_KIND_STATUS,
-} FoobarPanelItemKind;
-
-GType foobar_panel_item_kind_get_type( void );
-
-typedef enum
-{
- FOOBAR_PANEL_ITEM_ACTION_NONE,
- FOOBAR_PANEL_ITEM_ACTION_LAUNCHER,
- FOOBAR_PANEL_ITEM_ACTION_CONTROL_CENTER,
-} FoobarPanelItemAction;
-
-GType foobar_panel_item_action_get_type( void );
-
-typedef enum
-{
- FOOBAR_PANEL_ITEM_POSITION_START,
- FOOBAR_PANEL_ITEM_POSITION_CENTER,
- FOOBAR_PANEL_ITEM_POSITION_END,
-} FoobarPanelItemPosition;
-
-GType foobar_panel_item_position_get_type( void );
-
-typedef enum
-{
- FOOBAR_CONTROL_CENTER_ROW_CONNECTIVITY,
- FOOBAR_CONTROL_CENTER_ROW_AUDIO_OUTPUT,
- FOOBAR_CONTROL_CENTER_ROW_AUDIO_INPUT,
- FOOBAR_CONTROL_CENTER_ROW_BRIGHTNESS,
-} FoobarControlCenterRow;
-
-GType foobar_control_center_row_get_type( void );
-
-typedef enum
-{
- FOOBAR_CONTROL_CENTER_ALIGNMENT_START,
- FOOBAR_CONTROL_CENTER_ALIGNMENT_CENTER,
- FOOBAR_CONTROL_CENTER_ALIGNMENT_END,
- FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL,
-} FoobarControlCenterAlignment;
-
-GType foobar_control_center_alignment_get_type( void );
-
-typedef struct _FoobarGeneralConfiguration FoobarGeneralConfiguration;
-
-GType foobar_general_configuration_get_type ( void );
-FoobarGeneralConfiguration* foobar_general_configuration_new ( void );
-FoobarGeneralConfiguration* foobar_general_configuration_copy ( FoobarGeneralConfiguration const* self );
-void foobar_general_configuration_free ( FoobarGeneralConfiguration* self );
-gboolean foobar_general_configuration_equal ( FoobarGeneralConfiguration const* a,
- FoobarGeneralConfiguration const* b );
-gchar const* foobar_general_configuration_get_stylesheet( FoobarGeneralConfiguration const* self );
-void foobar_general_configuration_set_stylesheet( FoobarGeneralConfiguration* self,
- gchar const* value );
-
-typedef struct _FoobarPanelItemConfiguration FoobarPanelItemConfiguration;
-
-GType foobar_panel_item_configuration_get_type ( void );
-FoobarPanelItemConfiguration* foobar_panel_item_configuration_copy ( FoobarPanelItemConfiguration const* self );
-void foobar_panel_item_configuration_free ( FoobarPanelItemConfiguration* self );
-gboolean foobar_panel_item_configuration_equal ( FoobarPanelItemConfiguration const* a,
- FoobarPanelItemConfiguration const* b );
-FoobarPanelItemKind foobar_panel_item_configuration_get_kind ( FoobarPanelItemConfiguration const* self );
-gchar const* foobar_panel_item_configuration_get_name ( FoobarPanelItemConfiguration const* self );
-FoobarPanelItemPosition foobar_panel_item_configuration_get_position( FoobarPanelItemConfiguration const* self );
-FoobarPanelItemAction foobar_panel_item_configuration_get_action ( FoobarPanelItemConfiguration const* self );
-void foobar_panel_item_configuration_set_name ( FoobarPanelItemConfiguration* self,
- gchar const* value );
-void foobar_panel_item_configuration_set_position( FoobarPanelItemConfiguration* self,
- FoobarPanelItemPosition value );
-void foobar_panel_item_configuration_set_action ( FoobarPanelItemConfiguration* self,
- FoobarPanelItemAction value );
-
-FoobarPanelItemConfiguration* foobar_panel_item_icon_configuration_new ( void );
-gchar const* foobar_panel_item_icon_configuration_get_icon_name( FoobarPanelItemConfiguration const* self );
-void foobar_panel_item_icon_configuration_set_icon_name( FoobarPanelItemConfiguration* self,
- gchar const* value );
-
-FoobarPanelItemConfiguration* foobar_panel_item_clock_configuration_new ( void );
-gchar const* foobar_panel_item_clock_configuration_get_format( FoobarPanelItemConfiguration const* self );
-void foobar_panel_item_clock_configuration_set_format( FoobarPanelItemConfiguration* self,
- gchar const* value );
-
-FoobarPanelItemConfiguration* foobar_panel_item_workspaces_configuration_new ( void );
-gint foobar_panel_item_workspaces_configuration_get_button_size( FoobarPanelItemConfiguration const* self );
-gint foobar_panel_item_workspaces_configuration_get_spacing ( FoobarPanelItemConfiguration const* self );
-void foobar_panel_item_workspaces_configuration_set_button_size( FoobarPanelItemConfiguration* self,
- gint value );
-void foobar_panel_item_workspaces_configuration_set_spacing ( FoobarPanelItemConfiguration* self,
- gint value );
-
-FoobarPanelItemConfiguration* foobar_panel_item_status_configuration_new ( void );
-FoobarStatusItem const* foobar_panel_item_status_configuration_get_items ( FoobarPanelItemConfiguration const* self,
- gsize* out_count );
-gint foobar_panel_item_status_configuration_get_spacing ( FoobarPanelItemConfiguration const* self );
-gboolean foobar_panel_item_status_configuration_get_show_labels ( FoobarPanelItemConfiguration const* self );
-gboolean foobar_panel_item_status_configuration_get_enable_scrolling( FoobarPanelItemConfiguration const* self );
-void foobar_panel_item_status_configuration_set_items ( FoobarPanelItemConfiguration* self,
- FoobarStatusItem const* value,
- gsize value_count );
-void foobar_panel_item_status_configuration_set_spacing ( FoobarPanelItemConfiguration* self,
- gint value );
-void foobar_panel_item_status_configuration_set_show_labels ( FoobarPanelItemConfiguration* self,
- gboolean value );
-void foobar_panel_item_status_configuration_set_enable_scrolling( FoobarPanelItemConfiguration* self,
- gboolean value );
-
-typedef struct _FoobarPanelConfiguration FoobarPanelConfiguration;
-
-GType foobar_panel_configuration_get_type ( void );
-FoobarPanelConfiguration* foobar_panel_configuration_new ( void );
-FoobarPanelConfiguration* foobar_panel_configuration_copy ( FoobarPanelConfiguration const* self );
-void foobar_panel_configuration_free ( FoobarPanelConfiguration* self );
-gboolean foobar_panel_configuration_equal ( FoobarPanelConfiguration const* a,
- FoobarPanelConfiguration const* b );
-FoobarScreenEdge foobar_panel_configuration_get_position ( FoobarPanelConfiguration const* self );
-gint foobar_panel_configuration_get_margin ( FoobarPanelConfiguration const* self );
-gint foobar_panel_configuration_get_padding ( FoobarPanelConfiguration const* self );
-gint foobar_panel_configuration_get_size ( FoobarPanelConfiguration const* self );
-gint foobar_panel_configuration_get_spacing ( FoobarPanelConfiguration const* self );
-gboolean foobar_panel_configuration_get_multi_monitor( FoobarPanelConfiguration const* self );
-FoobarPanelItemConfiguration const* const* foobar_panel_configuration_get_items ( FoobarPanelConfiguration const* self,
- gsize* out_count );
-FoobarPanelItemConfiguration* const* foobar_panel_configuration_get_items_mut ( FoobarPanelConfiguration* self,
- gsize* out_count );
-void foobar_panel_configuration_set_position ( FoobarPanelConfiguration* self,
- FoobarScreenEdge value );
-void foobar_panel_configuration_set_margin ( FoobarPanelConfiguration* self,
- gint value );
-void foobar_panel_configuration_set_padding ( FoobarPanelConfiguration* self,
- gint value );
-void foobar_panel_configuration_set_size ( FoobarPanelConfiguration* self,
- gint value );
-void foobar_panel_configuration_set_spacing ( FoobarPanelConfiguration* self,
- gint value );
-void foobar_panel_configuration_set_multi_monitor( FoobarPanelConfiguration* self,
- gboolean value );
-void foobar_panel_configuration_set_items ( FoobarPanelConfiguration* self,
- FoobarPanelItemConfiguration const* const* value,
- gsize value_count );
-
-typedef struct _FoobarLauncherConfiguration FoobarLauncherConfiguration;
-
-GType foobar_launcher_configuration_get_type ( void );
-FoobarLauncherConfiguration* foobar_launcher_configuration_new ( void );
-FoobarLauncherConfiguration* foobar_launcher_configuration_copy ( FoobarLauncherConfiguration const* self );
-void foobar_launcher_configuration_free ( FoobarLauncherConfiguration* self );
-gboolean foobar_launcher_configuration_equal ( FoobarLauncherConfiguration const* a,
- FoobarLauncherConfiguration const* b );
-gint foobar_launcher_configuration_get_width ( FoobarLauncherConfiguration const* self );
-gint foobar_launcher_configuration_get_position ( FoobarLauncherConfiguration const* self );
-gint foobar_launcher_configuration_get_max_height( FoobarLauncherConfiguration const* self );
-void foobar_launcher_configuration_set_width ( FoobarLauncherConfiguration* self,
- gint value );
-void foobar_launcher_configuration_set_position ( FoobarLauncherConfiguration* self,
- gint value );
-void foobar_launcher_configuration_set_max_height( FoobarLauncherConfiguration* self,
- gint value );
-
-typedef struct _FoobarControlCenterConfiguration FoobarControlCenterConfiguration;
-
-GType foobar_control_center_configuration_get_type ( void );
-FoobarControlCenterConfiguration* foobar_control_center_configuration_new ( void );
-FoobarControlCenterConfiguration* foobar_control_center_configuration_copy ( FoobarControlCenterConfiguration const* self );
-void foobar_control_center_configuration_free ( FoobarControlCenterConfiguration* self );
-gboolean foobar_control_center_configuration_equal ( FoobarControlCenterConfiguration const* a,
- FoobarControlCenterConfiguration const* b );
-gint foobar_control_center_configuration_get_width ( FoobarControlCenterConfiguration const* self );
-gint foobar_control_center_configuration_get_height ( FoobarControlCenterConfiguration const* self );
-FoobarScreenEdge foobar_control_center_configuration_get_position ( FoobarControlCenterConfiguration const* self );
-gint foobar_control_center_configuration_get_offset ( FoobarControlCenterConfiguration const* self );
-gint foobar_control_center_configuration_get_padding ( FoobarControlCenterConfiguration const* self );
-gint foobar_control_center_configuration_get_spacing ( FoobarControlCenterConfiguration const* self );
-FoobarOrientation foobar_control_center_configuration_get_orientation( FoobarControlCenterConfiguration const* self );
-FoobarControlCenterAlignment foobar_control_center_configuration_get_alignment ( FoobarControlCenterConfiguration const* self );
-FoobarControlCenterRow const* foobar_control_center_configuration_get_rows ( FoobarControlCenterConfiguration const* self,
- gsize* out_count );
-void foobar_control_center_configuration_set_width ( FoobarControlCenterConfiguration* self,
- gint value );
-void foobar_control_center_configuration_set_height ( FoobarControlCenterConfiguration* self,
- gint value );
-void foobar_control_center_configuration_set_position ( FoobarControlCenterConfiguration* self,
- FoobarScreenEdge value );
-void foobar_control_center_configuration_set_offset ( FoobarControlCenterConfiguration* self,
- gint value );
-void foobar_control_center_configuration_set_padding ( FoobarControlCenterConfiguration* self,
- gint value );
-void foobar_control_center_configuration_set_spacing ( FoobarControlCenterConfiguration* self,
- gint value );
-void foobar_control_center_configuration_set_orientation( FoobarControlCenterConfiguration* self,
- FoobarOrientation value );
-void foobar_control_center_configuration_set_rows ( FoobarControlCenterConfiguration* self,
- FoobarControlCenterRow const* value,
- gsize value_count );
-void foobar_control_center_configuration_set_alignment ( FoobarControlCenterConfiguration* self,
- FoobarControlCenterAlignment value );
-
-typedef struct _FoobarNotificationConfiguration FoobarNotificationConfiguration;
-
-GType foobar_notification_configuration_get_type ( void );
-FoobarNotificationConfiguration* foobar_notification_configuration_new ( void );
-FoobarNotificationConfiguration* foobar_notification_configuration_copy ( FoobarNotificationConfiguration const* self );
-void foobar_notification_configuration_free ( FoobarNotificationConfiguration* self );
-gboolean foobar_notification_configuration_equal ( FoobarNotificationConfiguration const* a,
- FoobarNotificationConfiguration const* b );
-gint foobar_notification_configuration_get_width ( FoobarNotificationConfiguration const* self );
-gint foobar_notification_configuration_get_min_height ( FoobarNotificationConfiguration const* self );
-gint foobar_notification_configuration_get_spacing ( FoobarNotificationConfiguration const* self );
-gint foobar_notification_configuration_get_close_button_inset( FoobarNotificationConfiguration const* self );
-gchar const* foobar_notification_configuration_get_time_format ( FoobarNotificationConfiguration const* self );
-void foobar_notification_configuration_set_width ( FoobarNotificationConfiguration* self,
- gint value );
-void foobar_notification_configuration_set_min_height ( FoobarNotificationConfiguration* self,
- gint value );
-void foobar_notification_configuration_set_spacing ( FoobarNotificationConfiguration* self,
- gint value );
-void foobar_notification_configuration_set_close_button_inset( FoobarNotificationConfiguration* self,
- gint value );
-void foobar_notification_configuration_set_time_format ( FoobarNotificationConfiguration* self,
- gchar const* value );
-
-typedef struct _FoobarConfiguration FoobarConfiguration;
-
-GType foobar_configuration_get_type ( void );
-FoobarConfiguration* foobar_configuration_new ( void );
-FoobarConfiguration* foobar_configuration_copy ( FoobarConfiguration const* self );
-void foobar_configuration_free ( FoobarConfiguration* self );
-gboolean foobar_configuration_equal ( FoobarConfiguration const* a,
- FoobarConfiguration const* b );
-FoobarGeneralConfiguration const* foobar_configuration_get_general ( FoobarConfiguration const* self );
-FoobarGeneralConfiguration* foobar_configuration_get_general_mut ( FoobarConfiguration* self );
-FoobarPanelConfiguration const* foobar_configuration_get_panel ( FoobarConfiguration const* self );
-FoobarPanelConfiguration* foobar_configuration_get_panel_mut ( FoobarConfiguration* self );
-FoobarLauncherConfiguration const* foobar_configuration_get_launcher ( FoobarConfiguration const* self );
-FoobarLauncherConfiguration* foobar_configuration_get_launcher_mut ( FoobarConfiguration* self );
-FoobarControlCenterConfiguration const* foobar_configuration_get_control_center ( FoobarConfiguration const* self );
-FoobarControlCenterConfiguration* foobar_configuration_get_control_center_mut( FoobarConfiguration* self );
-FoobarNotificationConfiguration const* foobar_configuration_get_notifications ( FoobarConfiguration const* self );
-FoobarNotificationConfiguration* foobar_configuration_get_notifications_mut ( FoobarConfiguration* self );
-
-
-G_DECLARE_FINAL_TYPE( FoobarConfigurationService, foobar_configuration_service, FOOBAR, CONFIGURATION_SERVICE, GObject )
-
-FoobarConfigurationService* foobar_configuration_service_new ( void );
-FoobarConfiguration const* foobar_configuration_service_get_current( FoobarConfigurationService* self );
-
-G_END_DECLS
diff --git a/src/services/meson.build b/src/services/meson.build
deleted file mode 100644
index 368c2f1..0000000
--- a/src/services/meson.build
+++ /dev/null
@@ -1,15 +0,0 @@
-foobar_sources += files(
- 'battery-service.c',
- 'clock-service.c',
- 'brightness-service.c',
- 'workspace-service.c',
- 'notification-service.c',
- 'audio-service.c',
- 'network-service.c',
- 'bluetooth-service.c',
- 'application-service.c',
- 'quick-answer-service.c',
- 'configuration-service.c',
-)
-
-subdir('quick-answers')
diff --git a/src/services/network-service.c b/src/services/network-service.c
deleted file mode 100644
index b2d1334..0000000
--- a/src/services/network-service.c
+++ /dev/null
@@ -1,1577 +0,0 @@
-#include "services/network-service.h"
-#include <NetworkManager.h>
-#include <gtk/gtk.h>
-#include <stdint.h>
-
-typedef struct _ApNetworkMembership ApNetworkMembership;
-
-#define SCAN_REFRESH_INTERVAL 10000
-
-//
-// FoobarNetwork:
-//
-// Represents a Wi-Fi network, identified by its SSID. Instances contain a set of NMAccessPoints and the strength is
-// updated to match that of the best available AP.
-//
-
-struct _FoobarNetwork
-{
- GObject parent_instance;
- GBytes* ssid;
- gchar* name;
- gint strength;
- gboolean is_active;
- GHashTable* aps;
-};
-
-enum
-{
- NETWORK_PROP_NAME = 1,
- NETWORK_PROP_STRENGTH,
- NETWORK_PROP_IS_ACTIVE,
- N_NETWORK_PROPS,
-};
-
-static GParamSpec* network_props[N_NETWORK_PROPS] = { 0 };
-
-static void foobar_network_class_init ( FoobarNetworkClass* klass );
-static void foobar_network_init ( FoobarNetwork* self );
-static void foobar_network_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_network_finalize ( GObject* object );
-static FoobarNetwork* foobar_network_new ( void );
-static void foobar_network_set_ssid ( FoobarNetwork* self,
- GBytes* value );
-static void foobar_network_set_active ( FoobarNetwork* self,
- gboolean value );
-static void foobar_network_add_ap ( FoobarNetwork* self,
- NMAccessPoint* ap );
-static void foobar_network_remove_ap ( FoobarNetwork* self,
- NMAccessPoint* ap );
-static void foobar_network_update_strength ( FoobarNetwork* self );
-static void foobar_network_handle_strength_change( GObject* ap,
- GParamSpec* pspec,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarNetwork, foobar_network, G_TYPE_OBJECT )
-
-//
-// FoobarNetworkAdapterState:
-//
-// The current connection/connectivity state of a network adapter.
-//
-
-G_DEFINE_ENUM_TYPE(
- FoobarNetworkAdapterState,
- foobar_network_adapter_state,
- G_DEFINE_ENUM_VALUE( FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED, "disconnected" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_NETWORK_ADAPTER_STATE_CONNECTING, "connecting" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_NETWORK_ADAPTER_STATE_CONNECTED, "connected" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_NETWORK_ADAPTER_STATE_LIMITED, "limited" ) )
-
-//
-// FoobarNetworkAdapter:
-//
-// Base class for Wi-Fi/ethernet network adapters.
-//
-
-typedef struct _FoobarNetworkAdapterPrivate
-{
- NMDevice* device;
- NMActiveConnection* connection;
- FoobarNetworkAdapterState state;
- gulong connectivity_handler_id;
- gulong connection_handler_id;
- gulong connection_state_handler_id;
-} FoobarNetworkAdapterPrivate;
-
-enum
-{
- ADAPTER_PROP_STATE = 1,
- N_ADAPTER_PROPS,
-};
-
-static GParamSpec* adapter_props[N_ADAPTER_PROPS] = { 0 };
-
-static void foobar_network_adapter_class_init ( FoobarNetworkAdapterClass* klass );
-static void foobar_network_adapter_init ( FoobarNetworkAdapter* self );
-static void foobar_network_adapter_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_network_adapter_finalize ( GObject* object );
-static void foobar_network_adapter_set_device ( FoobarNetworkAdapter* self,
- NMDevice* value );
-static void foobar_network_adapter_update_state ( FoobarNetworkAdapter* self );
-static void foobar_network_adapter_handle_state_change ( GObject* sender,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_network_adapter_handle_connection_change( GObject* sender,
- GParamSpec* pspec,
- gpointer userdata );
-
-G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE( FoobarNetworkAdapter, foobar_network_adapter, G_TYPE_OBJECT )
-
-//
-// FoobarNetworkAdapterWired:
-//
-// Represents an ethernet network adapter.
-//
-
-struct _FoobarNetworkAdapterWired
-{
- FoobarNetworkAdapter parent_instance;
- NMDeviceEthernet* device;
- gulong speed_handler_id;
-};
-
-static void foobar_network_adapter_wired_class_init ( FoobarNetworkAdapterWiredClass* klass );
-static void foobar_network_adapter_wired_init ( FoobarNetworkAdapterWired* self );
-static void foobar_network_adapter_wired_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_network_adapter_wired_finalize ( GObject* object );
-static FoobarNetworkAdapterWired* foobar_network_adapter_wired_new ( NMDeviceEthernet* device );
-static void foobar_network_adapter_wired_handle_speed_change( GObject* device,
- GParamSpec* pspec,
- gpointer userdata );
-
-enum
-{
- WIRED_PROP_SPEED = 1,
- N_WIRED_PROPS,
-};
-
-static GParamSpec* wired_props[N_WIRED_PROPS] = { 0 };
-
-G_DEFINE_FINAL_TYPE( FoobarNetworkAdapterWired, foobar_network_adapter_wired, FOOBAR_TYPE_NETWORK_ADAPTER )
-
-//
-// FoobarNetworkAdapterWifi:
-//
-// Represents a Wi-Fi network adapter.
-//
-
-struct _FoobarNetworkAdapterWifi
-{
- FoobarNetworkAdapter parent_instance;
- GHashTable* ap_networks;
- GHashTable* named_networks;
- GListStore* networks;
- GtkFilterListModel* filtered_networks;
- GtkSortListModel* sorted_networks;
- FoobarNetwork* active;
- gboolean is_scanning;
- NMClient* client;
- NMDeviceWifi* device;
- gulong enabled_handler_id;
- gulong active_handler_id;
- gulong added_handler_id;
- gulong removed_handler_id;
- guint refresh_source_id;
-};
-
-enum
-{
- WIFI_PROP_NETWORKS = 1,
- WIFI_PROP_ACTIVE,
- WIFI_PROP_IS_ENABLED,
- WIFI_PROP_IS_SCANNING,
- N_WIFI_PROPS,
-};
-
-static GParamSpec* wifi_props[N_WIFI_PROPS] = { 0 };
-
-static void foobar_network_adapter_wifi_class_init ( FoobarNetworkAdapterWifiClass* klass );
-static void foobar_network_adapter_wifi_init ( FoobarNetworkAdapterWifi* self );
-static void foobar_network_adapter_wifi_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_network_adapter_wifi_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_network_adapter_wifi_finalize ( GObject* object );
-static FoobarNetworkAdapterWifi* foobar_network_adapter_wifi_new ( NMClient* client,
- NMDeviceWifi* device );
-static void foobar_network_adapter_wifi_add_ap_to_network ( FoobarNetworkAdapterWifi* self,
- NMAccessPoint* ap );
-static void foobar_network_adapter_wifi_update_active ( FoobarNetworkAdapterWifi* self );
-static void foobar_network_adapter_wifi_handle_ssid_change ( GObject* ap,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_network_adapter_wifi_handle_enabled_change( GObject* client,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_network_adapter_wifi_handle_active_change ( GObject* device,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_network_adapter_wifi_handle_added ( NMDeviceWifi* device,
- NMAccessPoint* ap,
- gpointer userdata );
-static void foobar_network_adapter_wifi_handle_removed ( NMDeviceWifi* device,
- NMAccessPoint* ap,
- gpointer userdata );
-static gboolean foobar_network_adapter_wifi_refresh_list ( gpointer userdata );
-static gboolean foobar_network_adapter_wifi_filter_func ( gpointer item,
- gpointer userdata );
-static gint foobar_network_adapter_wifi_sort_func ( gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarNetworkAdapterWifi, foobar_network_adapter_wifi, FOOBAR_TYPE_NETWORK_ADAPTER )
-
-//
-// ApNetworkMembership:
-//
-// Structure representing an AP's membership in a network. The AP is removed when the structure is destroyed.
-//
-
-struct _ApNetworkMembership
-{
- NMAccessPoint* ap;
- FoobarNetwork* network;
-};
-
-static ApNetworkMembership* ap_network_membership_new ( NMAccessPoint* ap,
- FoobarNetwork* network );
-static void ap_network_membership_destroy( ApNetworkMembership* self );
-
-//
-// FoobarNetworkService:
-//
-// Service monitoring the network state of the computer. This is implemented using the NetworkManager client library.
-//
-
-struct _FoobarNetworkService
-{
- GObject parent_instance;
- NMClient* client;
- FoobarNetworkAdapterWired* wired;
- FoobarNetworkAdapterWifi* wifi;
- FoobarNetworkAdapter* active;
- gulong primary_handler_id;
- gulong activating_handler_id;
-};
-
-enum
-{
- PROP_WIRED = 1,
- PROP_WIFI,
- PROP_ACTIVE,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_network_service_class_init ( FoobarNetworkServiceClass* klass );
-static void foobar_network_service_init ( FoobarNetworkService* self );
-static void foobar_network_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_network_service_finalize ( GObject* object );
-static void foobar_network_service_handle_connection_change( GObject* client,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_network_service_update_active ( FoobarNetworkService* self );
-
-G_DEFINE_FINAL_TYPE( FoobarNetworkService, foobar_network_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Wi-Fi Network
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for networks.
-//
-void foobar_network_class_init( FoobarNetworkClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_network_get_property;
- object_klass->finalize = foobar_network_finalize;
-
- network_props[NETWORK_PROP_NAME] = g_param_spec_string(
- "name",
- "Name",
- "The advertised SSID of the network.",
- NULL,
- G_PARAM_READABLE );
- network_props[NETWORK_PROP_STRENGTH] = g_param_spec_int(
- "strength",
- "Strength",
- "The strength of the network in percent.",
- 0,
- 100,
- 0,
- G_PARAM_READABLE );
- network_props[NETWORK_PROP_IS_ACTIVE] = g_param_spec_boolean(
- "is-active",
- "Is Active",
- "Indicates whether the network is currently used by the client.",
- FALSE,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_NETWORK_PROPS, network_props );
-}
-
-//
-// Instance initialization for networks.
-//
-void foobar_network_init( FoobarNetwork* self )
-{
- self->aps = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, NULL );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_network_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarNetwork* self = (FoobarNetwork*)object;
-
- switch ( prop_id )
- {
- case NETWORK_PROP_NAME:
- g_value_set_string( value, foobar_network_get_name( self ) );
- break;
- case NETWORK_PROP_STRENGTH:
- g_value_set_int( value, foobar_network_get_strength( self ) );
- break;
- case NETWORK_PROP_IS_ACTIVE:
- g_value_set_boolean( value, foobar_network_is_active( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for networks.
-//
-void foobar_network_finalize( GObject* object )
-{
- FoobarNetwork* self = (FoobarNetwork*)object;
-
- GHashTableIter iter;
- gpointer ap;
- g_hash_table_iter_init( &iter, self->aps );
- while ( g_hash_table_iter_next( &iter, &ap, NULL ) )
- {
- g_signal_handlers_disconnect_by_data( ap, self );
- }
-
- g_clear_pointer( &self->ssid, g_bytes_unref );
- g_clear_pointer( &self->name, g_free );
- g_clear_pointer( &self->aps, g_hash_table_unref );
-
- G_OBJECT_CLASS( foobar_network_parent_class )->finalize( object );
-}
-
-//
-// Create a new network instance.
-//
-FoobarNetwork* foobar_network_new( void )
-{
- return g_object_new( FOOBAR_TYPE_NETWORK, NULL );
-}
-
-//
-// Get the advertized SSID of the network.
-//
-gchar const* foobar_network_get_name( FoobarNetwork* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK( self ), NULL );
- return self->name;
-}
-
-//
-// Get the current strength percentage value for the network.
-//
-gint foobar_network_get_strength( FoobarNetwork* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK( self ), 0 );
- return self->strength;
-}
-
-//
-// Check whether the network is currently used by the client.
-//
-gboolean foobar_network_is_active( FoobarNetwork* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK( self ), FALSE );
- return self->is_active;
-}
-
-//
-// Update the advertized SSID of the network.
-//
-// This also updates the string representation (which may be a lossy conversion).
-//
-void foobar_network_set_ssid(
- FoobarNetwork* self,
- GBytes* value )
-{
- g_return_if_fail( FOOBAR_IS_NETWORK( self ) );
-
- if ( self->ssid != value )
- {
- g_clear_pointer( &self->ssid, g_bytes_unref );
- g_clear_pointer( &self->name, g_free );
-
- if ( value )
- {
- self->ssid = g_bytes_ref( value );
- gsize ssid_len;
- guint8 const* ssid_data = g_bytes_get_data( self->ssid, &ssid_len );
- self->name = nm_utils_ssid_to_utf8( ssid_data, ssid_len );
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), network_props[NETWORK_PROP_NAME] );
- }
-}
-
-//
-// Update the "active" flag of the network. This does not actually activate the network.
-//
-void foobar_network_set_active(
- FoobarNetwork* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_NETWORK( self ) );
-
- value = !!value;
- if ( self->is_active != value )
- {
- self->is_active = value;
- g_object_notify_by_pspec( G_OBJECT( self ), network_props[NETWORK_PROP_NAME] );
- }
-}
-
-//
-// Include the specified AP when computing the network's strength.
-//
-void foobar_network_add_ap(
- FoobarNetwork* self,
- NMAccessPoint* ap )
-{
- if ( g_hash_table_add( self->aps, g_object_ref( ap ) ) )
- {
- g_signal_connect( ap, "notify::strength", G_CALLBACK( foobar_network_handle_strength_change ), self );
- foobar_network_update_strength( self );
- }
-}
-
-//
-// Stop including the specified AP when computing the network's strength.
-//
-void foobar_network_remove_ap(
- FoobarNetwork* self,
- NMAccessPoint* ap )
-{
- if ( g_hash_table_remove( self->aps, ap ) )
- {
- g_signal_handlers_disconnect_by_data( ap, self );
- foobar_network_update_strength( self );
- }
-}
-
-//
-// Update the network's "strength" property to match that of the best AP for it.
-//
-void foobar_network_update_strength( FoobarNetwork* self )
-{
- gint strength = 0;
- GHashTableIter iter;
- gpointer ap;
- g_hash_table_iter_init( &iter, self->aps );
- while ( g_hash_table_iter_next( &iter, &ap, NULL ) )
- {
- gint ap_strength = nm_access_point_get_strength( ap );
- strength = MAX( strength, ap_strength );
- }
-
- strength = CLAMP( strength, 0, 100 );
- if ( self->strength != strength )
- {
- self->strength = strength;
- g_object_notify_by_pspec( G_OBJECT( self ), network_props[NETWORK_PROP_STRENGTH] );
- }
-}
-
-//
-// Called when the strength of an access point for this network has changed.
-//
-void foobar_network_handle_strength_change(
- GObject* ap,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)ap;
- (void)pspec;
- FoobarNetwork* self = (FoobarNetwork*)userdata;
-
- foobar_network_update_strength( self );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Network Adapter
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for network adapters.
-//
-void foobar_network_adapter_class_init( FoobarNetworkAdapterClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_network_adapter_get_property;
- object_klass->finalize = foobar_network_adapter_finalize;
-
- adapter_props[ADAPTER_PROP_STATE] = g_param_spec_enum(
- "state",
- "State",
- "The current connection state of the network.",
- FOOBAR_TYPE_NETWORK_ADAPTER_STATE,
- FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_ADAPTER_PROPS, adapter_props );
-}
-
-//
-// Instance initialization for network adapters.
-//
-void foobar_network_adapter_init( FoobarNetworkAdapter* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_network_adapter_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarNetworkAdapter* self = (FoobarNetworkAdapter*)object;
-
- switch ( prop_id )
- {
- case ADAPTER_PROP_STATE:
- g_value_set_enum( value, foobar_network_adapter_get_state( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for network adapters.
-//
-void foobar_network_adapter_finalize( GObject* object )
-{
- FoobarNetworkAdapter* self = (FoobarNetworkAdapter*)object;
-
- foobar_network_adapter_set_device( self, NULL );
-
- G_OBJECT_CLASS( foobar_network_adapter_parent_class )->finalize( object );
-}
-
-//
-// Get the current connection/connectivity state of the adapter.
-//
-FoobarNetworkAdapterState foobar_network_adapter_get_state( FoobarNetworkAdapter* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER( self ), FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED );
-
- FoobarNetworkAdapterPrivate* priv = foobar_network_adapter_get_instance_private( self );
- return priv->state;
-}
-
-//
-// Update the backing network device which is used to derive the adapter's state.
-//
-void foobar_network_adapter_set_device(
- FoobarNetworkAdapter* self,
- NMDevice* value )
-{
- FoobarNetworkAdapterPrivate* priv = foobar_network_adapter_get_instance_private( self );
- if ( priv->device != value )
- {
- g_clear_signal_handler( &priv->connection_state_handler_id, priv->connection );
- g_clear_signal_handler( &priv->connectivity_handler_id, priv->device );
- g_clear_signal_handler( &priv->connection_handler_id, priv->device );
- g_clear_object( &priv->connection );
- g_clear_object( &priv->device );
-
- priv->device = value;
- if ( priv->device )
- {
- g_object_ref( priv->device );
- priv->connectivity_handler_id = g_signal_connect(
- priv->device,
- "notify::ip4-connectivity",
- G_CALLBACK( foobar_network_adapter_handle_state_change ), self );
- priv->connection_handler_id = g_signal_connect(
- priv->device,
- "notify::active-connection",
- G_CALLBACK( foobar_network_adapter_handle_connection_change ), self );
- priv->connection = nm_device_get_active_connection( priv->device );
- if ( priv->connection )
- {
- g_object_ref( priv->connection );
- priv->connection_state_handler_id = g_signal_connect(
- priv->connection,
- "notify::state",
- G_CALLBACK( foobar_network_adapter_handle_state_change ),
- self );
- }
- }
-
- foobar_network_adapter_update_state( self );
- }
-}
-
-//
-// Derive the connection/connectivity state for the network from the backing device.
-//
-void foobar_network_adapter_update_state( FoobarNetworkAdapter* self )
-{
- FoobarNetworkAdapterPrivate* priv = foobar_network_adapter_get_instance_private( self );
-
- FoobarNetworkAdapterState state = FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED;
- if ( priv->device )
- {
- NMActiveConnection* connection = nm_device_get_active_connection( priv->device );
- if ( connection )
- {
- switch ( nm_active_connection_get_state( connection ) )
- {
- case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
- state = ( nm_device_get_connectivity( priv->device, AF_INET ) == NM_CONNECTIVITY_FULL )
- ? FOOBAR_NETWORK_ADAPTER_STATE_CONNECTED
- : FOOBAR_NETWORK_ADAPTER_STATE_LIMITED;
- break;
- case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
- state = FOOBAR_NETWORK_ADAPTER_STATE_CONNECTING;
- break;
- case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
- case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
- case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
- default:
- state = FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED;
- break;
- }
- }
- }
-
- if ( priv->state != state )
- {
- priv->state = state;
- g_object_notify_by_pspec( G_OBJECT( self ), adapter_props[ADAPTER_PROP_STATE] );
- }
-}
-
-//
-// Called when the "ip4-connectivity" property of the NetworkManager device or the "state" property of the active
-// connection has changed.
-//
-static void foobar_network_adapter_handle_state_change(
- GObject* sender,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)sender;
- (void)pspec;
- FoobarNetworkAdapter* self = (FoobarNetworkAdapter*)userdata;
-
- foobar_network_adapter_update_state( self );
-}
-
-//
-// Called when the "active-connection" property of the NetworkManager device has changed.
-//
-// This updates the state of the adapter and sets the connection up for state change listening.
-//
-static void foobar_network_adapter_handle_connection_change(
- GObject* sender,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)sender;
- (void)pspec;
- FoobarNetworkAdapter* self = (FoobarNetworkAdapter*)userdata;
- FoobarNetworkAdapterPrivate* priv = foobar_network_adapter_get_instance_private( self );
-
- g_clear_signal_handler( &priv->connection_state_handler_id, priv->connection );
- g_clear_object( &priv->connection );
-
- priv->connection = nm_device_get_active_connection( priv->device );
- if ( priv->connection )
- {
- g_object_ref( priv->connection );
- priv->connection_state_handler_id = g_signal_connect(
- priv->connection,
- "notify::state",
- G_CALLBACK( foobar_network_adapter_handle_state_change ),
- self );
- }
-
- foobar_network_adapter_update_state( self );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Wired Network Adapter
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for wired network adapters.
-//
-void foobar_network_adapter_wired_class_init( FoobarNetworkAdapterWiredClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_network_adapter_wired_get_property;
- object_klass->finalize = foobar_network_adapter_wired_finalize;
-
- wired_props[WIRED_PROP_SPEED] = g_param_spec_int64(
- "speed",
- "Speed",
- "The supported speed of the adapter in Mbit/s.",
- 0,
- INT64_MAX,
- 0,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_WIRED_PROPS, wired_props );
-}
-
-//
-// Instance initialization for wired network adapters.
-//
-void foobar_network_adapter_wired_init( FoobarNetworkAdapterWired* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_network_adapter_wired_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarNetworkAdapterWired* self = (FoobarNetworkAdapterWired*)object;
-
- switch ( prop_id )
- {
- case WIRED_PROP_SPEED:
- g_value_set_int64( value, foobar_network_adapter_wired_get_speed( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for wired network adapters.
-//
-void foobar_network_adapter_wired_finalize( GObject* object )
-{
- FoobarNetworkAdapterWired* self = (FoobarNetworkAdapterWired*)object;
-
- g_clear_signal_handler( &self->speed_handler_id, self->device );
- g_clear_object( &self->device );
-
- G_OBJECT_CLASS( foobar_network_adapter_wired_parent_class )->finalize( object );
-}
-
-//
-// Create a new wired network adapter wrapping the provided NetworkManager device.
-//
-FoobarNetworkAdapterWired* foobar_network_adapter_wired_new( NMDeviceEthernet* device )
-{
- FoobarNetworkAdapterWired* self = g_object_new( FOOBAR_TYPE_NETWORK_ADAPTER_WIRED, NULL );
- foobar_network_adapter_set_device( FOOBAR_NETWORK_ADAPTER( self ), NM_DEVICE( device ) );
-
- self->device = g_object_ref( device );
- self->speed_handler_id = g_signal_connect(
- self->device,
- "notify::speed",
- G_CALLBACK( foobar_network_adapter_wired_handle_speed_change ),
- self );
- return self;
-}
-
-//
-// Get the supported speed of the adapter in Mbit/s.
-//
-gint64 foobar_network_adapter_wired_get_speed( FoobarNetworkAdapterWired* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIRED( self ), 0 );
- return nm_device_ethernet_get_speed( self->device );
-}
-
-//
-// Called when the "speed" property of the NetworkManager device changes.
-//
-void foobar_network_adapter_wired_handle_speed_change(
- GObject* device,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)device;
- (void)pspec;
- FoobarNetworkAdapterWired* self = (FoobarNetworkAdapterWired*)userdata;
-
- g_object_notify_by_pspec( G_OBJECT( self ), wired_props[WIRED_PROP_SPEED] );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Wireless Network Adapter
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for wireless network adapters.
-//
-void foobar_network_adapter_wifi_class_init( FoobarNetworkAdapterWifiClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_network_adapter_wifi_get_property;
- object_klass->set_property = foobar_network_adapter_wifi_set_property;
- object_klass->finalize = foobar_network_adapter_wifi_finalize;
-
- wifi_props[WIFI_PROP_NETWORKS] = g_param_spec_object(
- "networks",
- "Networks",
- "Sorted list of available networks.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- wifi_props[WIFI_PROP_ACTIVE] = g_param_spec_object(
- "active",
- "Active",
- "The currently active network (if any).",
- FOOBAR_TYPE_NETWORK,
- G_PARAM_READABLE );
- wifi_props[WIFI_PROP_IS_ENABLED] = g_param_spec_boolean(
- "is-enabled",
- "Is Enabled",
- "Indicates whether the wifi adapter is currently enabled.",
- FALSE,
- G_PARAM_READWRITE );
- wifi_props[WIFI_PROP_IS_SCANNING] = g_param_spec_boolean(
- "is-scanning",
- "Is Scanning",
- "Indicates whether the wifi adapter is currently scanning for available networks.",
- FALSE,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_WIFI_PROPS, wifi_props );
-}
-
-//
-// Instance initialization for wireless network adapters.
-//
-void foobar_network_adapter_wifi_init( FoobarNetworkAdapterWifi* self )
-{
- self->ap_networks = g_hash_table_new_full(
- g_direct_hash,
- g_direct_equal,
- g_object_unref,
- (GDestroyNotify)ap_network_membership_destroy );
- self->named_networks = g_hash_table_new_full(
- g_bytes_hash,
- g_bytes_equal,
- (GDestroyNotify)g_bytes_unref,
- g_object_unref );
- self->networks = g_list_store_new( FOOBAR_TYPE_NETWORK );
-
- GtkCustomFilter* filter = gtk_custom_filter_new( foobar_network_adapter_wifi_filter_func, NULL, NULL );
- self->filtered_networks = gtk_filter_list_model_new(
- G_LIST_MODEL( g_object_ref( self->networks ) ),
- GTK_FILTER( filter ) );
-
- GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_network_adapter_wifi_sort_func, NULL, NULL );
- self->sorted_networks = gtk_sort_list_model_new(
- G_LIST_MODEL( g_object_ref( self->filtered_networks ) ),
- GTK_SORTER( sorter ) );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_network_adapter_wifi_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)object;
-
- switch ( prop_id )
- {
- case WIFI_PROP_NETWORKS:
- g_value_set_object( value, foobar_network_adapter_wifi_get_networks( self ) );
- break;
- case WIFI_PROP_ACTIVE:
- g_value_set_object( value, foobar_network_adapter_wifi_get_active( self ) );
- break;
- case WIFI_PROP_IS_ENABLED:
- g_value_set_boolean( value, foobar_network_adapter_wifi_is_enabled( self ) );
- break;
- case WIFI_PROP_IS_SCANNING:
- g_value_set_boolean( value, foobar_network_adapter_wifi_is_scanning( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Property setter implementation, mapping a property id to a method.
-//
-void foobar_network_adapter_wifi_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)object;
-
- switch ( prop_id )
- {
- case WIFI_PROP_IS_ENABLED:
- foobar_network_adapter_wifi_set_enabled( self, g_value_get_boolean( value ) );
- break;
- case WIFI_PROP_IS_SCANNING:
- foobar_network_adapter_wifi_set_scanning( self, g_value_get_boolean( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for wireless network adapters.
-//
-void foobar_network_adapter_wifi_finalize( GObject* object )
-{
- FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)object;
-
- GPtrArray const* aps = nm_device_wifi_get_access_points( self->device );
- for ( guint i = 0; i < aps->len; ++i )
- {
- NMAccessPoint* ap = g_ptr_array_index( aps, i );
- g_signal_handlers_disconnect_by_data( ap, self );
- }
-
- g_clear_handle_id( &self->refresh_source_id, g_source_remove );
- g_clear_signal_handler( &self->enabled_handler_id, self->client );
- g_clear_signal_handler( &self->active_handler_id, self->device );
- g_clear_signal_handler( &self->added_handler_id, self->device );
- g_clear_signal_handler( &self->removed_handler_id, self->device );
- g_clear_object( &self->client );
- g_clear_object( &self->device );
- g_clear_object( &self->sorted_networks );
- g_clear_object( &self->filtered_networks );
- g_clear_object( &self->networks );
- g_clear_object( &self->active );
- g_clear_pointer( &self->named_networks, g_hash_table_unref );
- g_clear_pointer( &self->ap_networks, g_hash_table_unref );
-
- G_OBJECT_CLASS( foobar_network_adapter_wifi_parent_class )->finalize( object );
-}
-
-//
-// Create a new wireless network adapter wrapping the provided NetworkManager device.
-//
-FoobarNetworkAdapterWifi* foobar_network_adapter_wifi_new(
- NMClient* client,
- NMDeviceWifi* device )
-{
- FoobarNetworkAdapterWifi* self = g_object_new( FOOBAR_TYPE_NETWORK_ADAPTER_WIFI, NULL );
- foobar_network_adapter_set_device( FOOBAR_NETWORK_ADAPTER( self ), NM_DEVICE( device ) );
-
- self->client = g_object_ref( client );
- self->device = g_object_ref( device );
- self->enabled_handler_id = g_signal_connect(
- self->client,
- "notify::wireless-enabled",
- G_CALLBACK( foobar_network_adapter_wifi_handle_enabled_change ),
- self );
- self->active_handler_id = g_signal_connect(
- self->device,
- "notify::active-access-point",
- G_CALLBACK( foobar_network_adapter_wifi_handle_active_change ),
- self );
- self->added_handler_id = g_signal_connect(
- self->device,
- "access-point-added",
- G_CALLBACK( foobar_network_adapter_wifi_handle_added ),
- self );
- self->removed_handler_id = g_signal_connect(
- self->device,
- "access-point-removed",
- G_CALLBACK( foobar_network_adapter_wifi_handle_removed ),
- self );
-
- GPtrArray const* aps = nm_device_wifi_get_access_points( self->device );
- for ( guint i = 0; i < aps->len; ++i )
- {
- NMAccessPoint* ap = g_ptr_array_index( aps, i );
- g_signal_connect(
- ap,
- "notify::ssid",
- G_CALLBACK( foobar_network_adapter_wifi_handle_ssid_change ),
- self );
- foobar_network_adapter_wifi_add_ap_to_network( self, ap );
- }
-
- foobar_network_adapter_wifi_update_active( self );
- return self;
-}
-
-//
-// Get a sorted list of available networks.
-//
-GListModel* foobar_network_adapter_wifi_get_networks( FoobarNetworkAdapterWifi* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ), NULL );
- return G_LIST_MODEL( self->sorted_networks );
-}
-
-//
-// Get the currently active network (if any).
-//
-FoobarNetwork* foobar_network_adapter_wifi_get_active( FoobarNetworkAdapterWifi* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ), NULL );
- return self->active;
-}
-
-//
-// Check whether the wifi adapter is currently enabled.
-//
-gboolean foobar_network_adapter_wifi_is_enabled( FoobarNetworkAdapterWifi* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ), FALSE );
- return nm_client_wireless_get_enabled( self->client );
-}
-
-//
-// Check whether the wifi adapter is currently scanning for available networks.
-//
-gboolean foobar_network_adapter_wifi_is_scanning( FoobarNetworkAdapterWifi* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ), FALSE );
- return self->is_scanning;
-}
-
-//
-// Update whether the wifi adapter is currently enabled.
-//
-void foobar_network_adapter_wifi_set_enabled(
- FoobarNetworkAdapterWifi* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ) );
-
- if ( !value ) { foobar_network_adapter_wifi_set_scanning( self, FALSE ); }
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- nm_client_wireless_set_enabled( self->client, value );
-#pragma GCC diagnostic pop
-}
-
-//
-// Update whether the wifi adapter is currently scanning for available networks.
-//
-void foobar_network_adapter_wifi_set_scanning(
- FoobarNetworkAdapterWifi* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_NETWORK_ADAPTER_WIFI( self ) );
-
- value = value && foobar_network_adapter_wifi_is_enabled( self );
- if ( self->is_scanning != value )
- {
- self->is_scanning = value;
- g_object_notify_by_pspec( G_OBJECT( self ), wifi_props[WIFI_PROP_IS_SCANNING] );
-
- // Periodically start scans until "is-scanning" is disabled again.
-
- g_clear_handle_id( &self->refresh_source_id, g_source_remove );
- if ( self->is_scanning )
- {
- foobar_network_adapter_wifi_refresh_list( self );
- self->refresh_source_id = g_timeout_add(
- SCAN_REFRESH_INTERVAL,
- foobar_network_adapter_wifi_refresh_list,
- self );
- }
- }
-}
-
-//
-// Ensure that a network for the specified access point exists.
-//
-// If the AP has an SSID, it is added to the network for this SSID. Otherwise, a new network for this AP is created just
-// for this network.
-//
-void foobar_network_adapter_wifi_add_ap_to_network(
- FoobarNetworkAdapterWifi* self,
- NMAccessPoint* ap )
-{
- // The AP is automatically added/removed from the network when using g_hash_table_replace because of the
- // ApNetworkMembership destroy function.
-
- GBytes* ssid = nm_access_point_get_ssid( ap );
- if ( ssid )
- {
- FoobarNetwork* network = g_hash_table_lookup( self->named_networks, ssid );
- if ( !network )
- {
- network = foobar_network_new( );
- foobar_network_set_ssid( network, ssid );
- g_list_store_append( self->networks, network );
- g_hash_table_insert( self->named_networks, g_bytes_ref( ssid ), network );
- }
-
- g_hash_table_replace( self->ap_networks, g_object_ref( ap ), ap_network_membership_new( ap, network ) );
- }
- else
- {
- g_autoptr( FoobarNetwork ) network = foobar_network_new( );
- g_hash_table_replace( self->ap_networks, g_object_ref( ap ), ap_network_membership_new( ap, network ) );
- }
-
- gtk_filter_changed( gtk_filter_list_model_get_filter( self->filtered_networks ), GTK_FILTER_CHANGE_DIFFERENT );
-}
-
-//
-// Update the active network based on the active access point.
-//
-void foobar_network_adapter_wifi_update_active( FoobarNetworkAdapterWifi* self )
-{
- NMAccessPoint* active_ap = nm_device_wifi_get_active_access_point( self->device );
- ApNetworkMembership* membership = active_ap ? g_hash_table_lookup( self->ap_networks, active_ap ) : NULL;
- FoobarNetwork* new_active = membership ? membership->network : NULL;
-
- if ( self->active != new_active )
- {
- if ( self->active )
- {
- foobar_network_set_active( self->active, FALSE );
- }
- g_clear_object( &self->active );
-
- self->active = new_active;
- if ( self->active )
- {
- g_object_ref( self->active );
- foobar_network_set_active( self->active, TRUE );
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), wifi_props[WIFI_PROP_ACTIVE] );
- }
-}
-
-//
-// Called when the SSID for an access point changes.
-//
-// This will remove the access point from the old network and associate it with its new network.
-//
-void foobar_network_adapter_wifi_handle_ssid_change(
- GObject* ap,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)pspec;
- FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
-
- foobar_network_adapter_wifi_add_ap_to_network( self, NM_ACCESS_POINT( ap ) );
- foobar_network_adapter_wifi_update_active( self );
-}
-
-//
-// Called when the "wireless-enabled" state of the client changes.
-//
-void foobar_network_adapter_wifi_handle_enabled_change(
- GObject* client,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)client;
- (void)pspec;
- FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
-
- g_object_notify_by_pspec( G_OBJECT( self ), wifi_props[WIFI_PROP_IS_ENABLED] );
-}
-
-//
-// Called when the active access point of the backing device changes.
-//
-void foobar_network_adapter_wifi_handle_active_change(
- GObject* device,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)device;
- (void)pspec;
- FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
-
- foobar_network_adapter_wifi_update_active( self );
-}
-
-//
-// Called when a new access point is added.
-//
-// This will associate the access point with its network.
-//
-void foobar_network_adapter_wifi_handle_added(
- NMDeviceWifi* device,
- NMAccessPoint* ap,
- gpointer userdata )
-{
- (void)device;
- FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
-
- g_signal_connect(
- ap,
- "notify::ssid",
- G_CALLBACK( foobar_network_adapter_wifi_handle_ssid_change ),
- self );
- foobar_network_adapter_wifi_add_ap_to_network( self, ap );
-}
-
-//
-// Called when an access point is removed.
-//
-// This will remove it from its network.
-//
-void foobar_network_adapter_wifi_handle_removed(
- NMDeviceWifi* device,
- NMAccessPoint* ap,
- gpointer userdata )
-{
- (void)device;
- FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
-
- g_signal_handlers_disconnect_by_data( ap, self );
- g_hash_table_remove( self->ap_networks, ap ); // Will remove the AP from the network.
- gtk_filter_changed( gtk_filter_list_model_get_filter( self->filtered_networks ), GTK_FILTER_CHANGE_MORE_STRICT );
-}
-
-//
-// Periodic callback to start a network scan.
-//
-gboolean foobar_network_adapter_wifi_refresh_list( gpointer userdata )
-{
- FoobarNetworkAdapterWifi* self = (FoobarNetworkAdapterWifi*)userdata;
-
- g_autoptr( GError ) error = NULL;
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- if ( !nm_device_wifi_request_scan( self->device, NULL, &error ) )
-#pragma GCC diagnostic pop
- {
- g_warning( "Unable to start Wi-Fi network scan: %s", error->message );
- }
-
- return G_SOURCE_CONTINUE;
-}
-
-//
-// Filtering callback for the list of networks. Only networks with at least one AP are shown.
-//
-gboolean foobar_network_adapter_wifi_filter_func(
- gpointer item,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarNetwork* network = (FoobarNetwork*)item;
- return g_hash_table_size( network->aps ) > 0;
-}
-
-//
-// Sorting callback for the list of networks.
-//
-gint foobar_network_adapter_wifi_sort_func(
- gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarNetwork* network_a = (FoobarNetwork*)item_a;
- FoobarNetwork* network_b = (FoobarNetwork*)item_b;
- return g_strcmp0( foobar_network_get_name( network_a ), foobar_network_get_name( network_b ) );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Access Point Network Memberships
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create an association between an access point and the network it belongs to.
-//
-ApNetworkMembership* ap_network_membership_new(
- NMAccessPoint* ap,
- FoobarNetwork* network )
-{
- ApNetworkMembership* self = g_new0( ApNetworkMembership, 1 );
- self->ap = ap;
- self->network = g_object_ref( network );
- foobar_network_add_ap( self->network, self->ap );
- return self;
-}
-
-//
-// Destroy an association between an access point and its previous network.
-//
-void ap_network_membership_destroy( ApNetworkMembership* self )
-{
- if ( self )
- {
- foobar_network_remove_ap( self->network, self->ap );
- g_object_unref( self->network );
- g_free( self );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the network service.
-//
-void foobar_network_service_class_init( FoobarNetworkServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_network_service_get_property;
- object_klass->finalize = foobar_network_service_finalize;
-
- props[PROP_WIRED] = g_param_spec_object(
- "wired",
- "Wired",
- "The wired network adapter (if available).",
- FOOBAR_TYPE_NETWORK_ADAPTER_WIRED,
- G_PARAM_READABLE );
- props[PROP_WIFI] = g_param_spec_object(
- "wifi",
- "WiFi",
- "The wireless network adapter (if available).",
- FOOBAR_TYPE_NETWORK_ADAPTER_WIFI,
- G_PARAM_READABLE );
- props[PROP_ACTIVE] = g_param_spec_object(
- "active",
- "Active",
- "The network adapter currently in use.",
- FOOBAR_TYPE_NETWORK_ADAPTER,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the network service.
-//
-void foobar_network_service_init( FoobarNetworkService* self )
-{
- g_autoptr( GError ) error = NULL;
- self->client = nm_client_new( NULL, &error );
- if ( !self->client )
- {
- g_warning( "Unable to connect to NetworkManager: %s", error->message );
- return;
- }
-
- NMDeviceEthernet* device_wired = NULL;
- NMDeviceWifi* device_wifi = NULL;
- GPtrArray const* devices = nm_client_get_devices( self->client );
- for ( guint i = 0; i < devices->len; ++i )
- {
- // XXX: Add support for multiple devices
- NMDevice* device = g_ptr_array_index( devices, i );
- if ( !device_wired && NM_IS_DEVICE_ETHERNET( device ) )
- {
- device_wired = NM_DEVICE_ETHERNET( device );
- }
- if ( !device_wifi && NM_IS_DEVICE_WIFI( device ) )
- {
- device_wifi = NM_DEVICE_WIFI( device );
- }
- if ( device_wired && device_wifi )
- {
- break;
- }
- }
-
- self->wired = device_wired ? foobar_network_adapter_wired_new( device_wired ) : NULL;
- self->wifi = device_wifi ? foobar_network_adapter_wifi_new( self->client, device_wifi ) : NULL;
-
- foobar_network_service_update_active( self );
- self->primary_handler_id = g_signal_connect(
- self->client,
- "notify::primary-connection",
- G_CALLBACK( foobar_network_service_handle_connection_change ),
- self );
- self->activating_handler_id = g_signal_connect(
- self->client,
- "notify::activating-connection",
- G_CALLBACK( foobar_network_service_handle_connection_change ),
- self );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_network_service_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarNetworkService* self = (FoobarNetworkService*)object;
-
- switch ( prop_id )
- {
- case PROP_WIRED:
- g_value_set_object( value, foobar_network_service_get_wired( self ) );
- break;
- case PROP_WIFI:
- g_value_set_object( value, foobar_network_service_get_wifi( self ) );
- break;
- case PROP_ACTIVE:
- g_value_set_object( value, foobar_network_service_get_active( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the network service.
-//
-void foobar_network_service_finalize( GObject* object )
-{
- FoobarNetworkService* self = (FoobarNetworkService*)object;
-
- g_clear_signal_handler( &self->primary_handler_id, self->client );
- g_clear_signal_handler( &self->activating_handler_id, self->client );
- g_clear_object( &self->wifi );
- g_clear_object( &self->wired );
- g_clear_object( &self->client );
-
- G_OBJECT_CLASS( foobar_network_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new network service instance.
-//
-FoobarNetworkService* foobar_network_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_NETWORK_SERVICE, NULL );
-}
-
-//
-// Get the wired network adapter (if available).
-//
-FoobarNetworkAdapterWired* foobar_network_service_get_wired( FoobarNetworkService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( self ), NULL );
- return self->wired;
-}
-
-//
-// Get the wireless network adapter (if available).
-//
-FoobarNetworkAdapterWifi* foobar_network_service_get_wifi( FoobarNetworkService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( self ), NULL );
- return self->wifi;
-}
-
-//
-// Get the network adapter currently in use.
-//
-FoobarNetworkAdapter* foobar_network_service_get_active( FoobarNetworkService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( self ), NULL );
- return self->active;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when either the primary connection or the activating connection of the client has changed.
-//
-void foobar_network_service_handle_connection_change(
- GObject* client,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)client;
- (void)pspec;
- FoobarNetworkService* self = (FoobarNetworkService*)userdata;
-
- foobar_network_service_update_active( self );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Update the active network adapter.
-//
-// This uses the primary connection of the client to determine whether the wireless or wired adapter is used. Otherwise,
-// the activating connection is used instead.
-//
-void foobar_network_service_update_active( FoobarNetworkService* self )
-{
- NMActiveConnection* connection = nm_client_get_primary_connection( self->client );
- connection = connection ? connection : nm_client_get_activating_connection( self->client );
- gchar const* type = connection ? nm_active_connection_get_connection_type( connection ) : NULL;
-
- FoobarNetworkAdapter* new_active;
- if ( !g_strcmp0( type, "802-11-wireless" ) )
- {
- new_active = FOOBAR_NETWORK_ADAPTER( self->wifi );
- }
- else if ( !g_strcmp0( type, "802-3-ethernet" ) )
- {
- new_active = FOOBAR_NETWORK_ADAPTER( self->wired );
- }
- else
- {
- new_active = self->wifi ? FOOBAR_NETWORK_ADAPTER( self->wifi ) : FOOBAR_NETWORK_ADAPTER( self->wired );
- }
-
- if ( self->active != new_active )
- {
- self->active = new_active;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_ACTIVE] );
- }
-}
diff --git a/src/services/network-service.h b/src/services/network-service.h
deleted file mode 100644
index 2307cec..0000000
--- a/src/services/network-service.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_NETWORK foobar_network_get_type( )
-#define FOOBAR_TYPE_NETWORK_ADAPTER_STATE foobar_network_adapter_state_get_type( )
-#define FOOBAR_TYPE_NETWORK_ADAPTER foobar_network_adapter_get_type( )
-#define FOOBAR_TYPE_NETWORK_ADAPTER_WIRED foobar_network_adapter_wired_get_type( )
-#define FOOBAR_TYPE_NETWORK_ADAPTER_WIFI foobar_network_adapter_wifi_get_type( )
-#define FOOBAR_TYPE_NETWORK_SERVICE foobar_network_service_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarNetwork, foobar_network, FOOBAR, NETWORK, GObject )
-
-gchar const* foobar_network_get_name ( FoobarNetwork* self );
-gint foobar_network_get_strength( FoobarNetwork* self );
-gboolean foobar_network_is_active ( FoobarNetwork* self );
-
-typedef enum
-{
- FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED,
- FOOBAR_NETWORK_ADAPTER_STATE_CONNECTING,
- FOOBAR_NETWORK_ADAPTER_STATE_CONNECTED,
- FOOBAR_NETWORK_ADAPTER_STATE_LIMITED,
-} FoobarNetworkAdapterState;
-
-GType foobar_network_adapter_state_get_type( void );
-
-G_DECLARE_DERIVABLE_TYPE( FoobarNetworkAdapter, foobar_network_adapter, FOOBAR, NETWORK_ADAPTER, GObject )
-
-struct _FoobarNetworkAdapterClass
-{
- GObjectClass parent_class;
-};
-
-FoobarNetworkAdapterState foobar_network_adapter_get_state( FoobarNetworkAdapter* self );
-
-G_DECLARE_FINAL_TYPE( FoobarNetworkAdapterWired, foobar_network_adapter_wired, FOOBAR, NETWORK_ADAPTER_WIRED, FoobarNetworkAdapter )
-
-gint64 foobar_network_adapter_wired_get_speed( FoobarNetworkAdapterWired* self );
-
-G_DECLARE_FINAL_TYPE( FoobarNetworkAdapterWifi, foobar_network_adapter_wifi, FOOBAR, NETWORK_ADAPTER_WIFI, FoobarNetworkAdapter )
-
-GListModel* foobar_network_adapter_wifi_get_networks( FoobarNetworkAdapterWifi* self );
-FoobarNetwork* foobar_network_adapter_wifi_get_active ( FoobarNetworkAdapterWifi* self );
-gboolean foobar_network_adapter_wifi_is_enabled ( FoobarNetworkAdapterWifi* self );
-gboolean foobar_network_adapter_wifi_is_scanning ( FoobarNetworkAdapterWifi* self );
-void foobar_network_adapter_wifi_set_enabled ( FoobarNetworkAdapterWifi* self,
- gboolean value );
-void foobar_network_adapter_wifi_set_scanning( FoobarNetworkAdapterWifi* self,
- gboolean value );
-
-G_DECLARE_FINAL_TYPE( FoobarNetworkService, foobar_network_service, FOOBAR, NETWORK_SERVICE, GObject )
-
-FoobarNetworkService* foobar_network_service_new ( void );
-FoobarNetworkAdapterWired* foobar_network_service_get_wired ( FoobarNetworkService* self );
-FoobarNetworkAdapterWifi* foobar_network_service_get_wifi ( FoobarNetworkService* self );
-FoobarNetworkAdapter* foobar_network_service_get_active( FoobarNetworkService* self );
-
-G_END_DECLS
diff --git a/src/services/notification-service.c b/src/services/notification-service.c
deleted file mode 100644
index 1a085f0..0000000
--- a/src/services/notification-service.c
+++ /dev/null
@@ -1,1833 +0,0 @@
-#include "services/notification-service.h"
-#include "dbus/notifications.h"
-#include "utils.h"
-#include <json-glib/json-glib.h>
-#include <gtk/gtk.h>
-
-#define DEFAULT_TIMEOUT 3000
-
-//
-// FoobarNotificationUrgency:
-//
-// The urgency of a notification as provided by the application that sent it.
-//
-
-G_DEFINE_ENUM_TYPE(
- FoobarNotificationUrgency,
- foobar_notification_urgency,
- G_DEFINE_ENUM_VALUE( FOOBAR_NOTIFICATION_URGENCY_LOSS_OF_COMFORT, "loss-of-comfort" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_NOTIFICATION_URGENCY_LOSS_OF_MONEY, "loss-of-money" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_NOTIFICATION_URGENCY_LOSS_OF_LIFE, "loss-of-life" ) )
-
-//
-// FoobarNotificationAction:
-//
-// A labeled action for a notification that can be invoked by the notification daemon (us).
-//
-
-struct _FoobarNotificationAction
-{
- GObject parent_instance;
- FoobarNotification* notification;
- gchar* id;
- gchar* label;
-};
-
-enum
-{
- ACTION_PROP_ID = 1,
- ACTION_PROP_LABEL,
- N_ACTION_PROPS,
-};
-
-static GParamSpec* action_props[N_ACTION_PROPS] = { 0 };
-
-static void foobar_notification_action_class_init ( FoobarNotificationActionClass* klass );
-static void foobar_notification_action_init ( FoobarNotificationAction* self );
-static void foobar_notification_action_get_property( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_notification_action_finalize ( GObject* object );
-static FoobarNotificationAction* foobar_notification_action_new ( FoobarNotification* notification );
-static void foobar_notification_action_set_id ( FoobarNotificationAction* self,
- gchar const* value );
-static void foobar_notification_action_set_label ( FoobarNotificationAction* self,
- gchar const* value );
-
-G_DEFINE_FINAL_TYPE( FoobarNotificationAction, foobar_notification_action, G_TYPE_OBJECT )
-
-//
-// FoobarNotification:
-//
-// A notification received by the notification daemon (us).
-//
-
-struct _FoobarNotification
-{
- GObject parent_instance;
- guint timeout_id;
- FoobarNotificationService* service;
- guint id;
- GPtrArray* actions;
- gchar* app_entry;
- gchar* app_name;
- gchar* body;
- gchar* summary;
- GdkPixbuf* image;
- gchar* image_path;
- gchar* image_data;
- gboolean is_dismissed;
- gboolean is_resident;
- gboolean is_transient;
- GDateTime* time;
- gint64 timeout;
- FoobarNotificationUrgency urgency;
-};
-
-enum
-{
- NOTIFICATION_PROP_ID = 1,
- NOTIFICATION_PROP_APP_ENTRY,
- NOTIFICATION_PROP_APP_NAME,
- NOTIFICATION_PROP_BODY,
- NOTIFICATION_PROP_SUMMARY,
- NOTIFICATION_PROP_IMAGE,
- NOTIFICATION_PROP_IS_DISMISSED,
- NOTIFICATION_PROP_IS_RESIDENT,
- NOTIFICATION_PROP_IS_TRANSIENT,
- NOTIFICATION_PROP_TIME,
- NOTIFICATION_PROP_TIMEOUT,
- NOTIFICATION_PROP_URGENCY,
- N_NOTIFICATION_PROPS,
-};
-
-static GParamSpec* notification_props[N_NOTIFICATION_PROPS] = { 0 };
-
-static void foobar_notification_class_init ( FoobarNotificationClass* klass );
-static void foobar_notification_init ( FoobarNotification* self );
-static void foobar_notification_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_notification_finalize ( GObject* object );
-static FoobarNotification* foobar_notification_new ( FoobarNotificationService* service );
-static gchar const* foobar_notification_get_image_path ( FoobarNotification* self );
-static gchar const* foobar_notification_get_image_data ( FoobarNotification* self );
-static void foobar_notification_set_id ( FoobarNotification* self,
- guint value );
-static void foobar_notification_set_app_entry ( FoobarNotification* self,
- gchar const* value );
-static void foobar_notification_set_app_name ( FoobarNotification* self,
- gchar const* value );
-static void foobar_notification_set_body ( FoobarNotification* self,
- gchar const* value );
-static void foobar_notification_set_summary ( FoobarNotification* self,
- gchar const* value );
-static void foobar_notification_set_image_from_path( FoobarNotification* self,
- gchar const* value );
-static void foobar_notification_set_image_from_data( FoobarNotification* self,
- gchar const* value );
-static void foobar_notification_set_image ( FoobarNotification* self,
- GdkPixbuf* value );
-static void foobar_notification_set_dismissed ( FoobarNotification* self,
- gboolean value );
-static void foobar_notification_set_resident ( FoobarNotification* self,
- gboolean value );
-static void foobar_notification_set_transient ( FoobarNotification* self,
- gboolean value );
-static void foobar_notification_set_time ( FoobarNotification* self,
- GDateTime* value );
-static void foobar_notification_set_timeout ( FoobarNotification* self,
- gint64 value );
-static void foobar_notification_set_urgency ( FoobarNotification* self,
- FoobarNotificationUrgency value );
-static void foobar_notification_add_action ( FoobarNotification* self,
- FoobarNotificationAction* action );
-static void foobar_notification_free_action ( gpointer action );
-static gboolean foobar_notification_handle_timeout ( gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarNotification, foobar_notification, G_TYPE_OBJECT )
-
-//
-// FoobarNotificationService:
-//
-// Service acting as the notification daemon, receiving incoming notifications from other applications. The service also
-// persistently saves previously received notifications until they are closed.
-//
-
-struct _FoobarNotificationService
-{
- GObject parent_instance;
- GListStore* notifications;
- GtkSortListModel* sorted_notifications;
- GtkFilterListModel* popup_notifications;
- FoobarNotifications* skeleton;
- guint bus_owner_id;
- guint next_id;
- gchar* cache_path;
- GMutex write_cache_mutex;
-};
-
-enum
-{
- PROP_NOTIFICATIONS = 1,
- PROP_POPUP_NOTIFICATIONS,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_notification_service_class_init ( FoobarNotificationServiceClass* klass );
-static void foobar_notification_service_init ( FoobarNotificationService* self );
-static void foobar_notification_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_notification_service_finalize ( GObject* object );
-static void foobar_notification_service_read_cache ( FoobarNotificationService* self );
-static void foobar_notification_service_write_cache ( FoobarNotificationService* self );
-static void foobar_notification_service_write_cache_cb ( GObject* object,
- GAsyncResult* result,
- gpointer userdata );
-static void foobar_notification_service_write_cache_async ( FoobarNotificationService* self,
- GPtrArray* notifications,
- GCancellable* cancellable,
- GAsyncReadyCallback callback,
- gpointer userdata );
-static gboolean foobar_notification_service_write_cache_finish ( FoobarNotificationService* self,
- GAsyncResult* result,
- GError** error );
-static void foobar_notification_service_write_cache_thread ( GTask* task,
- gpointer source_object,
- gpointer task_data,
- GCancellable* cancellable );
-static void foobar_notification_service_handle_bus_acquired ( GDBusConnection* connection,
- gchar const* name,
- gpointer userdata );
-static void foobar_notification_service_handle_bus_lost ( GDBusConnection* connection,
- gchar const* name,
- gpointer userdata );
-static gboolean foobar_notification_service_handle_notify ( FoobarNotifications* iface,
- GDBusMethodInvocation* invocation,
- gchar const* app_name,
- guint replaces_id,
- gchar const* image,
- gchar const* summary,
- gchar const* body,
- gchar const* const* actions,
- GVariant* hints,
- gint expiration,
- gpointer userdata );
-static gboolean foobar_notification_service_handle_close_notification ( FoobarNotifications* iface,
- GDBusMethodInvocation* invocation,
- guint id,
- gpointer userdata );
-static gboolean foobar_notification_service_handle_get_capabilities ( FoobarNotifications* iface,
- GDBusMethodInvocation* invocation,
- gpointer userdata );
-static gboolean foobar_notification_service_handle_get_server_information( FoobarNotifications* iface,
- GDBusMethodInvocation* invocation,
- gpointer userdata );
-static gboolean foobar_notification_service_popup_filter_func ( gpointer item,
- gpointer userdata );
-static gint foobar_notification_service_sort_func ( gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarNotificationService, foobar_notification_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Notification Action
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for notification actions.
-//
-void foobar_notification_action_class_init( FoobarNotificationActionClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_notification_action_get_property;
- object_klass->finalize = foobar_notification_action_finalize;
-
- action_props[ACTION_PROP_ID] = g_param_spec_string(
- "id",
- "ID",
- "Textual ID of the action within the notification.",
- NULL,
- G_PARAM_READABLE );
- action_props[ACTION_PROP_LABEL] = g_param_spec_string(
- "label",
- "Label",
- "Human-readable title for the action.",
- NULL,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_ACTION_PROPS, action_props );
-}
-
-//
-// Instance initialization for notification actions.
-//
-void foobar_notification_action_init( FoobarNotificationAction* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_notification_action_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarNotificationAction* self = (FoobarNotificationAction*)object;
-
- switch ( prop_id )
- {
- case ACTION_PROP_ID:
- g_value_set_string( value, foobar_notification_action_get_id( self ) );
- break;
- case ACTION_PROP_LABEL:
- g_value_set_string( value, foobar_notification_action_get_label( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for notification actions.
-//
-void foobar_notification_action_finalize( GObject* object )
-{
- FoobarNotificationAction* self = (FoobarNotificationAction*)object;
-
- g_clear_pointer( &self->id, g_free );
- g_clear_pointer( &self->label, g_free );
-
- G_OBJECT_CLASS( foobar_notification_action_parent_class )->finalize( object );
-}
-
-//
-// Create a new notification action that is owned by the given notification (captured as an unowned reference).
-//
-FoobarNotificationAction* foobar_notification_action_new( FoobarNotification* notification )
-{
- FoobarNotificationAction* self = g_object_new( FOOBAR_TYPE_NOTIFICATION_ACTION, NULL );
- self->notification = notification;
- return self;
-}
-
-//
-// Get the textual ID of the action within the notification.
-//
-gchar const* foobar_notification_action_get_id( FoobarNotificationAction* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_ACTION( self ), NULL );
- return self->id;
-}
-
-//
-// Get a human-readable title for the action.
-//
-gchar const* foobar_notification_action_get_label( FoobarNotificationAction* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_ACTION( self ), NULL );
- return self->label;
-}
-
-//
-// Update the textual ID of the action within the notification.
-//
-void foobar_notification_action_set_id(
- FoobarNotificationAction* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_ACTION( self ) );
-
- if ( g_strcmp0( self->id, value ) )
- {
- g_clear_pointer( &self->id, g_free );
- self->id = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), action_props[ACTION_PROP_ID] );
- }
-}
-
-//
-// Set a human-readable title for the action.
-//
-void foobar_notification_action_set_label(
- FoobarNotificationAction* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_ACTION( self ) );
-
- if ( g_strcmp0( self->label, value ) )
- {
- g_clear_pointer( &self->label, g_free );
- self->label = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), action_props[ACTION_PROP_LABEL] );
- }
-}
-
-//
-// Invoke this action by emitting a signal that can be handled by its sender application.
-//
-void foobar_notification_action_invoke( FoobarNotificationAction* self )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_ACTION( self ) );
-
- if ( self->notification && self->notification->service )
- {
- FoobarNotification* notification = self->notification;
- FoobarNotificationService* service = notification->service;
-
- foobar_notifications_emit_action_invoked(
- service->skeleton,
- foobar_notification_get_id( notification ),
- foobar_notification_action_get_id( self ) );
-
- if ( !foobar_notification_is_resident( notification ) )
- {
- foobar_notification_close( notification );
- }
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Notification
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for notifications.
-//
-void foobar_notification_class_init( FoobarNotificationClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_notification_get_property;
- object_klass->finalize = foobar_notification_finalize;
-
- notification_props[NOTIFICATION_PROP_ID] = g_param_spec_uint(
- "id",
- "ID",
- "Numeric ID of the notification.",
- 0,
- UINT_MAX,
- 0,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_APP_ENTRY] = g_param_spec_string(
- "app-entry",
- "App Entry",
- "The desktop file of the notification's application.",
- NULL,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_APP_NAME] = g_param_spec_string(
- "app-name",
- "App Name",
- "Human-readable name of the application.",
- NULL,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_BODY] = g_param_spec_string(
- "body",
- "Body",
- "Main content of the notification.",
- NULL,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_SUMMARY] = g_param_spec_string(
- "summary",
- "Summary",
- "Brief summary of the notification's content.",
- NULL,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_IMAGE] = g_param_spec_object(
- "image",
- "Image",
- "An image icon for the notification.",
- GDK_TYPE_PIXBUF,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_IS_DISMISSED] = g_param_spec_boolean(
- "is-dismissed",
- "Is Dismissed",
- "Indicates that the notification was dismissed or the timeout has passed.",
- FALSE,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_IS_RESIDENT] = g_param_spec_boolean(
- "is-resident",
- "Is Resident",
- "Indicates that the notification is not automatically removed once an action is invoked.",
- FALSE,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_IS_TRANSIENT] = g_param_spec_boolean(
- "is-transient",
- "Is Transient",
- "Indicates that the persistence capability should be bypassed.",
- FALSE,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_TIME] = g_param_spec_boxed(
- "time",
- "Time",
- "The timestamp at which the notification was received.",
- G_TYPE_DATE_TIME,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_TIMEOUT] = g_param_spec_int64(
- "timeout",
- "Timeout",
- "Timeout after which the notification auto-closes in milliseconds.",
- 0,
- INT64_MAX,
- 0,
- G_PARAM_READABLE );
- notification_props[NOTIFICATION_PROP_URGENCY] = g_param_spec_enum(
- "urgency",
- "Urgency",
- "The urgency state of the notification.",
- FOOBAR_TYPE_NOTIFICATION_URGENCY,
- FOOBAR_NOTIFICATION_URGENCY_LOSS_OF_MONEY,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_NOTIFICATION_PROPS, notification_props );
-}
-
-//
-// Instance initialization for notifications.
-//
-void foobar_notification_init( FoobarNotification* self )
-{
- self->actions = g_ptr_array_new_with_free_func( foobar_notification_free_action );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_notification_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarNotification* self = (FoobarNotification*)object;
-
- switch ( prop_id )
- {
- case NOTIFICATION_PROP_ID:
- g_value_set_int64( value, foobar_notification_get_id( self ) );
- break;
- case NOTIFICATION_PROP_APP_ENTRY:
- g_value_set_string( value, foobar_notification_get_app_entry( self ) );
- break;
- case NOTIFICATION_PROP_APP_NAME:
- g_value_set_string( value, foobar_notification_get_app_name( self ) );
- break;
- case NOTIFICATION_PROP_BODY:
- g_value_set_string( value, foobar_notification_get_body( self ) );
- break;
- case NOTIFICATION_PROP_SUMMARY:
- g_value_set_string( value, foobar_notification_get_summary( self ) );
- break;
- case NOTIFICATION_PROP_IMAGE:
- g_value_set_object( value, foobar_notification_get_image( self ) );
- break;
- case NOTIFICATION_PROP_IS_DISMISSED:
- g_value_set_boolean( value, foobar_notification_is_dismissed( self ) );
- break;
- case NOTIFICATION_PROP_IS_RESIDENT:
- g_value_set_boolean( value, foobar_notification_is_resident( self ) );
- break;
- case NOTIFICATION_PROP_IS_TRANSIENT:
- g_value_set_boolean( value, foobar_notification_is_transient( self ) );
- break;
- case NOTIFICATION_PROP_TIME:
- g_value_set_boxed( value, foobar_notification_get_time( self ) );
- break;
- case NOTIFICATION_PROP_TIMEOUT:
- g_value_set_int64( value, foobar_notification_get_timeout( self ) );
- break;
- case NOTIFICATION_PROP_URGENCY:
- g_value_set_enum( value, foobar_notification_get_urgency( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance initialization for notifications.
-//
-void foobar_notification_finalize( GObject* object )
-{
- FoobarNotification* self = (FoobarNotification*)object;
-
- g_clear_object( &self->image );
- g_clear_pointer( &self->actions, g_ptr_array_unref );
- g_clear_pointer( &self->app_entry, g_free );
- g_clear_pointer( &self->app_name, g_free );
- g_clear_pointer( &self->body, g_free );
- g_clear_pointer( &self->summary, g_free );
- g_clear_pointer( &self->image_path, g_free );
- g_clear_pointer( &self->image_data, g_free );
- g_clear_pointer( &self->time, g_date_time_unref );
-
- G_OBJECT_CLASS( foobar_notification_parent_class )->finalize( object );
-}
-
-//
-// Create a new notification that is owned by the given service (captured as an unowned reference).
-//
-FoobarNotification* foobar_notification_new( FoobarNotificationService* service )
-{
- FoobarNotification* self = g_object_new( FOOBAR_TYPE_NOTIFICATION, NULL );
- self->service = service;
- return self;
-}
-
-//
-// Numeric ID of the notification.
-//
-guint foobar_notification_get_id( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), 0 );
- return self->id;
-}
-
-//
-// A list of actions which can be invoked by notifying the notification's sender.
-//
-FoobarNotificationAction** foobar_notification_get_actions(
- FoobarNotification* self,
- guint* out_count )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), NULL );
- if ( out_count ) { *out_count = self->actions->len; }
- return (FoobarNotificationAction**)self->actions->pdata;
-}
-
-//
-// The desktop file of the notification's application.
-//
-gchar const* foobar_notification_get_app_entry( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), NULL );
- return self->app_entry;
-}
-
-//
-// Human-readable name of the application.
-//
-gchar const* foobar_notification_get_app_name( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), NULL );
- return self->app_name;
-}
-
-//
-// Main content of the notification.
-//
-gchar const* foobar_notification_get_body( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), NULL );
- return self->body;
-}
-
-//
-// Brief summary of the notification's content.
-//
-gchar const* foobar_notification_get_summary( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), NULL );
- return self->summary;
-}
-
-//
-// The source path for the notification's image if it was loaded from a file.
-//
-gchar const* foobar_notification_get_image_path( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), NULL );
- return self->image_path;
-}
-
-//
-// The base64-encoded data for the notification's image if it was loaded from data.
-//
-gchar const* foobar_notification_get_image_data( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), NULL );
- return self->image_data;
-}
-
-//
-// An image icon for the notification.
-//
-GdkPixbuf* foobar_notification_get_image( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), NULL );
- return self->image;
-}
-
-//
-// Indicates that the notification was dismissed or the timeout has passed.
-//
-gboolean foobar_notification_is_dismissed( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), FALSE );
- return self->is_dismissed;
-}
-
-//
-// Indicates that the notification is not automatically removed once an action is invoked.
-//
-gboolean foobar_notification_is_resident( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), FALSE );
- return self->is_resident;
-}
-
-//
-// Indicates that the persistence capability should be bypassed.
-//
-gboolean foobar_notification_is_transient( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), FALSE );
- return self->is_transient;
-}
-
-//
-// The timestamp at which the notification was received.
-//
-GDateTime* foobar_notification_get_time( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), NULL );
- return self->time;
-}
-
-//
-// Timeout after which the notification auto-closes in milliseconds.
-//
-gint64 foobar_notification_get_timeout( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), 0 );
- return self->timeout;
-}
-
-//
-// The urgency state of the notification.
-//
-FoobarNotificationUrgency foobar_notification_get_urgency( FoobarNotification* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION( self ), FOOBAR_NOTIFICATION_URGENCY_LOSS_OF_MONEY );
- return self->urgency;
-}
-
-//
-// Numeric ID of the notification.
-//
-void foobar_notification_set_id(
- FoobarNotification* self,
- guint value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( self->id != value )
- {
- self->id = value;
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_ID] );
- }
-}
-
-//
-// The desktop file of the notification's application.
-//
-void foobar_notification_set_app_entry(
- FoobarNotification* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( g_strcmp0( self->app_entry, value ) )
- {
- g_clear_pointer( &self->app_entry, g_free );
- self->app_entry = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_APP_ENTRY] );
- }
-}
-
-//
-// Human-readable name of the application.
-//
-void foobar_notification_set_app_name(
- FoobarNotification* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( g_strcmp0( self->app_name, value ) )
- {
- g_clear_pointer( &self->app_name, g_free );
- self->app_name = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_APP_NAME] );
- }
-}
-
-//
-// Main content of the notification.
-//
-void foobar_notification_set_body(
- FoobarNotification* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( g_strcmp0( self->body, value ) )
- {
- g_clear_pointer( &self->body, g_free );
- self->body = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_BODY] );
- }
-}
-
-//
-// Brief summary of the notification's content.
-//
-void foobar_notification_set_summary(
- FoobarNotification* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( g_strcmp0( self->summary, value ) )
- {
- g_clear_pointer( &self->summary, g_free );
- self->summary = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_SUMMARY] );
- }
-}
-
-//
-// Update the notification's image by trying to load a file at the provided path.
-//
-void foobar_notification_set_image_from_path(
- FoobarNotification* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( g_strcmp0( self->image_path, value ) )
- {
- g_clear_pointer( &self->image_path, g_free );
- g_clear_pointer( &self->image_data, g_free );
- g_clear_object( &self->image );
- self->image_path = g_strdup( value );
-
- g_autoptr( GError ) error = NULL;
- self->image = gdk_pixbuf_new_from_file( self->image_path, &error );
- if ( !self->image )
- {
- g_warning( "Invalid image path for notification: %s", error->message );
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_IMAGE] );
- }
-}
-
-//
-// Update the notification's image by trying to load base64-encoded data.
-//
-void foobar_notification_set_image_from_data(
- FoobarNotification* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( g_strcmp0( self->image_data, value ) )
- {
- g_clear_pointer( &self->image_path, g_free );
- g_clear_pointer( &self->image_data, g_free );
- g_clear_object( &self->image );
- self->image_data = g_strdup( value );
-
- gsize data_len;
- guchar* data = g_base64_decode( self->image_data, &data_len );
- g_autoptr( GError ) error = NULL;
- g_autoptr( GInputStream ) input = g_memory_input_stream_new_from_data( data, (gssize)data_len, g_free );
- self->image = gdk_pixbuf_new_from_stream( input, NULL, &error );
- if ( !self->image )
- {
- g_warning( "Invalid image data for notification: %s", error->message );
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_IMAGE] );
- }
-}
-
-//
-// Update the notification's image to a custom pixbuf.
-//
-static void foobar_notification_set_image(
- FoobarNotification* self,
- GdkPixbuf* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( self->image != value )
- {
- g_clear_pointer( &self->image_path, g_free );
- g_clear_pointer( &self->image_data, g_free );
- g_clear_object( &self->image );
-
- if ( value )
- {
- self->image = g_object_ref( value );
- g_autoptr( GError ) error = NULL;
- gchar* data;
- gsize data_len;
- if ( gdk_pixbuf_save_to_buffer( self->image, &data, &data_len, "png", &error, NULL ) )
- {
- self->image_data = g_base64_encode( (guchar const*)data, data_len );
- }
- else
- {
- g_warning( "Unable to save image for notification: %s", error->message );
- g_clear_object( &self->image );
- }
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_IMAGE] );
- }
-}
-
-//
-// Indicates that the notification was dismissed or the timeout has passed.
-//
-void foobar_notification_set_dismissed(
- FoobarNotification* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- value = !!value;
- if ( self->is_dismissed != value )
- {
- self->is_dismissed = value;
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_IS_DISMISSED] );
-
- if ( self->service )
- {
- gtk_filter_changed(
- gtk_filter_list_model_get_filter( self->service->popup_notifications ),
- value ? GTK_FILTER_CHANGE_MORE_STRICT : GTK_FILTER_CHANGE_LESS_STRICT );
- }
- }
-}
-
-//
-// Indicates that the notification is not automatically removed once an action is invoked.
-//
-void foobar_notification_set_resident(
- FoobarNotification* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- value = !!value;
- if ( self->is_resident != value )
- {
- self->is_resident = value;
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_IS_RESIDENT] );
- }
-}
-
-//
-// Indicates that the persistence capability should be bypassed.
-//
-void foobar_notification_set_transient(
- FoobarNotification* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- value = !!value;
- if ( self->is_transient != value )
- {
- self->is_transient = value;
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_IS_TRANSIENT] );
- }
-}
-
-//
-// The timestamp at which the notification was received.
-//
-void foobar_notification_set_time(
- FoobarNotification* self,
- GDateTime* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( self->time != value )
- {
- if ( self->time ) { g_date_time_unref( self->time ); }
- self->time = g_date_time_ref( value );
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_TIME] );
- }
-}
-
-//
-// Timeout after which the notification auto-closes in milliseconds.
-//
-void foobar_notification_set_timeout(
- FoobarNotification* self,
- gint64 value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- value = MAX( value, 0 );
- if ( self->timeout != value )
- {
- self->timeout = value;
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_TIMEOUT] );
- }
-}
-
-//
-// The urgency state of the notification.
-//
-void foobar_notification_set_urgency(
- FoobarNotification* self,
- FoobarNotificationUrgency value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( self->urgency != value )
- {
- self->urgency = value;
- g_object_notify_by_pspec( G_OBJECT( self ), notification_props[NOTIFICATION_PROP_URGENCY] );
- }
-}
-
-//
-// Register an action for the notification.
-//
-void foobar_notification_add_action(
- FoobarNotification* self,
- FoobarNotificationAction* action )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
- g_return_if_fail( action->notification == self );
-
- g_ptr_array_add( self->actions, g_object_ref( action ) );
-}
-
-//
-// Block the notification from automatically being dismissed.
-//
-void foobar_notification_block_timeout( FoobarNotification* self )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- g_clear_handle_id( &self->timeout_id, g_source_remove );
-}
-
-//
-// If not already started, start the timeout to automatically dismiss the notification after it's timeout.
-//
-void foobar_notification_resume_timeout( FoobarNotification* self )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( !self->is_dismissed && !self->timeout_id )
- {
- self->timeout_id = g_timeout_add_full(
- G_NOTIFICATION_PRIORITY_NORMAL,
- self->timeout,
- foobar_notification_handle_timeout,
- g_object_ref( self ),
- g_object_unref );
- }
-}
-
-//
-// Manually dismiss the notification, setting "is-dismissed" to TRUE.
-//
-void foobar_notification_dismiss( FoobarNotification* self )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- foobar_notification_set_dismissed( self, TRUE );
-}
-
-//
-// Close the notification, removing it from the parent service's list.
-//
-void foobar_notification_close( FoobarNotification* self )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION( self ) );
-
- if ( self->service )
- {
- FoobarNotificationService* service = self->service;
- self->service = NULL;
-
- guint id = foobar_notification_get_id( self );
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( service->notifications ) ); ++i )
- {
- if ( g_list_model_get_item( G_LIST_MODEL( service->notifications ), i ) == self )
- {
- g_list_store_remove( service->notifications, i );
- foobar_notification_service_write_cache( service );
- break;
- }
- }
-
- foobar_notifications_emit_notification_closed( service->skeleton, id, 3 );
- }
-}
-
-//
-// Called when an action is removed from a notification, resetting its weak reference to the notification.
-//
-void foobar_notification_free_action( gpointer action )
-{
- FoobarNotificationAction* object = (FoobarNotificationAction*)action;
- object->notification = NULL;
- g_object_unref( object );
-}
-
-//
-// Called after the notification's timeout has elapsed.
-//
-gboolean foobar_notification_handle_timeout( gpointer userdata )
-{
- FoobarNotification* notification = (FoobarNotification*)userdata;
-
- foobar_notification_dismiss( notification );
- notification->timeout_id = 0;
-
- return G_SOURCE_REMOVE;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the notification service.
-//
-void foobar_notification_service_class_init( FoobarNotificationServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_notification_service_get_property;
- object_klass->finalize = foobar_notification_service_finalize;
-
- props[PROP_NOTIFICATIONS] = g_param_spec_object(
- "notifications",
- "Notifications",
- "Sorted list of all notifications.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- props[PROP_POPUP_NOTIFICATIONS] = g_param_spec_object(
- "popup-notifications",
- "Popup Notifications",
- "Sorted list of all visible notifications (i.e. notifications that are not dismissed).",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the notification service.
-//
-void foobar_notification_service_init( FoobarNotificationService* self )
-{
- self->notifications = g_list_store_new( FOOBAR_TYPE_NOTIFICATION );
- self->cache_path = foobar_get_cache_path( "notifications.json" );
- g_mutex_init( &self->write_cache_mutex );
-
- GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_notification_service_sort_func, NULL, NULL );
- self->sorted_notifications = gtk_sort_list_model_new(
- G_LIST_MODEL( g_object_ref( self->notifications ) ),
- GTK_SORTER( sorter ) );
-
- GtkCustomFilter* popup_filter = gtk_custom_filter_new( foobar_notification_service_popup_filter_func, NULL, NULL );
- self->popup_notifications = gtk_filter_list_model_new(
- G_LIST_MODEL( g_object_ref( self->sorted_notifications ) ),
- GTK_FILTER( popup_filter ) );
-
- foobar_notification_service_read_cache( self );
-
- self->bus_owner_id = g_bus_own_name(
- G_BUS_TYPE_SESSION,
- "org.freedesktop.Notifications",
- G_BUS_NAME_OWNER_FLAGS_NONE,
- foobar_notification_service_handle_bus_acquired,
- NULL,
- foobar_notification_service_handle_bus_lost,
- self,
- NULL );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_notification_service_get_property( GObject* object, guint prop_id, GValue* value, GParamSpec* pspec )
-{
- FoobarNotificationService* self = (FoobarNotificationService*)object;
-
- switch ( prop_id )
- {
- case PROP_NOTIFICATIONS:
- g_value_set_object( value, foobar_notification_service_get_notifications( self ) );
- break;
- case PROP_POPUP_NOTIFICATIONS:
- g_value_set_object( value, foobar_notification_service_get_popup_notifications( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the notification service.
-//
-void foobar_notification_service_finalize( GObject* object )
-{
- FoobarNotificationService* self = (FoobarNotificationService*)object;
-
- if ( self->skeleton ) { g_dbus_interface_skeleton_unexport( G_DBUS_INTERFACE_SKELETON( self->skeleton ) ); }
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->notifications ) ); ++i )
- {
- FoobarNotification* notification = g_list_model_get_item( G_LIST_MODEL( self->notifications ), i );
- notification->service = NULL;
- }
-
- g_clear_object( &self->popup_notifications );
- g_clear_object( &self->sorted_notifications );
- g_clear_object( &self->notifications );
- g_clear_object( &self->skeleton );
- g_clear_handle_id( &self->bus_owner_id, g_bus_unown_name );
- g_clear_pointer( &self->cache_path, g_free );
-
- g_mutex_clear( &self->write_cache_mutex );
-
- G_OBJECT_CLASS( foobar_notification_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new notification service instance.
-//
-FoobarNotificationService* foobar_notification_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_NOTIFICATION_SERVICE, NULL );
-}
-
-//
-// Get a sorted list of all notifications.
-//
-GListModel* foobar_notification_service_get_notifications( FoobarNotificationService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_SERVICE( self ), NULL );
- return G_LIST_MODEL( self->sorted_notifications );
-}
-
-//
-// Get a sorted list of all visible notifications (i.e. notifications that are not dismissed).
-//
-GListModel* foobar_notification_service_get_popup_notifications( FoobarNotificationService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_SERVICE( self ), NULL );
- return G_LIST_MODEL( self->popup_notifications );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called after the process has asynchronously acquired the notification bus.
-//
-// This is where we can export notification daemon functionality using the corresponding DBus skeleton object.
-//
-void foobar_notification_service_handle_bus_acquired(
- GDBusConnection* connection,
- gchar const* name,
- gpointer userdata )
-{
- (void)name;
- FoobarNotificationService* self = (FoobarNotificationService*)userdata;
-
- self->skeleton = foobar_notifications_skeleton_new( );
- g_signal_connect(
- self->skeleton,
- "handle-notify",
- G_CALLBACK( foobar_notification_service_handle_notify ),
- self );
- g_signal_connect(
- self->skeleton,
- "handle-close-notification",
- G_CALLBACK( foobar_notification_service_handle_close_notification ),
- self );
- g_signal_connect(
- self->skeleton,
- "handle-get-capabilities",
- G_CALLBACK( foobar_notification_service_handle_get_capabilities ),
- self );
- g_signal_connect(
- self->skeleton,
- "handle-get-server-information",
- G_CALLBACK( foobar_notification_service_handle_get_server_information ),
- self );
-
- g_autoptr( GError ) error = NULL;
- if ( !g_dbus_interface_skeleton_export(
- G_DBUS_INTERFACE_SKELETON( self->skeleton ),
- connection,
- "/org/freedesktop/Notifications",
- &error ) )
- {
- g_warning( "Unable to export notifications interface: %s", error->message );
- }
-}
-
-//
-// Called after the process has failed to acquire the notification bus.
-//
-void foobar_notification_service_handle_bus_lost(
- GDBusConnection* connection,
- gchar const* name,
- gpointer userdata )
-{
- (void)connection;
- (void)name;
- (void)userdata;
-
- g_autofree gchar* other_name = NULL;
- g_autoptr( FoobarNotifications ) proxy = foobar_notifications_proxy_new_for_bus_sync(
- G_BUS_TYPE_SESSION,
- G_DBUS_PROXY_FLAGS_NONE,
- "org.freedesktop.Notifications",
- "/org/freedesktop/Notifications",
- NULL,
- NULL );
-
- if ( proxy )
- {
- foobar_notifications_call_get_server_information_sync(
- proxy,
- &other_name,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL );
- }
-
- g_warning(
- "Unable to register notification daemon because %s is already running.",
- other_name ? other_name : "(unknown)" );
-}
-
-//
-// DBus skeleton callback for the "Notify" method.
-//
-gboolean foobar_notification_service_handle_notify(
- FoobarNotifications* iface,
- GDBusMethodInvocation* invocation,
- gchar const* app_name,
- guint replaces_id,
- gchar const* image,
- gchar const* summary,
- gchar const* body,
- gchar const* const* actions,
- GVariant* hints,
- gint expiration,
- gpointer userdata )
-{
- FoobarNotificationService* self = (FoobarNotificationService*)userdata;
-
- g_autoptr( GDateTime ) time = g_date_time_new_now_local( );
- g_autoptr( FoobarNotification ) notification = foobar_notification_new( self );
- foobar_notification_set_id( notification, replaces_id ? replaces_id : self->next_id++ );
- foobar_notification_set_app_name( notification, app_name );
- foobar_notification_set_summary( notification, summary );
- foobar_notification_set_body( notification, body );
- foobar_notification_set_time( notification, time );
- foobar_notification_set_timeout( notification, expiration != -1 ? expiration : DEFAULT_TIMEOUT );
- if ( image && *image ) { foobar_notification_set_image_from_path( notification, image ); }
-
- if ( hints )
- {
- // Process hints to set some more properties for the notification.
-
- gsize hint_count = g_variant_n_children( hints );
- for ( gsize i = 0; i < hint_count; ++i )
- {
- g_autoptr( GVariant ) hint = g_variant_get_child_value( hints, i );
- g_autoptr( GVariant ) key = g_variant_get_child_value( hint, 0 );
- g_autoptr( GVariant ) value_wrapper = g_variant_get_child_value( hint, 1 );
- g_autoptr( GVariant ) value = g_variant_get_variant( value_wrapper );
- gchar const* key_str = g_variant_get_string( key, NULL );
-
- if ( !g_strcmp0( key_str, "desktop-entry" ) )
- {
- foobar_notification_set_app_entry( notification, g_variant_get_string( value, NULL ) );
- }
- else if ( !g_strcmp0( key_str, "resident" ) )
- {
- foobar_notification_set_resident( notification, g_variant_get_boolean( value ) );
- }
- else if ( !g_strcmp0( key_str, "transient" ) )
- {
- foobar_notification_set_transient( notification, g_variant_get_boolean( value ) );
- }
- else if ( !g_strcmp0( key_str, "urgency" ) )
- {
- foobar_notification_set_urgency( notification, g_variant_get_byte( value ) );
- }
-
- if ( !foobar_notification_get_image( notification ) )
- {
- if ( !g_strcmp0( key_str, "image-path" ) ||
- !g_strcmp0( key_str, "image_path" ) )
- {
- foobar_notification_set_image_from_path( notification, g_variant_get_string( value, NULL ) );
- }
- else if ( !g_strcmp0( key_str, "image-data" ) ||
- !g_strcmp0( key_str, "image_data" ) ||
- !g_strcmp0( key_str, "icon_data" ) )
- {
- gint32 width, height, row_stride, bits_per_sample, channels;
- gboolean has_alpha;
- g_variant_get(
- value,
- "(iiibiiay)",
- &width,
- &height,
- &row_stride,
- &has_alpha,
- &bits_per_sample,
- &channels,
- NULL );
-
- g_autoptr( GVariant ) data_variant = g_variant_get_child_value( value, 6 );
- gsize data_len;
- guint8 const* data = g_variant_get_fixed_array( data_variant, &data_len, sizeof( guint8 ) );
- g_autoptr( GBytes ) bytes = g_bytes_new( data, data_len );
-
- g_autoptr( GdkPixbuf ) pixbuf = gdk_pixbuf_new_from_bytes(
- bytes,
- GDK_COLORSPACE_RGB,
- has_alpha,
- bits_per_sample,
- width,
- height,
- row_stride );
- foobar_notification_set_image( notification, pixbuf );
- }
- }
- }
- }
-
- if ( actions )
- {
- // Actions are stored in a flat array containing the label followed by the ID of the action.
-
- while ( *actions )
- {
- gchar const* label = *actions++;
- gchar const* id = *actions++;
- if ( !id )
- {
- g_warning( "Action missing id: %s", label );
- break;
- }
-
- g_autoptr( FoobarNotificationAction ) action = foobar_notification_action_new( notification );
- foobar_notification_action_set_id( action, id );
- foobar_notification_action_set_label( action, label );
- foobar_notification_add_action( notification, action );
- }
- }
-
- // Add the notification to the list and start the timeout.
-
- g_list_store_append( self->notifications, notification );
- foobar_notification_resume_timeout( notification );
- foobar_notification_service_write_cache( self );
-
- foobar_notifications_complete_notify( iface, invocation, foobar_notification_get_id( notification ) );
- return G_DBUS_METHOD_INVOCATION_HANDLED;
-}
-
-//
-// DBus skeleton callback for the "CloseNotification" method.
-//
-gboolean foobar_notification_service_handle_close_notification(
- FoobarNotifications* iface,
- GDBusMethodInvocation* invocation,
- guint id,
- gpointer userdata )
-{
- FoobarNotificationService* self = (FoobarNotificationService*)userdata;
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->notifications ) ); ++i )
- {
- FoobarNotification* notification = g_list_model_get_item( G_LIST_MODEL( self->notifications ), i );
- if ( foobar_notification_get_id( notification ) == id )
- {
- foobar_notification_close( notification );
- break;
- }
- }
-
- foobar_notifications_complete_close_notification( iface, invocation );
- return G_DBUS_METHOD_INVOCATION_HANDLED;
-}
-
-//
-// DBus skeleton callback for the "GetCapabilities" method.
-//
-gboolean foobar_notification_service_handle_get_capabilities(
- FoobarNotifications* iface,
- GDBusMethodInvocation* invocation,
- gpointer userdata )
-{
- (void)userdata;
-
- gchar const* capabilities[] =
- {
- "action-icons",
- "actions",
- "body",
- "body-hyperlinks",
- "body-markup",
- "icon-static",
- "persistence",
- NULL
- };
- foobar_notifications_complete_get_capabilities( iface, invocation, capabilities );
- return G_DBUS_METHOD_INVOCATION_HANDLED;
-}
-
-//
-// DBus skeleton callback for the "GetServerInformation" method.
-//
-gboolean foobar_notification_service_handle_get_server_information(
- FoobarNotifications* iface,
- GDBusMethodInvocation* invocation,
- gpointer userdata )
-{
- (void)userdata;
-
- foobar_notifications_complete_get_server_information(
- iface,
- invocation,
- "Foobar",
- "Hannes Schulze",
- "1.0.0",
- "1.2" );
- return G_DBUS_METHOD_INVOCATION_HANDLED;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Synchronously read the cache file at self->cache_path, populating self->notifications and initializing self->next_id.
-//
-void foobar_notification_service_read_cache( FoobarNotificationService* self )
-{
- g_autoptr( GError ) error = NULL;
-
- if ( self->cache_path && g_file_test( self->cache_path, G_FILE_TEST_EXISTS ) )
- {
- g_autoptr( JsonParser ) parser = json_parser_new( );
- if ( !json_parser_load_from_mapped_file( parser, self->cache_path, &error ) )
- {
- g_warning( "Unable to read cached notifications: %s", error->message );
- return;
- }
-
- JsonNode* root_node = json_parser_get_root( parser );
- JsonArray* root_array = json_node_get_array( root_node );
- for ( guint i = 0; i < json_array_get_length( root_array ); ++i )
- {
- JsonObject* notification_object = json_array_get_object_element( root_array, i );
- g_autoptr( FoobarNotification ) notification = foobar_notification_new( self );
-
- foobar_notification_set_dismissed( notification, TRUE );
-
- guint id = json_object_get_int_member( notification_object, "id" );
- foobar_notification_set_id( notification, id );
-
- gchar const* app_entry = json_object_get_string_member_with_default( notification_object, "app-entry", NULL );
- foobar_notification_set_app_entry( notification, app_entry );
-
- gchar const* app_name = json_object_get_string_member_with_default( notification_object, "app-name", NULL );
- foobar_notification_set_app_name( notification, app_name );
-
- gchar const* body = json_object_get_string_member_with_default( notification_object, "body", NULL );
- foobar_notification_set_body( notification, body );
-
- gchar const* summary = json_object_get_string_member_with_default( notification_object, "summary", NULL );
- foobar_notification_set_summary( notification, summary );
-
- gchar const* image_path = json_object_get_string_member_with_default( notification_object, "image-path", NULL );
- gchar const* image_data = json_object_get_string_member_with_default( notification_object, "image-data", NULL );
- if ( image_path ) { foobar_notification_set_image_from_path( notification, image_path ); }
- else if ( image_data ) { foobar_notification_set_image_from_data( notification, image_data ); }
-
- gboolean is_resident = json_object_get_boolean_member( notification_object, "is-resident" );
- foobar_notification_set_resident( notification, is_resident );
-
- gchar const* time_str = json_object_get_string_member_with_default( notification_object, "time", NULL );
- GTimeZone* tz = g_time_zone_new_local( );
- GDateTime* time = time_str ? g_date_time_new_from_iso8601( time_str, tz ) : NULL;
- foobar_notification_set_time( notification, time );
- g_time_zone_unref( tz );
- if ( time ) { g_date_time_unref( time ); }
-
- gint64 timeout = json_object_get_int_member( notification_object, "timeout" );
- foobar_notification_set_timeout( notification, timeout );
-
- FoobarNotificationUrgency urgency = json_object_get_int_member( notification_object, "urgency" );
- foobar_notification_set_urgency( notification, urgency );
-
- JsonArray* actions_array = json_object_get_array_member( notification_object, "actions" );
- for ( guint j = 0; j < json_array_get_length( actions_array ); ++j )
- {
- JsonObject* action_object = json_array_get_object_element( actions_array, j );
- g_autoptr( FoobarNotificationAction ) action = foobar_notification_action_new( notification );
-
- gchar const* action_id = json_object_get_string_member_with_default( action_object, "id", NULL );
- foobar_notification_action_set_id( action, action_id );
-
- gchar const* label = json_object_get_string_member_with_default( action_object, "label", NULL );
- foobar_notification_action_set_label( action, label );
-
- foobar_notification_add_action( notification, action );
- }
-
- g_list_store_append( self->notifications, notification );
- self->next_id = MAX( self->next_id, id + 1 );
- }
- }
-}
-
-//
-// Asynchronously start writing the cache file at self->cache_path.
-//
-void foobar_notification_service_write_cache( FoobarNotificationService* self )
-{
- guint count = g_list_model_get_n_items( G_LIST_MODEL( self->notifications ) );
- g_autoptr( GPtrArray ) notifications = g_ptr_array_new_full( count, g_object_unref );
- for ( guint i = 0; i < count; ++i )
- {
- FoobarNotification* notification = g_list_model_get_item( G_LIST_MODEL( self->notifications ), i );
- if ( !foobar_notification_is_transient( notification ) )
- {
- g_ptr_array_add( notifications, g_object_ref( notification ) );
- }
- }
-
- foobar_notification_service_write_cache_async(
- self,
- g_steal_pointer( &notifications ),
- NULL,
- foobar_notification_service_write_cache_cb,
- NULL );
-}
-
-//
-// Callback invoked when the cache was written successfully or failed.
-//
-void foobar_notification_service_write_cache_cb(
- GObject* object,
- GAsyncResult* result,
- gpointer userdata )
-{
- (void)userdata;
- FoobarNotificationService* self = (FoobarNotificationService*)object;
-
- g_autoptr( GError ) error = NULL;
- if ( !foobar_notification_service_write_cache_finish( self, result, &error ) )
- {
- g_warning( "Unable to write cached notifications: %s", error->message );
- }
-}
-
-//
-// Asynchronously write the cache file at self->cache_path, using the provided snapshot of the list.
-//
-void foobar_notification_service_write_cache_async(
- FoobarNotificationService* self,
- GPtrArray* notifications,
- GCancellable* cancellable,
- GAsyncReadyCallback callback,
- gpointer userdata )
-{
- g_autoptr( GTask ) task = g_task_new( self, cancellable, callback, userdata );
- g_task_set_name( task, "write-notification-cache" );
- g_task_set_task_data( task, notifications, (GDestroyNotify)g_ptr_array_unref );
- g_task_run_in_thread( task, foobar_notification_service_write_cache_thread );
-}
-
-//
-// Get the asynchronous result for writing the cache file, returning TRUE on success, or FALSE on error.
-//
-gboolean foobar_notification_service_write_cache_finish(
- FoobarNotificationService* self,
- GAsyncResult* result,
- GError** error )
-{
- (void)self;
-
- return g_task_propagate_boolean( G_TASK( result ), error );
-}
-
-//
-// Task implementation for foobar_notification_service_write_cache_async, invoked on a background thread.
-//
-void foobar_notification_service_write_cache_thread(
- GTask* task,
- gpointer source_object,
- gpointer task_data,
- GCancellable* cancellable )
-{
- (void)cancellable;
- FoobarNotificationService* self = (FoobarNotificationService*)source_object;
- GPtrArray* notifications = (GPtrArray*)task_data;
-
- if ( !self->cache_path )
- {
- g_task_return_boolean( task, TRUE );
- return;
- }
-
- g_autoptr( GError ) error = NULL;
-
- g_autoptr( JsonBuilder ) builder = json_builder_new( );
- json_builder_begin_array( builder );
- for ( guint i = 0; i < notifications->len; ++i )
- {
- // Only init-only properties are read here so no extra synchronization is needed.
-
- FoobarNotification* notification = g_ptr_array_index( notifications, i );
-
- json_builder_begin_object( builder );
-
- json_builder_set_member_name( builder, "id" );
- json_builder_add_int_value( builder, foobar_notification_get_id( notification ) );
-
- json_builder_set_member_name( builder, "app-entry" );
- json_builder_add_string_value( builder, foobar_notification_get_app_entry( notification ) );
-
- json_builder_set_member_name( builder, "app-name" );
- json_builder_add_string_value( builder, foobar_notification_get_app_name( notification ) );
-
- json_builder_set_member_name( builder, "body" );
- json_builder_add_string_value( builder, foobar_notification_get_body( notification ) );
-
- json_builder_set_member_name( builder, "summary" );
- json_builder_add_string_value( builder, foobar_notification_get_summary( notification ) );
-
- json_builder_set_member_name( builder, "image-path" );
- json_builder_add_string_value( builder, foobar_notification_get_image_path( notification ) );
-
- json_builder_set_member_name( builder, "image-data" );
- json_builder_add_string_value( builder, foobar_notification_get_image_data( notification ) );
-
- json_builder_set_member_name( builder, "is-resident" );
- json_builder_add_boolean_value( builder, foobar_notification_is_resident( notification ) );
-
- GDateTime* time = foobar_notification_get_time( notification );
- gchar* time_str = time ? g_date_time_format_iso8601( time ) : NULL;
- json_builder_set_member_name( builder, "time" );
- json_builder_add_string_value( builder, time_str );
- g_free( time_str );
-
- json_builder_set_member_name( builder, "timeout" );
- json_builder_add_int_value( builder, foobar_notification_get_timeout( notification ) );
-
- json_builder_set_member_name( builder, "urgency" );
- json_builder_add_int_value( builder, foobar_notification_get_urgency( notification ) );
-
- guint action_count;
- FoobarNotificationAction** actions = foobar_notification_get_actions( notification, &action_count );
- json_builder_set_member_name( builder, "actions" );
- json_builder_begin_array( builder );
- for ( guint j = 0; j < action_count; ++j )
- {
- FoobarNotificationAction* action = actions[j];
-
- json_builder_begin_object( builder );
-
- json_builder_set_member_name( builder, "id" );
- json_builder_add_string_value( builder, foobar_notification_action_get_id( action ) );
-
- json_builder_set_member_name( builder, "label" );
- json_builder_add_string_value( builder, foobar_notification_action_get_label( action ) );
-
- json_builder_end_object( builder );
- }
- json_builder_end_array( builder );
-
- json_builder_end_object( builder );
- }
- json_builder_end_array( builder );
-
- g_autoptr( JsonNode ) root_node = json_builder_get_root( builder );
- g_autoptr( JsonGenerator ) generator = json_generator_new( );
- json_generator_set_root( generator, root_node );
-
- g_mutex_lock( &self->write_cache_mutex );
- gboolean success = json_generator_to_file( generator, self->cache_path, &error );
- g_mutex_unlock( &self->write_cache_mutex );
- if ( !success )
- {
- g_task_return_error( task, g_steal_pointer( &error ) );
- return;
- }
-
- g_task_return_boolean( task, TRUE );
-}
-
-//
-// Filtering callback for the list of pop-up notifications.
-//
-gboolean foobar_notification_service_popup_filter_func(
- gpointer item,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarNotification* notification = item;
- return !foobar_notification_is_dismissed( notification );
-}
-
-//
-// Sorting callback for the list of notifications.
-//
-// Notifications are sorted by timestamp in descending order.
-//
-gint foobar_notification_service_sort_func(
- gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarNotification* notification_a = (FoobarNotification*)item_a;
- FoobarNotification* notification_b = (FoobarNotification*)item_b;
- return -g_date_time_compare(
- foobar_notification_get_time( notification_a ),
- foobar_notification_get_time( notification_b ) );
-}
diff --git a/src/services/notification-service.h b/src/services/notification-service.h
deleted file mode 100644
index abcca4e..0000000
--- a/src/services/notification-service.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-#include <gdk/gdk.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_NOTIFICATION_URGENCY foobar_notification_urgency_get_type( )
-#define FOOBAR_TYPE_NOTIFICATION_ACTION foobar_notification_action_get_type( )
-#define FOOBAR_TYPE_NOTIFICATION foobar_notification_get_type( )
-#define FOOBAR_TYPE_NOTIFICATION_SERVICE foobar_notification_service_get_type( )
-
-typedef enum
-{
- // reminder to myself that i should have studied this shit instead of writing a fucking bar in c
- FOOBAR_NOTIFICATION_URGENCY_LOSS_OF_COMFORT = 0,
- FOOBAR_NOTIFICATION_URGENCY_LOSS_OF_MONEY = 1,
- FOOBAR_NOTIFICATION_URGENCY_LOSS_OF_LIFE = 2,
-} FoobarNotificationUrgency;
-
-GType foobar_notification_urgency_get_type( void );
-
-G_DECLARE_FINAL_TYPE( FoobarNotificationAction, foobar_notification_action, FOOBAR, NOTIFICATION_ACTION, GObject )
-
-gchar const* foobar_notification_action_get_id ( FoobarNotificationAction* self );
-gchar const* foobar_notification_action_get_label( FoobarNotificationAction* self );
-void foobar_notification_action_invoke ( FoobarNotificationAction* self );
-
-G_DECLARE_FINAL_TYPE( FoobarNotification, foobar_notification, FOOBAR, NOTIFICATION, GObject )
-
-guint foobar_notification_get_id ( FoobarNotification* self );
-FoobarNotificationAction** foobar_notification_get_actions ( FoobarNotification* self,
- guint* out_count );
-gchar const* foobar_notification_get_app_entry ( FoobarNotification* self );
-gchar const* foobar_notification_get_app_name ( FoobarNotification* self );
-gchar const* foobar_notification_get_body ( FoobarNotification* self );
-gchar const* foobar_notification_get_summary ( FoobarNotification* self );
-GdkPixbuf* foobar_notification_get_image ( FoobarNotification* self );
-gboolean foobar_notification_is_dismissed ( FoobarNotification* self );
-gboolean foobar_notification_is_resident ( FoobarNotification* self );
-gboolean foobar_notification_is_transient ( FoobarNotification* self );
-GDateTime* foobar_notification_get_time ( FoobarNotification* self );
-gint64 foobar_notification_get_timeout ( FoobarNotification* self );
-FoobarNotificationUrgency foobar_notification_get_urgency ( FoobarNotification* self );
-void foobar_notification_block_timeout ( FoobarNotification* self );
-void foobar_notification_resume_timeout( FoobarNotification* self );
-void foobar_notification_dismiss ( FoobarNotification* self );
-void foobar_notification_close ( FoobarNotification* self );
-
-G_DECLARE_FINAL_TYPE( FoobarNotificationService, foobar_notification_service, FOOBAR, NOTIFICATION_SERVICE, GObject )
-
-FoobarNotificationService* foobar_notification_service_new ( void );
-GListModel* foobar_notification_service_get_notifications ( FoobarNotificationService* self );
-GListModel* foobar_notification_service_get_popup_notifications( FoobarNotificationService* self );
-
-G_END_DECLS
diff --git a/src/services/quick-answer-service.c b/src/services/quick-answer-service.c
deleted file mode 100644
index 670a72f..0000000
--- a/src/services/quick-answer-service.c
+++ /dev/null
@@ -1,360 +0,0 @@
-#include "services/quick-answer-service.h"
-#include "services/quick-answers/math.h"
-#include "launcher-item.h"
-#include <gdk/gdk.h>
-
-//
-// FoobarQuickAnswer:
-//
-// A launcher item representing a quick, one-line answer to the query entered by the user.
-//
-
-struct _FoobarQuickAnswer
-{
- GObject parent_instance;
- gchar* title;
- gchar* value;
- GIcon* icon;
-};
-
-enum
-{
- ANSWER_PROP_VALUE = 1,
- ANSWER_PROP_TITLE,
- ANSWER_PROP_DESCRIPTION,
- ANSWER_PROP_ICON,
- N_ANSWER_PROPS,
-};
-
-static GParamSpec* answer_props[N_ANSWER_PROPS] = { 0 };
-
-static void foobar_quick_answer_class_init ( FoobarQuickAnswerClass* klass );
-static void foobar_quick_answer_launcher_item_interface_init( FoobarLauncherItemInterface* iface );
-static void foobar_quick_answer_init ( FoobarQuickAnswer* self );
-static void foobar_quick_answer_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_quick_answer_finalize ( GObject* object );
-static FoobarQuickAnswer* foobar_quick_answer_new ( void );
-static gchar const* foobar_quick_answer_get_title ( FoobarLauncherItem* self );
-static gchar const* foobar_quick_answer_get_description ( FoobarLauncherItem* self );
-static GIcon* foobar_quick_answer_get_icon ( FoobarLauncherItem* self );
-static void foobar_quick_answer_activate ( FoobarLauncherItem* self );
-static void foobar_quick_answer_set_title ( FoobarQuickAnswer* self,
- gchar const* value );
-static void foobar_quick_answer_set_value ( FoobarQuickAnswer* self,
- gchar const* value );
-static void foobar_quick_answer_set_icon ( FoobarQuickAnswer* self,
- GIcon* value );
-
-G_DEFINE_FINAL_TYPE_WITH_CODE(
- FoobarQuickAnswer,
- foobar_quick_answer,
- G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE( FOOBAR_TYPE_LAUNCHER_ITEM, foobar_quick_answer_launcher_item_interface_init ) )
-
-//
-// FoobarQuickAnswerService:
-//
-// Service providing quick answers for search queries in the launcher (if available). This includes evaluating
-// mathematical expressions.
-//
-
-struct _FoobarQuickAnswerService
-{
- GObject parent_instance;
-};
-
-static void foobar_quick_answer_service_class_init( FoobarQuickAnswerServiceClass* klass );
-static void foobar_quick_answer_service_init ( FoobarQuickAnswerService* self );
-static FoobarQuickAnswer* foobar_quick_answer_service_query_math( gchar const* query );
-
-G_DEFINE_FINAL_TYPE( FoobarQuickAnswerService, foobar_quick_answer_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Quick Answers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for quick answers.
-//
-void foobar_quick_answer_class_init( FoobarQuickAnswerClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_quick_answer_get_property;
- object_klass->finalize = foobar_quick_answer_finalize;
-
- gpointer launcher_item_iface = g_type_default_interface_peek( FOOBAR_TYPE_LAUNCHER_ITEM );
- answer_props[ANSWER_PROP_VALUE] = g_param_spec_string(
- "value",
- "Value",
- "The raw value of the quick answer.",
- NULL,
- G_PARAM_READABLE );
- answer_props[ANSWER_PROP_TITLE] = g_param_spec_override(
- "title",
- g_object_interface_find_property( launcher_item_iface, "title" ) );
- answer_props[ANSWER_PROP_DESCRIPTION] = g_param_spec_override(
- "description",
- g_object_interface_find_property( launcher_item_iface, "description" ) );
- answer_props[ANSWER_PROP_ICON] = g_param_spec_override(
- "icon",
- g_object_interface_find_property( launcher_item_iface, "icon" ) );
- g_object_class_install_properties( object_klass, N_ANSWER_PROPS, answer_props );
-}
-
-//
-// Static initialization of the FoobarLauncherItem interface.
-//
-void foobar_quick_answer_launcher_item_interface_init( FoobarLauncherItemInterface* iface )
-{
- iface->get_title = foobar_quick_answer_get_title;
- iface->get_description = foobar_quick_answer_get_description;
- iface->get_icon = foobar_quick_answer_get_icon;
- iface->activate = foobar_quick_answer_activate;
-}
-
-//
-// Instance initialization for quick answers.
-//
-void foobar_quick_answer_init( FoobarQuickAnswer* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_quick_answer_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarQuickAnswer* self = (FoobarQuickAnswer*)object;
-
- switch ( prop_id )
- {
- case ANSWER_PROP_VALUE:
- g_value_set_string( value, foobar_quick_answer_get_value( self ) );
- break;
- case ANSWER_PROP_TITLE:
- g_value_set_string( value, foobar_launcher_item_get_title( FOOBAR_LAUNCHER_ITEM ( self ) ) );
- break;
- case ANSWER_PROP_DESCRIPTION:
- g_value_set_string( value, foobar_launcher_item_get_description( FOOBAR_LAUNCHER_ITEM( self ) ) );
- break;
- case ANSWER_PROP_ICON:
- g_value_set_object( value, foobar_launcher_item_get_icon( FOOBAR_LAUNCHER_ITEM( self ) ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for quick answers.
-//
-void foobar_quick_answer_finalize( GObject* object )
-{
- FoobarQuickAnswer* self = (FoobarQuickAnswer*)object;
-
- g_clear_pointer( &self->title, g_free );
- g_clear_pointer( &self->value, g_free );
- g_clear_object( &self->icon );
-
- G_OBJECT_CLASS( foobar_quick_answer_parent_class )->finalize( object );
-}
-
-//
-// Create a new, default-initialized quick answer.
-//
-FoobarQuickAnswer* foobar_quick_answer_new( void )
-{
- return g_object_new( FOOBAR_TYPE_QUICK_ANSWER, NULL );
-}
-
-//
-// Get the raw value of the quick answer.
-//
-gchar const* foobar_quick_answer_get_value( FoobarQuickAnswer* self )
-{
- g_return_val_if_fail( FOOBAR_IS_QUICK_ANSWER( self ), NULL );
- return self->value;
-}
-
-//
-// Get the title for the quick answer.
-//
-gchar const* foobar_quick_answer_get_title( FoobarLauncherItem* item )
-{
- FoobarQuickAnswer* self = ( FoobarQuickAnswer* )item;
- return self->title;
-}
-
-//
-// Get the description shown below the title, always NULL for quick answers.
-//
-gchar const* foobar_quick_answer_get_description( FoobarLauncherItem* item )
-{
- (void)item;
- return NULL;
-}
-
-//
-// Get an icon representing the quick answer.
-//
-GIcon* foobar_quick_answer_get_icon( FoobarLauncherItem* item )
-{
- FoobarQuickAnswer* self = ( FoobarQuickAnswer* )item;
- return self->icon;
-}
-
-//
-// Copy the quick answer's value into the clipboard.
-//
-void foobar_quick_answer_activate( FoobarLauncherItem* item )
-{
- FoobarQuickAnswer* self = ( FoobarQuickAnswer* )item;
- if ( self->value )
- {
- GdkClipboard* clipboard = gdk_display_get_clipboard( gdk_display_get_default( ) );
- gdk_clipboard_set_text( clipboard, self->value );
- }
-}
-
-//
-// Update the raw value of the quick answer.
-//
-void foobar_quick_answer_set_value(
- FoobarQuickAnswer* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_QUICK_ANSWER( self ) );
-
- if ( g_strcmp0( self->value, value ) )
- {
- g_clear_pointer( &self->value, g_free );
- self->value = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), answer_props[ANSWER_PROP_VALUE] );
- }
-}
-
-//
-// Update the title for the quick answer.
-//
-void foobar_quick_answer_set_title(
- FoobarQuickAnswer* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_QUICK_ANSWER( self ) );
-
- if ( g_strcmp0( self->title, value ) )
- {
- g_clear_pointer( &self->title, g_free );
- self->title = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), answer_props[ANSWER_PROP_TITLE] );
- }
-}
-
-//
-// Update the icon representing the quick answer.
-//
-void foobar_quick_answer_set_icon(
- FoobarQuickAnswer* self,
- GIcon* value )
-{
- g_return_if_fail( FOOBAR_IS_QUICK_ANSWER( self ) );
-
- if ( self->icon != value )
- {
- g_clear_object( &self->icon );
- self->icon = value ? g_object_ref( value ) : NULL;
- g_object_notify_by_pspec( G_OBJECT( self ), answer_props[ANSWER_PROP_ICON] );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the quick answer service.
-//
-void foobar_quick_answer_service_class_init( FoobarQuickAnswerServiceClass* klass )
-{
- (void)klass;
-}
-
-//
-// Instance initialization for the quick answer service.
-//
-void foobar_quick_answer_service_init( FoobarQuickAnswerService* self )
-{
- (void)self;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new quick answer service instance.
-//
-FoobarQuickAnswerService* foobar_quick_answer_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_QUICK_ANSWER_SERVICE, NULL );
-}
-
-//
-// Query the service for a quick answer.
-//
-FoobarQuickAnswer* foobar_quick_answer_service_query(
- FoobarQuickAnswerService* self,
- gchar const* query )
-{
- (void)self;
-
- FoobarQuickAnswer* result = foobar_quick_answer_service_query_math( query );
- if ( result ) { return result; }
-
- return NULL;
-}
-
-//
-// Try to parse the query as a mathematical expression and evaluate it.
-//
-FoobarQuickAnswer* foobar_quick_answer_service_query_math( gchar const* query )
-{
- FoobarQuickAnswer* result = NULL;
-
- gsize token_count;
- g_autofree FoobarMathToken* tokens = foobar_math_lex( query, strlen( query ), &token_count );
- if ( tokens )
- {
- FoobarMathExpression* expr = foobar_math_parse( tokens, token_count );
- if ( expr )
- {
- // foobar_math_expression_print( expr, 0 );
-
- FoobarMathValue val = { 0 };
- if ( foobar_math_evaluate( expr, &val ) )
- {
- g_autofree gchar* value = foobar_math_value_to_string( val );
- g_autofree gchar* title = g_strdup_printf( "= %s", value );
- g_autoptr( GIcon ) icon = g_themed_icon_new( "fluent-calculator-symbolic" );
- result = foobar_quick_answer_new( );
- foobar_quick_answer_set_value( result, value );
- foobar_quick_answer_set_title( result, title );
- foobar_quick_answer_set_icon( result, icon );
- foobar_math_value_free( val );
- }
-
- foobar_math_expression_free( expr );
- }
- }
-
- return result;
-}
diff --git a/src/services/quick-answer-service.h b/src/services/quick-answer-service.h
deleted file mode 100644
index adb0701..0000000
--- a/src/services/quick-answer-service.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_QUICK_ANSWER foobar_quick_answer_get_type( )
-#define FOOBAR_TYPE_QUICK_ANSWER_SERVICE foobar_quick_answer_service_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarQuickAnswer, foobar_quick_answer, FOOBAR, QUICK_ANSWER, GObject )
-
-gchar const* foobar_quick_answer_get_value( FoobarQuickAnswer* self );
-
-G_DECLARE_FINAL_TYPE( FoobarQuickAnswerService, foobar_quick_answer_service, FOOBAR, QUICK_ANSWER_SERVICE, GObject )
-
-FoobarQuickAnswerService* foobar_quick_answer_service_new ( void );
-FoobarQuickAnswer* foobar_quick_answer_service_query ( FoobarQuickAnswerService* self,
- gchar const* query );
-
-G_END_DECLS
diff --git a/src/services/quick-answers/math-lexer.c b/src/services/quick-answers/math-lexer.c
deleted file mode 100644
index 7fbe877..0000000
--- a/src/services/quick-answers/math-lexer.c
+++ /dev/null
@@ -1,248 +0,0 @@
-#include "services/quick-answers/math.h"
-
-//
-// FoobarMathToken:
-//
-// A token in a mathematical expression. The token contents are left as-is and need not be null-terminated. For example,
-// "sin(pi) - 3" would be represented as:
-// - IDENTIFIER: "sin"
-// - PAREN_OPEN: "("
-// - IDENTIFIER: "pi"
-// - PAREN_CLOSE: ")"
-// - MINUS: "-"
-// - NUMBER: "3"
-//
-
-//
-// Lexer:
-//
-// The lexer context/state which is passed around.
-//
-
-typedef struct _Lexer Lexer;
-
-struct _Lexer
-{
- GArray* result;
- gchar const* input;
- gsize input_length;
- gsize position;
- gsize token_start;
-};
-
-static gboolean char_is_whitespace( char c );
-static gboolean char_is_digit ( char c );
-static gboolean char_is_identifier( char c );
-
-static char lexer_peek ( Lexer const* ctx,
- gsize offset );
-static char lexer_pop ( Lexer* ctx );
-static void lexer_begin_token ( Lexer* ctx );
-static void lexer_commit_token ( Lexer* ctx,
- FoobarMathTokenType type );
-static void lexer_commit_single( Lexer* ctx,
- FoobarMathTokenType type );
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Lexing
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Divide an input string into a list of tokens.
-//
-// On success, this will return a newly allocated array of tokens which should be freed using g_free. The number of
-// elements will be written into out_count.
-//
-// On error, this will return NULL.
-//
-FoobarMathToken* foobar_math_lex(
- gchar const* input,
- gsize input_length,
- gsize* out_count )
-{
- g_autoptr( GArray ) result = g_array_new( FALSE, TRUE, sizeof( FoobarMathToken ) );
-
- Lexer ctx = { 0 };
- ctx.result = result;
- ctx.input = input;
- ctx.input_length = input_length;
-
- while ( ctx.position < input_length )
- {
- if ( char_is_whitespace( lexer_peek( &ctx, 0 ) ) )
- {
- // Skip whitespace.
-
- lexer_pop( &ctx );
- }
- else if ( char_is_digit( lexer_peek( &ctx, 0 ) ) )
- {
- // Process numbers as a whole, don't allow whitespace inbetween.
-
- lexer_begin_token( &ctx );
- while ( char_is_digit( lexer_peek( &ctx, 0 ) ) )
- {
- lexer_pop( &ctx );
- }
-
- if ( lexer_peek( &ctx, 0 ) == '.' && char_is_digit( lexer_peek( &ctx, 1 ) ) )
- {
- lexer_pop( &ctx );
- while ( char_is_digit( lexer_peek( &ctx, 0 ) ) )
- {
- lexer_pop( &ctx );
- }
- }
-
- lexer_commit_token( &ctx, FOOBAR_MATH_TOKEN_NUMBER );
- }
- else if ( char_is_identifier( lexer_peek( &ctx, 0 ) ) )
- {
- // Don't validate identifiers, leave that to the parser.
-
- lexer_begin_token( &ctx );
- while ( char_is_identifier( lexer_peek( &ctx, 0 ) ) )
- {
- lexer_pop( &ctx );
- }
- lexer_commit_token( &ctx, FOOBAR_MATH_TOKEN_IDENTIFIER );
- }
- else if ( lexer_peek( &ctx, 0 ) == '(' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_PAREN_OPEN );
- }
- else if ( lexer_peek( &ctx, 0 ) == ')' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_PAREN_CLOSE );
- }
- else if ( lexer_peek( &ctx, 0 ) == '+' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_PLUS );
- }
- else if ( lexer_peek( &ctx, 0 ) == '-' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_MINUS );
- }
- else if ( lexer_peek( &ctx, 0 ) == '*' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_MUL );
- }
- else if ( lexer_peek( &ctx, 0 ) == '/' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_DIV );
- }
- else if ( lexer_peek( &ctx, 0 ) == '^' )
- {
- lexer_commit_single( &ctx, FOOBAR_MATH_TOKEN_POW );
- }
- else
- {
- // Unknown character.
-
- return NULL;
- }
- }
-
- return g_array_steal( result, out_count );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Character Classification
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Check if a character is a whitespace symbol (space, tab, carriage return).
-//
-gboolean char_is_whitespace( char c )
-{
- return c == ' ' || c == '\t' || c == '\r';
-}
-
-//
-// Check if a character is a decimal digit.
-//
-gboolean char_is_digit( char c )
-{
- return c >= '0' && c <= '9';
-}
-
-//
-// Check if a character belongs to an identifier (like "sin", "pi", etc.).
-//
-gboolean char_is_identifier( char c )
-{
- // Identifiers are all lowercase ASCII.
-
- return c >= 'a' && c <= 'z';
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// State Helpers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Peek at the current character without consuming it.
-//
-// If this is the end of the input string, return 0.
-//
-char lexer_peek(
- Lexer const* ctx,
- gsize offset )
-{
- gsize position = ctx->position + offset;
- if ( position >= ctx->input_length ) { return 0; }
-
- return ctx->input[position];
-}
-
-//
-// Consume the current character, moving forward to the next one.
-//
-// If this is the end of the input string, return 0.
-//
-char lexer_pop( Lexer* ctx )
-{
- if ( ctx->position >= ctx->input_length ) { return 0; }
-
- return ctx->input[ctx->position++];
-}
-
-//
-// Start recording a token by remembering the current position.
-//
-// The current character will be the first one in the token.
-//
-void lexer_begin_token( Lexer* ctx )
-{
- ctx->token_start = ctx->position;
-}
-
-//
-// Stop recording a token, saving the string between the call to lexer_begin_token as the specified type.
-//
-// The current character will not be a part of the token.
-//
-void lexer_commit_token(
- Lexer* ctx,
- FoobarMathTokenType type )
-{
- FoobarMathToken token = { 0 };
- token.data = &ctx->input[ctx->token_start];
- token.length = ctx->position - ctx->token_start;
- token.type = type;
- g_array_append_val( ctx->result, token );
-}
-
-//
-// Consume the current character and record it as a token.
-//
-// This is equivalent to calling lexer_begin_token, lexer_pop, and lexer_commit_token.
-//
-void lexer_commit_single(
- Lexer* ctx,
- FoobarMathTokenType type )
-{
- lexer_begin_token( ctx );
- lexer_pop( ctx );
- lexer_commit_token( ctx, type );
-}
diff --git a/src/services/quick-answers/math-parser.c b/src/services/quick-answers/math-parser.c
deleted file mode 100644
index 17d976d..0000000
--- a/src/services/quick-answers/math-parser.c
+++ /dev/null
@@ -1,414 +0,0 @@
-#include "services/quick-answers/math.h"
-
-//
-// Parser:
-//
-// The parser context/state which is passed around.
-//
-
-typedef struct _Parser Parser;
-
-struct _Parser
-{
- FoobarMathToken const* tokens;
- gsize tokens_count;
- gsize position;
-};
-
-//
-// InfixPrecedence:
-//
-// Value indicating how strongly infix operators should bind. The higher the value, the stronger they should bind.
-//
-
-typedef enum
-{
- INFIX_PRECEDENCE_INVALID = -1,
- INFIX_PRECEDENCE_NONE = 0,
- INFIX_PRECEDENCE_ADDITIVE = 1,
- INFIX_PRECEDENCE_MULTIPLICATIVE = 2,
- INFIX_PRECEDENCE_EXPONENTS = 3,
-} InfixPrecedence;
-
-static FoobarMathExpression* parser_process ( Parser* ctx,
- FoobarMathExpression* lhs,
- InfixPrecedence min_precedence );
-static FoobarMathExpression* parser_process_single ( Parser* ctx,
- gboolean allow_sign );
-static FoobarMathExpression* parser_process_identifier ( Parser* ctx );
-static FoobarMathExpression* parser_process_number ( Parser* ctx );
-static FoobarMathExpression* parser_process_function ( Parser* ctx,
- FoobarMathFunction function );
-static FoobarMathExpression* parser_process_parens ( Parser* ctx );
-static FoobarMathExpression* parser_process_sign ( Parser* ctx );
-static FoobarMathToken const* parser_peek ( Parser const* ctx );
-static FoobarMathToken const* parser_pop ( Parser* ctx );
-static gboolean parser_match_identifier ( FoobarMathToken const* token,
- gchar const* identifier );
-static InfixPrecedence parser_operator_precedence( FoobarMathToken const* token );
-static FoobarMathOperation parser_operator ( FoobarMathToken const* token );
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Parsing
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Transform a sequence of tokens into a mathematical expression.
-//
-// On success, this will return a newly allocated expression which should be freed using foobar_math_expression_free.
-//
-// On error, this will return NULL.
-//
-FoobarMathExpression* foobar_math_parse(
- FoobarMathToken const* tokens,
- gsize tokens_count )
-{
- Parser ctx = { 0 };
- ctx.tokens = tokens;
- ctx.tokens_count = tokens_count;
-
- FoobarMathExpression* lhs = parser_process_single( &ctx, TRUE );
- if ( !lhs ) { return NULL; }
-
- FoobarMathExpression* expr = parser_process( &ctx, lhs, INFIX_PRECEDENCE_NONE );
- if ( !expr ) { return NULL; }
-
- if ( parser_peek( &ctx ) )
- {
- foobar_math_expression_free( expr );
- return NULL;
- }
-
- return expr;
-}
-
-//
-// Process tokens until the end of the current precedence scope (indicated by min_precedence), starting at an operator.
-//
-FoobarMathExpression* parser_process(
- Parser* ctx,
- FoobarMathExpression* lhs,
- InfixPrecedence min_precedence )
-{
- FoobarMathToken const* op = parser_peek( ctx );
- InfixPrecedence op_precedence = parser_operator_precedence( op );
- while ( op_precedence >= min_precedence )
- {
- parser_pop( ctx );
-
- FoobarMathExpression* rhs = parser_process_single( ctx, FALSE );
- if ( !rhs )
- {
- foobar_math_expression_free( lhs );
- return NULL;
- }
-
- FoobarMathToken const* next_op = parser_peek( ctx );
- while ( parser_operator_precedence( next_op ) > op_precedence )
- {
- rhs = parser_process( ctx, rhs, op_precedence + 1 );
- if ( !rhs )
- {
- foobar_math_expression_free( lhs );
- return NULL;
- }
-
- next_op = parser_peek( ctx );
- }
-
- lhs = foobar_math_expression_new_operation( parser_operator( op ), lhs, rhs );
-
- op = parser_peek( ctx );
- op_precedence = parser_operator_precedence( op );
- }
-
- return lhs;
-}
-
-//
-// Process a single value which may be either a constant, function, number, expression enclosed in parenthesis or
-// negation.
-//
-// If an unexpected token is encountered (i.e. an operator), this will return FALSE.
-//
-FoobarMathExpression* parser_process_single(
- Parser* ctx,
- gboolean allow_sign )
-{
- FoobarMathToken const* token = parser_peek( ctx );
- if ( !token ) { return NULL; }
-
- switch ( token->type )
- {
- case FOOBAR_MATH_TOKEN_IDENTIFIER:
- return parser_process_identifier( ctx );
- case FOOBAR_MATH_TOKEN_NUMBER:
- return parser_process_number( ctx );
- case FOOBAR_MATH_TOKEN_PAREN_OPEN:
- return parser_process_parens( ctx );
- case FOOBAR_MATH_TOKEN_MINUS:
- case FOOBAR_MATH_TOKEN_PLUS:
- if ( !allow_sign ) { return NULL; }
- return parser_process_sign( ctx );
- case FOOBAR_MATH_TOKEN_PAREN_CLOSE:
- case FOOBAR_MATH_TOKEN_MUL:
- case FOOBAR_MATH_TOKEN_DIV:
- case FOOBAR_MATH_TOKEN_POW:
- return NULL;
- default:
- g_warn_if_reached( );
- return NULL;
- }
-}
-
-//
-// Parse an identifier.
-//
-// If the identifier is a function, this will expected parenthesis and an expression afterwards.
-//
-FoobarMathExpression* parser_process_identifier( Parser* ctx )
-{
- FoobarMathToken const* token = parser_pop( ctx );
- if ( parser_match_identifier( token, "pi" ) )
- {
- return foobar_math_expression_new_constant( FOOBAR_MATH_CONSTANT_PI );
- }
- else if ( parser_match_identifier( token, "e" ) )
- {
- return foobar_math_expression_new_constant( FOOBAR_MATH_CONSTANT_E );
- }
- else if ( parser_match_identifier( token, "exp" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_EXP );
- }
- else if ( parser_match_identifier( token, "sqrt" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_SQRT );
- }
- else if ( parser_match_identifier( token, "sin" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_SIN );
- }
- else if ( parser_match_identifier( token, "cos" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_COS );
- }
- else if ( parser_match_identifier( token, "sec" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_SEC );
- }
- else if ( parser_match_identifier( token, "csc" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_CSC );
- }
- else if ( parser_match_identifier( token, "tan" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_TAN );
- }
- else if ( parser_match_identifier( token, "cot" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_COT );
- }
- else if ( parser_match_identifier( token, "arcsin" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCSIN );
- }
- else if ( parser_match_identifier( token, "arccos" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCCOS );
- }
- else if ( parser_match_identifier( token, "arcsec" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCSEC );
- }
- else if ( parser_match_identifier( token, "arccsc" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCCSC );
- }
- else if ( parser_match_identifier( token, "arctan" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCTAN );
- }
- else if ( parser_match_identifier( token, "arccot" ) )
- {
- return parser_process_function( ctx, FOOBAR_MATH_FUNCTION_ARCCOT );
- }
- else
- {
- return NULL;
- }
-}
-
-//
-// Process a numeric value.
-//
-FoobarMathExpression* parser_process_number( Parser* ctx )
-{
- FoobarMathToken const* token = parser_pop( ctx );
- FoobarMathValue value = { 0 };
- if ( !foobar_math_value_from_string( token->data, token->length, &value ) )
- {
- return NULL;
- }
-
- return foobar_math_expression_new_value( value );
-}
-
-//
-// Process a function's parameter which should be enclosed in parenthesis.
-//
-FoobarMathExpression* parser_process_function(
- Parser* ctx,
- FoobarMathFunction function )
-{
- FoobarMathToken const* opening_paren = parser_peek( ctx );
- if ( !opening_paren || opening_paren->type != FOOBAR_MATH_TOKEN_PAREN_OPEN )
- {
- return NULL;
- }
-
- FoobarMathExpression* arg = parser_process_parens( ctx );
- if ( !arg ) { return NULL; }
-
- return foobar_math_expression_new_function( function, arg );
-}
-
-//
-// Process an expression enclosed in parenthesis.
-//
-FoobarMathExpression* parser_process_parens( Parser* ctx )
-{
- parser_pop( ctx );
- FoobarMathExpression* lhs = parser_process_single( ctx, TRUE );
- if ( !lhs ) { return NULL; }
- FoobarMathExpression* result = parser_process( ctx, lhs, INFIX_PRECEDENCE_NONE );
- if ( !result ) { return NULL; }
- FoobarMathToken const* closing_paren = parser_pop( ctx );
- if ( !closing_paren || closing_paren->type != FOOBAR_MATH_TOKEN_PAREN_CLOSE )
- {
- foobar_math_expression_free( result );
- return NULL;
- }
- return result;
-}
-
-//
-// Process a sign (- or +) which may be used for negation of an expression.
-//
-FoobarMathExpression* parser_process_sign( Parser* ctx )
-{
- FoobarMathToken const* token = parser_pop( ctx );
- FoobarMathExpression* result = parser_process_single( ctx, FALSE );
- if ( !result ) { return NULL; }
-
- if ( token->type == FOOBAR_MATH_TOKEN_MINUS )
- {
- return foobar_math_expression_new_function( FOOBAR_MATH_FUNCTION_NEGATE, result );
- }
- else
- {
- return result;
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// State Helpers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Peek at the current token without consuming it.
-//
-// If this is the end of the token list, return NULL.
-//
-FoobarMathToken const* parser_peek( Parser const* ctx )
-{
- if ( ctx->position >= ctx->tokens_count ) { return NULL; }
-
- return &ctx->tokens[ctx->position];
-}
-
-//
-// Consume the current token.
-//
-// If this is the end of the token list, return NULL.
-//
-FoobarMathToken const* parser_pop( Parser* ctx )
-{
- if ( ctx->position >= ctx->tokens_count ) { return NULL; }
-
- return &ctx->tokens[ctx->position++];
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helpers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Match a token's string against the given identifier string.
-//
-// This is necessary because the token's content is not a null-terminated string.
-//
-gboolean parser_match_identifier(
- FoobarMathToken const* token,
- gchar const* identifier )
-{
- if ( strlen( identifier ) != token->length ) { return FALSE; }
-
- return !strncmp( token->data, identifier, token->length );
-}
-
-//
-// Get the precedence of an infix operator token or INVALID.
-//
-InfixPrecedence parser_operator_precedence( FoobarMathToken const* token )
-{
- if ( token )
- {
- switch ( token->type )
- {
- case FOOBAR_MATH_TOKEN_PLUS:
- case FOOBAR_MATH_TOKEN_MINUS:
- return INFIX_PRECEDENCE_ADDITIVE;
- case FOOBAR_MATH_TOKEN_MUL:
- case FOOBAR_MATH_TOKEN_DIV:
- return INFIX_PRECEDENCE_MULTIPLICATIVE;
- case FOOBAR_MATH_TOKEN_POW:
- return INFIX_PRECEDENCE_EXPONENTS;
- case FOOBAR_MATH_TOKEN_IDENTIFIER:
- case FOOBAR_MATH_TOKEN_NUMBER:
- case FOOBAR_MATH_TOKEN_PAREN_OPEN:
- case FOOBAR_MATH_TOKEN_PAREN_CLOSE:
- default:
- break;
- }
- }
-
- return INFIX_PRECEDENCE_INVALID;
-}
-
-//
-// Get the operation for an infix operator token. This requires the token to be valid.
-//
-FoobarMathOperation parser_operator( FoobarMathToken const* token )
-{
- switch ( token->type )
- {
- case FOOBAR_MATH_TOKEN_PLUS:
- return FOOBAR_MATH_OPERATION_ADD;
- case FOOBAR_MATH_TOKEN_MINUS:
- return FOOBAR_MATH_OPERATION_SUB;
- case FOOBAR_MATH_TOKEN_MUL:
- return FOOBAR_MATH_OPERATION_MUL;
- case FOOBAR_MATH_TOKEN_DIV:
- return FOOBAR_MATH_OPERATION_DIV;
- case FOOBAR_MATH_TOKEN_POW:
- return FOOBAR_MATH_OPERATION_POW;
- case FOOBAR_MATH_TOKEN_IDENTIFIER:
- case FOOBAR_MATH_TOKEN_NUMBER:
- case FOOBAR_MATH_TOKEN_PAREN_OPEN:
- case FOOBAR_MATH_TOKEN_PAREN_CLOSE:
- default:
- g_warn_if_reached( );
- return 0;
- }
-}
diff --git a/src/services/quick-answers/math.c b/src/services/quick-answers/math.c
deleted file mode 100644
index 69219e2..0000000
--- a/src/services/quick-answers/math.c
+++ /dev/null
@@ -1,851 +0,0 @@
-#define _GNU_SOURCE
-
-#include "services/quick-answers/math.h"
-#include <math.h>
-
-//
-// FoobarMathExpression:
-//
-// A fully parsed mathematical expression which can be evaluated, for example "sin(pi) - 3" would be represented as:
-// - Operation: SUB
-// - Function: SIN
-// - Constant: PI
-// - Value: 3
-//
-
-struct _FoobarMathExpression
-{
- FoobarMathExpressionType type;
-
- union
- {
- struct
- {
- FoobarMathValue v;
- } value;
-
- struct
- {
- FoobarMathFunction f;
- FoobarMathExpression* input;
- } function;
-
- struct
- {
- FoobarMathConstant c;
- } constant;
-
- struct
- {
- FoobarMathOperation o;
- FoobarMathExpression* lhs;
- FoobarMathExpression* rhs;
- } operation;
- };
-};
-
-//
-// FoobarMathValue:
-//
-// Representation of a value. The goal of this memory structure is to store the value as an integer for as long as
-// possible. Because of this, there is a "decimal_places" field, so the actual value of an integer is
-// $v * 10^(-decimal_places)$.
-//
-// If absolutely necessary (for example, because we use a function like sin), the value is converted to a floating-point
-// value.
-//
-// Note that we use GMP to store the integer value to allow for arbitrarily large values. Because of this, the structure
-// should always be freed using foobar_math_value_free.
-//
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Expressions
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Helper to allocate a new FoobarMathExpression representing a value.
-//
-// Ownership of "value" is transferred to the new expression.
-//
-FoobarMathExpression* foobar_math_expression_new_value( FoobarMathValue value )
-{
- FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
- res->type = FOOBAR_MATH_EXPRESSION_VALUE;
- res->value.v = value;
- return res;
-}
-
-//
-// Helper to allocate a new FoobarMathExpression representing a function to be evaluated with another expression as its
-// parameter.
-//
-// Ownership of "input" is transferred to the new expression.
-//
-FoobarMathExpression* foobar_math_expression_new_function(
- FoobarMathFunction function,
- FoobarMathExpression* input )
-{
- FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
- res->type = FOOBAR_MATH_EXPRESSION_FUNCTION;
- res->function.f = function;
- res->function.input = input;
- return res;
-}
-
-//
-// Helper to allocate a new FoobarMathExpression representing a known constant.
-//
-FoobarMathExpression* foobar_math_expression_new_constant( FoobarMathConstant constant )
-{
- FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
- res->type = FOOBAR_MATH_EXPRESSION_CONSTANT;
- res->constant.c = constant;
- return res;
-}
-
-//
-// Helper to allocate a new FoobarMathExpression representing an operation to be evaluated with two expressions as its
-// parameters.
-//
-// Ownership of "lhs" and "rhs" is transferred to the new expression.
-//
-FoobarMathExpression* foobar_math_expression_new_operation(
- FoobarMathOperation operation,
- FoobarMathExpression* lhs,
- FoobarMathExpression* rhs )
-{
- FoobarMathExpression* res = g_new0( FoobarMathExpression, 1 );
- res->type = FOOBAR_MATH_EXPRESSION_OPERATION;
- res->operation.o = operation;
- res->operation.lhs = lhs;
- res->operation.rhs = rhs;
- return res;
-}
-
-//
-// Release the memory associated with a mathematical expression, along with the expressions it owns.
-//
-void foobar_math_expression_free( FoobarMathExpression* expression )
-{
- if ( expression )
- {
- switch ( expression->type )
- {
- case FOOBAR_MATH_EXPRESSION_VALUE:
- foobar_math_value_free( expression->value.v );
- break;
- case FOOBAR_MATH_EXPRESSION_FUNCTION:
- foobar_math_expression_free( expression->function.input );
- break;
- case FOOBAR_MATH_EXPRESSION_CONSTANT:
- break;
- case FOOBAR_MATH_EXPRESSION_OPERATION:
- foobar_math_expression_free( expression->operation.lhs );
- foobar_math_expression_free( expression->operation.rhs );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-
- g_free( expression );
- }
-}
-
-//
-// Print a tree representation of a mathematical expression.
-//
-// This is mainly useful for debugging. "indentation" describes the current indentation level for the recursive call
-// (should be 0 initially).
-//
-void foobar_math_expression_print(
- FoobarMathExpression const* expr,
- gint indentation )
-{
- for (gint i = 0; i < indentation; ++i) { g_print("| "); }
- switch ( expr->type )
- {
- case FOOBAR_MATH_EXPRESSION_VALUE:
- {
- g_autofree gchar* val = foobar_math_value_to_string( expr->value.v );
- g_print( "%s\n", val );
- break;
- }
- case FOOBAR_MATH_EXPRESSION_FUNCTION:
- switch ( expr->function.f )
- {
- case FOOBAR_MATH_FUNCTION_NEGATE:
- g_print( "-\n" );
- break;
- case FOOBAR_MATH_FUNCTION_SIN:
- g_print( "sin\n" );
- break;
- case FOOBAR_MATH_FUNCTION_COS:
- g_print( "cos\n" );
- break;
- case FOOBAR_MATH_FUNCTION_SEC:
- g_print( "sec\n" );
- break;
- case FOOBAR_MATH_FUNCTION_CSC:
- g_print( "csc\n" );
- break;
- case FOOBAR_MATH_FUNCTION_TAN:
- g_print( "tan\n" );
- break;
- case FOOBAR_MATH_FUNCTION_COT:
- g_print( "cot\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCSIN:
- g_print( "arcsin\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCCOS:
- g_print( "arccos\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCSEC:
- g_print( "arcsec\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCCSC:
- g_print( "arccsc\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCTAN:
- g_print( "arctan\n" );
- break;
- case FOOBAR_MATH_FUNCTION_ARCCOT:
- g_print( "arccot\n" );
- break;
- case FOOBAR_MATH_FUNCTION_EXP:
- g_print( "exp\n" );
- break;
- case FOOBAR_MATH_FUNCTION_SQRT:
- g_print( "sqrt\n" );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
- foobar_math_expression_print( expr->function.input, indentation + 1 );
- break;
- case FOOBAR_MATH_EXPRESSION_CONSTANT:
- switch ( expr->constant.c )
- {
- case FOOBAR_MATH_CONSTANT_PI:
- g_print( "pi\n" );
- break;
- case FOOBAR_MATH_CONSTANT_E:
- g_print( "e\n" );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
- break;
- case FOOBAR_MATH_EXPRESSION_OPERATION:
- switch ( expr->operation.o )
- {
- case FOOBAR_MATH_OPERATION_ADD:
- g_print( "+\n" );
- break;
- case FOOBAR_MATH_OPERATION_SUB:
- g_print( "-\n" );
- break;
- case FOOBAR_MATH_OPERATION_MUL:
- g_print( "*\n" );
- break;
- case FOOBAR_MATH_OPERATION_DIV:
- g_print( "/\n" );
- break;
- case FOOBAR_MATH_OPERATION_POW:
- g_print( "^\n" );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
- foobar_math_expression_print( expr->operation.lhs, indentation + 1 );
- foobar_math_expression_print( expr->operation.rhs, indentation + 1 );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-}
-
-//
-// Evaluate an expression, producing a value.
-//
-// On success (indicated by the return value TRUE), the value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_evaluate(
- FoobarMathExpression const* expr,
- FoobarMathValue* out_value )
-{
- switch ( expr->type )
- {
- case FOOBAR_MATH_EXPRESSION_VALUE:
- foobar_math_value_copy( expr->value.v, out_value );
- return TRUE;
- case FOOBAR_MATH_EXPRESSION_CONSTANT:
- switch ( expr->constant.c )
- {
- case FOOBAR_MATH_CONSTANT_PI:
- foobar_math_value_from_float( M_PIl, out_value );
- return TRUE;
- case FOOBAR_MATH_CONSTANT_E:
- foobar_math_value_from_float( M_El, out_value );
- return TRUE;
- default:
- g_warn_if_reached( );
- return FALSE;
- }
- case FOOBAR_MATH_EXPRESSION_FUNCTION:
- {
- FoobarMathValue input = { 0 };
- if ( !foobar_math_evaluate( expr->function.input, &input ) ) { return FALSE; }
-
- if ( expr->function.f == FOOBAR_MATH_FUNCTION_NEGATE )
- {
- *out_value = input;
- foobar_math_value_negate( out_value );
- return TRUE;
- }
-
- long double input_float = foobar_math_value_to_float( input );
- foobar_math_value_free( input );
-
- long double result = NAN;
- switch ( expr->function.f )
- {
- case FOOBAR_MATH_FUNCTION_SIN:
- result = sinl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_COS:
- result = cosl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_SEC:
- result = 1.L / cosl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_CSC:
- result = 1.L / sinl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_TAN:
- result = tanl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_COT:
- result = 1.L / tanl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_ARCSIN:
- result = asinl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_ARCCOS:
- result = acosl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_ARCSEC:
- result = acosl( 1.L / input_float );
- break;
- case FOOBAR_MATH_FUNCTION_ARCCSC:
- result = asinl( 1.L / input_float );
- break;
- case FOOBAR_MATH_FUNCTION_ARCTAN:
- result = atanl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_ARCCOT:
- result = atanl( 1.L / input_float );
- break;
- case FOOBAR_MATH_FUNCTION_EXP:
- result = expl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_SQRT:
- result = sqrtl( input_float );
- break;
- case FOOBAR_MATH_FUNCTION_NEGATE:
- default:
- g_warn_if_reached( );
- return FALSE;
- }
-
- if ( isnanl( result ) || isinfl( result ) ) { return FALSE; }
-
- foobar_math_value_from_float( result, out_value );
- return TRUE;
- }
- case FOOBAR_MATH_EXPRESSION_OPERATION:
- {
- FoobarMathValue lhs = { 0 };
- if ( !foobar_math_evaluate( expr->operation.lhs, &lhs ) ) { return FALSE; }
- FoobarMathValue rhs = { 0 };
- if ( !foobar_math_evaluate( expr->operation.rhs, &rhs ) )
- {
- foobar_math_value_free( lhs );
- return FALSE;
- }
-
- gboolean success = FALSE;
- switch ( expr->operation.o )
- {
- case FOOBAR_MATH_OPERATION_ADD:
- success = foobar_math_value_add( &lhs, &rhs, out_value );
- break;
- case FOOBAR_MATH_OPERATION_SUB:
- success = foobar_math_value_sub( &lhs, &rhs, out_value );
- break;
- case FOOBAR_MATH_OPERATION_MUL:
- success = foobar_math_value_mul( &lhs, &rhs, out_value );
- break;
- case FOOBAR_MATH_OPERATION_DIV:
- success = foobar_math_value_div( &lhs, &rhs, out_value );
- break;
- case FOOBAR_MATH_OPERATION_POW:
- success = foobar_math_value_pow( &lhs, &rhs, out_value );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-
- foobar_math_value_free( lhs );
- foobar_math_value_free( rhs );
-
- return success;
- }
- default:
- g_warn_if_reached( );
- return FALSE;
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Values
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Initialize a new integer value to zero.
-//
-// After this, the structure should be freed using foobar_math_value_free.
-//
-void foobar_math_value_new_int( FoobarMathValue* out_value )
-{
- out_value->type = FOOBAR_MATH_VALUE_INT;
- mpz_init( out_value->int_value.v );
- out_value->int_value.decimal_places = 0;
-}
-
-//
-// Initialize a new value from its floating point representation.
-//
-// Although technically not necessary, you should still call foobar_math_value_free on the returned value.
-//
-void foobar_math_value_from_float(
- long double value,
- FoobarMathValue* out_value )
-{
- out_value->type = FOOBAR_MATH_VALUE_FLOAT;
- out_value->float_value.v = value;
-}
-
-//
-// Parse a value from its string representation. This will always return an integer representation.
-//
-// On success (return value TRUE), the structure should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_from_string(
- gchar const* input,
- gsize input_length,
- FoobarMathValue* out_value )
-{
- g_autofree gchar* without_sep = g_new0( gchar, input_length + 1 );
- gsize sep_pos = input_length - 1;
- gsize j = 0;
- for ( gsize i = 0; i < input_length; ++i )
- {
- if ( input[i] == '.' )
- {
- sep_pos = i;
- }
- else
- {
- without_sep[j++] = input[i];
- }
- }
-
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = input_length - 1 - sep_pos;
- if ( mpz_init_set_str( out_value->int_value.v, without_sep, 10 ) == -1 )
- {
- mpz_clear( out_value->int_value.v );
- return FALSE;
- }
-
- return TRUE;
-}
-
-//
-// Create a copy of a value, allocating necessary memory.
-//
-// After this, the structure should be freed using foobar_math_value_free.
-//
-void foobar_math_value_copy(
- FoobarMathValue value,
- FoobarMathValue* out_value )
-{
- *out_value = value;
-
- switch ( value.type )
- {
- case FOOBAR_MATH_VALUE_INT:
- mpz_init_set( out_value->int_value.v, value.int_value.v );
- break;
- case FOOBAR_MATH_VALUE_FLOAT:
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-}
-
-//
-// Release memory associated with a value.
-//
-// This is necessary because the integer representation is stored using GMP, which can store arbitrarily large values.
-//
-void foobar_math_value_free( FoobarMathValue value )
-{
- switch ( value.type )
- {
- case FOOBAR_MATH_VALUE_INT:
- mpz_clear( value.int_value.v );
- break;
- case FOOBAR_MATH_VALUE_FLOAT:
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Operations
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Operation equivalent to "value *= -1".
-//
-void foobar_math_value_negate( FoobarMathValue* value )
-{
- switch ( value->type )
- {
- case FOOBAR_MATH_VALUE_INT:
- mpz_neg( value->int_value.v, value->int_value.v );
- break;
- case FOOBAR_MATH_VALUE_FLOAT:
- value->float_value.v = -value->float_value.v;
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-}
-
-//
-// Operation setting "out_value = lhs + rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_add(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( foobar_math_value_unify_integers( lhs, rhs ) )
- {
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places;
- mpz_add( out_value->int_value.v, lhs->int_value.v, rhs->int_value.v );
- return TRUE;
- }
-
- long double lhs_float = foobar_math_value_to_float( *lhs );
- long double rhs_float = foobar_math_value_to_float( *rhs );
- foobar_math_value_from_float( lhs_float + rhs_float, out_value );
- return TRUE;
-}
-
-//
-// Operation setting "out_value = lhs - rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_sub(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( foobar_math_value_unify_integers( lhs, rhs ) )
- {
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places;
- mpz_sub( out_value->int_value.v, lhs->int_value.v, rhs->int_value.v );
- return TRUE;
- }
-
- long double lhs_float = foobar_math_value_to_float( *lhs );
- long double rhs_float = foobar_math_value_to_float( *rhs );
- foobar_math_value_from_float( lhs_float - rhs_float, out_value );
- return TRUE;
-}
-
-//
-// Operation setting "out_value = lhs * rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_mul(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( lhs->type == FOOBAR_MATH_VALUE_INT && rhs->type == FOOBAR_MATH_VALUE_INT )
- {
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places + rhs->int_value.decimal_places;
- mpz_mul( out_value->int_value.v, lhs->int_value.v, rhs->int_value.v );
- return TRUE;
- }
-
- long double lhs_float = foobar_math_value_to_float( *lhs );
- long double rhs_float = foobar_math_value_to_float( *rhs );
- foobar_math_value_from_float( lhs_float * rhs_float, out_value );
- return TRUE;
-}
-
-//
-// Operation setting "out_value = lhs / rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_div(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( lhs->type == FOOBAR_MATH_VALUE_INT && rhs->type == FOOBAR_MATH_VALUE_INT )
- {
- if ( mpz_sgn( rhs->int_value.v ) == 0 ) { return FALSE; }
-
- if ( rhs->int_value.decimal_places > lhs->int_value.decimal_places )
- {
- foobar_math_value_unify_integers( lhs, rhs );
- }
-
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places - rhs->int_value.decimal_places;
- mpz_t rem;
- mpz_init( rem );
- mpz_divmod( out_value->int_value.v, rem, lhs->int_value.v, rhs->int_value.v );
- gboolean result_is_int = mpz_sgn( rem ) == 0;
- mpz_clear( rem );
-
- if ( result_is_int ) { return TRUE; }
- foobar_math_value_free( *out_value );
- }
-
- long double lhs_float = foobar_math_value_to_float( *lhs );
- long double rhs_float = foobar_math_value_to_float( *rhs );
- long double result = lhs_float / rhs_float;
- if ( isnanl( result ) || isinfl( result ) ) { return FALSE; }
- foobar_math_value_from_float( result, out_value );
- return TRUE;
-}
-
-//
-// Operation setting "out_value = lhs ^ rhs".
-//
-// After this, the result value should be freed using foobar_math_value_free.
-//
-gboolean foobar_math_value_pow(
- FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value )
-{
- if ( lhs->type == FOOBAR_MATH_VALUE_INT &&
- rhs->type == FOOBAR_MATH_VALUE_INT &&
- rhs->int_value.decimal_places == 0 &&
- mpz_fits_ulong_p( rhs->int_value.v ) )
- {
- unsigned long exp = mpz_get_ui( rhs->int_value.v );
- foobar_math_value_new_int( out_value );
- out_value->int_value.decimal_places = lhs->int_value.decimal_places * exp;
- mpz_pow_ui( out_value->int_value.v, lhs->int_value.v, exp );
- return TRUE;
- }
-
- long double lhs_float = foobar_math_value_to_float( *lhs );
- long double rhs_float = foobar_math_value_to_float( *rhs );
- long double result = powl( lhs_float, rhs_float );
- if ( isnanl( result ) || isinfl( result ) ) { return FALSE; }
- foobar_math_value_from_float( result, out_value );
- return TRUE;
-}
-
-//
-// Ensure that two integer values have the same number of decimal places.
-//
-// This may change the in-memory-representation of a value, but not its actual value. It only increases the precision of
-// one value
-//
-// If either a or b is not stored as an integer, this will return FALSE.
-//
-gboolean foobar_math_value_unify_integers(
- FoobarMathValue* a,
- FoobarMathValue* b )
-{
- if ( a->type != FOOBAR_MATH_VALUE_INT || b->type != FOOBAR_MATH_VALUE_INT ) { return FALSE; }
-
- // Extend the less precise representation so both numbers have the same number of decimal places.
-
- gsize decimal_places = MAX( a->int_value.decimal_places, b->int_value.decimal_places );
-
- mpz_t exp;
- mpz_init( exp );
-
- if ( decimal_places > a->int_value.decimal_places )
- {
- mpz_ui_pow_ui( exp, 10, decimal_places - a->int_value.decimal_places );
- mpz_mul( a->int_value.v, a->int_value.v, exp );
- }
-
- if ( decimal_places > b->int_value.decimal_places )
- {
- mpz_ui_pow_ui( exp, 10, decimal_places - b->int_value.decimal_places );
- mpz_mul( b->int_value.v, b->int_value.v, exp );
- }
-
- mpz_clear( exp );
-
- a->int_value.decimal_places = b->int_value.decimal_places = decimal_places;
-
- return TRUE;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Conversions
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Format a value as a string.
-//
-// The result is a null-terminated string which should be freed using g_free.
-//
-gchar* foobar_math_value_to_string( FoobarMathValue value )
-{
- switch ( value.type )
- {
- case FOOBAR_MATH_VALUE_INT:
- {
- gsize cap = mpz_sizeinbase( value.int_value.v, 10 );
- cap = MAX( cap, value.int_value.decimal_places + 1 ); // pad with zeroes if necessary
- gchar* result = g_new0( gchar, cap + 3 ); // length + null terminator + sign + separator
- mpz_get_str( result, 10, value.int_value.v );
- gsize len = strlen( result ); // actual size may be 1 char less (+ len now also includes sign)
- gboolean has_sign = result[0] == '-';
- if ( has_sign )
- {
- // add the sign back later, makes everything else easier
- result += 1;
- len -= 1;
- }
- // pad with leading zeroes if necessary
- gsize padded_len = MAX( len, value.int_value.decimal_places + 1 );
- memmove( &result[padded_len - len], result, len + 1 );
- memset( result, '0', padded_len - len );
- len = padded_len;
- gboolean trailing_zeros = TRUE;
- gsize decimal_places = value.int_value.decimal_places;
- gsize actual_decimal_places = decimal_places;
- for ( gsize i = 0; i < decimal_places; ++i )
- {
- if ( trailing_zeros && result[len - 1 - i] == '0' )
- {
- result[len - 1 - i] = '\0';
- actual_decimal_places -= 1;
- }
- else
- {
- trailing_zeros = FALSE;
- }
- }
- if ( actual_decimal_places > 0 )
- {
- memmove( &result[len + 1 - decimal_places], &result[len - decimal_places], decimal_places + 1 );
- result[len - decimal_places] = '.';
- }
- if ( has_sign )
- {
- result -= 1;
- len += 1;
- }
- return result;
- }
- case FOOBAR_MATH_VALUE_FLOAT:
- {
- gchar* result = g_strdup_printf( "%.30Lf", value.float_value.v );
-
- // trim trailing zeros
- size_t len = strlen( result );
- for ( gsize i = 0; i < len; ++i )
- {
- if ( result[len - 1 - i] == '.' )
- {
- result[len - 1 - i] = '\0';
- break;
- }
- else if ( result[len - 1 - i] == '0' )
- {
- result[len - 1 - i] = '\0';
- }
- else
- {
- break;
- }
- }
-
- return result;
- }
- default:
- g_warn_if_reached( );
- return NULL;
- }
-}
-
-//
-// Convert a value to its floating-point representation.
-//
-// This may be a lossy conversion and should only be done if absolutely necessary.
-//
-long double foobar_math_value_to_float( FoobarMathValue value )
-{
- switch ( value.type )
- {
- case FOOBAR_MATH_VALUE_INT:
- {
- mpz_t exp, integer, frac;
- mpz_inits( exp, integer, frac, NULL );
- mpz_ui_pow_ui( exp, 10, value.int_value.decimal_places );
- mpz_mod( frac, value.int_value.v, exp );
- mpz_div( integer, value.int_value.v, exp );
- mpf_t f_exp, f_frac;
- mpf_inits( f_exp, f_frac, NULL );
- mpf_set_z( f_exp, exp );
- mpf_set_z( f_frac, frac );
- mpf_div( f_frac, f_frac, f_exp );
- long double result = mpz_get_d( integer ) + mpf_get_d( f_frac );
- mpf_clears( f_exp, f_frac, NULL );
- mpz_clears( exp, integer, frac, NULL );
- return result;
- }
- case FOOBAR_MATH_VALUE_FLOAT:
- return value.float_value.v;
- default:
- g_warn_if_reached( );
- return 0;
- }
-}
diff --git a/src/services/quick-answers/math.h b/src/services/quick-answers/math.h
deleted file mode 100644
index fafa73d..0000000
--- a/src/services/quick-answers/math.h
+++ /dev/null
@@ -1,148 +0,0 @@
-#pragma once
-
-#include <gio/gio.h>
-#include <gmp.h>
-
-G_BEGIN_DECLS
-
-typedef enum
-{
- FOOBAR_MATH_VALUE_INT,
- FOOBAR_MATH_VALUE_FLOAT,
-} FoobarMathValueType;
-
-typedef struct _FoobarMathValue FoobarMathValue;
-
-struct _FoobarMathValue
-{
- FoobarMathValueType type;
-
- union
- {
- struct
- {
- mpz_t v;
- gsize decimal_places;
- } int_value;
-
- struct
- {
- long double v;
- } float_value;
- };
-};
-
-typedef enum
-{
- FOOBAR_MATH_TOKEN_IDENTIFIER,
- FOOBAR_MATH_TOKEN_NUMBER,
- FOOBAR_MATH_TOKEN_PAREN_OPEN,
- FOOBAR_MATH_TOKEN_PAREN_CLOSE,
- FOOBAR_MATH_TOKEN_PLUS,
- FOOBAR_MATH_TOKEN_MINUS,
- FOOBAR_MATH_TOKEN_MUL,
- FOOBAR_MATH_TOKEN_DIV,
- FOOBAR_MATH_TOKEN_POW,
-} FoobarMathTokenType;
-
-typedef struct _FoobarMathToken FoobarMathToken;
-
-struct _FoobarMathToken
-{
- FoobarMathTokenType type;
- gchar const* data;
- gsize length;
-};
-
-typedef enum
-{
- FOOBAR_MATH_EXPRESSION_VALUE,
- FOOBAR_MATH_EXPRESSION_FUNCTION,
- FOOBAR_MATH_EXPRESSION_CONSTANT,
- FOOBAR_MATH_EXPRESSION_OPERATION,
-} FoobarMathExpressionType;
-
-typedef enum
-{
- FOOBAR_MATH_FUNCTION_NEGATE,
- FOOBAR_MATH_FUNCTION_SIN,
- FOOBAR_MATH_FUNCTION_COS,
- FOOBAR_MATH_FUNCTION_SEC,
- FOOBAR_MATH_FUNCTION_CSC,
- FOOBAR_MATH_FUNCTION_TAN,
- FOOBAR_MATH_FUNCTION_COT,
- FOOBAR_MATH_FUNCTION_ARCSIN,
- FOOBAR_MATH_FUNCTION_ARCCOS,
- FOOBAR_MATH_FUNCTION_ARCSEC,
- FOOBAR_MATH_FUNCTION_ARCCSC,
- FOOBAR_MATH_FUNCTION_ARCTAN,
- FOOBAR_MATH_FUNCTION_ARCCOT,
- FOOBAR_MATH_FUNCTION_EXP,
- FOOBAR_MATH_FUNCTION_SQRT,
-} FoobarMathFunction;
-
-typedef enum
-{
- FOOBAR_MATH_CONSTANT_PI,
- FOOBAR_MATH_CONSTANT_E,
-} FoobarMathConstant;
-
-typedef enum
-{
- FOOBAR_MATH_OPERATION_ADD,
- FOOBAR_MATH_OPERATION_SUB,
- FOOBAR_MATH_OPERATION_MUL,
- FOOBAR_MATH_OPERATION_DIV,
- FOOBAR_MATH_OPERATION_POW,
-} FoobarMathOperation;
-
-typedef struct _FoobarMathExpression FoobarMathExpression;
-
-FoobarMathToken* foobar_math_lex ( gchar const* input,
- gsize input_length,
- gsize* out_count );
-FoobarMathExpression* foobar_math_parse ( FoobarMathToken const* tokens,
- gsize tokens_count );
-FoobarMathExpression* foobar_math_expression_new_value ( FoobarMathValue value );
-FoobarMathExpression* foobar_math_expression_new_function ( FoobarMathFunction function,
- FoobarMathExpression* input );
-FoobarMathExpression* foobar_math_expression_new_constant ( FoobarMathConstant constant );
-FoobarMathExpression* foobar_math_expression_new_operation( FoobarMathOperation operation,
- FoobarMathExpression* lhs,
- FoobarMathExpression* rhs );
-void foobar_math_expression_print ( FoobarMathExpression const* expr,
- gint indentation );
-gboolean foobar_math_evaluate ( FoobarMathExpression const* expr,
- FoobarMathValue* out_value );
-void foobar_math_expression_free ( FoobarMathExpression* expression );
-void foobar_math_value_new_int ( FoobarMathValue* out_value );
-void foobar_math_value_from_float ( long double value,
- FoobarMathValue* out_value );
-gboolean foobar_math_value_from_string ( gchar const* input,
- gsize input_length,
- FoobarMathValue* out_value );
-void foobar_math_value_copy ( FoobarMathValue value,
- FoobarMathValue* out_value );
-void foobar_math_value_free ( FoobarMathValue value );
-void foobar_math_value_negate ( FoobarMathValue* value );
-gboolean foobar_math_value_add ( FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value );
-gboolean foobar_math_value_sub ( FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value );
-gboolean foobar_math_value_mul ( FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value );
-gboolean foobar_math_value_div ( FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value );
-gboolean foobar_math_value_pow ( FoobarMathValue* lhs,
- FoobarMathValue* rhs,
- FoobarMathValue* out_value );
-gboolean foobar_math_value_unify_integers ( FoobarMathValue* a,
- FoobarMathValue* b );
-gchar* foobar_math_value_to_string ( FoobarMathValue value );
-long double foobar_math_value_to_float ( FoobarMathValue value );
-
-G_END_DECLS
diff --git a/src/services/quick-answers/math.test.c b/src/services/quick-answers/math.test.c
deleted file mode 100644
index 416b576..0000000
--- a/src/services/quick-answers/math.test.c
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "services/quick-answers/math.h"
-#include <mutest.h>
-
-#define SERIALIZATION_TEST( value, identifier ) \
- static void serialization_value_##identifier##_spec( void ) \
- { \
- FoobarMathValue val; \
- \
- gchar const input[] = #value; \
- mutest_expect( \
- "parse result", \
- mutest_bool_value( foobar_math_value_from_string( input, sizeof(input) - 1, &val) ), \
- mutest_to_be_true, \
- NULL ); \
- g_autofree gchar* output = foobar_math_value_to_string( val ); \
- mutest_expect( \
- "string representation", \
- mutest_string_value( output ), \
- mutest_to_be, \
- input, \
- NULL ); \
- \
- foobar_math_value_free( val ); \
- }
-
-SERIALIZATION_TEST( 0, 0 )
-SERIALIZATION_TEST( 1, 1 )
-SERIALIZATION_TEST( -1, neg_1 )
-SERIALIZATION_TEST( 1.2, 1_2 )
-SERIALIZATION_TEST( -1.2, neg_1_2 )
-SERIALIZATION_TEST( 1.30002, 1_30002 )
-SERIALIZATION_TEST( -1.30002, neg_1_30002 )
-SERIALIZATION_TEST( 0.2, 0_2 )
-SERIALIZATION_TEST( -0.2, neg_0_2 )
-SERIALIZATION_TEST( 0.00002, 0_00002 )
-SERIALIZATION_TEST( -0.00002, neg_0_00002 )
-SERIALIZATION_TEST( 123, 123 )
-SERIALIZATION_TEST( 123.456, 123_456 )
-SERIALIZATION_TEST( -123, neg_123 )
-SERIALIZATION_TEST( -123.456, neg_123_456 )
-
-#undef SERIALIZATION_TEST
-
-static void serialization_suite( void )
-{
- mutest_it( "value 0", serialization_value_0_spec );
- mutest_it( "value 1", serialization_value_1_spec );
- mutest_it( "value -1", serialization_value_neg_1_spec );
- mutest_it( "value 1.2", serialization_value_1_2_spec );
- mutest_it( "value -1.2", serialization_value_neg_1_2_spec );
- mutest_it( "value 1.30002", serialization_value_1_30002_spec );
- mutest_it( "value -1.30002", serialization_value_neg_1_30002_spec );
- mutest_it( "value 0.2", serialization_value_0_2_spec );
- mutest_it( "value -0.2", serialization_value_neg_0_2_spec );
- mutest_it( "value 0.00002", serialization_value_0_00002_spec );
- mutest_it( "value -0.00002", serialization_value_neg_0_00002_spec );
- mutest_it( "value 123", serialization_value_123_spec );
- mutest_it( "value 123.456", serialization_value_123_456_spec );
- mutest_it( "value -123", serialization_value_neg_123_spec );
- mutest_it( "value -123.456", serialization_value_neg_123_456_spec );
-}
-#define OPERATION_TEST( op, value_a, value_b, value_res, identifier ) \
- static void operation_##identifier##_spec( void ) \
- { \
- FoobarMathValue a, b, res; \
- \
- gchar const input_a[] = #value_a; \
- gchar const input_b[] = #value_b; \
- mutest_expect( \
- "input a parsed", \
- mutest_bool_value( foobar_math_value_from_string( input_a, sizeof(input_a) - 1, &a) ), \
- mutest_to_be_true, \
- NULL ); \
- mutest_expect( \
- "input b parsed", \
- mutest_bool_value( foobar_math_value_from_string( input_b, sizeof(input_b) - 1, &b) ), \
- mutest_to_be_true, \
- NULL ); \
- foobar_math_value_##op( &a, &b, &res ); \
- g_autofree gchar* output = foobar_math_value_to_string( res ); \
- mutest_expect( \
- "result", \
- mutest_string_value( output ), \
- mutest_to_be, \
- #value_res, \
- NULL ); \
- \
- foobar_math_value_free( a ); \
- foobar_math_value_free( b ); \
- foobar_math_value_free( res ); \
- }
-
-OPERATION_TEST( add, 123, 456, 579, 123_plus_456)
-OPERATION_TEST( add, 123.456, 11, 134.456, 123_456_plus_11)
-OPERATION_TEST( add, 11, 123.456, 134.456, 11_plus_123_456)
-OPERATION_TEST( sub, 123, 456, -333, 123_minus_456)
-OPERATION_TEST( sub, 123.456, 11, 112.456, 123_456_minus_11)
-OPERATION_TEST( sub, 11, 123.456, -112.456, 11_minus_123_456)
-OPERATION_TEST( mul, 123, 3, 369, 123_times_3 )
-OPERATION_TEST( mul, 12, 0.05, 0.6, 12_times_0_05 )
-OPERATION_TEST( mul, 0.05, 12, 0.6, 0_05_times_12 )
-OPERATION_TEST( div, 369, 3, 123, 369_div_3 )
-OPERATION_TEST( div, 369, 2, 184.5, 369_div_2 )
-OPERATION_TEST( div, 12, 0.05, 240, 12_div_0_05 )
-OPERATION_TEST( div, 0.08, 4, 0.02, 0_08_div_4 )
-OPERATION_TEST( pow, 2, 3, 8, 2_pow_3 )
-OPERATION_TEST( pow, 4, 0.5, 2, 4_pow_0_5 )
-
-#undef OPERATION_TEST
-
-static void addition_suite( void )
-{
- mutest_it( "calculate 123 + 456", operation_123_plus_456_spec );
- mutest_it( "calculate 123.456 + 11", operation_123_456_plus_11_spec );
- mutest_it( "calculate 11 + 123.456", operation_11_plus_123_456_spec );
-}
-
-static void subtraction_suite( void )
-{
- mutest_it( "calculate 123 - 456", operation_123_minus_456_spec );
- mutest_it( "calculate 123.456 - 11", operation_123_456_minus_11_spec );
- mutest_it( "calculate 11 - 123.456", operation_11_minus_123_456_spec );
-}
-
-static void multiplication_suite( void )
-{
- mutest_it( "calculate 123 * 3", operation_123_times_3_spec );
- mutest_it( "calculate 12 * 0.05", operation_12_times_0_05_spec );
- mutest_it( "calculate 0.05 * 12", operation_0_05_times_12_spec );
-}
-
-static void division_suite( void )
-{
- mutest_it( "calculate 369 / 3", operation_369_div_3_spec );
- mutest_it( "calculate 369 / 2", operation_369_div_2_spec );
- mutest_it( "calculate 12 / 0.05", operation_12_div_0_05_spec );
- mutest_it( "calculate 0.08 / 4", operation_0_08_div_4_spec );
-}
-
-static void power_suite( void )
-{
- mutest_it( "calculate 2 ^ 3", operation_2_pow_3_spec );
- mutest_it( "calculate 4 ^ 0.5", operation_4_pow_0_5_spec );
-}
-
-MUTEST_MAIN(
- mutest_describe( "Serialization", serialization_suite );
- mutest_describe( "Addition", addition_suite );
- mutest_describe( "Subtraction", subtraction_suite );
- mutest_describe( "Multiplication", multiplication_suite );
- mutest_describe( "Division", division_suite );
- mutest_describe( "Power", power_suite );
-)
diff --git a/src/services/quick-answers/meson.build b/src/services/quick-answers/meson.build
deleted file mode 100644
index ae468c2..0000000
--- a/src/services/quick-answers/meson.build
+++ /dev/null
@@ -1,9 +0,0 @@
-foobar_sources += files(
- 'math-lexer.c',
- 'math-parser.c',
- 'math.c',
-)
-
-foobar_tests += {
- 'math': files('math.test.c'),
-}
diff --git a/src/services/workspace-service.c b/src/services/workspace-service.c
deleted file mode 100644
index 75ea06a..0000000
--- a/src/services/workspace-service.c
+++ /dev/null
@@ -1,1386 +0,0 @@
-#include "services/workspace-service.h"
-#include <json-glib/json-glib.h>
-#include <gtk/gtk.h>
-#include <string.h>
-
-typedef struct _EventData EventData;
-typedef struct _EventHandler EventHandler;
-
-//
-// FoobarWorkspaceFlags:
-//
-// Some information about the type/state of a workspace.
-//
-
-G_DEFINE_FLAGS_TYPE(
- FoobarWorkspaceFlags,
- foobar_workspace_flags,
- G_DEFINE_ENUM_VALUE( FOOBAR_WORKSPACE_FLAGS_NONE, "none" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_WORKSPACE_FLAGS_ACTIVE, "active" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_WORKSPACE_FLAGS_VISIBLE, "visible" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_WORKSPACE_FLAGS_SPECIAL, "special" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_WORKSPACE_FLAGS_PERSISTENT, "persistent" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_WORKSPACE_FLAGS_URGENT, "urgent" ) )
-
-//
-// FoobarWorkspace:
-//
-// Representation of a workspace.
-//
-
-struct _FoobarWorkspace
-{
- GObject parent_instance;
- FoobarWorkspaceService* 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 GParamSpec* workspace_props[N_WORKSPACE_PROPS] = { 0 };
-
-static void foobar_workspace_class_init ( FoobarWorkspaceClass* klass );
-static void foobar_workspace_init ( FoobarWorkspace* self );
-static void foobar_workspace_get_property( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_workspace_finalize ( GObject* object );
-static FoobarWorkspace* foobar_workspace_new ( FoobarWorkspaceService* service );
-static void foobar_workspace_set_id ( FoobarWorkspace* self,
- gint64 value );
-static void foobar_workspace_set_name ( FoobarWorkspace* self,
- gchar const* value );
-static void foobar_workspace_set_monitor ( FoobarWorkspace* self,
- gchar const* value );
-static void foobar_workspace_set_flags ( FoobarWorkspace* self,
- FoobarWorkspaceFlags value );
-
-G_DEFINE_FINAL_TYPE( FoobarWorkspace, foobar_workspace, G_TYPE_OBJECT )
-
-//
-// FoobarWorkspaceService:
-//
-// Service monitoring the workspaces provided by the window manager. This is implemented by communicating directly with
-// Hyprland through its IPC socket.
-//
-// Based on the waybar implementation:
-// https://github.com/Alexays/Waybar/blob/master/src/modules/hyprland/workspaces.cpp
-//
-
-struct _FoobarWorkspaceService
-{
- GObject parent_instance;
- GListStore* workspaces;
- GtkSortListModel* sorted_workspaces;
- GCancellable* event_cancellable;
- GThread* event_thread;
- gchar* rx_path;
- gchar* tx_path;
-};
-
-enum
-{
- PROP_WORKSPACES = 1,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-enum
-{
- SIGNAL_MONITOR_CONFIGURATION_CHANGED,
- N_SIGNALS,
-};
-static unsigned signals[N_SIGNALS] = { 0 };
-
-static void foobar_workspace_service_class_init ( FoobarWorkspaceServiceClass* klass );
-static void foobar_workspace_service_init ( FoobarWorkspaceService* self );
-static void foobar_workspace_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_workspace_service_dispose ( GObject* object );
-static void foobar_workspace_service_finalize ( GObject* object );
-static gboolean foobar_workspace_service_handle_workspace_created ( EventData* data );
-static gboolean foobar_workspace_service_handle_workspace_destroyed ( EventData* data );
-static gboolean foobar_workspace_service_handle_workspace_moved ( EventData* data );
-static gboolean foobar_workspace_service_handle_workspace_renamed ( EventData* data );
-static gboolean foobar_workspace_service_handle_workspace_activated ( EventData* data );
-static gboolean foobar_workspace_service_handle_special_workspace_activated( EventData* data );
-static gboolean foobar_workspace_service_handle_monitor_focused ( EventData* data );
-static gboolean foobar_workspace_service_handle_window_urgent ( EventData* data );
-static gboolean foobar_workspace_service_handle_config_reloaded ( EventData* data );
-static gpointer foobar_workspace_service_event_thread_func ( gpointer userdata );
-static void foobar_workspace_service_dispatch_event ( FoobarWorkspaceService* self,
- gchar* message );
-static gchar* foobar_workspace_service_send_request ( FoobarWorkspaceService* self,
- gchar const* request,
- GError** error );
-static JsonNode* foobar_workspace_service_send_json_request ( FoobarWorkspaceService* self,
- gchar const* request,
- GError** error );
-static void foobar_workspace_service_initialize ( FoobarWorkspaceService* self );
-static void foobar_workspace_service_update_active ( FoobarWorkspaceService* self,
- GListStore* workspaces );
-static void foobar_workspace_service_add_workspace ( FoobarWorkspaceService* self,
- GListStore* workspaces,
- JsonObject* workspace_object,
- gboolean is_persistent );
-static FoobarWorkspace* foobar_workspace_service_find_workspace ( GListModel* workspaces,
- gint64 id,
- guint* out_index );
-static gboolean foobar_workspace_service_is_invalid_name ( gchar const* name );
-static gint64 foobar_workspace_service_parse_id ( gchar const* str );
-static gchar* foobar_workspace_service_get_hyprland_base_path ( void );
-static gint foobar_workspace_service_compare_ids ( gconstpointer a,
- gconstpointer b );
-static gint foobar_workspace_service_sort_func ( gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarWorkspaceService, foobar_workspace_service, G_TYPE_OBJECT )
-
-//
-// EventData:
-//
-// Custom userdata passed to hyprland event handlers in a single pointer.
-//
-
-struct _EventData
-{
- FoobarWorkspaceService* service;
- gchar* name;
- gchar* payload;
-};
-
-static EventData* event_data_new ( FoobarWorkspaceService* service,
- gchar const* name,
- gchar const* payload );
-static void event_data_free( EventData* self );
-
-//
-// EventHandler:
-//
-// Description for the handler of a single hyprland event.
-//
-
-struct _EventHandler
-{
- gchar const* name;
- gboolean ( *fn )( EventData* data );
-};
-
-//
-// Static list of registered event handlers.
-//
-static EventHandler const event_handlers[] =
- {
- { .name = "createworkspacev2", .fn = foobar_workspace_service_handle_workspace_created },
- { .name = "destroyworkspacev2", .fn = foobar_workspace_service_handle_workspace_destroyed },
- { .name = "moveworkspacev2", .fn = foobar_workspace_service_handle_workspace_moved },
- { .name = "renameworkspace", .fn = foobar_workspace_service_handle_workspace_renamed },
- { .name = "workspace", .fn = foobar_workspace_service_handle_workspace_activated },
- { .name = "activespecial", .fn = foobar_workspace_service_handle_special_workspace_activated },
- { .name = "focusedmon", .fn = foobar_workspace_service_handle_monitor_focused },
- { .name = "urgent", .fn = foobar_workspace_service_handle_window_urgent },
- { .name = "configreloaded", .fn = foobar_workspace_service_handle_config_reloaded },
- };
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Workspace
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for workspaces.
-//
-void foobar_workspace_class_init( FoobarWorkspaceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_workspace_get_property;
- object_klass->finalize = foobar_workspace_finalize;
-
- workspace_props[WORKSPACE_PROP_ID] = g_param_spec_int64(
- "id",
- "ID",
- "Numeric ID of the workspace.",
- INT64_MIN,
- INT64_MAX,
- 0,
- G_PARAM_READABLE );
- workspace_props[WORKSPACE_PROP_NAME] = g_param_spec_string(
- "name",
- "Name",
- "Optional human-readable workspace name.",
- NULL,
- G_PARAM_READABLE );
- workspace_props[WORKSPACE_PROP_MONITOR] = g_param_spec_string(
- "monitor",
- "Monitor",
- "Connector ID of the workspace's monitor.",
- NULL,
- G_PARAM_READABLE );
- workspace_props[WORKSPACE_PROP_FLAGS] = g_param_spec_flags(
- "flags",
- "Flags",
- "Flags indicating the current state of the workspace.",
- FOOBAR_TYPE_WORKSPACE_FLAGS,
- FOOBAR_WORKSPACE_FLAGS_NONE,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_WORKSPACE_PROPS, workspace_props );
-}
-
-//
-// Instance initialization for workspaces.
-//
-void foobar_workspace_init( FoobarWorkspace* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_workspace_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarWorkspace* self = (FoobarWorkspace*)object;
-
- switch ( prop_id )
- {
- case WORKSPACE_PROP_ID:
- g_value_set_int64( value, foobar_workspace_get_id( self ) );
- break;
- case WORKSPACE_PROP_NAME:
- g_value_set_string( value, foobar_workspace_get_name( self ) );
- break;
- case WORKSPACE_PROP_MONITOR:
- g_value_set_string( value, foobar_workspace_get_monitor( self ) );
- break;
- case WORKSPACE_PROP_FLAGS:
- g_value_set_flags( value, foobar_workspace_get_flags( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for workspaces.
-//
-void foobar_workspace_finalize( GObject* object )
-{
- FoobarWorkspace* self = (FoobarWorkspace*)object;
-
- g_clear_pointer( &self->name, g_free );
- g_clear_pointer( &self->monitor, g_free );
-
- G_OBJECT_CLASS( foobar_workspace_parent_class )->finalize( object );
-}
-
-//
-// Create a new workspace that is owned by the given service (captured as an unowned reference).
-//
-FoobarWorkspace* foobar_workspace_new( FoobarWorkspaceService* service )
-{
- FoobarWorkspace* self = g_object_new( FOOBAR_TYPE_WORKSPACE, NULL );
- self->service = service;
- return self;
-}
-
-//
-// Numeric ID of the workspace.
-//
-gint64 foobar_workspace_get_id( FoobarWorkspace* self )
-{
- g_return_val_if_fail( FOOBAR_IS_WORKSPACE( self ), 0 );
- return self->id;
-}
-
-//
-// Optional human-readable workspace name.
-//
-gchar const* foobar_workspace_get_name( FoobarWorkspace* self )
-{
- g_return_val_if_fail( FOOBAR_IS_WORKSPACE( self ), NULL );
- return self->name;
-}
-
-//
-// Connector ID of the workspace's monitor.
-//
-gchar const* foobar_workspace_get_monitor( FoobarWorkspace* self )
-{
- g_return_val_if_fail( FOOBAR_IS_WORKSPACE( self ), NULL );
- return self->monitor;
-}
-
-//
-// Flags indicating the current state of the workspace.
-//
-FoobarWorkspaceFlags foobar_workspace_get_flags( FoobarWorkspace* self )
-{
- g_return_val_if_fail( FOOBAR_IS_WORKSPACE( self ), FOOBAR_WORKSPACE_FLAGS_NONE );
- return self->flags;
-}
-
-//
-// Numeric ID of the workspace.
-//
-void foobar_workspace_set_id(
- FoobarWorkspace* self,
- gint64 value )
-{
- g_return_if_fail( FOOBAR_IS_WORKSPACE( self ) );
-
- if ( self->id != value )
- {
- self->id = value;
- g_object_notify_by_pspec( G_OBJECT( self ), workspace_props[WORKSPACE_PROP_ID] );
- }
-}
-
-//
-// Optional human-readable workspace name.
-//
-void foobar_workspace_set_name(
- FoobarWorkspace* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_WORKSPACE( self ) );
-
- if ( g_strcmp0( self->name, value ) )
- {
- g_clear_pointer( &self->name, g_free );
- self->name = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), workspace_props[WORKSPACE_PROP_NAME] );
- }
-}
-
-//
-// Connector ID of the workspace's monitor.
-//
-void foobar_workspace_set_monitor(
- FoobarWorkspace* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_WORKSPACE( self ) );
-
- if ( g_strcmp0( self->monitor, value ) )
- {
- g_clear_pointer( &self->monitor, g_free );
- self->monitor = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), workspace_props[WORKSPACE_PROP_MONITOR] );
- }
-}
-
-//
-// Flags indicating the current state of the workspace.
-//
-void foobar_workspace_set_flags(
- FoobarWorkspace* self,
- FoobarWorkspaceFlags value )
-{
- g_return_if_fail( FOOBAR_IS_WORKSPACE( self ) );
-
- if ( self->flags != value )
- {
- self->flags = value;
- g_object_notify_by_pspec( G_OBJECT( self ), workspace_props[WORKSPACE_PROP_FLAGS] );
- }
-}
-
-//
-// Let this workspace become the active one.
-//
-void foobar_workspace_activate( FoobarWorkspace* self )
-{
- g_return_if_fail( FOOBAR_IS_WORKSPACE( self ) );
- if ( !self->service ) { return; }
-
- g_autofree gchar* request = NULL;
- if ( foobar_workspace_get_id( self ) > 0 )
- {
- // Normal
- request = g_strdup_printf( "dispatch workspace %lld", (long long)foobar_workspace_get_id( self ) );
- }
- else if ( !( foobar_workspace_get_flags( self ) & FOOBAR_WORKSPACE_FLAGS_SPECIAL ) )
- {
- // Named (this includes persistent)
- request = g_strdup_printf( "dispatch workspace name:%s", foobar_workspace_get_name( self ) );
- }
- else if ( foobar_workspace_get_id( self ) != -99 )
- {
- // Named special
- request = g_strdup_printf( "dispatch togglespecialworkspace %s", foobar_workspace_get_name( self ) );
- }
- else
- {
- // Special
- request = g_strdup( "dispatch togglespecialworkspace" );
- }
-
- g_autoptr( GError ) error = NULL;
- g_free( foobar_workspace_service_send_request( self->service, request, &error ) );
- if ( error )
- {
- g_warning( "Unable to activate workspace: %s", error->message );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the workspace service.
-//
-void foobar_workspace_service_class_init( FoobarWorkspaceServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_workspace_service_get_property;
- object_klass->dispose = foobar_workspace_service_dispose;
- object_klass->finalize = foobar_workspace_service_finalize;
-
- props[PROP_WORKSPACES] = g_param_spec_object(
- "workspaces",
- "Workspaces",
- "Sorted list of all workspaces.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-
- signals[SIGNAL_MONITOR_CONFIGURATION_CHANGED] = g_signal_new(
- "monitor-configuration-changed",
- FOOBAR_TYPE_WORKSPACE_SERVICE,
- G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
- 0,
- NULL,
- NULL,
- NULL,
- G_TYPE_NONE,
- 0 );
-}
-
-//
-// Instance initialization for the workspace service.
-//
-void foobar_workspace_service_init( FoobarWorkspaceService* self )
-{
- self->workspaces = g_list_store_new( FOOBAR_TYPE_WORKSPACE );
-
- GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_workspace_service_sort_func, NULL, NULL );
- self->sorted_workspaces = gtk_sort_list_model_new(
- G_LIST_MODEL( g_object_ref( self->workspaces ) ),
- GTK_SORTER( sorter ) );
-
- g_autofree gchar* base_path = foobar_workspace_service_get_hyprland_base_path( );
- if ( !base_path )
- {
- g_warning( "Unable to connect to hyprland -- is it currently running?" );
- return;
- }
-
- self->tx_path = g_strdup_printf( "%s/.socket.sock", base_path );
- self->rx_path = g_strdup_printf( "%s/.socket2.sock", base_path );
- self->event_cancellable = g_cancellable_new( );
- self->event_thread = g_thread_new( "workspace-event-listener", foobar_workspace_service_event_thread_func, self );
-
- foobar_workspace_service_initialize( self );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_workspace_service_get_property( GObject* object, guint prop_id, GValue* value, GParamSpec* pspec )
-{
- FoobarWorkspaceService* self = (FoobarWorkspaceService*)object;
-
- switch ( prop_id )
- {
- case PROP_WORKSPACES:
- g_value_set_object( value, foobar_workspace_service_get_workspaces( 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_workspace_service_dispose( GObject* object )
-{
- FoobarWorkspaceService* self = (FoobarWorkspaceService*)object;
-
- if ( self->event_thread )
- {
- g_cancellable_cancel( self->event_cancellable );
- g_thread_join( self->event_thread );
- self->event_thread = NULL;
- }
-
- G_OBJECT_CLASS( foobar_workspace_service_parent_class )->dispose( object );
-}
-
-//
-// Instance cleanup for the workspace service.
-//
-void foobar_workspace_service_finalize( GObject* object )
-{
- FoobarWorkspaceService* self = (FoobarWorkspaceService*)object;
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->workspaces ) ); ++i )
- {
- FoobarWorkspace* 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->rx_path, g_free );
- g_clear_pointer( &self->tx_path, g_free );
-
- G_OBJECT_CLASS( foobar_workspace_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new workspace service instance.
-//
-FoobarWorkspaceService* foobar_workspace_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_WORKSPACE_SERVICE, NULL );
-}
-
-//
-// Get a sorted list of all workspaces.
-//
-GListModel* foobar_workspace_service_get_workspaces( FoobarWorkspaceService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_WORKSPACE_SERVICE( self ), NULL );
- return G_LIST_MODEL( self->sorted_workspaces );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Event Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Handler for the "createworkspacev2" event.
-//
-// The payload has the format "<id>,<name>" where <id> is the numeric ID.
-//
-gboolean foobar_workspace_service_handle_workspace_created( EventData* data )
-{
- gchar* save;
- gchar const* id_str = strtok_r( data->payload, ",", &save );
- gchar const* name = strtok_r( NULL, ",", &save );
- g_return_val_if_fail( id_str && name, G_SOURCE_REMOVE );
-
- if ( foobar_workspace_service_is_invalid_name( name ) ) { return G_SOURCE_REMOVE; }
- gint64 id = foobar_workspace_service_parse_id( id_str );
-
- // Get additional information about the workspace.
-
- g_autoptr( GError ) error = NULL;
- g_autoptr( JsonNode ) workspaces_node = foobar_workspace_service_send_json_request(
- data->service,
- "j/workspaces",
- &error );
- if ( !workspaces_node )
- {
- g_warning( "Unable to load workspaces: %s", error->message );
- return G_SOURCE_REMOVE;
- }
-
- JsonArray* workspaces_array = json_node_get_array( workspaces_node );
- JsonObject* workspace_object = NULL;
- for ( guint i = 0; i < json_array_get_length( workspaces_array ); ++i )
- {
- JsonObject* it = json_array_get_object_element( workspaces_array, i );
- if ( id == json_object_get_int_member( it, "id" ) )
- {
- workspace_object = it;
- break;
- }
- }
-
- if ( !workspace_object )
- {
- g_warning( "Unable to find new workspace." );
- return G_SOURCE_REMOVE;
- }
-
- // Check if the workspace is persistent by looking for a rule with its ID.
-
- gboolean is_persistent = FALSE;
-
- g_autoptr( JsonNode ) rules_node = foobar_workspace_service_send_json_request(
- data->service,
- "j/workspacerules",
- &error );
- if ( !rules_node )
- {
- g_warning( "Unable to load workspace rules: %s", error->message );
- return G_SOURCE_REMOVE;
- }
-
- JsonArray* rules_array = json_node_get_array( rules_node );
- for ( guint i = 0; i < json_array_get_length( rules_array ); ++i )
- {
- JsonObject* rule_object = json_array_get_object_element( rules_array, i );
- gchar const* rule_id_str = json_object_get_string_member_with_default( rule_object, "workspaceString", NULL );
- if ( id == foobar_workspace_service_parse_id( rule_id_str ) )
- {
- is_persistent = json_object_get_boolean_member_with_default( rule_object, "persistent", FALSE );
- break;
- }
- }
-
- foobar_workspace_service_add_workspace( data->service, data->service->workspaces, workspace_object, is_persistent );
- foobar_workspace_service_update_active( data->service, data->service->workspaces );
- return G_SOURCE_REMOVE;
-}
-
-//
-// Handler for the "destroyworkspacev2" event.
-//
-// The payload has the format "<id>,<name>" where <id> is the numeric ID.
-//
-gboolean foobar_workspace_service_handle_workspace_destroyed( EventData* data )
-{
- gchar* save;
- gchar const* id_str = strtok_r( data->payload, ",", &save );
- gchar const* name = strtok_r( NULL, ",", &save );
- g_return_val_if_fail( id_str && name, G_SOURCE_REMOVE );
-
- if ( foobar_workspace_service_is_invalid_name( name ) ) { return G_SOURCE_REMOVE; }
- gint64 id = foobar_workspace_service_parse_id( id_str );
-
- GListStore* workspaces = data->service->workspaces;
- guint index;
- FoobarWorkspace* workspace = foobar_workspace_service_find_workspace( G_LIST_MODEL( workspaces ), id, &index );
- if ( workspace )
- {
- workspace->service = NULL;
- g_list_store_remove( workspaces, index );
- }
-
- return G_SOURCE_REMOVE;
-}
-
-//
-// Handler for the "moveworkspacev2" event.
-//
-// The payload has the format "<id>,<name>,<monitor>" where <id> is the numeric ID of the workspace.
-//
-gboolean foobar_workspace_service_handle_workspace_moved( EventData* data )
-{
- gchar* save;
- gchar const* workspace_id_str = strtok_r( data->payload, ",", &save );
- gchar const* workspace_name = strtok_r( NULL, ",", &save );
- gchar const* monitor_name = strtok_r( NULL, ",", &save );
- g_return_val_if_fail( workspace_id_str && workspace_name && monitor_name, G_SOURCE_REMOVE );
-
- gint64 workspace_id = foobar_workspace_service_parse_id( workspace_id_str );
- FoobarWorkspace* workspace = foobar_workspace_service_find_workspace(
- G_LIST_MODEL( data->service->workspaces ),
- workspace_id,
- NULL );
- if ( workspace )
- {
- foobar_workspace_set_monitor( workspace, monitor_name );
- g_signal_emit( data->service, signals[SIGNAL_MONITOR_CONFIGURATION_CHANGED], 0 );
- }
- foobar_workspace_service_update_active( data->service, data->service->workspaces );
-
- return G_SOURCE_REMOVE;
-}
-
-//
-// Handler for the "renameworkspace" event.
-//
-// The payload has the format "<id>,<name>" where <id> is the numeric ID of the workspace.
-//
-gboolean foobar_workspace_service_handle_workspace_renamed( EventData* data )
-{
- gchar* save;
- gchar const* id_str = strtok_r( data->payload, ",", &save );
- gchar const* new_name = strtok_r( NULL, ",", &save );
- g_return_val_if_fail( id_str && new_name, G_SOURCE_REMOVE );
-
- gint64 id = foobar_workspace_service_parse_id( id_str );
- FoobarWorkspace* workspace = foobar_workspace_service_find_workspace(
- G_LIST_MODEL( data->service->workspaces ),
- id,
- NULL );
- if ( workspace )
- {
- foobar_workspace_set_name( workspace, new_name );
- gtk_sorter_changed(
- gtk_sort_list_model_get_sorter( data->service->sorted_workspaces ),
- GTK_SORTER_CHANGE_DIFFERENT );
- }
-
- return G_SOURCE_REMOVE;
-}
-
-//
-// Handler for the "workspace" event.
-//
-gboolean foobar_workspace_service_handle_workspace_activated( EventData* data )
-{
- foobar_workspace_service_update_active( data->service, data->service->workspaces );
-
- return G_SOURCE_REMOVE;
-}
-
-//
-// Handler for the "activespecial" event.
-//
-gboolean foobar_workspace_service_handle_special_workspace_activated( EventData* data )
-{
- foobar_workspace_service_update_active( data->service, data->service->workspaces );
-
- return G_SOURCE_REMOVE;
-}
-
-//
-// Handler for the "focusedmon" event.
-//
-gboolean foobar_workspace_service_handle_monitor_focused( EventData* data )
-{
- foobar_workspace_service_update_active( data->service, data->service->workspaces );
-
- return G_SOURCE_REMOVE;
-}
-
-//
-// Handler for the "urgent" event.
-//
-// The payload has the format "<address>" where <address> is the sender window's address.
-//
-gboolean foobar_workspace_service_handle_window_urgent( EventData* data )
-{
- gchar const* address = data->payload;
-
- // Find the client with the given address.
-
- g_autoptr( GError ) error = NULL;
- g_autoptr( JsonNode ) clients_node = foobar_workspace_service_send_json_request(
- data->service,
- "j/clients",
- &error );
- if ( !clients_node )
- {
- g_warning( "Unable to load clients: %s", error->message );
- return G_SOURCE_REMOVE;
- }
-
- JsonArray* clients_array = json_node_get_array( clients_node );
- JsonObject* client_object = NULL;
- for ( guint i = 0; i < json_array_get_length( clients_array ); ++i )
- {
- JsonObject* it = json_array_get_object_element( clients_array, i );
- gchar const* it_address = json_object_get_string_member_with_default( it, "address", "" );
- if ( g_str_has_suffix( it_address, address ) )
- {
- client_object = it;
- break;
- }
- }
-
- if ( !client_object ) { return G_SOURCE_REMOVE; }
-
- // Find the workspace this window belongs to and mark it as "urgent".
-
- JsonObject* workspace_object = json_object_get_object_member( client_object, "workspace" );
- gint64 id = json_object_get_int_member( workspace_object, "id" );
- FoobarWorkspace* workspace = foobar_workspace_service_find_workspace(
- G_LIST_MODEL( data->service->workspaces ),
- id,
- NULL );
- if ( workspace )
- {
- FoobarWorkspaceFlags flags = foobar_workspace_get_flags( workspace ) | FOOBAR_WORKSPACE_FLAGS_URGENT;
- foobar_workspace_set_flags( workspace, flags );
- }
-
- return G_SOURCE_REMOVE;
-}
-
-//
-// Handler for the "configreloaded" event.
-//
-gboolean foobar_workspace_service_handle_config_reloaded( EventData* data )
-{
- foobar_workspace_service_initialize( data->service );
-
- return G_SOURCE_REMOVE;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Implementation of the event thread.
-//
-// This thread listens on hyprland's event socket (socket.2.sock) for incoming event notifications and then submits
-// their registered handlers (if any) to the main loop.
-//
-gpointer foobar_workspace_service_event_thread_func( gpointer userdata )
-{
- FoobarWorkspaceService* self = (FoobarWorkspaceService*)userdata;
-
- g_autoptr( GError ) error = NULL;
- g_autoptr( GSocket ) sock = g_socket_new(
- G_SOCKET_FAMILY_UNIX,
- G_SOCKET_TYPE_STREAM,
- G_SOCKET_PROTOCOL_DEFAULT,
- &error );
- if ( !sock )
- {
- g_warning( "Unable to create socket for hyprland communication: %s", error->message );
- return NULL;
- }
-
- g_autoptr( GSocketAddress ) addr = g_unix_socket_address_new( self->rx_path );
- if ( !g_socket_connect( sock, addr, self->event_cancellable, &error ) &&
- !g_cancellable_is_cancelled( self->event_cancellable ) )
- {
- g_warning( "Unable to connect socket to hyprland: %s", error->message );
- return NULL;
- }
-
- // Each event is sent as a complete line, separated by a newline character.
-
- GString* prev_buf = NULL; // for longer, incomplete messages
- gchar buf[1024];
- while ( !g_cancellable_is_cancelled( self->event_cancellable ) )
- {
- // Synchronously receive data from the socket, but support cancellation using Gio.
-
- gssize received = g_socket_receive( sock, buf, sizeof( buf ), self->event_cancellable, &error );
- if ( error && !g_cancellable_is_cancelled( self->event_cancellable ) )
- {
- g_warning( "Unable to receive events from hyprland: %s", error->message );
- }
- if ( received <= 0 ) { break; }
-
- gchar* start = buf;
- gchar* end = start + received;
- while ( start < end )
- {
- gchar* next_newline = g_strstr_len( start, end - start, "\n" );
- if ( !next_newline )
- {
- // Incomplete message (missing newline) -> store it in the buffer
-
- prev_buf = ( prev_buf != NULL )
- ? g_string_append_len( prev_buf, start, end - start )
- : g_string_new_len( start, end - start );
- break;
- }
-
- if ( prev_buf )
- {
- // Prepend buffered data before the actual data.
-
- prev_buf = g_string_append_len( prev_buf, start, next_newline - start );
- foobar_workspace_service_dispatch_event( self, prev_buf->str );
-
- g_string_free( prev_buf, TRUE );
- prev_buf = NULL;
- }
- else
- {
- // Skip allocation and use the buffer contents on the stack directly.
-
- *next_newline = '\0';
- foobar_workspace_service_dispatch_event( self, start );
- }
-
- start = next_newline + 1;
- }
- }
-
- if ( prev_buf ) { g_string_free( prev_buf, TRUE ); }
-
- return NULL;
-}
-
-//
-// Called when an entire event has been received on the event thread.
-//
-// This will look through the registered handlers and (if found) invoke the event's handler on the main thread.
-//
-void foobar_workspace_service_dispatch_event(
- FoobarWorkspaceService* self,
- gchar* message )
-{
- gchar* delimiter = strstr( message, ">>" );
- if ( !delimiter )
- {
- g_warning( "Received an invalid message from hyprland: %s", message );
- return;
- }
-
- *delimiter = '\0';
- gchar* name = message;
- gchar* payload = delimiter + 2;
- for ( gsize i = 0; i < G_N_ELEMENTS( event_handlers ); ++i )
- {
- EventHandler handler = event_handlers[i];
- if ( !strcmp( name, handler.name ) )
- {
- g_idle_add_full(
- G_PRIORITY_DEFAULT,
- G_SOURCE_FUNC( handler.fn ),
- event_data_new( self, name, payload ),
- (GDestroyNotify)event_data_free );
- break;
- }
- }
-}
-
-//
-// Synchronously send a command to hyprland's control socket (socket.sock), returning the entire response.
-//
-gchar* foobar_workspace_service_send_request(
- FoobarWorkspaceService* self,
- gchar const* request,
- GError** error )
-{
- g_autoptr( GSocket ) sock = g_socket_new(
- G_SOCKET_FAMILY_UNIX,
- G_SOCKET_TYPE_STREAM,
- G_SOCKET_PROTOCOL_DEFAULT,
- error );
- if ( !sock ) { return NULL; }
-
- g_autoptr( GSocketAddress ) addr = g_unix_socket_address_new( self->tx_path );
- if ( !g_socket_connect( sock, addr, NULL, error ) ) { return NULL; }
-
- if ( g_socket_send( sock, request, strlen( request ), NULL, error ) == -1 ) { return NULL; }
-
- GString* str = g_string_sized_new( 0 );
- gchar buf[8192];
- while ( TRUE )
- {
- gssize received = g_socket_receive( sock, buf, sizeof( buf ), self->event_cancellable, error );
- if ( received < 0 ) { return g_string_free( str, TRUE ); }
-
- if ( received == 0 ) { break; }
- str = g_string_append_len( str, buf, received );
- }
-
- return g_string_free( str, FALSE );
-}
-
-//
-// Synchronously send a command to hyprland's control socket (socket.sock), returning the parsed the JSON response.
-//
-JsonNode* foobar_workspace_service_send_json_request(
- FoobarWorkspaceService* self,
- gchar const* request,
- GError** error )
-{
- g_autofree gchar* json = foobar_workspace_service_send_request( self, request, error );
- if ( !json ) { return NULL; }
-
- g_autoptr( JsonParser ) parser = json_parser_new( );
- if ( !json_parser_load_from_data( parser, json, (gssize)strlen( json ), error ) ) { return NULL; }
-
- return json_parser_steal_root( parser );
-}
-
-//
-// Refresh the list of workspaces by actively sending a request to hyprland.
-//
-void foobar_workspace_service_initialize( FoobarWorkspaceService* self )
-{
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->workspaces ) ); ++i )
- {
- FoobarWorkspace* workspace = g_list_model_get_item( G_LIST_MODEL( self->workspaces ), i );
- workspace->service = NULL;
- }
- g_list_store_remove_all( self->workspaces );
- g_autoptr( GListStore ) workspaces = g_list_store_new( FOOBAR_TYPE_WORKSPACE );
-
- {
- g_autoptr( GError ) error = NULL;
- g_autoptr( JsonNode ) workspaces_node = foobar_workspace_service_send_json_request(
- self,
- "j/workspaces",
- &error );
- if ( !workspaces_node )
- {
- g_warning( "Unable to load workspaces: %s", error->message );
- return;
- }
-
- JsonArray* workspaces_array = json_node_get_array( workspaces_node );
- for ( guint i = 0; i < json_array_get_length( workspaces_array ); ++i )
- {
- JsonObject* workspace_object = json_array_get_object_element( workspaces_array, i );
- foobar_workspace_service_add_workspace( self, workspaces, workspace_object, FALSE );
- }
- }
-
- {
- // Load configured persistent workspaces.
-
- g_autoptr( GError ) error = NULL;
- g_autoptr( JsonNode ) rules_node = foobar_workspace_service_send_json_request(
- self,
- "j/workspacerules",
- &error );
- if ( rules_node )
- {
- JsonArray* rules_array = json_node_get_array( rules_node );
- for ( guint i = 0; i < json_array_get_length( rules_array ); ++i )
- {
- JsonObject* rule_object = json_array_get_object_element( rules_array, i );
- gchar const* name = json_object_get_string_member_with_default( rule_object, "workspaceString", NULL );
- if ( !name )
- {
- g_warning( "Found an invalid workspaceString value, skipping." );
- continue;
- }
-
- if ( !json_object_get_boolean_member_with_default( rule_object, "persistent", FALSE ) ) { continue; }
-
- gint64 id = foobar_workspace_service_parse_id( name );
- json_object_set_int_member( rule_object, "id", id );
- json_object_set_string_member( rule_object, "name", name );
-
- foobar_workspace_service_add_workspace( self, workspaces, rule_object, TRUE );
- }
- }
- else { g_warning( "Unable to load workspace rules: %s", error->message ); }
- }
-
- foobar_workspace_service_update_active( self, workspaces );
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( workspaces ) ); ++i )
- {
- FoobarWorkspace* workspace = g_list_model_get_item( G_LIST_MODEL( workspaces ), i );
- g_list_store_append( self->workspaces, workspace );
- }
-}
-
-//
-// Update the currently active/visible workspaces by actively sending a request to hyprland.
-//
-void foobar_workspace_service_update_active(
- FoobarWorkspaceService* self,
- GListStore* workspaces )
-{
- g_autoptr( GArray ) visible_workspace_ids = g_array_new( FALSE, FALSE, sizeof( gint64 ) );
- gint64 active_workspace_id = 0;
- gint64 active_special_workspace_id = 0;
-
- {
- // Get all active workspaces
-
- g_autoptr( GError ) error = NULL;
- g_autoptr( JsonNode ) monitors_node = foobar_workspace_service_send_json_request(
- self,
- "j/monitors",
- &error );
- if ( monitors_node )
- {
- JsonArray* monitors_array = json_node_get_array( monitors_node );
- for ( guint i = 0; i < json_array_get_length( monitors_array ); ++i )
- {
- JsonObject* monitor_object = json_array_get_object_element( monitors_array, i );
- gint64 workspace_id = 0;
- gint64 special_workspace_id = 0;
-
- if ( json_object_has_member( monitor_object, "activeWorkspace" ) )
- {
- JsonObject* workspace_object = json_object_get_object_member( monitor_object, "activeWorkspace" );
- workspace_id = json_object_get_int_member_with_default( workspace_object, "id", 0 );
- if ( workspace_id ) { g_array_append_val( visible_workspace_ids, workspace_id ); }
- }
-
- if ( json_object_has_member( monitor_object, "specialWorkspace" ) )
- {
- JsonObject* workspace_object = json_object_get_object_member( monitor_object, "specialWorkspace" );
- special_workspace_id = json_object_get_int_member_with_default( workspace_object, "id", 0 );
- if ( special_workspace_id ) { g_array_append_val( visible_workspace_ids, special_workspace_id ); }
- }
-
- // If this is the focused monitor, then the visible workspace is also the active one.
-
- if ( json_object_get_boolean_member_with_default( monitor_object, "focused", FALSE ) )
- {
- active_workspace_id = workspace_id;
- active_special_workspace_id = special_workspace_id;
- }
- }
- }
- else { g_warning( "Unable to load monitors: %s", error->message ); }
- }
-
- // Sort visible workspaces to allow binary search.
-
- g_array_sort( visible_workspace_ids, foobar_workspace_service_compare_ids );
-
- // Go through all workspaces and update their state.
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( workspaces ) ); ++i )
- {
- FoobarWorkspace* workspace = g_list_model_get_item( G_LIST_MODEL( workspaces ), i );
- FoobarWorkspaceFlags flags = foobar_workspace_get_flags( workspace );
- gint64 id = foobar_workspace_get_id( workspace );
-
- // Active
- gboolean is_active = id == active_workspace_id || id == active_special_workspace_id;
- flags = is_active ? flags | FOOBAR_WORKSPACE_FLAGS_ACTIVE : flags & ~FOOBAR_WORKSPACE_FLAGS_ACTIVE;
-
- // Disable urgency if workspace is active
- if ( is_active ) { flags = flags & ~FOOBAR_WORKSPACE_FLAGS_URGENT; }
-
- // Visible
- gboolean is_visible = g_array_binary_search(
- visible_workspace_ids,
- &id,
- foobar_workspace_service_compare_ids,
- NULL );
- flags = is_visible ? flags | FOOBAR_WORKSPACE_FLAGS_VISIBLE : flags & ~FOOBAR_WORKSPACE_FLAGS_VISIBLE;
-
- foobar_workspace_set_flags( workspace, flags );
- }
-}
-
-//
-// Add or update a workspace described by a JSON object.
-//
-void foobar_workspace_service_add_workspace(
- FoobarWorkspaceService* self,
- GListStore* workspaces,
- JsonObject* workspace_object,
- gboolean is_persistent )
-{
- gchar const* name = json_object_get_string_member( workspace_object, "name" );
- gint64 id = json_object_get_int_member( workspace_object, "id" );
- gboolean is_special = g_str_has_prefix( name, "special" );
- if ( g_str_has_prefix( name, "name:" ) ) { name += strlen( "name:" ); }
- else if ( g_str_has_prefix( name, "special:" ) ) { name += strlen( "special:" ); }
-
- FoobarWorkspace* existing = foobar_workspace_service_find_workspace( G_LIST_MODEL( workspaces ), id, NULL );
- if ( existing )
- {
- // Only update persistence.
-
- FoobarWorkspaceFlags flags = foobar_workspace_get_flags( existing );
- flags = is_persistent ? flags | FOOBAR_WORKSPACE_FLAGS_PERSISTENT : flags & ~FOOBAR_WORKSPACE_FLAGS_PERSISTENT;
- foobar_workspace_set_flags( existing, flags );
- }
- else
- {
- // Found a new workspace -> create a new object for it.
-
- FoobarWorkspaceFlags flags = FOOBAR_WORKSPACE_FLAGS_NONE;
- flags = is_persistent ? flags | FOOBAR_WORKSPACE_FLAGS_PERSISTENT : flags;
- flags = is_special ? flags | FOOBAR_WORKSPACE_FLAGS_SPECIAL : flags;
-
- g_autoptr( FoobarWorkspace ) workspace = foobar_workspace_new( self );
- foobar_workspace_set_id( workspace, id );
- foobar_workspace_set_name( workspace, name );
- foobar_workspace_set_monitor( workspace, json_object_get_string_member( workspace_object, "monitor" ) );
- foobar_workspace_set_flags( workspace, flags );
- g_list_store_append( workspaces, workspace );
- }
-}
-
-//
-// Find an existing workspace object by its ID.
-//
-FoobarWorkspace* foobar_workspace_service_find_workspace(
- GListModel* workspaces,
- gint64 id,
- guint* out_index )
-{
- for ( guint i = 0; i < g_list_model_get_n_items( workspaces ); ++i )
- {
- FoobarWorkspace* workspace = g_list_model_get_item( workspaces, i );
- if ( foobar_workspace_get_id( workspace ) == id )
- {
- if ( out_index ) { *out_index = i; }
- return workspace;
- }
- }
-
- return NULL;
-}
-
-//
-// Hyprland's IPC sometimes reports the creation of workspaces strangely named `special:special:<some_name>`. This
-// function checks for that and is used to avoid creating (and then removing) such workspaces.
-//
-// See hyprwm/Hyprland#3424 for more info.
-//
-gboolean foobar_workspace_service_is_invalid_name( gchar const* name )
-{
- return g_str_has_prefix( name, "special:special:" );
-}
-
-//
-// Parse an ID string and return its numeric code.
-//
-gint64 foobar_workspace_service_parse_id( gchar const* str )
-{
- if ( !g_strcmp0( str, "special" ) ) { return -99; }
-
- return strtoll( str, NULL, 10 );
-}
-
-//
-// Get the base path for hyprland socket files.
-//
-gchar* foobar_workspace_service_get_hyprland_base_path( void )
-{
- gchar const* instance_signature = g_getenv( "HYPRLAND_INSTANCE_SIGNATURE" );
- if ( !instance_signature )
- {
- return NULL;
- }
-
- // for Hyprland >= 0.40
- gchar* result = g_strdup_printf( "%s/hypr/%s", g_get_user_runtime_dir( ), instance_signature );
- if ( g_file_test( result, G_FILE_TEST_EXISTS ) )
- {
- return result;
- }
-
- // for Hyprland < 0.40
- g_free( result );
- result = g_strdup_printf( "%s/hypr/%s", g_get_tmp_dir( ), instance_signature );
- if ( g_file_test( result, G_FILE_TEST_EXISTS ) )
- {
- return result;
- }
-
- g_free( result );
- return NULL;
-}
-
-//
-// Comparison function for two workspace IDs, used for binary search.
-//
-gint foobar_workspace_service_compare_ids(
- gconstpointer a,
- gconstpointer b )
-{
- gint64 const* a_int = a;
- gint64 const* b_int = b;
-
- if ( *a_int < *b_int ) { return -1; }
- if ( *a_int > *b_int ) { return 1; }
- 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_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.
-//
-static EventData* event_data_new(
- FoobarWorkspaceService* service,
- gchar const* name,
- gchar const* payload )
-{
- EventData* self = g_new0( EventData, 1 );
- self->service = g_object_ref( service );
- self->name = g_strdup( name );
- self->payload = g_strdup( payload );
- return self;
-}
-
-//
-// Destroy an EventData structure.
-//
-static void event_data_free( EventData* self )
-{
- if ( self )
- {
- g_object_unref( self->service );
- g_free( self->name );
- g_free( self->payload );
- g_free( self );
- }
-}
diff --git a/src/services/workspace-service.h b/src/services/workspace-service.h
deleted file mode 100644
index c80e7dc..0000000
--- a/src/services/workspace-service.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_WORKSPACE_FLAGS foobar_workspace_flags_get_type( )
-#define FOOBAR_TYPE_WORKSPACE foobar_workspace_get_type( )
-#define FOOBAR_TYPE_WORKSPACE_SERVICE foobar_workspace_service_get_type( )
-
-typedef enum
-{
- FOOBAR_WORKSPACE_FLAGS_NONE = 0,
- // Indicates that the workspace is a globally active one (i.e. visible on the focused monitor).
- FOOBAR_WORKSPACE_FLAGS_ACTIVE = 1 << 0,
- // Indicates that the workspace is currently visible.
- FOOBAR_WORKSPACE_FLAGS_VISIBLE = 1 << 1,
- // Indicates that the workspace is a special workspace.
- FOOBAR_WORKSPACE_FLAGS_SPECIAL = 1 << 2,
- // Indicates that the workspace is persistent (i.e. configured in the config file).
- FOOBAR_WORKSPACE_FLAGS_PERSISTENT = 1 << 3,
- // Indicates that the workspace requires an urgent action (e.g. a notification).
- FOOBAR_WORKSPACE_FLAGS_URGENT = 1 << 4,
-} FoobarWorkspaceFlags;
-
-GType foobar_workspace_flags_get_type( void );
-
-G_DECLARE_FINAL_TYPE( FoobarWorkspace, foobar_workspace, FOOBAR, WORKSPACE, GObject )
-
-gint64 foobar_workspace_get_id ( FoobarWorkspace* self );
-gchar const* foobar_workspace_get_name ( FoobarWorkspace* self );
-gchar const* foobar_workspace_get_monitor( FoobarWorkspace* self );
-FoobarWorkspaceFlags foobar_workspace_get_flags ( FoobarWorkspace* self );
-void foobar_workspace_activate ( FoobarWorkspace* self );
-
-G_DECLARE_FINAL_TYPE( FoobarWorkspaceService, foobar_workspace_service, FOOBAR, WORKSPACE_SERVICE, GObject )
-
-FoobarWorkspaceService* foobar_workspace_service_new ( void );
-GListModel* foobar_workspace_service_get_workspaces( FoobarWorkspaceService* self );
-
-G_END_DECLS