summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2025-06-19 23:17:58 +0200
committerHannes Schulze <haschu0103@outlook.de>2025-06-19 23:17:58 +0200
commit1af3d14f3f995645a0de08a54b244c19840709e9 (patch)
tree3c4098863d8a8fd004a2acc8912a23b6036ab4dd /src
parent40efcf60a5f39674b5137f11de7bcfe37fb121b5 (diff)
init hs project
Diffstat (limited to 'src')
-rw-r--r--src/Main.hs2
-rw-r--r--src/application.c691
-rw-r--r--src/application.h19
-rw-r--r--src/control-center.c821
-rw-r--r--src/control-center.h27
-rw-r--r--src/dbus/com.github.hannesschulze.foobar.xml8
-rw-r--r--src/dbus/meson.build37
-rw-r--r--src/dbus/org.bluez.xml66
-rw-r--r--src/dbus/org.freedesktop.Notifications.xml35
-rw-r--r--src/dbus/org.freedesktop.UPower.xml12
-rw-r--r--src/launcher-item.c73
-rw-r--r--src/launcher-item.h28
-rw-r--r--src/launcher.c648
-rw-r--r--src/launcher.h20
-rw-r--r--src/main.c12
-rw-r--r--src/meson.build74
-rw-r--r--src/notification-area.c196
-rw-r--r--src/notification-area.h18
-rw-r--r--src/panel.c348
-rw-r--r--src/panel.h34
-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
-rw-r--r--src/utils.c25
-rw-r--r--src/utils.h9
-rw-r--r--src/widgets/control-center/control-button.c496
-rw-r--r--src/widgets/control-center/control-button.h31
-rw-r--r--src/widgets/control-center/control-details-item.c307
-rw-r--r--src/widgets/control-center/control-details-item.h32
-rw-r--r--src/widgets/control-center/control-details.c289
-rw-r--r--src/widgets/control-center/control-details.h22
-rw-r--r--src/widgets/control-center/control-slider.c428
-rw-r--r--src/widgets/control-center/control-slider.h28
-rw-r--r--src/widgets/control-center/meson.build6
-rw-r--r--src/widgets/inset-container.c363
-rw-r--r--src/widgets/inset-container.h22
-rw-r--r--src/widgets/limit-container.c341
-rw-r--r--src/widgets/limit-container.h22
-rw-r--r--src/widgets/meson.build8
-rw-r--r--src/widgets/notification-widget.c821
-rw-r--r--src/widgets/notification-widget.h50
-rw-r--r--src/widgets/panel/meson.build7
-rw-r--r--src/widgets/panel/panel-item-clock.c152
-rw-r--r--src/widgets/panel/panel-item-clock.h15
-rw-r--r--src/widgets/panel/panel-item-icon.c92
-rw-r--r--src/widgets/panel/panel-item-icon.h13
-rw-r--r--src/widgets/panel/panel-item-status.c955
-rw-r--r--src/widgets/panel/panel-item-status.h25
-rw-r--r--src/widgets/panel/panel-item-workspaces.c327
-rw-r--r--src/widgets/panel/panel-item-workspaces.h16
-rw-r--r--src/widgets/panel/panel-item.c195
-rw-r--r--src/widgets/panel/panel-item.h23
78 files changed, 2 insertions, 22726 deletions
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
index 0000000..3b09145
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,2 @@
+main :: IO ()
+main = putStrLn "Hello, world!"
diff --git a/src/application.c b/src/application.c
deleted file mode 100644
index bad62b1..0000000
--- a/src/application.c
+++ /dev/null
@@ -1,691 +0,0 @@
-#include "application.h"
-#include "panel.h"
-#include "launcher.h"
-#include "control-center.h"
-#include "notification-area.h"
-#include "dbus/server.h"
-#include "services/battery-service.h"
-#include "services/clock-service.h"
-#include "services/brightness-service.h"
-#include "services/quick-answer-service.h"
-#include "services/workspace-service.h"
-#include "services/notification-service.h"
-#include "services/audio-service.h"
-#include "services/network-service.h"
-#include "services/bluetooth-service.h"
-#include "services/application-service.h"
-#include "services/configuration-service.h"
-
-//
-// FoobarApplication:
-//
-// Entry point for the application. This class is responsible for parsing and handling command line arguments, creating
-// services and presenting windows as needed.
-//
-// Only the panel windows are application windows -- when all panels are closed, the application quits. Because of this,
-// the application can't own a reference to the panel windows but needs to store their window IDs. Since all other
-// windows are not attached to the application object, the application can hold a reference on them.
-//
-
-struct _FoobarApplication
-{
- GtkApplication parent_instance;
- FoobarBatteryService* battery_service;
- FoobarClockService* clock_service;
- FoobarBrightnessService* brightness_service;
- FoobarWorkspaceService* workspace_service;
- FoobarNotificationService* notification_service;
- FoobarAudioService* audio_service;
- FoobarNetworkService* network_service;
- FoobarBluetoothService* bluetooth_service;
- FoobarApplicationService* application_service;
- FoobarQuickAnswerService* quick_answer_service;
- FoobarConfigurationService* configuration_service;
- gulong config_handler_id;
- FoobarServer* server_skeleton;
- GtkCssProvider* style_provider;
- guint bus_owner_id;
- GArray* panel_window_ids;
- gboolean panel_is_multi_monitor;
- GListModel* monitors;
- gulong monitors_handler_id;
- FoobarLauncher* launcher;
- FoobarControlCenter* control_center;
- FoobarNotificationArea* notification_area;
- gboolean option_inspector;
- gboolean option_quit;
- gboolean option_toggle_launcher;
- gboolean option_toggle_control_center;
-};
-
-static void foobar_application_class_init ( FoobarApplicationClass* klass );
-static void foobar_application_init ( FoobarApplication* self );
-static void foobar_application_activate ( GApplication* app );
-static int foobar_application_command_line ( GApplication* app,
- GApplicationCommandLine* cmdline );
-static void foobar_application_finalize ( GObject* object );
-static void foobar_application_handle_bus_acquired ( GDBusConnection* connection,
- gchar const* name,
- gpointer userdata );
-static gboolean foobar_application_handle_inspector ( FoobarServer* server,
- GDBusMethodInvocation* invocation,
- gpointer userdata );
-static gboolean foobar_application_handle_quit ( FoobarServer* server,
- GDBusMethodInvocation* invocation,
- gpointer userdata );
-static gboolean foobar_application_handle_toggle_launcher ( FoobarServer* server,
- GDBusMethodInvocation* invocation,
- gpointer userdata );
-static gboolean foobar_application_handle_toggle_control_center( FoobarServer* server,
- GDBusMethodInvocation* invocation,
- gpointer userdata );
-static void foobar_application_handle_config_changed ( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_application_handle_monitors_changed ( GListModel* list,
- guint position,
- guint removed,
- guint added,
- gpointer userdata );
-static void foobar_application_destroy_panels ( FoobarApplication* self );
-static void foobar_application_create_panels ( FoobarApplication* self );
-static guint foobar_application_create_panel ( FoobarApplication* self,
- GdkMonitor* monitor );
-
-G_DEFINE_FINAL_TYPE( FoobarApplication, foobar_application, GTK_TYPE_APPLICATION )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Application Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the application.
-//
-void foobar_application_class_init( FoobarApplicationClass* klass )
-{
- GApplicationClass* app_klass = G_APPLICATION_CLASS( klass );
- app_klass->activate = foobar_application_activate;
- app_klass->command_line = foobar_application_command_line;
-
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->finalize = foobar_application_finalize;
-}
-
-//
-// Instance initialization for the application.
-//
-void foobar_application_init( FoobarApplication* self )
-{
- g_application_set_default( G_APPLICATION( self ) );
- g_application_set_option_context_summary( G_APPLICATION( self ), "A bar." );
-
- GOptionEntry const options[] =
- {
- {
- .long_name = "inspector",
- .short_name = 'i',
- .flags = G_OPTION_FLAG_NONE,
- .arg = G_OPTION_ARG_NONE,
- .arg_data = &self->option_inspector,
- .description = "Open the GTK inspector for the active bar instance.",
- .arg_description = NULL,
- },
- {
- .long_name = "quit",
- .short_name = 'q',
- .flags = G_OPTION_FLAG_NONE,
- .arg = G_OPTION_ARG_NONE,
- .arg_data = &self->option_quit,
- .description = "Quit the active bar instance.",
- .arg_description = NULL,
- },
- {
- .long_name = "toggle-launcher",
- .short_name = 'l',
- .flags = G_OPTION_FLAG_NONE,
- .arg = G_OPTION_ARG_NONE,
- .arg_data = &self->option_toggle_launcher,
- .description = "Toggle the launcher visibility for the active bar instance.",
- .arg_description = NULL,
- },
- {
- .long_name = "toggle-control-center",
- .short_name = 'c',
- .flags = G_OPTION_FLAG_NONE,
- .arg = G_OPTION_ARG_NONE,
- .arg_data = &self->option_toggle_control_center,
- .description = "Toggle the control center visibility for the active bar instance.",
- .arg_description = NULL,
- },
- { 0 },
- };
- g_application_add_main_option_entries( G_APPLICATION( self ), options );
-}
-
-//
-// Called by GTK when the application is activated (only for a single instance of the application).
-//
-// This is where the services are created and the windows are presented.
-//
-void foobar_application_activate( GApplication* app )
-{
- FoobarApplication* self = (FoobarApplication*)app;
-
- // Initialize services.
-
- self->battery_service = foobar_battery_service_new( );
- self->clock_service = foobar_clock_service_new( );
- self->brightness_service = foobar_brightness_service_new( );
- self->workspace_service = foobar_workspace_service_new( );
- self->notification_service = foobar_notification_service_new( );
- self->audio_service = foobar_audio_service_new( );
- self->network_service = foobar_network_service_new( );
- self->bluetooth_service = foobar_bluetooth_service_new( );
- self->application_service = foobar_application_service_new( );
- self->quick_answer_service = foobar_quick_answer_service_new( );
- self->configuration_service = foobar_configuration_service_new( );
-
- // Enforce a uniform style by forcing Adwaita and shipping our own icons.
-
- GtkSettings* settings = gtk_settings_get_default( );
- g_object_set( settings, "gtk-theme-name", "Adwaita", NULL );
-
- GtkIconTheme* icon_theme = gtk_icon_theme_get_for_display( gdk_display_get_default( ) );
- gtk_icon_theme_add_resource_path( icon_theme, "/foobar/icons" );
-
- // This application instance now becomes the "server" process, listening for commands from clients (which can be
- // invoked via the command line).
-
- self->bus_owner_id = g_bus_own_name(
- G_BUS_TYPE_SESSION,
- g_application_get_application_id( G_APPLICATION( self ) ),
- G_BUS_NAME_OWNER_FLAGS_NONE,
- foobar_application_handle_bus_acquired,
- NULL,
- NULL,
- self,
- NULL );
-
- // React to configuration file changes.
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_application_apply_configuration( self, foobar_configuration_get_general( config ) );
- self->panel_is_multi_monitor = foobar_panel_configuration_get_multi_monitor( foobar_configuration_get_panel( config ) );
- self->config_handler_id = g_signal_connect(
- self->configuration_service,
- "notify::current",
- G_CALLBACK( foobar_application_handle_config_changed ),
- self );
-
- // React to monitor configuration changes (for multi-monitor support in the panel).
-
- self->monitors = gdk_display_get_monitors( gdk_display_get_default( ) );
- g_object_ref( self->monitors );
-
- self->monitors_handler_id = g_signal_connect(
- self->monitors,
- "items-changed",
- G_CALLBACK( foobar_application_handle_monitors_changed ),
- self );
-
- // Create all the windows.
-
- foobar_application_create_panels( self );
-
- self->launcher = foobar_launcher_new(
- self->application_service,
- self->quick_answer_service,
- self->configuration_service );
- g_object_ref( self->launcher );
-
- self->control_center = foobar_control_center_new(
- self->brightness_service,
- self->audio_service,
- self->network_service,
- self->bluetooth_service,
- self->notification_service,
- self->configuration_service );
- g_object_ref( self->control_center );
-
- self->notification_area = foobar_notification_area_new(
- self->notification_service,
- self->configuration_service );
- g_object_ref( self->notification_area );
-
- // Only present the notification area, all other windows start out as hidden windows.
-
- gtk_window_present( GTK_WINDOW( self->notification_area ) );
-}
-
-//
-// Called before activating the application, when GTK has parsed the command line arguments.
-//
-// This method decides whether the application should be activated (if it is the first instance). Otherwise it will
-// handle command line flags for controlling the application.
-//
-int foobar_application_command_line(
- GApplication* app,
- GApplicationCommandLine* cmdline )
-{
- (void)cmdline;
- FoobarApplication* self = (FoobarApplication*)app;
-
- // Try to open a connection to the existing server.
-
- g_autoptr( GError ) error = NULL;
- g_autoptr( FoobarServer ) proxy = foobar_server_proxy_new_for_bus_sync(
- G_BUS_TYPE_SESSION,
- G_DBUS_PROXY_FLAGS_NONE,
- g_application_get_application_id( G_APPLICATION( self ) ),
- "/com/github/hannesschulze/foobar/server",
- NULL,
- &error );
-
- if ( !proxy )
- {
- g_printerr( "Unable to create proxy: %s\n", error->message );
- return 1;
- }
-
- // If there is no existing server application, the "name owner" will not be set.
-
- g_autofree gchar* owner = g_dbus_proxy_get_name_owner( G_DBUS_PROXY( proxy ) );
- if ( owner )
- {
- // There is an active instance -> this is a client. Handle all command line flags.
-
- if ( self->option_inspector && !foobar_server_call_inspector_sync( proxy, NULL, &error ) )
- {
- g_printerr( "Unable to open inspector: %s\n", error->message );
- return 2;
- }
-
- if ( self->option_toggle_launcher && !foobar_server_call_toggle_launcher_sync( proxy, NULL, &error ) )
- {
- g_printerr( "Unable to toggle launcher: %s\n", error->message );
- return 2;
- }
-
- if ( self->option_toggle_control_center && !foobar_server_call_toggle_control_center_sync( proxy, NULL, &error ) )
- {
- g_printerr( "Unable to toggle control center: %s\n", error->message );
- return 2;
- }
-
- if ( self->option_quit && !foobar_server_call_quit_sync( proxy, NULL, &error ) )
- {
- g_printerr( "Unable to quit server: %s\n", error->message );
- return 2;
- }
- }
- else if ( !self->option_quit )
- {
- // This is the main application -> launch the server.
-
- g_application_activate( G_APPLICATION( self ) );
- }
-
- return 0;
-}
-
-//
-// Instance cleanup for the application.
-//
-void foobar_application_finalize( GObject* object )
-{
- FoobarApplication* self = (FoobarApplication*)object;
-
- // Destroy the DBus server.
-
- if ( self->server_skeleton )
- {
- g_dbus_interface_skeleton_unexport( G_DBUS_INTERFACE_SKELETON( self->server_skeleton ) );
- }
-
- // Destroy non-application windows.
-
- if ( self->launcher ) { gtk_window_destroy( GTK_WINDOW( self->launcher ) ); }
- if ( self->control_center ) { gtk_window_destroy( GTK_WINDOW( self->control_center ) ); }
- if ( self->notification_area ) { gtk_window_destroy( GTK_WINDOW( self->notification_area ) ); }
-
- // Destroy all other objects.
-
- g_clear_handle_id( &self->bus_owner_id, g_bus_unown_name );
- g_clear_signal_handler( &self->config_handler_id, self->configuration_service );
- g_clear_signal_handler( &self->monitors_handler_id, self->monitors );
- g_clear_object( &self->battery_service );
- g_clear_object( &self->clock_service );
- g_clear_object( &self->brightness_service );
- g_clear_object( &self->workspace_service );
- g_clear_object( &self->notification_service );
- g_clear_object( &self->audio_service );
- g_clear_object( &self->network_service );
- g_clear_object( &self->bluetooth_service );
- g_clear_object( &self->application_service );
- g_clear_object( &self->quick_answer_service );
- g_clear_object( &self->configuration_service );
- g_clear_object( &self->style_provider );
- g_clear_object( &self->server_skeleton );
- g_clear_object( &self->launcher );
- g_clear_object( &self->control_center );
- g_clear_object( &self->notification_area );
- g_clear_object( &self->monitors );
- g_clear_pointer( &self->panel_window_ids, g_array_unref );
-
- G_OBJECT_CLASS( foobar_application_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new application instance.
-//
-FoobarApplication* foobar_application_new( void )
-{
- return g_object_new(
- FOOBAR_TYPE_APPLICATION,
- "application-id",
- "com.github.hannesschulze.foobar",
- "flags",
- G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_NON_UNIQUE,
- NULL );
-}
-
-//
-// Toggle the launcher window's visibility.
-//
-void foobar_application_toggle_launcher( FoobarApplication* self )
-{
- g_return_if_fail( FOOBAR_IS_APPLICATION( self ) );
-
- gboolean previously_visible = gtk_widget_is_visible( GTK_WIDGET( self->launcher ) );
- gtk_widget_set_visible( GTK_WIDGET( self->launcher ), !previously_visible );
-}
-
-//
-// Toggle the control center window's visibility.
-//
-void foobar_application_toggle_control_center( FoobarApplication* self )
-{
- g_return_if_fail( FOOBAR_IS_APPLICATION( self ) );
-
- gboolean previously_visible = gtk_widget_is_visible( GTK_WIDGET( self->control_center ) );
- gtk_widget_set_visible( GTK_WIDGET( self->control_center ), !previously_visible );
-}
-
-//
-// Apply the general application configuration provided by the configuration service.
-//
-void foobar_application_apply_configuration(
- FoobarApplication* self,
- FoobarGeneralConfiguration const* config )
-{
- g_return_if_fail( FOOBAR_IS_APPLICATION( self ) );
- g_return_if_fail( config != NULL );
-
- GdkDisplay* display = gdk_display_get_default( );
-
- // Remove the old stylesheet from the application.
-
- if ( self->style_provider )
- {
- gtk_style_context_remove_provider_for_display( display, GTK_STYLE_PROVIDER( self->style_provider ) );
- g_clear_object( &self->style_provider );
- }
-
- // Add the new stylesheet.
-
- g_autoptr( GFile ) file = g_file_new_for_uri( foobar_general_configuration_get_stylesheet( config ) );
- self->style_provider = gtk_css_provider_new( );
- gtk_css_provider_load_from_file( self->style_provider, file );
- gtk_style_context_add_provider_for_display(
- display,
- GTK_STYLE_PROVIDER( self->style_provider ),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called after the application server process has asynchronously acquired the bus.
-//
-// This is where we can export server functionality using the corresponding DBus skeleton object.
-//
-void foobar_application_handle_bus_acquired(
- GDBusConnection* connection,
- gchar const* name,
- gpointer userdata )
-{
- (void)name;
- FoobarApplication* self = (FoobarApplication*)userdata;
-
- self->server_skeleton = foobar_server_skeleton_new( );
- g_signal_connect(
- self->server_skeleton,
- "handle-inspector",
- G_CALLBACK( foobar_application_handle_inspector ),
- self );
- g_signal_connect(
- self->server_skeleton,
- "handle-quit",
- G_CALLBACK( foobar_application_handle_quit ),
- self );
- g_signal_connect(
- self->server_skeleton,
- "handle-toggle-launcher",
- G_CALLBACK( foobar_application_handle_toggle_launcher ),
- self );
- g_signal_connect(
- self->server_skeleton,
- "handle-toggle-control-center",
- G_CALLBACK( foobar_application_handle_toggle_control_center ),
- self );
-
- g_autoptr( GError ) error = NULL;
- if ( !g_dbus_interface_skeleton_export(
- G_DBUS_INTERFACE_SKELETON( self->server_skeleton ),
- connection,
- "/com/github/hannesschulze/foobar/server",
- &error ) )
- {
- g_warning( "Unable to export server interface: %s", error->message );
- }
-}
-
-//
-// DBus skeleton callback for the "Inspector" method.
-//
-gboolean foobar_application_handle_inspector(
- FoobarServer* server,
- GDBusMethodInvocation* invocation,
- gpointer userdata )
-{
- (void)userdata;
-
- gtk_window_set_interactive_debugging( TRUE );
-
- foobar_server_complete_inspector( server, invocation );
- return G_DBUS_METHOD_INVOCATION_HANDLED;
-}
-
-//
-// DBus skeleton callback for the "Quit" method.
-//
-gboolean foobar_application_handle_quit(
- FoobarServer* server,
- GDBusMethodInvocation* invocation,
- gpointer userdata )
-{
- FoobarApplication* self = (FoobarApplication*)userdata;
-
- foobar_application_destroy_panels( self );
-
- foobar_server_complete_quit( server, invocation );
- return G_DBUS_METHOD_INVOCATION_HANDLED;
-}
-
-//
-// DBus skeleton callback for the "ToggleLauncher" method.
-//
-gboolean foobar_application_handle_toggle_launcher(
- FoobarServer* server,
- GDBusMethodInvocation* invocation,
- gpointer userdata )
-{
- FoobarApplication* self = (FoobarApplication*)userdata;
-
- foobar_application_toggle_launcher( self );
-
- foobar_server_complete_toggle_launcher( server, invocation );
- return G_DBUS_METHOD_INVOCATION_HANDLED;
-}
-
-//
-// DBus skeleton callback for the "ToggleControlCenter" method.
-//
-gboolean foobar_application_handle_toggle_control_center(
- FoobarServer* server,
- GDBusMethodInvocation* invocation,
- gpointer userdata )
-{
- FoobarApplication* self = (FoobarApplication*)userdata;
-
- foobar_application_toggle_control_center( self );
-
- foobar_server_complete_toggle_control_center( server, invocation );
- return G_DBUS_METHOD_INVOCATION_HANDLED;
-}
-
-//
-// Signal handler called when the global configuration file has changed.
-//
-void foobar_application_handle_config_changed(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- (void)pspec;
- FoobarApplication* self = (FoobarApplication*)userdata;
-
- // Apply general settings.
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_application_apply_configuration( self, foobar_configuration_get_general( config ) );
-
- // Update panel instances depending on whether multi-monitor mode is enabled.
-
- FoobarPanelConfiguration const* panel_config = foobar_configuration_get_panel( config );
- gboolean panel_is_multi_monitor = foobar_panel_configuration_get_multi_monitor( panel_config );
- if ( self->panel_is_multi_monitor != panel_is_multi_monitor )
- {
- self->panel_is_multi_monitor = panel_is_multi_monitor;
-
- // Panel windows keep the application alive, so we need to "hold" it while we re-create the panel windows.
-
- g_application_hold( G_APPLICATION( self ) );
- foobar_application_destroy_panels( self );
- foobar_application_create_panels( self );
- g_application_release( G_APPLICATION( self ) );
- }
-}
-
-//
-// Signal handler called when the list of GdkMonitor objects has changed, possibly requiring us to re-create the panel
-// windows.
-//
-void foobar_application_handle_monitors_changed(
- GListModel* list,
- guint position,
- guint removed,
- guint added,
- gpointer userdata )
-{
- (void)list;
- (void)position;
- (void)removed;
- (void)added;
- FoobarApplication* self = (FoobarApplication*)userdata;
-
- if ( self->panel_is_multi_monitor )
- {
- // Panel windows keep the application alive, so we need to "hold" it while we re-create the panel windows.
-
- g_application_hold( G_APPLICATION( self ) );
- foobar_application_destroy_panels( self );
- foobar_application_create_panels( self );
- g_application_release( G_APPLICATION( self ) );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Destroy all currently active panel windows.
-//
-void foobar_application_destroy_panels( FoobarApplication* self )
-{
- for ( guint i = 0; i < self->panel_window_ids->len; ++i )
- {
- guint window_id = g_array_index( self->panel_window_ids, guint, i );
- GtkWindow* window = gtk_application_get_window_by_id( GTK_APPLICATION( self ), window_id );
- if ( window ) { gtk_window_destroy( window ); }
- }
-
- g_clear_pointer( &self->panel_window_ids, g_array_unref );
-}
-
-//
-// Create and present panel windows for
-// - each monitor, if multi-monitor mode is enabled
-// - the current monitor, if multi-monitor mode is disabled.
-//
-void foobar_application_create_panels( FoobarApplication* self )
-{
- self->panel_window_ids = g_array_new( FALSE, FALSE, sizeof( guint ) );
-
- if ( self->panel_is_multi_monitor )
- {
- for ( guint i = 0; i < g_list_model_get_n_items( self->monitors ); ++i )
- {
- GdkMonitor* monitor = g_list_model_get_item( self->monitors, i );
- guint id = foobar_application_create_panel( self, monitor );
- g_array_append_val( self->panel_window_ids, id );
- }
- }
- else
- {
- guint id = foobar_application_create_panel( self, NULL );
- g_array_append_val( self->panel_window_ids, id );
- }
-}
-
-//
-// Create and present a panel for a single monitor (or NULL to use the current monitor).
-//
-guint foobar_application_create_panel(
- FoobarApplication* self,
- GdkMonitor* monitor )
-{
- FoobarPanel* panel = foobar_panel_new(
- GTK_APPLICATION( self ),
- monitor,
- self->battery_service,
- self->clock_service,
- self->brightness_service,
- self->workspace_service,
- self->audio_service,
- self->network_service,
- self->bluetooth_service,
- self->notification_service,
- self->configuration_service );
- gtk_window_present( GTK_WINDOW( panel ) );
-
- return gtk_application_window_get_id( GTK_APPLICATION_WINDOW( panel ) );
-}
diff --git a/src/application.h b/src/application.h
deleted file mode 100644
index c5353c3..0000000
--- a/src/application.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-#include "services/configuration-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_APPLICATION foobar_application_get_type( )
-#define FOOBAR_APPLICATION_DEFAULT FOOBAR_APPLICATION( g_application_get_default( ) )
-
-G_DECLARE_FINAL_TYPE( FoobarApplication, foobar_application, FOOBAR, APPLICATION, GtkApplication )
-
-FoobarApplication* foobar_application_new ( void );
-void foobar_application_toggle_launcher ( FoobarApplication* self );
-void foobar_application_toggle_control_center( FoobarApplication* self );
-void foobar_application_apply_configuration ( FoobarApplication* self,
- FoobarGeneralConfiguration const* config );
-
-G_END_DECLS
diff --git a/src/control-center.c b/src/control-center.c
deleted file mode 100644
index bb22830..0000000
--- a/src/control-center.c
+++ /dev/null
@@ -1,821 +0,0 @@
-#include "control-center.h"
-#include "widgets/control-center/control-button.h"
-#include "widgets/control-center/control-slider.h"
-#include "widgets/control-center/control-details.h"
-#include "widgets/control-center/control-details-item.h"
-#include "widgets/inset-container.h"
-#include "widgets/notification-widget.h"
-#include <gtk4-layer-shell.h>
-
-#define STACK_ITEM_LIST "list"
-#define STACK_ITEM_PLACEHOLDER "placeholder"
-
-//
-// FoobarControlCenter:
-//
-// The control center is made up of two sections:
-// - The "controls" section for configuring wi-fi, volume, brightness, etc.
-// - The "notifications" section showing all notifications (including dismissed ones) and allowing the user to close
-// them.
-//
-
-struct _FoobarControlCenter
-{
- GtkWindow parent_instance;
- GtkWidget* control_container;
- GtkWidget* notification_list;
- GtkWidget* notification_container;
- GtkWidget* notification_placeholder;
- GtkWidget* notification_stack;
- GtkWidget* layout;
- FoobarBrightnessService* brightness_service;
- FoobarAudioService* audio_service;
- FoobarNetworkService* network_service;
- FoobarBluetoothService* bluetooth_service;
- FoobarNotificationService* notification_service;
- FoobarConfigurationService* configuration_service;
- gchar* notification_time_format;
- gint notification_min_height;
- gint notification_close_button_inset;
- gint padding;
- gint spacing;
- gulong config_handler_id;
-};
-
-static void foobar_control_center_class_init ( FoobarControlCenterClass* klass );
-static void foobar_control_center_init ( FoobarControlCenter* self );
-static void foobar_control_center_finalize ( GObject* object );
-static void foobar_control_center_handle_notification_setup ( GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata );
-static void foobar_control_center_handle_network_setup ( GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata );
-static void foobar_control_center_handle_bluetooth_device_setup ( GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata );
-static void foobar_control_center_handle_bluetooth_device_activate ( GtkListView* view,
- guint position,
- gpointer userdata );
-static void foobar_control_center_handle_audio_device_setup ( GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata );
-static void foobar_control_center_handle_audio_device_activate ( GtkListView* view,
- guint position,
- gpointer userdata );
-static void foobar_control_center_handle_config_change ( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-static FoobarControlDetailsAccessory foobar_control_center_compute_bluetooth_device_accessory ( GtkExpression* expression,
- FoobarBluetoothDeviceState state,
- gpointer userdata );
-static gchar* foobar_control_center_compute_visible_notification_child_name( GtkExpression* expression,
- guint count,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarControlCenter, foobar_control_center, GTK_TYPE_WINDOW )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Window Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the control center.
-//
-void foobar_control_center_class_init( FoobarControlCenterClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->finalize = foobar_control_center_finalize;
-}
-
-//
-// Instance initialization for the control center.
-//
-void foobar_control_center_init( FoobarControlCenter* self )
-{
- self->control_container = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- gtk_widget_add_css_class( self->control_container, "controls" );
- gtk_widget_set_hexpand( self->control_container, TRUE );
-
- GtkListItemFactory* notification_factory = gtk_signal_list_item_factory_new( );
- g_signal_connect(
- notification_factory,
- "setup",
- G_CALLBACK( foobar_control_center_handle_notification_setup ),
- self );
- self->notification_list = gtk_list_view_new( NULL, notification_factory );
- gtk_widget_add_css_class( self->notification_list, "notifications" );
-
- // XXX: Get rid of this container to enable virtualization; currently needed because I haven't figured out how to
- // set padding without using css (setting margin on list view causes glitches when scrolling).
- self->notification_container = foobar_inset_container_new( );
- foobar_inset_container_set_child( FOOBAR_INSET_CONTAINER( self->notification_container ), self->notification_list );
-
- GtkWidget* scrolled_window = gtk_scrolled_window_new( );
- gtk_scrolled_window_set_child( GTK_SCROLLED_WINDOW( scrolled_window ), self->notification_container );
- gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_window ), GTK_POLICY_NEVER, GTK_POLICY_EXTERNAL );
-
- self->notification_placeholder = gtk_label_new( "No Notifications" );
- gtk_widget_add_css_class( self->notification_placeholder, "placeholder" );
- gtk_label_set_ellipsize( GTK_LABEL( self->notification_placeholder ), PANGO_ELLIPSIZE_END );
- gtk_label_set_wrap( GTK_LABEL( self->notification_placeholder ), FALSE );
-
- self->notification_stack = gtk_stack_new( );
- gtk_stack_add_named( GTK_STACK( self->notification_stack ), scrolled_window, STACK_ITEM_LIST );
- gtk_stack_add_named( GTK_STACK( self->notification_stack ), self->notification_placeholder, STACK_ITEM_PLACEHOLDER );
- gtk_widget_set_vexpand( self->notification_stack, TRUE );
- gtk_widget_set_hexpand( self->notification_stack, TRUE );
-
- self->layout = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- gtk_box_append( GTK_BOX( self->layout ), self->control_container );
- gtk_box_append( GTK_BOX( self->layout ), gtk_separator_new( GTK_ORIENTATION_HORIZONTAL ) );
- gtk_box_append( GTK_BOX( self->layout ), self->notification_stack );
-
- g_autoptr( GtkSizeGroup ) size_group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL );
- gtk_size_group_add_widget( size_group, self->control_container );
- gtk_size_group_add_widget( size_group, self->notification_stack );
-
- gtk_window_set_child( GTK_WINDOW( self ), self->layout );
- gtk_window_set_title( GTK_WINDOW( self ), "Foobar Control Center" );
- gtk_widget_add_css_class( GTK_WIDGET( self ), "control-center" );
- gtk_layer_init_for_window( GTK_WINDOW( self ) );
- gtk_layer_set_namespace( GTK_WINDOW( self ), "foobar-control-center" );
-}
-
-//
-// Instance cleanup for the control center.
-//
-void foobar_control_center_finalize( GObject* object )
-{
- FoobarControlCenter* self = (FoobarControlCenter*)object;
-
- g_clear_signal_handler( &self->config_handler_id, self->configuration_service );
- g_clear_object( &self->brightness_service );
- g_clear_object( &self->audio_service );
- g_clear_object( &self->network_service );
- g_clear_object( &self->bluetooth_service );
- g_clear_object( &self->configuration_service );
- g_clear_object( &self->notification_service );
- g_clear_pointer( &self->notification_time_format, g_free );
-
- G_OBJECT_CLASS( foobar_control_center_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new control center instance.
-//
-FoobarControlCenter* foobar_control_center_new(
- FoobarBrightnessService* brightness_service,
- FoobarAudioService* audio_service,
- FoobarNetworkService* network_service,
- FoobarBluetoothService* bluetooth_service,
- FoobarNotificationService* notification_service,
- FoobarConfigurationService* configuration_service )
-{
- g_return_val_if_fail( FOOBAR_IS_BRIGHTNESS_SERVICE( brightness_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( audio_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( network_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( bluetooth_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_SERVICE( notification_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_CONFIGURATION_SERVICE( configuration_service ), NULL );
-
- FoobarControlCenter* self = g_object_new( FOOBAR_TYPE_CONTROL_CENTER, NULL );
- self->brightness_service = g_object_ref( brightness_service );
- self->audio_service = g_object_ref( audio_service );
- self->network_service = g_object_ref( network_service );
- self->bluetooth_service = g_object_ref( bluetooth_service );
- self->notification_service = g_object_ref( notification_service );
- self->configuration_service = g_object_ref( configuration_service );
-
- // Apply the configuration and subscribe to changes.
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_control_center_apply_configuration(
- self,
- foobar_configuration_get_control_center( config ),
- foobar_configuration_get_notifications( config ) );
- self->config_handler_id = g_signal_connect(
- self->configuration_service,
- "notify::current",
- G_CALLBACK( foobar_control_center_handle_config_change ),
- self );
-
- // Set up the notifications list view.
-
- GListModel* source_model = foobar_notification_service_get_notifications( self->notification_service );
- GtkNoSelection* selection_model = gtk_no_selection_new( g_object_ref( source_model ) );
- gtk_list_view_set_model( GTK_LIST_VIEW( self->notification_list ), GTK_SELECTION_MODEL( selection_model ) );
-
- // Set up bindings.
-
- {
- GtkExpression* count_expr = gtk_property_expression_new( GTK_TYPE_SORT_LIST_MODEL, NULL, "n-items" );
- GtkExpression* child_params[] = { count_expr };
- GtkExpression* child_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( child_params ),
- child_params,
- G_CALLBACK( foobar_control_center_compute_visible_notification_child_name ),
- NULL,
- NULL );
- gtk_expression_bind( child_expr, self->notification_stack, "visible-child-name", source_model );
- }
-
- return self;
-}
-
-//
-// Apply the control center configuration provided by the configuration service, additionally using the provided
-// notification configuration for some settings.
-//
-void foobar_control_center_apply_configuration(
- FoobarControlCenter* self,
- FoobarControlCenterConfiguration const* config,
- FoobarNotificationConfiguration const* notification_config )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_CENTER( self ) );
- g_return_if_fail( config != NULL );
- g_return_if_fail( notification_config != NULL );
-
- // Copy configuration into member variables.
-
- g_clear_pointer( &self->notification_time_format, g_free );
- self->notification_time_format = g_strdup( foobar_notification_configuration_get_time_format( notification_config ) );
- self->notification_min_height = foobar_notification_configuration_get_min_height( notification_config );
- self->notification_close_button_inset = foobar_notification_configuration_get_close_button_inset( notification_config );
- self->padding = foobar_control_center_configuration_get_padding( config );
- self->spacing = foobar_control_center_configuration_get_spacing( config );
-
- // Configure the window.
-
- gtk_window_set_default_size(
- GTK_WINDOW( self ),
- foobar_control_center_configuration_get_width( config ),
- foobar_control_center_configuration_get_height( config ) );
-
- FoobarScreenEdge position = foobar_control_center_configuration_get_position( config );
- FoobarControlCenterAlignment alignment = foobar_control_center_configuration_get_alignment( config );
- gboolean attach_left = ( position == FOOBAR_SCREEN_EDGE_LEFT );
- gboolean attach_right = ( position == FOOBAR_SCREEN_EDGE_RIGHT );
- gboolean attach_top = ( position == FOOBAR_SCREEN_EDGE_TOP );
- gboolean attach_bottom = ( position == FOOBAR_SCREEN_EDGE_BOTTOM );
- switch ( position )
- {
- case FOOBAR_SCREEN_EDGE_LEFT:
- case FOOBAR_SCREEN_EDGE_RIGHT:
- attach_top =
- ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_START ) ||
- ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL );
- attach_bottom =
- ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_END ) ||
- ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL );
- break;
- case FOOBAR_SCREEN_EDGE_TOP:
- case FOOBAR_SCREEN_EDGE_BOTTOM:
- attach_left =
- ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_START ) ||
- ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL );
- attach_right =
- ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_END ) ||
- ( alignment == FOOBAR_CONTROL_CENTER_ALIGNMENT_FILL );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_LEFT, attach_left );
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_RIGHT, attach_right );
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_TOP, attach_top );
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_BOTTOM, attach_bottom );
-
- gint offset = foobar_control_center_configuration_get_offset( config );
- gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_LEFT, attach_left ? offset : 0 );
- gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_RIGHT, attach_right ? offset : 0 );
- gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_TOP, attach_top ? offset : 0 );
- gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_BOTTOM, attach_bottom ? offset : 0 );
-
- // Configure the orientation.
-
- FoobarOrientation orientation = foobar_control_center_configuration_get_orientation( config );
- switch ( orientation )
- {
- case FOOBAR_ORIENTATION_HORIZONTAL:
- gtk_orientable_set_orientation( GTK_ORIENTABLE( self->layout ), GTK_ORIENTATION_HORIZONTAL );
- break;
- case FOOBAR_ORIENTATION_VERTICAL:
- gtk_orientable_set_orientation( GTK_ORIENTABLE( self->layout ), GTK_ORIENTATION_VERTICAL );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-
- // Adjust spacing for the containers.
-
- foobar_inset_container_set_inset_vertical( FOOBAR_INSET_CONTAINER( self->notification_container ), -self->spacing / 2 );
- gtk_widget_set_margin_top( self->notification_container, self->padding );
- gtk_widget_set_margin_bottom( self->notification_container, self->padding );
-
- gtk_widget_set_margin_start( self->notification_placeholder, self->padding );
- gtk_widget_set_margin_end( self->notification_placeholder, self->padding );
- gtk_widget_set_margin_top( self->notification_placeholder, self->padding );
- gtk_widget_set_margin_bottom( self->notification_placeholder, self->padding );
-
- gtk_box_set_spacing( GTK_BOX( self->control_container ), self->spacing );
- gtk_widget_set_margin_top( self->control_container, self->padding );
- gtk_widget_set_margin_bottom( self->control_container, self->padding );
-
- // Re-create list items by resetting the factory.
-
- GtkListItemFactory* factory = gtk_list_view_get_factory( GTK_LIST_VIEW( self->notification_list ) );
- g_object_ref( factory );
- gtk_list_view_set_factory( GTK_LIST_VIEW( self->notification_list ), NULL );
- gtk_list_view_set_factory( GTK_LIST_VIEW( self->notification_list ), factory );
- g_object_unref( factory );
-
- // Re-create control rows.
-
- GtkWidget* row_widget;
- while ( ( row_widget = gtk_widget_get_first_child( self->control_container ) ) )
- {
- gtk_widget_unparent( row_widget );
- }
-
- gsize rows_count;
- FoobarControlCenterRow const* rows = foobar_control_center_configuration_get_rows( config, &rows_count );
- for ( gsize i = 0; i < rows_count; ++i )
- {
- // Add rows. If there are details for a row, add them into another box that is then added to the
- // control_container to circumvent the spacing of the box.
-
- switch ( rows[i] )
- {
- case FOOBAR_CONTROL_CENTER_ROW_CONNECTIVITY:
- {
- // Create the buttons for network and bluetooth.
-
- GtkWidget* network_button = foobar_control_button_new( );
-
- GtkWidget* bluetooth_button = foobar_control_button_new( );
- foobar_control_button_set_icon_name( FOOBAR_CONTROL_BUTTON( bluetooth_button ), "fluent-bluetooth-symbolic" );
- foobar_control_button_set_label( FOOBAR_CONTROL_BUTTON( bluetooth_button ), "Bluetooth" );
-
- GtkWidget* button_container = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, self->spacing );
- gtk_box_set_homogeneous( GTK_BOX( button_container ), TRUE );
- gtk_widget_set_margin_start( button_container, self->padding );
- gtk_widget_set_margin_end( button_container, self->padding );
- gtk_box_append( GTK_BOX( button_container ), network_button );
- gtk_box_append( GTK_BOX( button_container ), bluetooth_button );
-
- GtkWidget* row = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- gtk_box_append( GTK_BOX( row ), button_container );
-
- // Show either a button for managing Wi-Fi or a static element for ethernet.
-
- FoobarNetworkAdapterWifi* wifi_adapter = foobar_network_service_get_wifi( self->network_service );
- if ( wifi_adapter )
- {
- foobar_control_button_set_icon_name( FOOBAR_CONTROL_BUTTON( network_button ), "fluent-wifi-1-symbolic" );
- foobar_control_button_set_label( FOOBAR_CONTROL_BUTTON( network_button ), "Wi-Fi" );
- foobar_control_button_set_can_expand( FOOBAR_CONTROL_BUTTON( network_button ), TRUE );
- foobar_control_button_set_can_toggle( FOOBAR_CONTROL_BUTTON( network_button ), TRUE );
- g_object_bind_property(
- wifi_adapter,
- "is-enabled",
- network_button,
- "is-toggled",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL );
- g_object_bind_property(
- network_button,
- "is-toggled",
- network_button,
- "can-expand",
- G_BINDING_SYNC_CREATE );
- g_object_bind_property(
- network_button,
- "is-expanded",
- wifi_adapter,
- "is-scanning",
- G_BINDING_SYNC_CREATE );
-
- GtkListItemFactory* wifi_item_factory = gtk_signal_list_item_factory_new( );
- g_signal_connect(
- wifi_item_factory,
- "setup",
- G_CALLBACK( foobar_control_center_handle_network_setup ),
- self );
-
- GListModel* wifi_source_model = g_object_ref( foobar_network_adapter_wifi_get_networks( wifi_adapter ) );
- GtkNoSelection* wifi_selection_model = gtk_no_selection_new( wifi_source_model );
-
- GtkWidget* wifi_list_view = gtk_list_view_new( GTK_SELECTION_MODEL( wifi_selection_model ), wifi_item_factory );
-
- GtkWidget* wifi_details = foobar_control_details_new( );
- foobar_control_details_set_inset_top( FOOBAR_CONTROL_DETAILS( wifi_details ), self->spacing );
- foobar_control_details_set_child( FOOBAR_CONTROL_DETAILS( wifi_details ), wifi_list_view );
- g_object_bind_property(
- network_button,
- "is-expanded",
- wifi_details,
- "is-expanded",
- G_BINDING_SYNC_CREATE );
-
- gtk_box_append( GTK_BOX( row ), wifi_details );
- }
- else
- {
- foobar_control_button_set_icon_name( FOOBAR_CONTROL_BUTTON( network_button ), "fluent-virtual-network-symbolic" );
- foobar_control_button_set_label( FOOBAR_CONTROL_BUTTON( network_button ), "Ethernet" );
- foobar_control_button_set_can_expand( FOOBAR_CONTROL_BUTTON( network_button ), FALSE );
- foobar_control_button_set_can_toggle( FOOBAR_CONTROL_BUTTON( network_button ), FALSE );
- }
-
- // Enable interactions for the bluetooth button if available.
-
- g_object_bind_property(
- self->bluetooth_service,
- "is-available",
- bluetooth_button,
- "can-toggle",
- G_BINDING_SYNC_CREATE );
- g_object_bind_property(
- self->bluetooth_service,
- "is-enabled",
- bluetooth_button,
- "is-toggled",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL );
- g_object_bind_property(
- bluetooth_button,
- "is-toggled",
- bluetooth_button,
- "can-expand",
- G_BINDING_SYNC_CREATE );
- g_object_bind_property(
- bluetooth_button,
- "is-expanded",
- self->bluetooth_service,
- "is-scanning",
- G_BINDING_SYNC_CREATE );
-
- GtkListItemFactory* bluetooth_item_factory = gtk_signal_list_item_factory_new( );
- g_signal_connect(
- bluetooth_item_factory,
- "setup",
- G_CALLBACK( foobar_control_center_handle_bluetooth_device_setup ),
- self );
-
- GListModel* bluetooth_source_model = g_object_ref( foobar_bluetooth_service_get_devices( self->bluetooth_service ) );
- GtkNoSelection* bluetooth_selection_model = gtk_no_selection_new( bluetooth_source_model );
-
- GtkWidget* bluetooth_list_view = gtk_list_view_new(
- GTK_SELECTION_MODEL( bluetooth_selection_model ),
- bluetooth_item_factory );
- gtk_list_view_set_single_click_activate( GTK_LIST_VIEW( bluetooth_list_view ), TRUE );
- g_signal_connect(
- bluetooth_list_view,
- "activate",
- G_CALLBACK( foobar_control_center_handle_bluetooth_device_activate ),
- NULL );
-
- GtkWidget* bluetooth_details = foobar_control_details_new( );
- foobar_control_details_set_inset_top( FOOBAR_CONTROL_DETAILS( bluetooth_details ), self->spacing );
- foobar_control_details_set_child( FOOBAR_CONTROL_DETAILS( bluetooth_details ), bluetooth_list_view );
- g_object_bind_property(
- bluetooth_button,
- "is-expanded",
- bluetooth_details,
- "is-expanded",
- G_BINDING_SYNC_CREATE );
-
- gtk_box_append( GTK_BOX( row ), bluetooth_details );
-
- gtk_box_append( GTK_BOX( self->control_container ), row );
- break;
- }
- case FOOBAR_CONTROL_CENTER_ROW_AUDIO_OUTPUT:
- case FOOBAR_CONTROL_CENTER_ROW_AUDIO_INPUT:
- {
- FoobarAudioDevice* default_device;
- GListModel* device_list;
- gchar const* icon_name;
- gchar const* label;
- if ( rows[i] == FOOBAR_CONTROL_CENTER_ROW_AUDIO_OUTPUT )
- {
- default_device = foobar_audio_service_get_default_output( self->audio_service );
- device_list = foobar_audio_service_get_outputs( self->audio_service );
- icon_name = "fluent-speaker-2-symbolic";
- label = "Audio Output";
- }
- else
- {
- default_device = foobar_audio_service_get_default_input( self->audio_service );
- device_list = foobar_audio_service_get_inputs( self->audio_service );
- icon_name = "fluent-microphone-symbolic";
- label = "Audio Input";
- }
-
- // Create the slider for controlling the volume.
-
- GtkWidget* slider = foobar_control_slider_new( );
- gtk_widget_set_margin_start( slider, self->padding );
- gtk_widget_set_margin_end( slider, self->padding );
- foobar_control_slider_set_icon_name( FOOBAR_CONTROL_SLIDER( slider ), icon_name );
- foobar_control_slider_set_label( FOOBAR_CONTROL_SLIDER( slider ), label );
- foobar_control_slider_set_can_expand( FOOBAR_CONTROL_SLIDER( slider ), TRUE );
- g_object_bind_property(
- default_device,
- "volume",
- slider,
- "percentage",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL );
-
- // Create the details view for selecting the audio device.
-
- GtkListItemFactory* item_factory = gtk_signal_list_item_factory_new( );
- g_signal_connect( item_factory, "setup", G_CALLBACK( foobar_control_center_handle_audio_device_setup ), self );
-
- GListModel* source_model = g_object_ref( device_list );
- GtkNoSelection* selection_model = gtk_no_selection_new( source_model );
-
- GtkWidget* list_view = gtk_list_view_new( GTK_SELECTION_MODEL( selection_model ), item_factory );
- gtk_list_view_set_single_click_activate( GTK_LIST_VIEW( list_view ), TRUE );
- g_signal_connect(
- list_view,
- "activate",
- G_CALLBACK( foobar_control_center_handle_audio_device_activate ),
- NULL );
-
- GtkWidget* details = foobar_control_details_new( );
- foobar_control_details_set_inset_top( FOOBAR_CONTROL_DETAILS( details ), self->spacing );
- foobar_control_details_set_child( FOOBAR_CONTROL_DETAILS( details ), list_view );
- g_object_bind_property(
- slider,
- "is-expanded",
- details,
- "is-expanded",
- G_BINDING_SYNC_CREATE );
-
- GtkWidget* row = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- gtk_box_append( GTK_BOX( row ), slider );
- gtk_box_append( GTK_BOX( row ), details );
-
- gtk_box_append( GTK_BOX( self->control_container ), row );
- break;
- }
- case FOOBAR_CONTROL_CENTER_ROW_BRIGHTNESS:
- {
- // Create the slider for adjusting the brightness.
-
- GtkWidget* slider = foobar_control_slider_new( );
- gtk_widget_set_margin_start( slider, self->padding );
- gtk_widget_set_margin_end( slider, self->padding );
- foobar_control_slider_set_icon_name( FOOBAR_CONTROL_SLIDER( slider ), "fluent-brightness-high-symbolic" );
- foobar_control_slider_set_label( FOOBAR_CONTROL_SLIDER( slider ), "Brightness" );
- g_object_bind_property(
- self->brightness_service,
- "percentage",
- slider,
- "percentage",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL );
-
- gtk_box_append( GTK_BOX( self->control_container ), slider );
- break;
- }
- default:
- {
- g_warn_if_reached( );
- break;
- }
- }
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called by the notification list view to create a widget for displaying a notification.
-//
-void foobar_control_center_handle_notification_setup(
- GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata )
-{
- (void)factory;
- FoobarControlCenter* self = (FoobarControlCenter*)userdata;
-
- GtkWidget* widget = foobar_notification_widget_new( );
- foobar_notification_widget_set_close_action( FOOBAR_NOTIFICATION_WIDGET( widget ), FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION_REMOVE );
- foobar_notification_widget_set_time_format( FOOBAR_NOTIFICATION_WIDGET( widget ), self->notification_time_format );
- foobar_notification_widget_set_min_height( FOOBAR_NOTIFICATION_WIDGET( widget ), self->notification_min_height );
- foobar_notification_widget_set_close_button_inset( FOOBAR_NOTIFICATION_WIDGET( widget ), self->notification_close_button_inset );
- foobar_notification_widget_set_inset_start( FOOBAR_NOTIFICATION_WIDGET( widget ), self->padding );
- foobar_notification_widget_set_inset_end( FOOBAR_NOTIFICATION_WIDGET( widget ), self->padding );
- foobar_notification_widget_set_inset_top( FOOBAR_NOTIFICATION_WIDGET( widget ), self->spacing / 2 );
- foobar_notification_widget_set_inset_bottom( FOOBAR_NOTIFICATION_WIDGET( widget ), self->spacing / 2 );
- gtk_list_item_set_child( list_item, widget );
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- gtk_expression_bind( item_expr, widget, "notification", list_item );
- }
-}
-
-//
-// Called by the wi-fi details list view to create a widget for displaying a network.
-//
-void foobar_control_center_handle_network_setup(
- GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata )
-{
- (void)factory;
- FoobarControlCenter* self = (FoobarControlCenter*)userdata;
-
- GtkWidget* item = foobar_control_details_item_new( );
- gtk_widget_set_margin_start( item, self->padding );
- gtk_widget_set_margin_end( item, self->padding );
- gtk_list_item_set_child( list_item, item );
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* label_expr = gtk_property_expression_new( FOOBAR_TYPE_NETWORK, item_expr, "name" );
- gtk_expression_bind( label_expr, item, "label", list_item );
- }
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* checked_expr = gtk_property_expression_new( FOOBAR_TYPE_NETWORK, item_expr, "is-active" );
- gtk_expression_bind( checked_expr, item, "is-checked", list_item );
- }
-}
-
-//
-// Called by the bluetooth details list view to create a widget for displaying a device.
-//
-void foobar_control_center_handle_bluetooth_device_setup(
- GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata )
-{
- (void)factory;
- FoobarControlCenter* self = (FoobarControlCenter*)userdata;
-
- GtkWidget* item = foobar_control_details_item_new( );
- gtk_widget_set_margin_start( item, self->padding );
- gtk_widget_set_margin_end( item, self->padding );
- gtk_list_item_set_child( list_item, item );
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* label_expr = gtk_property_expression_new( FOOBAR_TYPE_BLUETOOTH_DEVICE, item_expr, "name" );
- gtk_expression_bind( label_expr, item, "label", list_item );
- }
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* state_expr = gtk_property_expression_new( FOOBAR_TYPE_BLUETOOTH_DEVICE, item_expr, "state" );
- GtkExpression* accessory_params[] = { state_expr };
- GtkExpression* accessory_expr = gtk_cclosure_expression_new(
- FOOBAR_TYPE_CONTROL_DETAILS_ACCESSORY,
- NULL,
- G_N_ELEMENTS( accessory_params ),
- accessory_params,
- G_CALLBACK( foobar_control_center_compute_bluetooth_device_accessory ),
- NULL,
- NULL );
- gtk_expression_bind( accessory_expr, item, "accessory", list_item );
- }
-}
-
-//
-// Called by the bluetooth details list view when a device was selected.
-//
-void foobar_control_center_handle_bluetooth_device_activate(
- GtkListView* view,
- guint position,
- gpointer userdata )
-{
- (void)view;
- (void)userdata;
-
- GListModel* device_list = G_LIST_MODEL( gtk_list_view_get_model( view ) );
- FoobarBluetoothDevice* device = g_list_model_get_item( device_list, position );
- foobar_bluetooth_device_toggle_connection( device );
-}
-
-//
-// Called by the audio details list view to create a widget for displaying a device.
-//
-void foobar_control_center_handle_audio_device_setup(
- GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata )
-{
- (void)factory;
- FoobarControlCenter* self = (FoobarControlCenter*)userdata;
-
- GtkWidget* item = foobar_control_details_item_new( );
- gtk_widget_set_margin_start( item, self->padding );
- gtk_widget_set_margin_end( item, self->padding );
- gtk_list_item_set_child( list_item, item );
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* label_expr = gtk_property_expression_new( FOOBAR_TYPE_AUDIO_DEVICE, item_expr, "description" );
- gtk_expression_bind( label_expr, item, "label", list_item );
- }
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* checked_expr = gtk_property_expression_new( FOOBAR_TYPE_AUDIO_DEVICE, item_expr, "is-default" );
- gtk_expression_bind( checked_expr, item, "is-checked", list_item );
- }
-}
-
-//
-// Called by the audio details list view when a device was selected.
-//
-void foobar_control_center_handle_audio_device_activate(
- GtkListView* view,
- guint position,
- gpointer userdata )
-{
- (void)view;
- (void)userdata;
-
- GListModel* device_list = G_LIST_MODEL( gtk_list_view_get_model( view ) );
- FoobarAudioDevice* device = g_list_model_get_item( device_list, position );
- foobar_audio_device_make_default( device );
-}
-
-//
-// Signal handler called when the global configuration file has changed.
-//
-void foobar_control_center_handle_config_change(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- (void)pspec;
- FoobarControlCenter* self = (FoobarControlCenter*)userdata;
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_control_center_apply_configuration(
- self,
- foobar_configuration_get_control_center( config ),
- foobar_configuration_get_notifications( config ) );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Value Converters
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Derive the item accessory from a bluetooth device's connection state.
-//
-FoobarControlDetailsAccessory foobar_control_center_compute_bluetooth_device_accessory(
- GtkExpression* expression,
- FoobarBluetoothDeviceState state,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- switch ( state )
- {
- case FOOBAR_BLUETOOTH_DEVICE_STATE_DISCONNECTED:
- return FOOBAR_CONTROL_DETAILS_ACCESSORY_NONE;
- case FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTING:
- return FOOBAR_CONTROL_DETAILS_ACCESSORY_PROGRESS;
- case FOOBAR_BLUETOOTH_DEVICE_STATE_CONNECTED:
- return FOOBAR_CONTROL_DETAILS_ACCESSORY_CHECKED;
- default:
- g_warn_if_reached( );
- return FOOBAR_CONTROL_DETAILS_ACCESSORY_NONE;
- }
-}
-
-//
-// Derive the visible child in the "notifications" stack from the number of notifications.
-//
-// If there are no notifications, a placeholder is shown.
-//
-gchar* foobar_control_center_compute_visible_notification_child_name(
- GtkExpression* expression,
- guint count,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- return count > 0 ? g_strdup( STACK_ITEM_LIST ) : g_strdup( STACK_ITEM_PLACEHOLDER );
-}
diff --git a/src/control-center.h b/src/control-center.h
deleted file mode 100644
index c320017..0000000
--- a/src/control-center.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-#include "services/brightness-service.h"
-#include "services/audio-service.h"
-#include "services/network-service.h"
-#include "services/bluetooth-service.h"
-#include "services/notification-service.h"
-#include "services/configuration-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_CONTROL_CENTER foobar_control_center_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarControlCenter, foobar_control_center, FOOBAR, CONTROL_CENTER, GtkWindow )
-
-FoobarControlCenter* foobar_control_center_new ( FoobarBrightnessService* brightness_service,
- FoobarAudioService* audio_service,
- FoobarNetworkService* network_service,
- FoobarBluetoothService* bluetooth_service,
- FoobarNotificationService* notification_service,
- FoobarConfigurationService* configuration_service );
-void foobar_control_center_apply_configuration( FoobarControlCenter* self,
- FoobarControlCenterConfiguration const* config,
- FoobarNotificationConfiguration const* notification_config );
-
-G_END_DECLS
diff --git a/src/dbus/com.github.hannesschulze.foobar.xml b/src/dbus/com.github.hannesschulze.foobar.xml
deleted file mode 100644
index e8373a6..0000000
--- a/src/dbus/com.github.hannesschulze.foobar.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<node>
- <interface name="com.github.hannesschulze.foobar.Server">
- <method name="Inspector"/>
- <method name="Quit"/>
- <method name="ToggleLauncher"/>
- <method name="ToggleControlCenter"/>
- </interface>
-</node>
diff --git a/src/dbus/meson.build b/src/dbus/meson.build
deleted file mode 100644
index 80062bd..0000000
--- a/src/dbus/meson.build
+++ /dev/null
@@ -1,37 +0,0 @@
-foobar_sources += gnome.gdbus_codegen(
- 'server',
- 'com.github.hannesschulze.foobar.xml',
- namespace: 'Foobar',
- annotations: [
- ['com.github.hannesschulze.foobar.Server', 'org.gtk.GDBus.C.Name', 'Server'],
- ],
-)
-
-foobar_sources += gnome.gdbus_codegen(
- 'notifications',
- 'org.freedesktop.Notifications.xml',
- namespace: 'Foobar',
- annotations: [
- ['org.freedesktop.Notifications', 'org.gtk.GDBus.C.Name', 'Notifications'],
- ],
-)
-
-foobar_sources += gnome.gdbus_codegen(
- 'upower',
- 'org.freedesktop.UPower.xml',
- namespace: 'Foobar',
- annotations: [
- ['org.freedesktop.UPower.Device', 'org.gtk.GDBus.C.Name', 'UPowerDevice'],
- ],
-)
-
-foobar_sources += gnome.gdbus_codegen(
- 'bluez',
- 'org.bluez.xml',
- namespace: 'Foobar',
- annotations: [
- ['org.bluez.Adapter1', 'org.gtk.GDBus.C.Name', 'BluezAdapter'],
- ['org.bluez.Device1', 'org.gtk.GDBus.C.Name', 'BluezDevice'],
- ['org.bluez.AgentManager1', 'org.gtk.GDBus.C.Name', 'BluezAgentManager'],
- ],
-)
diff --git a/src/dbus/org.bluez.xml b/src/dbus/org.bluez.xml
deleted file mode 100644
index 669e325..0000000
--- a/src/dbus/org.bluez.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<node>
- <interface name="org.bluez.Adapter1">
- <method name="StartDiscovery"/>
- <method name="StopDiscovery"/>
- <method name="RemoveDevice">
- <arg name="device" type="o" direction="in"/>
- </method>
- <method name="SetDiscoveryFilter">
- <arg name="properties" type="a{sv}" direction="in"/>
- </method>
- <property name="Address" type="s" access="read"/>
- <property name="Name" type="s" access="read"/>
- <property name="Alias" type="s" access="readwrite"/>
- <property name="Class" type="u" access="read"/>
- <property name="Powered" type="b" access="readwrite"/>
- <property name="PowerState" type="s" access="read"/>
- <property name="Discoverable" type="b" access="readwrite"/>
- <property name="DiscoverableTimeout" type="u" access="readwrite"/>
- <property name="Pairable" type="b" access="readwrite"/>
- <property name="PairableTimeout" type="u" access="readwrite"/>
- <property name="Discovering" type="b" access="read"/>
- <property name="UUIDs" type="as" access="read"/>
- <property name="Modalias" type="s" access="read"/>
- </interface>
-
- <interface name="org.bluez.Device1">
- <method name="Disconnect"/>
- <method name="Connect"/>
- <method name="ConnectProfile">
- <arg name="UUID" type="s" direction="in"/>
- </method>
- <method name="DisconnectProfile">
- <arg name="UUID" type="s" direction="in"/>
- </method>
- <method name="Pair"/>
- <method name="CancelPairing"/>
- <property name="Address" type="s" access="read"/>
- <property name="Name" type="s" access="read"/>
- <property name="Alias" type="s" access="readwrite"/>
- <property name="Class" type="u" access="read"/>
- <property name="Appearance" type="q" access="read"/>
- <property name="Icon" type="s" access="read"/>
- <property name="Paired" type="b" access="read"/>
- <property name="Trusted" type="b" access="readwrite"/>
- <property name="Blocked" type="b" access="readwrite"/>
- <property name="LegacyPairing" type="b" access="read"/>
- <property name="RSSI" type="n" access="read"/>
- <property name="Connected" type="b" access="read"/>
- <property name="UUIDs" type="as" access="read"/>
- <property name="Modalias" type="s" access="read"/>
- <property name="Adapter" type="o" access="read"/>
- </interface>
-
- <interface name="org.bluez.AgentManager1">
- <method name="RegisterAgent">
- <arg name="agent" type="o" direction="in"/>
- <arg name="capability" type="s" direction="in"/>
- </method>
- <method name="UnregisterAgent">
- <arg name="agent" type="o" direction="in"/>
- </method>
- <method name="RequestDefaultAgent">
- <arg name="agent" type="o" direction="in"/>
- </method>
- </interface>
-</node>
diff --git a/src/dbus/org.freedesktop.Notifications.xml b/src/dbus/org.freedesktop.Notifications.xml
deleted file mode 100644
index 6b1d2f2..0000000
--- a/src/dbus/org.freedesktop.Notifications.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<node>
- <interface name="org.freedesktop.Notifications">
- <method name="Notify">
- <arg type="s" direction="in"/>
- <arg type="u" direction="in"/>
- <arg type="s" direction="in"/>
- <arg type="s" direction="in"/>
- <arg type="s" direction="in"/>
- <arg type="as" direction="in"/>
- <arg type="a{sv}" direction="in"/>
- <arg type="i" direction="in"/>
- <arg type="u" direction="out"/>
- </method>
- <method name="CloseNotification">
- <arg type="u" direction="in" name="id"/>
- </method>
- <method name="GetCapabilities">
- <arg type="as" direction="out"/>
- </method>
- <method name="GetServerInformation">
- <arg type="s" direction="out"/>
- <arg type="s" direction="out"/>
- <arg type="s" direction="out"/>
- <arg type="s" direction="out"/>
- </method>
- <signal name="NotificationClosed">
- <arg type="u"/>
- <arg type="u"/>
- </signal>
- <signal name="ActionInvoked">
- <arg type="u"/>
- <arg type="s"/>
- </signal>
- </interface>
-</node>
diff --git a/src/dbus/org.freedesktop.UPower.xml b/src/dbus/org.freedesktop.UPower.xml
deleted file mode 100644
index 18fc41d..0000000
--- a/src/dbus/org.freedesktop.UPower.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<node>
- <interface name="org.freedesktop.UPower.Device">
- <property name="State" type="u" access="read"/>
- <property name="Percentage" type="d" access="read"/>
- <property name="IsPresent" type="b" access="read"/>
- <property name="TimeToEmpty" type="x" access="read"/>
- <property name="TimeToFull" type="x" access="read"/>
- <property name="Energy" type="d" access="read"/>
- <property name="EnergyFull" type="d" access="read"/>
- <property name="EnergyRate" type="d" access="read"/>
- </interface>
-</node>
diff --git a/src/launcher-item.c b/src/launcher-item.c
deleted file mode 100644
index c4de70b..0000000
--- a/src/launcher-item.c
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "launcher-item.h"
-
-static void foobar_launcher_item_default_init( FoobarLauncherItemInterface* iface );
-
-G_DEFINE_INTERFACE( FoobarLauncherItem, foobar_launcher_item, G_TYPE_OBJECT )
-
-//
-// Static initialization for the launcher item interface.
-//
-void foobar_launcher_item_default_init( FoobarLauncherItemInterface* iface )
-{
- g_object_interface_install_property( iface, g_param_spec_string(
- "title",
- "Title",
- "The title text displayed in the launcher.",
- NULL,
- G_PARAM_READABLE ) );
- g_object_interface_install_property( iface, g_param_spec_string(
- "description",
- "Description",
- "Text displayed below the title.",
- NULL,
- G_PARAM_READABLE ) );
- g_object_interface_install_property( iface, g_param_spec_object(
- "icon",
- "Icon",
- "Icon to show for the item.",
- G_TYPE_ICON,
- G_PARAM_READABLE ) );
-}
-
-//
-// Get the title text displayed in the launcher.
-//
-gchar const* foobar_launcher_item_get_title( FoobarLauncherItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_LAUNCHER_ITEM( self ), NULL );
- FoobarLauncherItemInterface* iface = FOOBAR_LAUNCHER_ITEM_GET_IFACE( self );
- g_return_val_if_fail( iface->get_title != NULL, NULL );
- return iface->get_title( self );
-}
-
-//
-// Get the text displayed below the title.
-//
-gchar const* foobar_launcher_item_get_description( FoobarLauncherItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_LAUNCHER_ITEM( self ), NULL );
- FoobarLauncherItemInterface* iface = FOOBAR_LAUNCHER_ITEM_GET_IFACE( self );
- g_return_val_if_fail( iface->get_description != NULL, NULL );
- return iface->get_description( self );
-}
-
-//
-// Get the icon to show for the item.
-//
-GIcon* foobar_launcher_item_get_icon( FoobarLauncherItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_LAUNCHER_ITEM( self ), NULL );
- FoobarLauncherItemInterface* iface = FOOBAR_LAUNCHER_ITEM_GET_IFACE( self );
- g_return_val_if_fail( iface->get_icon != NULL, NULL );
- return iface->get_icon( self );
-}
-
-//
-// Activate the item, e.g. by launching the application.
-//
-void foobar_launcher_item_activate( FoobarLauncherItem* self )
-{
- g_return_if_fail( FOOBAR_IS_LAUNCHER_ITEM( self ) );
- FoobarLauncherItemInterface* iface = FOOBAR_LAUNCHER_ITEM_GET_IFACE( self );
- if ( iface->activate ) { iface->activate( self ); }
-}
diff --git a/src/launcher-item.h b/src/launcher-item.h
deleted file mode 100644
index b2d274a..0000000
--- a/src/launcher-item.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <glib-object.h>
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_LAUNCHER_ITEM foobar_launcher_item_get_type( )
-
-G_DECLARE_INTERFACE( FoobarLauncherItem, foobar_launcher_item, FOOBAR, LAUNCHER_ITEM, GObject )
-
-struct _FoobarLauncherItemInterface
-{
- GTypeInterface parent_interface;
-
- gchar const* ( *get_title ) ( FoobarLauncherItem* self );
- gchar const* ( *get_description )( FoobarLauncherItem* self );
- GIcon* ( *get_icon ) ( FoobarLauncherItem* self );
- void ( *activate ) ( FoobarLauncherItem* self );
-};
-
-gchar const* foobar_launcher_item_get_title ( FoobarLauncherItem* self );
-gchar const* foobar_launcher_item_get_description( FoobarLauncherItem* self );
-GIcon* foobar_launcher_item_get_icon ( FoobarLauncherItem* self );
-void foobar_launcher_item_activate ( FoobarLauncherItem* self );
-
-G_END_DECLS
-
diff --git a/src/launcher.c b/src/launcher.c
deleted file mode 100644
index f4b34d8..0000000
--- a/src/launcher.c
+++ /dev/null
@@ -1,648 +0,0 @@
-#include "launcher.h"
-#include "launcher-item.h"
-#include "widgets/limit-container.h"
-#include <gtk4-layer-shell.h>
-#include <gdk/gdkkeysyms.h>
-#include <string.h>
-
-//
-// FoobarLauncher:
-//
-// A window allowing the user to quickly search for applications and launch them.
-//
-// Note that it should be possible to continue typing, even when the results list view is focused. Conversely, the arrow
-// keys can be used to select an item, even when the search input is focused.
-//
-
-struct _FoobarLauncher
-{
- GtkWindow parent_instance;
- gchar** search_terms;
- GtkWidget* search_text;
- GtkWidget* list_view;
- GtkWidget* limit_container;
- GtkFilterListModel* filter_model;
- GListStore* quick_answer_model;
- GtkFlattenListModel* flatten_model;
- GtkSingleSelection* selection_model;
- FoobarApplicationService* application_service;
- FoobarQuickAnswerService* quick_answer_service;
- FoobarConfigurationService* configuration_service;
- gulong config_handler_id;
-};
-
-static void foobar_launcher_class_init ( FoobarLauncherClass* klass );
-static void foobar_launcher_init ( FoobarLauncher* self );
-static void foobar_launcher_finalize ( GObject* object );
-static void foobar_launcher_handle_search_changed ( GtkEditable* editable,
- gpointer userdata );
-static void foobar_launcher_handle_search_activate ( GtkText* text,
- gpointer userdata );
-static gboolean foobar_launcher_handle_search_key ( GtkEventControllerKey* controller,
- guint keyval,
- guint keycode,
- GdkModifierType state,
- gpointer userdata );
-static gboolean foobar_launcher_handle_list_key ( GtkEventControllerKey* controller,
- guint keyval,
- guint keycode,
- GdkModifierType state,
- gpointer userdata );
-static gboolean foobar_launcher_handle_window_key ( GtkEventControllerKey* controller,
- guint keyval,
- guint keycode,
- GdkModifierType state,
- gpointer userdata );
-static void foobar_launcher_handle_item_setup ( GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata );
-static void foobar_launcher_handle_item_activate ( GtkListView* view,
- guint position,
- gpointer userdata );
-static void foobar_launcher_handle_config_change ( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_launcher_handle_show ( GtkWidget* widget,
- gpointer userdata );
-static gboolean foobar_launcher_compute_icon_visible ( GtkExpression* expression,
- GIcon* icon,
- gpointer userdata );
-static gboolean foobar_launcher_compute_label_visible ( GtkExpression* expression,
- gchar const* label,
- gpointer userdata );
-static gboolean foobar_launcher_compute_separator_visible( GtkExpression* expression,
- guint item_count,
- gpointer userdata );
-static gboolean foobar_launcher_filter_func ( gpointer item,
- gpointer userdata );
-static gboolean foobar_launcher_is_navigation_key ( guint keyval );
-
-G_DEFINE_FINAL_TYPE( FoobarLauncher, foobar_launcher, GTK_TYPE_WINDOW )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Window Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the launcher.
-//
-void foobar_launcher_class_init( FoobarLauncherClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->finalize = foobar_launcher_finalize;
-}
-
-//
-// Instance initialization for the launcher.
-//
-void foobar_launcher_init( FoobarLauncher* self )
-{
- self->search_terms = g_new0( gchar*, 1 );
-
- // Set up the search input and an event controller for auto-switching focus to the result list.
-
- GtkEventController* search_controller = gtk_event_controller_key_new( );
- g_signal_connect( search_controller, "key-pressed", G_CALLBACK( foobar_launcher_handle_search_key ), self );
- g_signal_connect( search_controller, "key-released", G_CALLBACK( foobar_launcher_handle_search_key ), self );
-
- self->search_text = gtk_text_new( );
- gtk_text_set_placeholder_text( GTK_TEXT( self->search_text ), "Search…" );
- gtk_widget_add_controller( self->search_text, search_controller );
- gtk_widget_set_hexpand( self->search_text, TRUE );
- g_signal_connect( self->search_text, "changed", G_CALLBACK( foobar_launcher_handle_search_changed ), self );
- g_signal_connect( self->search_text, "activate", G_CALLBACK( foobar_launcher_handle_search_activate ), self );
-
- GtkWidget* search_icon = gtk_image_new_from_icon_name( "fluent-search-symbolic" );
-
- GtkWidget* search = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_box_append( GTK_BOX( search ), search_icon );
- gtk_box_append( GTK_BOX( search ), self->search_text );
- gtk_widget_add_css_class( search, "search" );
-
- // Set up the separator (only shown if the results are not empty).
-
- GtkWidget *separator = gtk_separator_new( GTK_ORIENTATION_HORIZONTAL );
-
- // Set up the results list view and an event controller for auto-switching focus to the input.
-
- GtkListItemFactory* item_factory = gtk_signal_list_item_factory_new( );
- g_signal_connect( item_factory, "setup", G_CALLBACK( foobar_launcher_handle_item_setup ), NULL );
-
- GtkCustomFilter* filter = gtk_custom_filter_new( foobar_launcher_filter_func, self, NULL );
-
- self->filter_model = gtk_filter_list_model_new( NULL, GTK_FILTER( filter ) );
-
- self->quick_answer_model = g_list_store_new( FOOBAR_TYPE_LAUNCHER_ITEM );
-
- GListStore* flattened_models = g_list_store_new( G_TYPE_LIST_MODEL );
- g_list_store_append( flattened_models, self->quick_answer_model );
- g_list_store_append( flattened_models, self->filter_model );
-
- self->flatten_model = gtk_flatten_list_model_new( G_LIST_MODEL( flattened_models ) );
-
- self->selection_model = gtk_single_selection_new( G_LIST_MODEL( g_object_ref( self->flatten_model ) ) );
-
- {
- GtkExpression* list_expr = gtk_constant_expression_new( GTK_TYPE_FLATTEN_LIST_MODEL, self->flatten_model );
- GtkExpression* count_expr = gtk_property_expression_new( GTK_TYPE_FLATTEN_LIST_MODEL, list_expr, "n-items" );
- GtkExpression* visible_params[] = { count_expr };
- GtkExpression* visible_expr = gtk_cclosure_expression_new(
- G_TYPE_BOOLEAN,
- NULL,
- G_N_ELEMENTS( visible_params ),
- visible_params,
- G_CALLBACK( foobar_launcher_compute_separator_visible ),
- NULL,
- NULL );
- gtk_expression_bind( visible_expr, separator, "visible", NULL );
- }
-
- GtkEventController* list_controller = gtk_event_controller_key_new( );
- g_signal_connect( list_controller, "key-pressed", G_CALLBACK( foobar_launcher_handle_list_key ), self );
- g_signal_connect( list_controller, "key-released", G_CALLBACK( foobar_launcher_handle_list_key ), self );
-
- self->list_view = gtk_list_view_new( GTK_SELECTION_MODEL( g_object_ref( self->selection_model ) ), item_factory );
- gtk_list_view_set_single_click_activate( GTK_LIST_VIEW( self->list_view ), TRUE );
- gtk_scrollable_set_vscroll_policy( GTK_SCROLLABLE( self->list_view ), GTK_SCROLL_MINIMUM );
- gtk_widget_add_controller( self->list_view, list_controller );
- g_signal_connect( self->list_view, "activate", G_CALLBACK( foobar_launcher_handle_item_activate ), self );
-
- GtkWidget* scrolled_window = gtk_scrolled_window_new( );
- // XXX: Use GTK_POLICY_AUTOMATIC instead of GTK_POLICY_EXTERNAL once I figured out how to remove the minimum
- // height in this case. For now, no scrollbar is shown.
- gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_window ), GTK_POLICY_NEVER, GTK_POLICY_EXTERNAL );
- gtk_scrolled_window_set_child( GTK_SCROLLED_WINDOW( scrolled_window ), self->list_view );
- gtk_scrolled_window_set_propagate_natural_height( GTK_SCROLLED_WINDOW( scrolled_window ), TRUE );
- gtk_widget_set_vexpand( scrolled_window, TRUE );
-
- // Set up the layout.
-
- GtkWidget* layout = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- gtk_box_append( GTK_BOX( layout ), search );
- gtk_box_append( GTK_BOX( layout ), separator );
- gtk_box_append( GTK_BOX( layout ), scrolled_window );
-
- // Let the launcher take up space to accommodate the result list's natural height until the configured maximum
- // height.
-
- self->limit_container = foobar_limit_container_new( );
- foobar_limit_container_set_child( FOOBAR_LIMIT_CONTAINER( self->limit_container ), layout );
-
- // Set up the window, listening for the "Escape" key.
-
- GtkEventController* window_controller = gtk_event_controller_key_new( );
- g_signal_connect( window_controller, "key-released", G_CALLBACK( foobar_launcher_handle_window_key ), self );
-
- gtk_window_set_child( GTK_WINDOW( self ), self->limit_container );
- gtk_widget_add_controller( GTK_WIDGET( self ), window_controller );
- gtk_window_set_title( GTK_WINDOW( self ), "Foobar Launcher" );
- gtk_widget_add_css_class( GTK_WIDGET( self ), "launcher" );
- gtk_layer_init_for_window( GTK_WINDOW( self ) );
- gtk_layer_set_keyboard_mode( GTK_WINDOW( self ), GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE );
- gtk_layer_set_namespace( GTK_WINDOW( self ), "foobar-launcher" );
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_TOP, TRUE );
- g_signal_connect( self, "show", G_CALLBACK( foobar_launcher_handle_show ), self );
-}
-
-//
-// Instance cleanup for the launcher.
-//
-void foobar_launcher_finalize( GObject* object )
-{
- FoobarLauncher* self = (FoobarLauncher*)object;
-
- g_clear_signal_handler( &self->config_handler_id, self->configuration_service );
- g_clear_object( &self->filter_model );
- g_clear_object( &self->quick_answer_model );
- g_clear_object( &self->flatten_model );
- g_clear_object( &self->selection_model );
- g_clear_object( &self->application_service );
- g_clear_object( &self->quick_answer_service );
- g_clear_object( &self->configuration_service );
- g_clear_pointer( &self->search_terms, g_strfreev );
-
- G_OBJECT_CLASS( foobar_launcher_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new launcher instance.
-//
-FoobarLauncher* foobar_launcher_new(
- FoobarApplicationService* application_service,
- FoobarQuickAnswerService* quick_answer_service,
- FoobarConfigurationService* configuration_service )
-{
- g_return_val_if_fail( FOOBAR_IS_APPLICATION_SERVICE( application_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_QUICK_ANSWER_SERVICE( quick_answer_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_CONFIGURATION_SERVICE( configuration_service ), NULL );
-
- FoobarLauncher* self = g_object_new( FOOBAR_TYPE_LAUNCHER, NULL );
- self->application_service = g_object_ref( application_service );
- self->quick_answer_service = g_object_ref( quick_answer_service );
- self->configuration_service = g_object_ref( configuration_service );
-
- // Set up the result list view's source model.
-
- GListModel* source_model = foobar_application_service_get_items( self->application_service );
- gtk_filter_list_model_set_model( self->filter_model, g_object_ref( source_model ) );
-
- // Apply the configuration and subscribe to changes.
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_launcher_apply_configuration( self, foobar_configuration_get_launcher( config ) );
- self->config_handler_id = g_signal_connect(
- self->configuration_service,
- "notify::current",
- G_CALLBACK( foobar_launcher_handle_config_change ),
- self );
-
- return self;
-}
-
-//
-// Apply the launcher configuration provided by the configuration service.
-//
-void foobar_launcher_apply_configuration(
- FoobarLauncher* self,
- FoobarLauncherConfiguration const* config )
-{
- g_return_if_fail( FOOBAR_IS_LAUNCHER( self ) );
- g_return_if_fail( config != NULL );
-
- gtk_window_set_default_size( GTK_WINDOW( self ), foobar_launcher_configuration_get_width( config ), 2 );
- gtk_layer_set_margin(
- GTK_WINDOW( self ),
- GTK_LAYER_SHELL_EDGE_TOP,
- foobar_launcher_configuration_get_position( config ) );
- foobar_limit_container_set_max_height(
- FOOBAR_LIMIT_CONTAINER( self->limit_container ),
- foobar_launcher_configuration_get_max_height( config ) );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when the user confirms the selection while the search input is focused.
-//
-// We forward the activation to the results list view.
-//
-void foobar_launcher_handle_search_activate(
- GtkText* text,
- gpointer userdata )
-{
- (void)text;
- FoobarLauncher* self = (FoobarLauncher*)userdata;
-
- guint selection = gtk_single_selection_get_selected( self->selection_model );
- if ( selection != GTK_INVALID_LIST_POSITION )
- {
- gtk_widget_activate_action( self->list_view, "list.activate-item", "u", selection );
- }
-}
-
-//
-// Called when the search query has changed.
-//
-// We update the tokenized search terms and then the result filter.
-//
-void foobar_launcher_handle_search_changed(
- GtkEditable* editable,
- gpointer userdata )
-{
- FoobarLauncher* self = (FoobarLauncher*)userdata;
-
- // Update quick answer.
-
- g_autoptr(FoobarQuickAnswer) answer = foobar_quick_answer_service_query( self->quick_answer_service, gtk_editable_get_text( editable ) );
- g_list_store_remove_all( self->quick_answer_model );
- if (answer)
- {
- g_list_store_append(self->quick_answer_model, answer);
- }
-
- // Update filter.
-
- GStrvBuilder* terms_builder = g_strv_builder_new( );
- g_autofree gchar* query = g_strdup( gtk_editable_get_text( editable ) );
- gchar* query_start = query;
- gchar* save;
- gchar* token;
- while ( ( token = strtok_r( query_start, " \t", &save ) ) )
- {
- if ( *token ) { g_strv_builder_add( terms_builder, token ); }
- query_start = NULL;
- }
-
- g_clear_pointer( &self->search_terms, g_strfreev );
- self->search_terms = g_strv_builder_end( terms_builder );
-
- GtkFilter* filter = gtk_filter_list_model_get_filter( self->filter_model );
- gtk_filter_changed( filter, GTK_FILTER_CHANGE_DIFFERENT );
-}
-
-//
-// Handle keyboard events while the search input is focused.
-//
-// If this is a navigational key, the event is forwarded to the results list view.
-//
-gboolean foobar_launcher_handle_search_key(
- GtkEventControllerKey* controller,
- guint keyval,
- guint keycode,
- GdkModifierType state,
- gpointer userdata )
-{
- (void)keycode;
- (void)state;
- FoobarLauncher* self = (FoobarLauncher*)userdata;
-
- if ( foobar_launcher_is_navigation_key( keyval ) )
- {
- gtk_widget_grab_focus( self->list_view );
- return gtk_event_controller_key_forward( controller, self->list_view );
- }
- else
- {
- return FALSE;
- }
-}
-
-//
-// Handle keyboard events while the list view is focused.
-//
-// If this is not a navigational key, the event is forwarded to the search input, which is also focused.
-//
-gboolean foobar_launcher_handle_list_key(
- GtkEventControllerKey* controller,
- guint keyval,
- guint keycode,
- GdkModifierType state,
- gpointer userdata )
-{
- (void)keycode;
- (void)state;
- FoobarLauncher* self = (FoobarLauncher*)userdata;
-
- if ( foobar_launcher_is_navigation_key( keyval ) )
- {
- return FALSE;
- }
- else
- {
- gtk_text_grab_focus_without_selecting( GTK_TEXT( self->search_text ) );
- return gtk_event_controller_key_forward( controller, self->search_text );
- }
-}
-
-//
-// Handle general keyboard events for the window.
-//
-// This is used to allow the user to close the launcher by pressing the "Escape" key.
-//
-gboolean foobar_launcher_handle_window_key(
- GtkEventControllerKey* controller,
- guint keyval,
- guint keycode,
- GdkModifierType state,
- gpointer userdata )
-{
- (void)controller;
- (void)keycode;
- (void)state;
- FoobarLauncher* self = (FoobarLauncher*)userdata;
-
- if ( keyval == GDK_KEY_Escape )
- {
- gtk_widget_set_visible( GTK_WIDGET( self ), FALSE );
- return TRUE;
- }
- else
- {
- return FALSE;
- }
-}
-
-//
-// Called by the result list view to create a widget for displaying an application item.
-//
-void foobar_launcher_handle_item_setup(
- GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata )
-{
- (void)factory;
- (void)userdata;
-
- // Set up layout.
-
- GtkWidget* icon = gtk_image_new( );
- gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
- gtk_widget_add_css_class( icon, "icon" );
-
- GtkWidget* title = gtk_label_new( NULL );
- gtk_label_set_justify( GTK_LABEL( title ), GTK_JUSTIFY_LEFT );
- gtk_label_set_xalign( GTK_LABEL( title ), 0 );
- gtk_label_set_wrap( GTK_LABEL( title ), FALSE );
- gtk_label_set_ellipsize( GTK_LABEL( title ), PANGO_ELLIPSIZE_END );
- gtk_widget_add_css_class( title, "name" );
-
- GtkWidget* description = gtk_label_new( NULL );
- gtk_label_set_justify( GTK_LABEL( description ), GTK_JUSTIFY_LEFT );
- gtk_label_set_xalign( GTK_LABEL( description ), 0 );
- gtk_label_set_wrap( GTK_LABEL( description ), FALSE );
- gtk_label_set_ellipsize( GTK_LABEL( description ), PANGO_ELLIPSIZE_END );
- gtk_widget_add_css_class( description, "description" );
-
- GtkWidget* column = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- gtk_widget_set_hexpand( column, TRUE );
- gtk_widget_set_valign( column, GTK_ALIGN_CENTER );
- gtk_box_append( GTK_BOX( column ), title );
- gtk_box_append( GTK_BOX( column ), description );
-
- GtkWidget* row = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_widget_add_css_class( row, "item" );
- gtk_box_append( GTK_BOX( row ), icon );
- gtk_box_append( GTK_BOX( row ), column );
-
- gtk_list_item_set_child( list_item, row );
-
- // Set up bindings.
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* icon_expr = gtk_property_expression_new( FOOBAR_TYPE_LAUNCHER_ITEM, item_expr, "icon" );
- gtk_expression_bind( gtk_expression_ref( icon_expr ), icon, "gicon", list_item );
- GtkExpression* visible_params[] = { icon_expr };
- GtkExpression* visible_expr = gtk_cclosure_expression_new(
- G_TYPE_BOOLEAN,
- NULL,
- G_N_ELEMENTS( visible_params ),
- visible_params,
- G_CALLBACK( foobar_launcher_compute_icon_visible ),
- NULL,
- NULL );
- gtk_expression_bind( visible_expr, icon, "visible", list_item );
- }
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* title_expr = gtk_property_expression_new( FOOBAR_TYPE_LAUNCHER_ITEM, item_expr, "title" );
- gtk_expression_bind( title_expr, title, "label", list_item );
- }
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* description_expr = gtk_property_expression_new( FOOBAR_TYPE_LAUNCHER_ITEM, item_expr, "description" );
- gtk_expression_bind( gtk_expression_ref( description_expr ), description, "label", list_item );
- GtkExpression* visible_params[] = { description_expr };
- GtkExpression* visible_expr = gtk_cclosure_expression_new(
- G_TYPE_BOOLEAN,
- NULL,
- G_N_ELEMENTS( visible_params ),
- visible_params,
- G_CALLBACK( foobar_launcher_compute_label_visible ),
- NULL,
- NULL );
- gtk_expression_bind( visible_expr, description, "visible", list_item );
- }
-}
-
-//
-// Called by the result list view when an application was selected.
-//
-void foobar_launcher_handle_item_activate(
- GtkListView* view,
- guint position,
- gpointer userdata )
-{
- (void)view;
- FoobarLauncher* self = (FoobarLauncher*)userdata;
-
- GListModel* list = G_LIST_MODEL( gtk_list_view_get_model( view ) );
- FoobarLauncherItem* item = g_list_model_get_item( list, position );
- foobar_launcher_item_activate( item );
- gtk_widget_set_visible( GTK_WIDGET( self ), FALSE );
-}
-
-//
-// Signal handler called when the global configuration file has changed.
-//
-void foobar_launcher_handle_config_change(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- (void)pspec;
- FoobarLauncher* self = (FoobarLauncher*)userdata;
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_launcher_apply_configuration( self, foobar_configuration_get_launcher( config ) );
-}
-
-//
-// Called before the launcher is presented.
-//
-// This is used to reset the UI state.
-//
-void foobar_launcher_handle_show(
- GtkWidget* widget,
- gpointer userdata )
-{
- (void)widget;
- FoobarLauncher* self = (FoobarLauncher*)userdata;
-
- gtk_editable_set_text( GTK_EDITABLE( self->search_text ), "" );
- if ( g_list_model_get_n_items( G_LIST_MODEL( self->selection_model ) ) > 0 )
- {
- gtk_list_view_scroll_to(
- GTK_LIST_VIEW( self->list_view ),
- 0,
- GTK_LIST_SCROLL_FOCUS | GTK_LIST_SCROLL_SELECT,
- NULL );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Value Converters
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Derive the visibility of an application icon from its value.
-//
-gboolean foobar_launcher_compute_icon_visible(
- GtkExpression* expression,
- GIcon* icon,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- return icon != NULL;
-}
-
-//
-// Derive the visibility of an application label from its value.
-//
-gboolean foobar_launcher_compute_label_visible(
- GtkExpression* expression,
- gchar const* label,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- return label != NULL;
-}
-
-//
-// Derive the visibility of the separator between search input and result list view from the number of results.
-//
-gboolean foobar_launcher_compute_separator_visible(
- GtkExpression* expression,
- guint item_count,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- return item_count > 0;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Match a result item against the current search query.
-//
-gboolean foobar_launcher_filter_func( gpointer item, gpointer userdata )
-{
- FoobarLauncher* self = (FoobarLauncher*)userdata;
- FoobarApplicationItem* app = (FoobarApplicationItem*)item;
-
- return foobar_application_item_match( app, (gchar const* const*)self->search_terms );
-}
-
-//
-// Check if a key value is that of a navigation key (i.e., an arrow key).
-//
-gboolean foobar_launcher_is_navigation_key( guint keyval )
-{
- switch ( keyval )
- {
- case GDK_KEY_Up:
- case GDK_KEY_Down:
- case GDK_KEY_Left:
- case GDK_KEY_Right:
- return TRUE;
- default:
- return FALSE;
- }
-}
diff --git a/src/launcher.h b/src/launcher.h
deleted file mode 100644
index 5d92607..0000000
--- a/src/launcher.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-#include "services/application-service.h"
-#include "services/configuration-service.h"
-#include "services/quick-answer-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_LAUNCHER foobar_launcher_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarLauncher, foobar_launcher, FOOBAR, LAUNCHER, GtkWindow )
-
-FoobarLauncher* foobar_launcher_new ( FoobarApplicationService* application_service,
- FoobarQuickAnswerService* quick_answer_service,
- FoobarConfigurationService* configuration_service );
-void foobar_launcher_apply_configuration( FoobarLauncher* self,
- FoobarLauncherConfiguration const* config );
-
-G_END_DECLS
diff --git a/src/main.c b/src/main.c
deleted file mode 100644
index acabbdc..0000000
--- a/src/main.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "application.h"
-
-//
-// Entry point of the application, runs the GTK app.
-//
-int main(
- int argc,
- char** argv )
-{
- g_autoptr( FoobarApplication ) app = foobar_application_new( );
- return g_application_run( G_APPLICATION( app ), argc, argv );
-}
diff --git a/src/meson.build b/src/meson.build
deleted file mode 100644
index b9383fb..0000000
--- a/src/meson.build
+++ /dev/null
@@ -1,74 +0,0 @@
-foobar_sources = files(
- 'application.c',
- 'utils.c',
- 'panel.c',
- 'launcher.c',
- 'launcher-item.c',
- 'control-center.c',
- 'notification-area.c',
-)
-foobar_tests = {}
-
-subdir('dbus')
-subdir('services')
-subdir('widgets')
-
-c_args = [
- '-Wall',
- '-Werror',
- '-Wswitch-enum',
- '-Wswitch-default',
- '-Wunused',
- '-Wuninitialized',
-]
-
-deps = [
- glib_dep,
- gio_dep,
- gio_unix_dep,
- gtk_dep,
- json_dep,
- gmp_dep,
- nm_dep,
- m_dep,
- layershell_dep,
- gvc_dep,
-]
-
-libfoobar = static_library(
- meson.project_name(),
- foobar_sources,
- include_directories: include_directories('.'),
- dependencies: deps,
- install: false,
- c_args: c_args
-)
-
-libfoobar_dep = declare_dependency(
- link_with: libfoobar,
- include_directories: include_directories('.'),
- dependencies: deps,
-)
-
-executable(
- meson.project_name(),
- 'main.c',
- styles_resource,
- icons_resource,
- dependencies: [ libfoobar_dep ],
- install: true,
- c_args: c_args
-)
-
-if mutest_dep.found()
- foreach name, sources : foobar_tests
- test(
- name,
- executable(
- 'foobar-test-' + name,
- sources,
- dependencies: [ libfoobar_dep, mutest_dep ],
- ),
- )
- endforeach
-endif
diff --git a/src/notification-area.c b/src/notification-area.c
deleted file mode 100644
index 2db5397..0000000
--- a/src/notification-area.c
+++ /dev/null
@@ -1,196 +0,0 @@
-#include "notification-area.h"
-#include "widgets/notification-widget.h"
-#include <gtk4-layer-shell.h>
-
-//
-// FoobarNotificationArea:
-//
-// A (usually invisible) window in the corner of the screen, displaying incoming notifications before they are either
-// dismissed or a timeout has passed.
-//
-
-struct _FoobarNotificationArea
-{
- GtkWindow parent_instance;
- GtkWidget* notification_list;
- FoobarNotificationService* notification_service;
- FoobarConfigurationService* configuration_service;
- gint min_height;
- gint spacing;
- gint close_button_inset;
- gchar* time_format;
- gulong config_handler_id;
-};
-
-static void foobar_notification_area_class_init ( FoobarNotificationAreaClass* klass );
-static void foobar_notification_area_init ( FoobarNotificationArea* self );
-static void foobar_notification_area_finalize ( GObject* object );
-static void foobar_notification_area_handle_config_change( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_notification_area_handle_item_setup ( GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarNotificationArea, foobar_notification_area, GTK_TYPE_WINDOW )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Window Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the notification area.
-//
-void foobar_notification_area_class_init( FoobarNotificationAreaClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->finalize = foobar_notification_area_finalize;
-}
-
-//
-// Instance initialization for the notification area.
-//
-void foobar_notification_area_init( FoobarNotificationArea* self )
-{
- GtkListItemFactory* notification_factory = gtk_signal_list_item_factory_new( );
- g_signal_connect( notification_factory, "setup", G_CALLBACK( foobar_notification_area_handle_item_setup ), self );
- self->notification_list = gtk_list_view_new( NULL, notification_factory );
-
- gtk_window_set_child( GTK_WINDOW( self ), self->notification_list );
- gtk_window_set_title( GTK_WINDOW( self ), "Foobar Notification Area" );
- gtk_widget_add_css_class( GTK_WIDGET( self ), "notification-area" );
- gtk_layer_init_for_window( GTK_WINDOW( self ) );
- gtk_layer_set_namespace( GTK_WINDOW( self ), "foobar-notification-area" );
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_RIGHT, TRUE );
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_TOP, TRUE );
-}
-
-//
-// Instance cleanup for the notification area.
-//
-void foobar_notification_area_finalize( GObject* object )
-{
- FoobarNotificationArea* self = (FoobarNotificationArea*)object;
-
- g_clear_signal_handler( &self->config_handler_id, self->configuration_service );
- g_clear_object( &self->notification_service );
- g_clear_object( &self->configuration_service );
- g_clear_pointer( &self->time_format, g_free );
-
- G_OBJECT_CLASS( foobar_notification_area_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new notification area instance.
-//
-FoobarNotificationArea* foobar_notification_area_new(
- FoobarNotificationService* notification_service,
- FoobarConfigurationService* configuration_service )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_SERVICE( notification_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_CONFIGURATION_SERVICE( configuration_service ), NULL );
-
- FoobarNotificationArea* self = g_object_new( FOOBAR_TYPE_NOTIFICATION_AREA, NULL );
- self->notification_service = g_object_ref( notification_service );
- self->configuration_service = g_object_ref( configuration_service );
-
- // Apply the configuration and subscribe to changes.
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_notification_area_apply_configuration( self, foobar_configuration_get_notifications( config ) );
- self->config_handler_id = g_signal_connect(
- self->configuration_service,
- "notify::current",
- G_CALLBACK( foobar_notification_area_handle_config_change ),
- self );
-
- // Set up the notifications list view.
-
- GListModel* source_model = foobar_notification_service_get_popup_notifications( self->notification_service );
- GtkNoSelection* selection_model = gtk_no_selection_new( g_object_ref( source_model ) );
- gtk_list_view_set_model( GTK_LIST_VIEW( self->notification_list ), GTK_SELECTION_MODEL( selection_model ) );
-
- return self;
-}
-
-//
-// Apply the notification configuration provided by the configuration service.
-//
-void foobar_notification_area_apply_configuration(
- FoobarNotificationArea* self,
- FoobarNotificationConfiguration const* config )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_AREA( self ) );
- g_return_if_fail( config != NULL );
-
- // Copy configuration into member variables.
-
- gint width = foobar_notification_configuration_get_width( config );
- self->min_height = foobar_notification_configuration_get_min_height( config );
- self->spacing = foobar_notification_configuration_get_spacing( config );
- self->close_button_inset = foobar_notification_configuration_get_close_button_inset( config );
- g_clear_pointer( &self->time_format, g_free );
- self->time_format = g_strdup( foobar_notification_configuration_get_time_format( config ) );
-
- // Recreate list items by resetting the factory.
-
- GtkListItemFactory* factory = gtk_list_view_get_factory( GTK_LIST_VIEW( self->notification_list ) );
- g_object_ref( factory );
- gtk_list_view_set_factory( GTK_LIST_VIEW( self->notification_list ), NULL );
- gtk_list_view_set_factory( GTK_LIST_VIEW( self->notification_list ), factory );
- g_object_unref( factory );
-
- // We need to use height 2, otherwise it crashes for some reason.
-
- gtk_window_set_default_size( GTK_WINDOW( self ), width + self->spacing, 2 );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Signal handler called when the global configuration file has changed.
-//
-void foobar_notification_area_handle_config_change(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- (void)pspec;
- FoobarNotificationArea* self = (FoobarNotificationArea*)userdata;
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_notification_area_apply_configuration( self, foobar_configuration_get_notifications( config ) );
-}
-
-//
-// Called by the notification list view to create a widget for displaying a notification.
-//
-void foobar_notification_area_handle_item_setup(
- GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata )
-{
- (void)factory;
- FoobarNotificationArea* self = (FoobarNotificationArea*)userdata;
-
- GtkWidget* widget = foobar_notification_widget_new( );
- foobar_notification_widget_set_close_action( FOOBAR_NOTIFICATION_WIDGET( widget ), FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION_DISMISS );
- foobar_notification_widget_set_time_format( FOOBAR_NOTIFICATION_WIDGET( widget ), self->time_format );
- foobar_notification_widget_set_min_height( FOOBAR_NOTIFICATION_WIDGET( widget ), self->min_height );
- foobar_notification_widget_set_close_button_inset( FOOBAR_NOTIFICATION_WIDGET( widget ), self->close_button_inset );
- foobar_notification_widget_set_inset_end( FOOBAR_NOTIFICATION_WIDGET( widget ), self->spacing );
- foobar_notification_widget_set_inset_top( FOOBAR_NOTIFICATION_WIDGET( widget ), self->spacing );
- gtk_list_item_set_child( list_item, widget );
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- gtk_expression_bind( item_expr, widget, "notification", list_item );
- }
-}
diff --git a/src/notification-area.h b/src/notification-area.h
deleted file mode 100644
index e2e546f..0000000
--- a/src/notification-area.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-#include "services/notification-service.h"
-#include "services/configuration-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_NOTIFICATION_AREA foobar_notification_area_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarNotificationArea, foobar_notification_area, FOOBAR, NOTIFICATION_AREA, GtkWindow )
-
-FoobarNotificationArea* foobar_notification_area_new ( FoobarNotificationService* notification_service,
- FoobarConfigurationService* configuration_service );
-void foobar_notification_area_apply_configuration( FoobarNotificationArea* self,
- FoobarNotificationConfiguration const* config );
-
-G_END_DECLS
diff --git a/src/panel.c b/src/panel.c
deleted file mode 100644
index 5bc7662..0000000
--- a/src/panel.c
+++ /dev/null
@@ -1,348 +0,0 @@
-#include "panel.h"
-#include "widgets/panel/panel-item-icon.h"
-#include "widgets/panel/panel-item-clock.h"
-#include "widgets/panel/panel-item-workspaces.h"
-#include "widgets/panel/panel-item-status.h"
-#include <gtk4-layer-shell.h>
-
-//
-// FoobarPanel:
-//
-// A panel that is divided into start, center and end sections. Each section can be configured to present a range of
-// items.
-//
-
-struct _FoobarPanel
-{
- GtkWindow parent_instance;
- GtkWidget* main_layout;
- GtkWidget* start_items;
- GtkWidget* center_items;
- GtkWidget* end_items;
- GPtrArray* item_widgets;
- GdkMonitor* monitor;
- FoobarBatteryService* battery_service;
- FoobarClockService* clock_service;
- FoobarBrightnessService* brightness_service;
- FoobarWorkspaceService* workspace_service;
- FoobarAudioService* audio_service;
- FoobarNetworkService* network_service;
- FoobarBluetoothService* bluetooth_service;
- FoobarNotificationService* notification_service;
- FoobarConfigurationService* configuration_service;
- gulong config_handler_id;
-};
-
-static void foobar_panel_class_init ( FoobarPanelClass* klass );
-static void foobar_panel_init ( FoobarPanel* self );
-static void foobar_panel_finalize ( GObject* object );
-static void foobar_panel_handle_config_change( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarPanel, foobar_panel, GTK_TYPE_APPLICATION_WINDOW )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Window Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the panel.
-//
-void foobar_panel_class_init( FoobarPanelClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->finalize = foobar_panel_finalize;
-}
-
-//
-// Instance initialization for the panel.
-//
-void foobar_panel_init( FoobarPanel* self )
-{
- self->start_items = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- self->center_items = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- self->end_items = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
-
- self->main_layout = gtk_center_box_new( );
- gtk_center_box_set_start_widget( GTK_CENTER_BOX( self->main_layout ), self->start_items );
- gtk_center_box_set_center_widget( GTK_CENTER_BOX( self->main_layout ), self->center_items );
- gtk_center_box_set_end_widget( GTK_CENTER_BOX( self->main_layout ), self->end_items );
-
- gtk_window_set_child( GTK_WINDOW( self ), self->main_layout );
- gtk_window_set_title( GTK_WINDOW( self ), "Foobar Panel" );
- gtk_widget_add_css_class( GTK_WIDGET( self ), "panel" );
- gtk_layer_init_for_window( GTK_WINDOW( self ) );
- gtk_layer_set_namespace( GTK_WINDOW( self ), "foobar-panel" );
- gtk_layer_auto_exclusive_zone_enable( GTK_WINDOW( self ) );
-}
-
-//
-// Instance cleanup for the panel.
-//
-void foobar_panel_finalize( GObject* object )
-{
- FoobarPanel* self = (FoobarPanel*)object;
-
- g_clear_signal_handler( &self->config_handler_id, self->configuration_service );
- g_clear_object( &self->monitor );
- g_clear_object( &self->battery_service );
- g_clear_object( &self->clock_service );
- g_clear_object( &self->brightness_service );
- g_clear_object( &self->workspace_service );
- g_clear_object( &self->audio_service );
- g_clear_object( &self->network_service );
- g_clear_object( &self->bluetooth_service );
- g_clear_object( &self->notification_service );
- g_clear_object( &self->configuration_service );
- g_clear_pointer( &self->item_widgets, g_ptr_array_unref );
-
- G_OBJECT_CLASS( foobar_panel_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new panel instance.
-//
-// An optional GdkMonitor instance can be provided for multi-monitor setups. Otherwise, the default monitor is used.
-//
-FoobarPanel* foobar_panel_new(
- GtkApplication* app,
- GdkMonitor* monitor,
- FoobarBatteryService* battery_service,
- FoobarClockService* clock_service,
- FoobarBrightnessService* brightness_service,
- FoobarWorkspaceService* workspace_service,
- FoobarAudioService* audio_service,
- FoobarNetworkService* network_service,
- FoobarBluetoothService* bluetooth_service,
- FoobarNotificationService* notification_service,
- FoobarConfigurationService* configuration_service )
-{
- g_return_val_if_fail( GTK_IS_APPLICATION( app ), NULL );
- g_return_val_if_fail( monitor == NULL || GDK_IS_MONITOR( monitor ), NULL );
- g_return_val_if_fail( FOOBAR_IS_BATTERY_SERVICE( battery_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_CLOCK_SERVICE( clock_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_BRIGHTNESS_SERVICE( brightness_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_WORKSPACE_SERVICE( workspace_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( audio_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( network_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( bluetooth_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_SERVICE( notification_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_CONFIGURATION_SERVICE( configuration_service ), NULL );
-
- FoobarPanel* self = g_object_new( FOOBAR_TYPE_PANEL, "application", app, NULL );
- self->battery_service = g_object_ref( battery_service );
- self->clock_service = g_object_ref( clock_service );
- self->brightness_service = g_object_ref( brightness_service );
- self->workspace_service = g_object_ref( workspace_service );
- self->audio_service = g_object_ref( audio_service );
- self->network_service = g_object_ref( network_service );
- self->bluetooth_service = g_object_ref( bluetooth_service );
- self->notification_service = g_object_ref( notification_service );
- self->configuration_service = g_object_ref( configuration_service );
-
- // Set the monitor.
-
- if ( monitor )
- {
- self->monitor = g_object_ref( monitor );
- gtk_layer_set_monitor( GTK_WINDOW( self ), self->monitor );
- }
-
- // Apply the configuration and subscribe to changes.
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_panel_apply_configuration( self, foobar_configuration_get_panel( config ) );
- self->config_handler_id = g_signal_connect(
- self->configuration_service,
- "notify::current",
- G_CALLBACK( foobar_panel_handle_config_change ),
- self );
-
- return self;
-}
-
-//
-// Apply the panel configuration provided by the configuration service.
-//
-void foobar_panel_apply_configuration(
- FoobarPanel* self,
- FoobarPanelConfiguration const* config )
-{
- g_return_if_fail( FOOBAR_IS_PANEL( self ) );
- g_return_if_fail( config != NULL );
-
- // Derive the orientation from the panel's position.
-
- FoobarScreenEdge position = foobar_panel_configuration_get_position( config );
- GtkOrientation orientation;
- gchar const* orientation_css_class;
- switch ( position )
- {
- case FOOBAR_SCREEN_EDGE_LEFT:
- case FOOBAR_SCREEN_EDGE_RIGHT:
- orientation = GTK_ORIENTATION_VERTICAL;
- orientation_css_class = "vertical";
- break;
- case FOOBAR_SCREEN_EDGE_TOP:
- case FOOBAR_SCREEN_EDGE_BOTTOM:
- orientation = GTK_ORIENTATION_HORIZONTAL;
- orientation_css_class = "horizontal";
- break;
- default:
- g_warn_if_reached( );
- return;
- }
-
- // Configure the window.
-
- gtk_widget_remove_css_class( GTK_WIDGET( self ), "horizontal" );
- gtk_widget_remove_css_class( GTK_WIDGET( self ), "vertical" );
- gtk_widget_add_css_class( GTK_WIDGET( self ), orientation_css_class );
-
- gint size = foobar_panel_configuration_get_size( config );
- gtk_window_set_default_size(
- GTK_WINDOW( self ),
- orientation == GTK_ORIENTATION_VERTICAL ? size : -1,
- orientation == GTK_ORIENTATION_HORIZONTAL ? size : -1 );
-
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_LEFT, position != FOOBAR_SCREEN_EDGE_RIGHT );
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_RIGHT, position != FOOBAR_SCREEN_EDGE_LEFT );
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_TOP, position != FOOBAR_SCREEN_EDGE_BOTTOM );
- gtk_layer_set_anchor( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_BOTTOM, position != FOOBAR_SCREEN_EDGE_TOP );
-
- // Configure layout spacing.
-
- gint margin = foobar_panel_configuration_get_margin( config );
- gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_LEFT, position != FOOBAR_SCREEN_EDGE_RIGHT ? margin : 0 );
- gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_RIGHT, position != FOOBAR_SCREEN_EDGE_LEFT ? margin : 0 );
- gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_TOP, position != FOOBAR_SCREEN_EDGE_BOTTOM ? margin : 0 );
- gtk_layer_set_margin( GTK_WINDOW( self ), GTK_LAYER_SHELL_EDGE_BOTTOM, position != FOOBAR_SCREEN_EDGE_TOP ? margin : 0 );
-
- gint padding = foobar_panel_configuration_get_padding( config );
- gtk_widget_set_margin_start( GTK_WIDGET( self->main_layout ), orientation == GTK_ORIENTATION_HORIZONTAL ? padding : 0 );
- gtk_widget_set_margin_end( GTK_WIDGET( self->main_layout ), orientation == GTK_ORIENTATION_HORIZONTAL ? padding : 0 );
- gtk_widget_set_margin_top( GTK_WIDGET( self->main_layout ), orientation == GTK_ORIENTATION_VERTICAL ? padding : 0 );
- gtk_widget_set_margin_bottom( GTK_WIDGET( self->main_layout ), orientation == GTK_ORIENTATION_VERTICAL ? padding : 0 );
-
- gint spacing = foobar_panel_configuration_get_spacing( config );
- gtk_box_set_spacing( GTK_BOX(self->start_items), spacing );
- gtk_box_set_spacing( GTK_BOX(self->center_items), spacing );
- gtk_box_set_spacing( GTK_BOX(self->end_items), spacing );
- gtk_widget_set_margin_top( GTK_WIDGET( self->center_items ), orientation == GTK_ORIENTATION_VERTICAL ? spacing : 0 );
- gtk_widget_set_margin_bottom( GTK_WIDGET( self->center_items ), orientation == GTK_ORIENTATION_VERTICAL ? spacing : 0 );
- gtk_widget_set_margin_start( GTK_WIDGET( self->center_items ), orientation == GTK_ORIENTATION_HORIZONTAL ? spacing : 0 );
- gtk_widget_set_margin_end( GTK_WIDGET( self->center_items ), orientation == GTK_ORIENTATION_HORIZONTAL ? spacing : 0 );
-
- // Configure the container orientations.
-
- gtk_orientable_set_orientation( GTK_ORIENTABLE( self->main_layout ), orientation );
- gtk_orientable_set_orientation( GTK_ORIENTABLE( self->start_items ), orientation );
- gtk_orientable_set_orientation( GTK_ORIENTABLE( self->center_items ), orientation );
- gtk_orientable_set_orientation( GTK_ORIENTABLE( self->end_items ), orientation );
-
- // Re-create panel items.
-
- if ( self->item_widgets )
- {
- for ( guint i = 0; i < self->item_widgets->len; ++i )
- {
- gtk_widget_unparent( g_ptr_array_index( self->item_widgets, i ) );
- }
- g_clear_pointer( &self->item_widgets, g_ptr_array_unref );
- }
-
- gsize items_count;
- FoobarPanelItemConfiguration const* const* items = foobar_panel_configuration_get_items( config, &items_count );
- self->item_widgets = g_ptr_array_sized_new( items_count );
- for ( gsize i = 0; i < items_count; ++i )
- {
- // Create an item for the specified type and configuration.
-
- FoobarPanelItemConfiguration const* item = items[i];
- FoobarPanelItem* item_widget;
- switch ( foobar_panel_item_configuration_get_kind( item ) )
- {
- case FOOBAR_PANEL_ITEM_KIND_ICON:
- item_widget = foobar_panel_item_icon_new( item );
- break;
- case FOOBAR_PANEL_ITEM_KIND_CLOCK:
- item_widget = foobar_panel_item_clock_new(
- item,
- self->clock_service );
- break;
- case FOOBAR_PANEL_ITEM_KIND_WORKSPACES:
- item_widget = foobar_panel_item_workspaces_new(
- item,
- self->monitor,
- self->workspace_service );
- break;
- case FOOBAR_PANEL_ITEM_KIND_STATUS:
- item_widget = foobar_panel_item_status_new(
- item,
- self->battery_service,
- self->brightness_service,
- self->audio_service,
- self->network_service,
- self->bluetooth_service,
- self->notification_service );
- break;
- default:
- g_warn_if_reached( );
- continue;
- }
-
- // Propagate the panel's orientation if the item supports it.
-
- if ( GTK_IS_ORIENTABLE( item_widget ) )
- {
- gtk_orientable_set_orientation( GTK_ORIENTABLE( item_widget ), orientation );
- }
-
- // Add the item to the correct container, depending on its configured position.
-
- switch ( foobar_panel_item_configuration_get_position( item ) )
- {
- case FOOBAR_PANEL_ITEM_POSITION_START:
- gtk_box_append( GTK_BOX( self->start_items ), GTK_WIDGET( item_widget ) );
- break;
- case FOOBAR_PANEL_ITEM_POSITION_CENTER:
- gtk_box_append( GTK_BOX( self->center_items ), GTK_WIDGET( item_widget ) );
- break;
- case FOOBAR_PANEL_ITEM_POSITION_END:
- gtk_box_append( GTK_BOX( self->end_items ), GTK_WIDGET( item_widget ) );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-
- // Add the item to the pointer array for easier cleanup.
-
- g_ptr_array_add( self->item_widgets, GTK_WIDGET( item_widget ) );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Signal handler called when the global configuration file has changed.
-//
-void foobar_panel_handle_config_change(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- (void)pspec;
- FoobarPanel* self = (FoobarPanel*)userdata;
-
- FoobarConfiguration const* config = foobar_configuration_service_get_current( self->configuration_service );
- foobar_panel_apply_configuration( self, foobar_configuration_get_panel( config ) );
-}
diff --git a/src/panel.h b/src/panel.h
deleted file mode 100644
index e64a7d7..0000000
--- a/src/panel.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-#include "services/configuration-service.h"
-#include "services/battery-service.h"
-#include "services/clock-service.h"
-#include "services/brightness-service.h"
-#include "services/workspace-service.h"
-#include "services/audio-service.h"
-#include "services/network-service.h"
-#include "services/bluetooth-service.h"
-#include "services/notification-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_PANEL foobar_panel_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarPanel, foobar_panel, FOOBAR, PANEL, GtkApplicationWindow )
-
-FoobarPanel* foobar_panel_new ( GtkApplication* app,
- GdkMonitor* monitor,
- FoobarBatteryService* battery_service,
- FoobarClockService* clock_service,
- FoobarBrightnessService* brightness_service,
- FoobarWorkspaceService* workspace_service,
- FoobarAudioService* audio_service,
- FoobarNetworkService* network_service,
- FoobarBluetoothService* bluetooth_service,
- FoobarNotificationService* notification_service,
- FoobarConfigurationService* configuration_service );
-void foobar_panel_apply_configuration( FoobarPanel* self,
- FoobarPanelConfiguration const* config );
-
-G_END_DECLS
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
diff --git a/src/utils.c b/src/utils.c
deleted file mode 100644
index dae3e65..0000000
--- a/src/utils.c
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "utils.h"
-#include <gio/gio.h>
-
-//
-// Return the path for a file in the user's XDG cache directory. The result should be freed using g_free.
-//
-// This will also ensure that the application's cache directory exists.
-//
-gchar* foobar_get_cache_path( gchar const* filename )
-{
- g_autoptr( GError ) error = NULL;
- g_autofree gchar* directory_path = g_strdup_printf( "%s/foobar", g_get_user_cache_dir( ) );
-
- if ( !g_file_test( directory_path, G_FILE_TEST_EXISTS ) )
- {
- g_autoptr( GFile ) cache_dir = g_file_new_for_path( directory_path );
- if ( !g_file_make_directory_with_parents( cache_dir, NULL, &error ) )
- {
- g_warning( "Unable to create cache directory: %s", error->message );
- return NULL;
- }
- }
-
- return g_strdup_printf( "%s/%s", directory_path, filename );
-}
diff --git a/src/utils.h b/src/utils.h
deleted file mode 100644
index 0cd6e38..0000000
--- a/src/utils.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-gchar* foobar_get_cache_path( gchar const* filename );
-
-G_END_DECLS
diff --git a/src/widgets/control-center/control-button.c b/src/widgets/control-center/control-button.c
deleted file mode 100644
index 5b6d991..0000000
--- a/src/widgets/control-center/control-button.c
+++ /dev/null
@@ -1,496 +0,0 @@
-#include "widgets/control-center/control-button.h"
-
-//
-// FoobarControlButton:
-//
-// A button in the "controls" section of the control center. This can be a regular button or a toggle button with an
-// optional secondary button to toggle a details view.
-//
-
-struct _FoobarControlButton
-{
- GtkWidget parent_instance;
- gchar* icon_name;
- gchar* label;
- gboolean can_expand;
- gboolean can_toggle;
- gboolean is_expanded;
- gboolean is_toggled;
-};
-
-enum
-{
- PROP_ICON_NAME = 1,
- PROP_LABEL,
- PROP_CAN_EXPAND,
- PROP_CAN_TOGGLE,
- PROP_IS_EXPANDED,
- PROP_IS_TOGGLED,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_control_button_class_init ( FoobarControlButtonClass* klass );
-static void foobar_control_button_init ( FoobarControlButton* self );
-static void foobar_control_button_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_control_button_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_control_button_dispose ( GObject* object );
-static void foobar_control_button_finalize ( GObject* object );
-static void foobar_control_button_handle_primary_clicked( GtkButton* button,
- gpointer userdata );
-static void foobar_control_button_handle_expand_clicked ( GtkButton* button,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarControlButton, foobar_control_button, GTK_TYPE_WIDGET )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Widget Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for control buttons.
-//
-void foobar_control_button_class_init( FoobarControlButtonClass* klass )
-{
- GtkWidgetClass* widget_klass = GTK_WIDGET_CLASS( klass );
- gtk_widget_class_set_css_name( widget_klass, "control-button" );
- gtk_widget_class_set_layout_manager_type( widget_klass, GTK_TYPE_BOX_LAYOUT );
-
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_control_button_get_property;
- object_klass->set_property = foobar_control_button_set_property;
- object_klass->dispose = foobar_control_button_dispose;
- object_klass->finalize = foobar_control_button_finalize;
-
- props[PROP_ICON_NAME] = g_param_spec_string(
- "icon-name",
- "Icon Name",
- "Name of the icon to be displayed next to the label.",
- NULL,
- G_PARAM_READWRITE );
- props[PROP_LABEL] = g_param_spec_string(
- "label",
- "Label",
- "The button's main label.",
- NULL,
- G_PARAM_READWRITE );
- props[PROP_CAN_EXPAND] = g_param_spec_boolean(
- "can-expand",
- "Can Expand",
- "Indicates whether some details are hidden and can be revealed by a toggle button next to the main action.",
- FALSE,
- G_PARAM_READWRITE );
- props[PROP_CAN_TOGGLE] = g_param_spec_boolean(
- "can-toggle",
- "Can Toggle",
- "Indicates whether the main button can be toggled.",
- FALSE,
- G_PARAM_READWRITE );
- props[PROP_IS_EXPANDED] = g_param_spec_boolean(
- "is-expanded",
- "Is Expanded",
- "Indicates whether additional details should be shown.",
- FALSE,
- G_PARAM_READWRITE );
- props[PROP_IS_TOGGLED] = g_param_spec_boolean(
- "is-toggled",
- "Is Toggled",
- "Indicates whether the main button is currently toggled.",
- FALSE,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for control buttons.
-//
-void foobar_control_button_init( FoobarControlButton* self )
-{
- GtkWidget* icon = gtk_image_new( );
- gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
- g_object_bind_property(
- self,
- "icon-name",
- icon,
- "icon-name",
- G_BINDING_SYNC_CREATE );
-
- GtkWidget* label = gtk_label_new( NULL );
- gtk_widget_set_valign( label, GTK_ALIGN_CENTER );
- gtk_widget_set_hexpand( label, TRUE );
- gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT );
- gtk_label_set_xalign( GTK_LABEL( label ), 0 );
- g_object_bind_property(
- self,
- "label",
- label,
- "label",
- G_BINDING_SYNC_CREATE );
-
- GtkWidget* content = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_box_append( GTK_BOX( content ), icon );
- gtk_box_append( GTK_BOX( content ), label );
-
- GtkWidget* primary_button = gtk_button_new( );
- gtk_widget_add_css_class( primary_button, "primary" );
- gtk_widget_set_hexpand( primary_button, TRUE );
- gtk_button_set_child( GTK_BUTTON( primary_button ), content );
- g_object_bind_property(
- self,
- "can-toggle",
- primary_button,
- "sensitive",
- G_BINDING_SYNC_CREATE );
- g_signal_connect( primary_button, "clicked", G_CALLBACK( foobar_control_button_handle_primary_clicked ), self );
-
- GtkWidget* expand_button = gtk_button_new_from_icon_name( "fluent-chevron-right-symbolic" );
- gtk_widget_add_css_class( expand_button, "expand" );
- g_object_bind_property(
- self,
- "can-expand",
- expand_button,
- "visible",
- G_BINDING_SYNC_CREATE );
- g_signal_connect( expand_button, "clicked", G_CALLBACK( foobar_control_button_handle_expand_clicked ), self );
-
- GtkLayoutManager* layout = gtk_widget_get_layout_manager( GTK_WIDGET( self ) );
- gtk_orientable_set_orientation( GTK_ORIENTABLE( layout ), GTK_ORIENTATION_HORIZONTAL );
- gtk_widget_insert_before( primary_button, GTK_WIDGET( self ), NULL );
- gtk_widget_insert_before( expand_button, GTK_WIDGET( self ), NULL );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_control_button_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarControlButton* self = (FoobarControlButton*)object;
-
- switch ( prop_id )
- {
- case PROP_ICON_NAME:
- g_value_set_string( value, foobar_control_button_get_icon_name( self ) );
- break;
- case PROP_LABEL:
- g_value_set_string( value, foobar_control_button_get_label( self ) );
- break;
- case PROP_CAN_EXPAND:
- g_value_set_boolean( value, foobar_control_button_can_expand( self ) );
- break;
- case PROP_CAN_TOGGLE:
- g_value_set_boolean( value, foobar_control_button_can_toggle( self ) );
- break;
- case PROP_IS_EXPANDED:
- g_value_set_boolean( value, foobar_control_button_is_expanded( self ) );
- break;
- case PROP_IS_TOGGLED:
- g_value_set_boolean( value, foobar_control_button_is_toggled( 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_control_button_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarControlButton* self = (FoobarControlButton*)object;
-
- switch ( prop_id )
- {
- case PROP_ICON_NAME:
- foobar_control_button_set_icon_name( self, g_value_get_string( value ) );
- break;
- case PROP_LABEL:
- foobar_control_button_set_label( self, g_value_get_string( value ) );
- break;
- case PROP_CAN_EXPAND:
- foobar_control_button_set_can_expand( self, g_value_get_boolean( value ) );
- break;
- case PROP_CAN_TOGGLE:
- foobar_control_button_set_can_toggle( self, g_value_get_boolean( value ) );
- break;
- case PROP_IS_EXPANDED:
- foobar_control_button_set_expanded( self, g_value_get_boolean( value ) );
- break;
- case PROP_IS_TOGGLED:
- foobar_control_button_set_toggled( self, g_value_get_boolean( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance de-initialization for control buttons.
-//
-void foobar_control_button_dispose( GObject* object )
-{
- FoobarControlButton* self = (FoobarControlButton*)object;
-
- GtkWidget* child;
- while ( ( child = gtk_widget_get_first_child( GTK_WIDGET( self ) ) ) )
- {
- gtk_widget_unparent( child );
- }
-
- G_OBJECT_CLASS( foobar_control_button_parent_class )->dispose( object );
-}
-
-//
-// Instance cleanup for control buttons.
-//
-void foobar_control_button_finalize( GObject* object )
-{
- FoobarControlButton* self = (FoobarControlButton*)object;
-
- g_clear_pointer( &self->icon_name, g_free );
- g_clear_pointer( &self->label, g_free );
-
- G_OBJECT_CLASS( foobar_control_button_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new control button instance.
-//
-GtkWidget* foobar_control_button_new( void )
-{
- return g_object_new( FOOBAR_TYPE_CONTROL_BUTTON, NULL );
-}
-
-//
-// Get the name of the icon to be displayed next to the label.
-//
-gchar const* foobar_control_button_get_icon_name( FoobarControlButton* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ), NULL );
- return self->icon_name;
-}
-
-//
-// Get the button's main label.
-//
-gchar const* foobar_control_button_get_label( FoobarControlButton* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ), NULL );
- return self->label;
-}
-
-//
-// Check whether some details are hidden and can be revealed by a toggle button next to the main action.
-//
-gboolean foobar_control_button_can_expand( FoobarControlButton* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ), FALSE );
- return self->can_expand;
-}
-
-//
-// Check whether the main button can be toggled.
-//
-gboolean foobar_control_button_can_toggle( FoobarControlButton* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ), FALSE );
- return self->can_toggle;
-}
-
-//
-// Check whether additional details should currently be shown.
-//
-gboolean foobar_control_button_is_expanded( FoobarControlButton* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ), FALSE );
- return self->is_expanded;
-}
-
-//
-// Check whether the main button is currently toggled.
-//
-gboolean foobar_control_button_is_toggled( FoobarControlButton* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ), FALSE );
- return self->is_toggled;
-}
-
-//
-// Update the name of the icon to be displayed next to the label.
-//
-void foobar_control_button_set_icon_name(
- FoobarControlButton* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ) );
-
- if ( g_strcmp0( self->icon_name, value ) )
- {
- g_clear_pointer( &self->icon_name, g_free );
- self->icon_name = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_ICON_NAME] );
- }
-}
-
-//
-// Update the button's main label.
-//
-void foobar_control_button_set_label(
- FoobarControlButton* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_BUTTON( 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 ), props[PROP_LABEL] );
- }
-}
-
-//
-// Update whether some details are hidden and can be revealed by a toggle button next to the main action.
-//
-void foobar_control_button_set_can_expand(
- FoobarControlButton* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ) );
-
- if ( !value ) { foobar_control_button_set_expanded( self, FALSE ); }
-
- value = !!value;
- if ( self->can_expand != value )
- {
- self->can_expand = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CAN_EXPAND] );
- }
-}
-
-//
-// Update whether the main button can be toggled.
-//
-void foobar_control_button_set_can_toggle(
- FoobarControlButton* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ) );
-
- if ( !value ) { foobar_control_button_set_toggled( self, FALSE ); }
-
- value = !!value;
- if ( self->can_toggle != value )
- {
- self->can_toggle = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CAN_TOGGLE] );
- }
-}
-
-//
-// Update whether additional details are currently shown.
-//
-void foobar_control_button_set_expanded(
- FoobarControlButton* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ) );
-
- value = value && self->can_expand;
- if ( self->is_expanded != value )
- {
- self->is_expanded = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_EXPANDED] );
-
- if ( self->is_expanded )
- {
- gtk_widget_add_css_class( GTK_WIDGET( self ), "expanded" );
- }
- else
- {
- gtk_widget_remove_css_class( GTK_WIDGET( self ), "expanded" );
- }
- }
-}
-
-//
-// Update whether the main button is currently toggled.
-//
-void foobar_control_button_set_toggled(
- FoobarControlButton* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_BUTTON( self ) );
-
- value = value && self->can_toggle;
- if ( self->is_toggled != value )
- {
- self->is_toggled = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_TOGGLED] );
-
- if ( self->is_toggled )
- {
- gtk_widget_add_css_class( GTK_WIDGET( self ), "toggled" );
- }
- else
- {
- gtk_widget_remove_css_class( GTK_WIDGET( self ), "toggled" );
- }
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when the primary button was clicked.
-//
-// If allowed, this will toggle the button.
-//
-void foobar_control_button_handle_primary_clicked( GtkButton* button, gpointer userdata )
-{
- (void)button;
- FoobarControlButton* self = (FoobarControlButton*)userdata;
-
- if ( foobar_control_button_can_toggle( self ) )
- {
- foobar_control_button_set_toggled( self, !foobar_control_button_is_toggled( self ) );
- }
-}
-
-//
-// Called when the expand button was clicked.
-//
-// If allowed, this will toggle the "expanded" state of the button.
-//
-void foobar_control_button_handle_expand_clicked( GtkButton* button, gpointer userdata )
-{
- (void)button;
- FoobarControlButton* self = (FoobarControlButton*)userdata;
-
- if ( foobar_control_button_can_expand( self ) )
- {
- foobar_control_button_set_expanded( self, !foobar_control_button_is_expanded( self ) );
- }
-}
diff --git a/src/widgets/control-center/control-button.h b/src/widgets/control-center/control-button.h
deleted file mode 100644
index d4c3737..0000000
--- a/src/widgets/control-center/control-button.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_CONTROL_BUTTON foobar_control_button_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarControlButton, foobar_control_button, FOOBAR, CONTROL_BUTTON, GtkWidget )
-
-GtkWidget* foobar_control_button_new ( void );
-gchar const* foobar_control_button_get_icon_name ( FoobarControlButton* self );
-gchar const* foobar_control_button_get_label ( FoobarControlButton* self );
-gboolean foobar_control_button_can_expand ( FoobarControlButton* self );
-gboolean foobar_control_button_can_toggle ( FoobarControlButton* self );
-gboolean foobar_control_button_is_expanded ( FoobarControlButton* self );
-gboolean foobar_control_button_is_toggled ( FoobarControlButton* self );
-void foobar_control_button_set_icon_name ( FoobarControlButton* self,
- gchar const* value );
-void foobar_control_button_set_label ( FoobarControlButton* self,
- gchar const* value );
-void foobar_control_button_set_can_expand( FoobarControlButton* self,
- gboolean value );
-void foobar_control_button_set_can_toggle( FoobarControlButton* self,
- gboolean value );
-void foobar_control_button_set_expanded ( FoobarControlButton* self,
- gboolean value );
-void foobar_control_button_set_toggled ( FoobarControlButton* self,
- gboolean value );
-
-G_END_DECLS
diff --git a/src/widgets/control-center/control-details-item.c b/src/widgets/control-center/control-details-item.c
deleted file mode 100644
index cb54dd3..0000000
--- a/src/widgets/control-center/control-details-item.c
+++ /dev/null
@@ -1,307 +0,0 @@
-#include "widgets/control-center/control-details-item.h"
-
-//
-// FoobarControlDetailsAccessory:
-//
-// The accessory shown next to an item in the control center details list (if any).
-//
-
-G_DEFINE_ENUM_TYPE(
- FoobarControlDetailsAccessory,
- foobar_control_details_accessory,
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_DETAILS_ACCESSORY_NONE, "none" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_DETAILS_ACCESSORY_PROGRESS, "progress" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_CONTROL_DETAILS_ACCESSORY_CHECKED, "checked" ) )
-
-//
-// FoobarControlDetailsItem:
-//
-// Standard row layout for items in a FoobarControlDetails list. The rows are made up of a label and an accessory that
-// is shown or hidden depending on the item's state.
-//
-
-struct _FoobarControlDetailsItem
-{
- GtkWidget parent_instance;
- GtkWidget* spinner;
- GtkWidget* checkmark;
- gchar* label;
- FoobarControlDetailsAccessory accessory;
-};
-
-enum
-{
- PROP_LABEL = 1,
- PROP_ACCESSORY,
- PROP_IS_CHECKED,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_control_details_item_class_init ( FoobarControlDetailsItemClass* klass );
-static void foobar_control_details_item_init ( FoobarControlDetailsItem* self );
-static void foobar_control_details_item_get_property( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_control_details_item_set_property( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_control_details_item_dispose ( GObject* object );
-static void foobar_control_details_item_finalize ( GObject* object );
-
-G_DEFINE_FINAL_TYPE( FoobarControlDetailsItem, foobar_control_details_item, GTK_TYPE_WIDGET )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Widget Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for control details items.
-//
-void foobar_control_details_item_class_init( FoobarControlDetailsItemClass* klass )
-{
- GtkWidgetClass* widget_klass = GTK_WIDGET_CLASS( klass );
- gtk_widget_class_set_css_name( widget_klass, "control-details-item" );
- gtk_widget_class_set_layout_manager_type( widget_klass, GTK_TYPE_BOX_LAYOUT );
-
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_control_details_item_get_property;
- object_klass->set_property = foobar_control_details_item_set_property;
- object_klass->dispose = foobar_control_details_item_dispose;
- object_klass->finalize = foobar_control_details_item_finalize;
-
- props[PROP_LABEL] = g_param_spec_string(
- "label",
- "Label",
- "Label shown for the item.",
- NULL,
- G_PARAM_READWRITE );
- props[PROP_ACCESSORY] = g_param_spec_enum(
- "accessory",
- "Accessory",
- "Accessory shown for the item.",
- FOOBAR_TYPE_CONTROL_DETAILS_ACCESSORY,
- 0,
- G_PARAM_READWRITE );
- props[PROP_IS_CHECKED] = g_param_spec_boolean(
- "is-checked",
- "Is Checked",
- "Current checked-state for the item.",
- FALSE,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for control details items.
-//
-void foobar_control_details_item_init( FoobarControlDetailsItem* self )
-{
- GtkWidget* label = gtk_label_new( NULL );
- gtk_widget_set_hexpand( label, TRUE );
- gtk_widget_set_valign( label, GTK_ALIGN_CENTER );
- gtk_label_set_ellipsize( GTK_LABEL( label ), PANGO_ELLIPSIZE_END );
- gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT );
- gtk_label_set_xalign( GTK_LABEL( label ), 0 );
- gtk_label_set_wrap( GTK_LABEL( label ), FALSE );
- g_object_bind_property(
- self,
- "label",
- label,
- "label",
- G_BINDING_SYNC_CREATE );
-
- self->spinner = gtk_spinner_new( );
- gtk_spinner_set_spinning( GTK_SPINNER( self->spinner ), TRUE );
- gtk_widget_add_css_class( self->spinner, "accessory" );
- gtk_widget_set_valign( self->spinner, GTK_ALIGN_CENTER );
- gtk_widget_set_visible( self->spinner, FALSE );
-
- self->checkmark = gtk_image_new_from_icon_name( "fluent-checkmark-symbolic" );
- gtk_widget_add_css_class( self->checkmark, "checkmark" );
- gtk_widget_add_css_class( self->checkmark, "accessory" );
- gtk_widget_set_valign( self->checkmark, GTK_ALIGN_CENTER );
- gtk_widget_set_visible( self->checkmark, FALSE );
-
- gtk_widget_insert_before( label, GTK_WIDGET( self ), NULL );
- gtk_widget_insert_before( self->spinner, GTK_WIDGET( self ), NULL );
- gtk_widget_insert_before( self->checkmark, GTK_WIDGET( self ), NULL );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_control_details_item_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarControlDetailsItem* self = (FoobarControlDetailsItem*)object;
-
- switch ( prop_id )
- {
- case PROP_LABEL:
- g_value_set_string( value, foobar_control_details_item_get_label( self ) );
- break;
- case PROP_ACCESSORY:
- g_value_set_enum( value, foobar_control_details_item_get_accessory( self ) );
- break;
- case PROP_IS_CHECKED:
- g_value_set_boolean( value, foobar_control_details_item_is_checked( 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_control_details_item_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarControlDetailsItem* self = (FoobarControlDetailsItem*)object;
-
- switch ( prop_id )
- {
- case PROP_LABEL:
- foobar_control_details_item_set_label( self, g_value_get_string( value ) );
- break;
- case PROP_ACCESSORY:
- foobar_control_details_item_set_accessory( self, g_value_get_enum( value ) );
- break;
- case PROP_IS_CHECKED:
- foobar_control_details_item_set_checked( self, g_value_get_boolean( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance de-initialization for control details items.
-//
-void foobar_control_details_item_dispose( GObject* object )
-{
- FoobarControlDetailsItem* self = (FoobarControlDetailsItem*)object;
-
- GtkWidget* child;
- while ( ( child = gtk_widget_get_first_child( GTK_WIDGET( self ) ) ) )
- {
- gtk_widget_unparent( child );
- }
-
- G_OBJECT_CLASS( foobar_control_details_item_parent_class )->dispose( object );
-}
-
-//
-// Instance cleanup for control details items.
-//
-void foobar_control_details_item_finalize( GObject* object )
-{
- FoobarControlDetailsItem* self = (FoobarControlDetailsItem*)object;
-
- g_clear_pointer( &self->label, g_free );
-
- G_OBJECT_CLASS( foobar_control_details_item_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new control details item instance.
-//
-GtkWidget* foobar_control_details_item_new( void )
-{
- return g_object_new( FOOBAR_TYPE_CONTROL_DETAILS_ITEM, NULL );
-}
-
-//
-// Get the label shown for the item.
-//
-gchar const* foobar_control_details_item_get_label( FoobarControlDetailsItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_DETAILS_ITEM( self ), NULL );
- return self->label;
-}
-
-//
-// Get the accessory shown for the item.
-//
-FoobarControlDetailsAccessory foobar_control_details_item_get_accessory( FoobarControlDetailsItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_DETAILS_ITEM( self ), 0 );
- return self->accessory;
-}
-
-//
-// Get the current checked-state for the item.
-//
-gboolean foobar_control_details_item_is_checked( FoobarControlDetailsItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_DETAILS_ITEM( self ), FALSE );
- return self->accessory == FOOBAR_CONTROL_DETAILS_ACCESSORY_CHECKED;
-}
-
-//
-// Update the label shown for the item.
-//
-void foobar_control_details_item_set_label(
- FoobarControlDetailsItem* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_DETAILS_ITEM( 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 ), props[PROP_LABEL] );
- }
-}
-
-//
-// Update the current accessory shown for the item.
-//
-void foobar_control_details_item_set_accessory(
- FoobarControlDetailsItem* self,
- FoobarControlDetailsAccessory value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_DETAILS_ITEM( self ) );
-
- if ( self->accessory != value )
- {
- self->accessory = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_ACCESSORY] );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_CHECKED] );
-
- gtk_widget_set_visible( self->spinner, self->accessory == FOOBAR_CONTROL_DETAILS_ACCESSORY_PROGRESS );
- gtk_widget_set_visible( self->checkmark, self->accessory == FOOBAR_CONTROL_DETAILS_ACCESSORY_CHECKED );
- }
-}
-
-//
-// Update the current checked-state for the item.
-//
-void foobar_control_details_item_set_checked(
- FoobarControlDetailsItem* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_DETAILS_ITEM( self ) );
-
- FoobarControlDetailsAccessory accessory = value
- ? FOOBAR_CONTROL_DETAILS_ACCESSORY_CHECKED
- : FOOBAR_CONTROL_DETAILS_ACCESSORY_NONE;
- foobar_control_details_item_set_accessory( self, accessory );
-}
diff --git a/src/widgets/control-center/control-details-item.h b/src/widgets/control-center/control-details-item.h
deleted file mode 100644
index ff890f9..0000000
--- a/src/widgets/control-center/control-details-item.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_CONTROL_DETAILS_ACCESSORY foobar_control_details_accessory_get_type( )
-#define FOOBAR_TYPE_CONTROL_DETAILS_ITEM foobar_control_details_item_get_type( )
-
-typedef enum
-{
- FOOBAR_CONTROL_DETAILS_ACCESSORY_NONE,
- FOOBAR_CONTROL_DETAILS_ACCESSORY_CHECKED,
- FOOBAR_CONTROL_DETAILS_ACCESSORY_PROGRESS,
-} FoobarControlDetailsAccessory;
-
-GType foobar_control_details_accessory_get_type( void );
-
-G_DECLARE_FINAL_TYPE( FoobarControlDetailsItem, foobar_control_details_item, FOOBAR, CONTROL_DETAILS_ITEM, GtkWidget )
-
-GtkWidget* foobar_control_details_item_new ( void );
-gchar const* foobar_control_details_item_get_label ( FoobarControlDetailsItem* self );
-FoobarControlDetailsAccessory foobar_control_details_item_get_accessory( FoobarControlDetailsItem* self );
-gboolean foobar_control_details_item_is_checked ( FoobarControlDetailsItem* self );
-void foobar_control_details_item_set_label ( FoobarControlDetailsItem* self,
- gchar const* value );
-void foobar_control_details_item_set_accessory( FoobarControlDetailsItem* self,
- FoobarControlDetailsAccessory value );
-void foobar_control_details_item_set_checked ( FoobarControlDetailsItem* self,
- gboolean value );
-
-G_END_DECLS
diff --git a/src/widgets/control-center/control-details.c b/src/widgets/control-center/control-details.c
deleted file mode 100644
index 72a90aa..0000000
--- a/src/widgets/control-center/control-details.c
+++ /dev/null
@@ -1,289 +0,0 @@
-#include "widgets/control-center/control-details.h"
-
-//
-// FoobarControlDetails:
-//
-// A revealer for details in the "controls" section of the control center. This widget is usually used to embed a list
-// view. It takes care of scrolling by embedding the child in a GtkScrolledWindow.
-//
-
-struct _FoobarControlDetails
-{
- GtkWidget parent_instance;
- GtkWidget* child;
- gboolean is_expanded;
- gint inset_top;
-};
-
-enum
-{
- PROP_CHILD = 1,
- PROP_IS_EXPANDED,
- PROP_INSET_TOP,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_control_details_class_init ( FoobarControlDetailsClass* klass );
-static void foobar_control_details_init ( FoobarControlDetails* self );
-static void foobar_control_details_get_property( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_control_details_set_property( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_control_details_dispose ( GObject* object );
-
-G_DEFINE_FINAL_TYPE( FoobarControlDetails, foobar_control_details, GTK_TYPE_WIDGET )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Widget Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for control details.
-//
-void foobar_control_details_class_init( FoobarControlDetailsClass* klass )
-{
- GtkWidgetClass* widget_klass = GTK_WIDGET_CLASS( klass );
- gtk_widget_class_set_css_name( widget_klass, "control-details" );
- gtk_widget_class_set_layout_manager_type( widget_klass, GTK_TYPE_BIN_LAYOUT );
-
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_control_details_get_property;
- object_klass->set_property = foobar_control_details_set_property;
- object_klass->dispose = foobar_control_details_dispose;
-
- props[PROP_CHILD] = g_param_spec_object(
- "child",
- "Child",
- "Child widget for the details revealer.",
- GTK_TYPE_WIDGET,
- G_PARAM_READWRITE );
- props[PROP_IS_EXPANDED] = g_param_spec_boolean(
- "is-expanded",
- "Is Expanded",
- "Current state of the details revealer.",
- FALSE,
- G_PARAM_READWRITE );
- props[PROP_INSET_TOP] = g_param_spec_int(
- "inset-top",
- "Inset Top",
- "Inset from the top edge (only visible when expanded).",
- 0,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for control details.
-//
-void foobar_control_details_init( FoobarControlDetails* self )
-{
- GtkWidget* scrolled_window = gtk_scrolled_window_new( );
- gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_window ), GTK_POLICY_NEVER, GTK_POLICY_EXTERNAL );
- gtk_scrolled_window_set_propagate_natural_height( GTK_SCROLLED_WINDOW( scrolled_window ), TRUE );
- g_object_bind_property(
- self,
- "child",
- scrolled_window,
- "child",
- G_BINDING_SYNC_CREATE );
- g_object_bind_property(
- self,
- "inset-top",
- scrolled_window,
- "margin-top",
- G_BINDING_SYNC_CREATE );
-
- GtkWidget* revealer = gtk_revealer_new( );
- gtk_revealer_set_transition_type( GTK_REVEALER( revealer ), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN );
- gtk_revealer_set_transition_duration( GTK_REVEALER( revealer ), 100 );
- gtk_revealer_set_child( GTK_REVEALER( revealer ), scrolled_window );
- g_object_bind_property(
- self,
- "is-expanded",
- revealer,
- "reveal-child",
- G_BINDING_SYNC_CREATE );
-
- gtk_widget_set_parent( revealer, GTK_WIDGET( self ) );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_control_details_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarControlDetails* self = (FoobarControlDetails*)object;
-
- switch ( prop_id )
- {
- case PROP_CHILD:
- g_value_set_object( value, foobar_control_details_get_child( self ) );
- break;
- case PROP_IS_EXPANDED:
- g_value_set_boolean( value, foobar_control_details_is_expanded( self ) );
- break;
- case PROP_INSET_TOP:
- g_value_set_int( value, foobar_control_details_get_inset_top( 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_control_details_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarControlDetails* self = (FoobarControlDetails*)object;
-
- switch ( prop_id )
- {
- case PROP_CHILD:
- foobar_control_details_set_child( self, g_value_get_object( value ) );
- break;
- case PROP_IS_EXPANDED:
- foobar_control_details_set_expanded( self, g_value_get_boolean( value ) );
- break;
- case PROP_INSET_TOP:
- foobar_control_details_set_inset_top( self, g_value_get_int( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance de-initialization for control details.
-//
-void foobar_control_details_dispose( GObject* object )
-{
- FoobarControlDetails* self = (FoobarControlDetails*)object;
-
- GtkWidget* child;
- while ( ( child = gtk_widget_get_first_child( GTK_WIDGET( self ) ) ) )
- {
- gtk_widget_unparent( child );
- }
-
- g_clear_object( &self->child );
-
- G_OBJECT_CLASS( foobar_control_details_parent_class )->dispose( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new control details instance.
-//
-GtkWidget* foobar_control_details_new( void )
-{
- return g_object_new( FOOBAR_TYPE_CONTROL_DETAILS, NULL );
-}
-
-//
-// Get the child widget for the details revealer.
-//
-GtkWidget* foobar_control_details_get_child( FoobarControlDetails* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ), NULL );
- return self->child;
-}
-
-//
-// Get the current state of the details revealer.
-//
-gboolean foobar_control_details_is_expanded( FoobarControlDetails* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ), FALSE );
- return self->is_expanded;
-}
-
-//
-// Get the inset from the top edge (only visible when expanded).
-//
-gint foobar_control_details_get_inset_top( FoobarControlDetails* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ), FALSE );
- return self->inset_top;
-}
-
-//
-// Update the child widget for the details revealer.
-//
-void foobar_control_details_set_child(
- FoobarControlDetails* self,
- GtkWidget* value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ) );
-
- if ( self->child != value )
- {
- g_clear_object( &self->child );
- if ( value ) { self->child = g_object_ref( value ); }
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CHILD] );
- }
-}
-
-//
-// Update the current state of the details revealer.
-//
-void foobar_control_details_set_expanded(
- FoobarControlDetails* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ) );
-
- value = !!value;
- if ( self->is_expanded != value )
- {
- self->is_expanded = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_EXPANDED] );
-
- if ( self->is_expanded )
- {
- gtk_widget_add_css_class( GTK_WIDGET( self ), "expanded" );
- }
- else
- {
- gtk_widget_remove_css_class( GTK_WIDGET( self ), "expanded" );
- }
- }
-}
-
-//
-// Update the inset from the top edge (only visible when expanded).
-//
-void foobar_control_details_set_inset_top(
- FoobarControlDetails* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_DETAILS( self ) );
-
- value = MAX( value, 0 );
- if ( self->inset_top != value )
- {
- self->inset_top = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_INSET_TOP] );
- }
-}
diff --git a/src/widgets/control-center/control-details.h b/src/widgets/control-center/control-details.h
deleted file mode 100644
index ba7b5ec..0000000
--- a/src/widgets/control-center/control-details.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_CONTROL_DETAILS foobar_control_details_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarControlDetails, foobar_control_details, FOOBAR, CONTROL_DETAILS, GtkWidget )
-
-GtkWidget* foobar_control_details_new ( void );
-GtkWidget* foobar_control_details_get_child ( FoobarControlDetails* self );
-gboolean foobar_control_details_is_expanded ( FoobarControlDetails* self );
-gint foobar_control_details_get_inset_top( FoobarControlDetails* self );
-void foobar_control_details_set_child ( FoobarControlDetails* self,
- GtkWidget* value );
-void foobar_control_details_set_expanded ( FoobarControlDetails* self,
- gboolean value );
-void foobar_control_details_set_inset_top( FoobarControlDetails* self,
- gint value );
-
-G_END_DECLS
diff --git a/src/widgets/control-center/control-slider.c b/src/widgets/control-center/control-slider.c
deleted file mode 100644
index 9c69190..0000000
--- a/src/widgets/control-center/control-slider.c
+++ /dev/null
@@ -1,428 +0,0 @@
-#include "widgets/control-center/control-slider.h"
-
-//
-// FoobarControlSlider:
-//
-// A slider in the "controls" section of the control center with a title and icon, as well as an optional secondary
-// button to toggle a details view.
-//
-
-struct _FoobarControlSlider
-{
- GtkWidget parent_instance;
- gchar* icon_name;
- gchar* label;
- gboolean can_expand;
- gboolean is_expanded;
- gint percentage;
-};
-
-enum
-{
- PROP_ICON_NAME = 1,
- PROP_LABEL,
- PROP_CAN_EXPAND,
- PROP_IS_EXPANDED,
- PROP_PERCENTAGE,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_control_slider_class_init ( FoobarControlSliderClass* klass );
-static void foobar_control_slider_init ( FoobarControlSlider* self );
-static void foobar_control_slider_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_control_slider_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_control_slider_dispose ( GObject* object );
-static void foobar_control_slider_finalize ( GObject* object );
-static void foobar_control_slider_handle_expand_clicked( GtkButton* button,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarControlSlider, foobar_control_slider, GTK_TYPE_WIDGET )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Widget Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for control sliders.
-//
-void foobar_control_slider_class_init( FoobarControlSliderClass* klass )
-{
- GtkWidgetClass* widget_klass = GTK_WIDGET_CLASS( klass );
- gtk_widget_class_set_css_name( widget_klass, "control-slider" );
- gtk_widget_class_set_layout_manager_type( widget_klass, GTK_TYPE_BOX_LAYOUT );
-
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_control_slider_get_property;
- object_klass->set_property = foobar_control_slider_set_property;
- object_klass->dispose = foobar_control_slider_dispose;
- object_klass->finalize = foobar_control_slider_finalize;
-
- props[PROP_ICON_NAME] = g_param_spec_string(
- "icon-name",
- "Icon Name",
- "Name of the icon to be displayed next to the label.",
- NULL,
- G_PARAM_READWRITE );
- props[PROP_LABEL] = g_param_spec_string(
- "label",
- "Label",
- "The slider's main label.",
- NULL,
- G_PARAM_READWRITE );
- props[PROP_CAN_EXPAND] = g_param_spec_boolean(
- "can-expand",
- "Can Expand",
- "Indicates whether some details are hidden and can be revealed by a toggle button next to the slider.",
- FALSE,
- G_PARAM_READWRITE );
- props[PROP_IS_EXPANDED] = g_param_spec_boolean(
- "is-expanded",
- "Is Expanded",
- "Indicates whether additional details should be shown.",
- FALSE,
- G_PARAM_READWRITE );
- props[PROP_PERCENTAGE] = g_param_spec_int(
- "percentage",
- "Percentage",
- "Value shown by the slider.",
- 0,
- 100,
- 0,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for control sliders.
-//
-void foobar_control_slider_init( FoobarControlSlider* self )
-{
- GtkWidget* icon = gtk_image_new( );
- gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
- g_object_bind_property(
- self,
- "icon-name",
- icon,
- "icon-name",
- G_BINDING_SYNC_CREATE );
-
- GtkWidget* label = gtk_label_new( NULL );
- gtk_widget_set_valign( label, GTK_ALIGN_CENTER );
- gtk_widget_set_hexpand( label, TRUE );
- gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT );
- gtk_label_set_xalign( GTK_LABEL( label ), 0 );
- g_object_bind_property(
- self,
- "label",
- label,
- "label",
- G_BINDING_SYNC_CREATE );
-
- GtkWidget* header = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_box_append( GTK_BOX( header ), icon );
- gtk_box_append( GTK_BOX( header ), label );
-
- GtkWidget* scale = gtk_scale_new_with_range( GTK_ORIENTATION_HORIZONTAL, 0, 100, 2 );
- g_object_bind_property(
- self,
- "percentage",
- gtk_range_get_adjustment( GTK_RANGE( scale ) ),
- "value",
- G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL );
-
- GtkWidget* primary_content = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- gtk_widget_add_css_class( primary_content, "primary" );
- gtk_box_append( GTK_BOX( primary_content ), header );
- gtk_box_append( GTK_BOX( primary_content ), scale );
-
- GtkWidget* expand_button = gtk_button_new_from_icon_name( "fluent-chevron-right-symbolic" );
- gtk_widget_add_css_class( expand_button, "expand" );
- g_object_bind_property(
- self,
- "can-expand",
- expand_button,
- "visible",
- G_BINDING_SYNC_CREATE );
- g_signal_connect( expand_button, "clicked", G_CALLBACK( foobar_control_slider_handle_expand_clicked ), self );
-
- GtkLayoutManager* layout = gtk_widget_get_layout_manager( GTK_WIDGET( self ) );
- gtk_orientable_set_orientation( GTK_ORIENTABLE( layout ), GTK_ORIENTATION_HORIZONTAL );
- gtk_widget_insert_before( primary_content, GTK_WIDGET( self ), NULL );
- gtk_widget_insert_before( expand_button, GTK_WIDGET( self ), NULL );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_control_slider_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarControlSlider* self = (FoobarControlSlider*)object;
-
- switch ( prop_id )
- {
- case PROP_ICON_NAME:
- g_value_set_string( value, foobar_control_slider_get_icon_name( self ) );
- break;
- case PROP_LABEL:
- g_value_set_string( value, foobar_control_slider_get_label( self ) );
- break;
- case PROP_CAN_EXPAND:
- g_value_set_boolean( value, foobar_control_slider_can_expand( self ) );
- break;
- case PROP_IS_EXPANDED:
- g_value_set_boolean( value, foobar_control_slider_is_expanded( self ) );
- break;
- case PROP_PERCENTAGE:
- g_value_set_int( value, foobar_control_slider_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_control_slider_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarControlSlider* self = (FoobarControlSlider*)object;
-
- switch ( prop_id )
- {
- case PROP_ICON_NAME:
- foobar_control_slider_set_icon_name( self, g_value_get_string( value ) );
- break;
- case PROP_LABEL:
- foobar_control_slider_set_label( self, g_value_get_string( value ) );
- break;
- case PROP_CAN_EXPAND:
- foobar_control_slider_set_can_expand( self, g_value_get_boolean( value ) );
- break;
- case PROP_IS_EXPANDED:
- foobar_control_slider_set_expanded( self, g_value_get_boolean( value ) );
- break;
- case PROP_PERCENTAGE:
- foobar_control_slider_set_percentage( self, g_value_get_int( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance de-initialization for control sliders.
-//
-void foobar_control_slider_dispose( GObject* object )
-{
- FoobarControlSlider* self = (FoobarControlSlider*)object;
-
- GtkWidget* child;
- while ( ( child = gtk_widget_get_first_child( GTK_WIDGET( self ) ) ) )
- {
- gtk_widget_unparent( child );
- }
-
- G_OBJECT_CLASS( foobar_control_slider_parent_class )->dispose( object );
-}
-
-//
-// Instance cleanup for control sliders.
-//
-void foobar_control_slider_finalize( GObject* object )
-{
- FoobarControlSlider* self = (FoobarControlSlider*)object;
-
- g_clear_pointer( &self->icon_name, g_free );
- g_clear_pointer( &self->label, g_free );
-
- G_OBJECT_CLASS( foobar_control_slider_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new control slider instance.
-//
-GtkWidget* foobar_control_slider_new( void )
-{
- return g_object_new( FOOBAR_TYPE_CONTROL_SLIDER, NULL );
-}
-
-//
-// Get the name of the icon to be displayed next to the label.
-//
-gchar const* foobar_control_slider_get_icon_name( FoobarControlSlider* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_SLIDER( self ), NULL );
- return self->icon_name;
-}
-
-//
-// Get the slider's main label.
-//
-gchar const* foobar_control_slider_get_label( FoobarControlSlider* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_SLIDER( self ), NULL );
- return self->label;
-}
-
-//
-// Check whether some details are hidden and can be revealed by a toggle button next to the slider.
-//
-gboolean foobar_control_slider_can_expand( FoobarControlSlider* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_SLIDER( self ), FALSE );
- return self->can_expand;
-}
-
-//
-// Check whether additional details should currently be shown.
-//
-gboolean foobar_control_slider_is_expanded( FoobarControlSlider* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_SLIDER( self ), FALSE );
- return self->is_expanded;
-}
-
-//
-// Get the value shown by the slider.
-//
-gint foobar_control_slider_get_percentage( FoobarControlSlider* self )
-{
- g_return_val_if_fail( FOOBAR_IS_CONTROL_SLIDER( self ), FALSE );
- return self->percentage;
-}
-
-//
-// Update the name of the icon to be displayed next to the label.
-//
-void foobar_control_slider_set_icon_name(
- FoobarControlSlider* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_SLIDER( self ) );
-
- if ( g_strcmp0( self->icon_name, value ) )
- {
- g_clear_pointer( &self->icon_name, g_free );
- self->icon_name = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_ICON_NAME] );
- }
-}
-
-//
-// Update the slider's main label.
-//
-void foobar_control_slider_set_label(
- FoobarControlSlider* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_SLIDER( 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 ), props[PROP_LABEL] );
- }
-}
-
-//
-// Update whether some details are hidden and can be revealed by a toggle button next to the slider.
-//
-void foobar_control_slider_set_can_expand(
- FoobarControlSlider* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_SLIDER( self ) );
-
- if ( !value ) { foobar_control_slider_set_expanded( self, FALSE ); }
-
- value = !!value;
- if ( self->can_expand != value )
- {
- self->can_expand = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CAN_EXPAND] );
- }
-}
-
-//
-// Update whether additional details are currently shown.
-//
-void foobar_control_slider_set_expanded(
- FoobarControlSlider* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_SLIDER( self ) );
-
- value = value && self->can_expand;
- if ( self->is_expanded != value )
- {
- self->is_expanded = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_IS_EXPANDED] );
-
- if ( self->is_expanded )
- {
- gtk_widget_add_css_class( GTK_WIDGET( self ), "expanded" );
- }
- else
- {
- gtk_widget_remove_css_class( GTK_WIDGET( self ), "expanded" );
- }
- }
-}
-
-//
-// Update the value shown by the slider.
-//
-void foobar_control_slider_set_percentage( FoobarControlSlider* self, gint value )
-{
- g_return_if_fail( FOOBAR_IS_CONTROL_SLIDER( self ) );
-
- value = CLAMP( value, 0, 100 );
- if ( self->percentage != value )
- {
- self->percentage = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_PERCENTAGE] );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when the expand button was clicked.
-//
-// If allowed, this will toggle the "expanded" state of the slider.
-//
-void foobar_control_slider_handle_expand_clicked( GtkButton* button, gpointer userdata )
-{
- (void)button;
- FoobarControlSlider* self = (FoobarControlSlider*)userdata;
-
- if ( foobar_control_slider_can_expand( self ) )
- {
- foobar_control_slider_set_expanded( self, !foobar_control_slider_is_expanded( self ) );
- }
-}
diff --git a/src/widgets/control-center/control-slider.h b/src/widgets/control-center/control-slider.h
deleted file mode 100644
index f797b5c..0000000
--- a/src/widgets/control-center/control-slider.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_CONTROL_SLIDER foobar_control_slider_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarControlSlider, foobar_control_slider, FOOBAR, CONTROL_SLIDER, GtkWidget )
-
-GtkWidget* foobar_control_slider_new ( void );
-gchar const* foobar_control_slider_get_icon_name ( FoobarControlSlider* self );
-gchar const* foobar_control_slider_get_label ( FoobarControlSlider* self );
-gboolean foobar_control_slider_can_expand ( FoobarControlSlider* self );
-gboolean foobar_control_slider_is_expanded ( FoobarControlSlider* self );
-gint foobar_control_slider_get_percentage( FoobarControlSlider* self );
-void foobar_control_slider_set_icon_name ( FoobarControlSlider* self,
- gchar const* value );
-void foobar_control_slider_set_label ( FoobarControlSlider* self,
- gchar const* value );
-void foobar_control_slider_set_can_expand( FoobarControlSlider* self,
- gboolean value );
-void foobar_control_slider_set_expanded ( FoobarControlSlider* self,
- gboolean value );
-void foobar_control_slider_set_percentage( FoobarControlSlider* self,
- gint value );
-
-G_END_DECLS
diff --git a/src/widgets/control-center/meson.build b/src/widgets/control-center/meson.build
deleted file mode 100644
index 2cdc9ca..0000000
--- a/src/widgets/control-center/meson.build
+++ /dev/null
@@ -1,6 +0,0 @@
-foobar_sources += files(
- 'control-button.c',
- 'control-slider.c',
- 'control-details.c',
- 'control-details-item.c',
-)
diff --git a/src/widgets/inset-container.c b/src/widgets/inset-container.c
deleted file mode 100644
index 052f872..0000000
--- a/src/widgets/inset-container.c
+++ /dev/null
@@ -1,363 +0,0 @@
-#include "widgets/inset-container.h"
-
-//
-// FoobarInsetContainer:
-//
-// A custom widget which renders a child with an inset/padding.
-//
-// Notably, it also supports negative inset values which is useful in order to provide uniform spacing between widgets.
-//
-
-struct _FoobarInsetContainer
-{
- GtkWidget parent_instance;
- GtkWidget* child;
- gint inset_horizontal;
- gint inset_vertical;
-};
-
-enum
-{
- PROP_CHILD = 1,
- PROP_INSET_HORIZONTAL,
- PROP_INSET_VERTICAL,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_inset_container_class_init ( FoobarInsetContainerClass* klass );
-static void foobar_inset_container_init ( FoobarInsetContainer* self );
-static void foobar_inset_container_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_inset_container_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_inset_container_dispose ( GObject* object );
-static GtkSizeRequestMode foobar_inset_container_get_request_mode( GtkWidget* widget );
-static void foobar_inset_container_measure ( GtkWidget* widget,
- GtkOrientation orientation,
- int for_size,
- int* minimum,
- int* natural,
- int* minimum_baseline,
- int* natural_baseline );
-static void foobar_inset_container_size_allocate ( GtkWidget* widget,
- int width,
- int height,
- int baseline );
-
-G_DEFINE_FINAL_TYPE( FoobarInsetContainer, foobar_inset_container, GTK_TYPE_WIDGET )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Widget Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for inset containers.
-//
-void foobar_inset_container_class_init( FoobarInsetContainerClass* klass )
-{
- GtkWidgetClass* widget_klass = GTK_WIDGET_CLASS( klass );
- widget_klass->get_request_mode = foobar_inset_container_get_request_mode;
- widget_klass->measure = foobar_inset_container_measure;
- widget_klass->size_allocate = foobar_inset_container_size_allocate;
- gtk_widget_class_set_css_name( widget_klass, "inset-container" );
-
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_inset_container_get_property;
- object_klass->set_property = foobar_inset_container_set_property;
- object_klass->dispose = foobar_inset_container_dispose;
-
- props[PROP_CHILD] = g_param_spec_object(
- "child",
- "Child",
- "Child widget for the panel item.",
- GTK_TYPE_WIDGET,
- G_PARAM_READWRITE );
- props[PROP_INSET_HORIZONTAL] = g_param_spec_int(
- "inset-horizontal",
- "Inset Horizontal",
- "The horizontal offset (may be negative).",
- INT_MIN,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- props[PROP_INSET_VERTICAL] = g_param_spec_int(
- "inset-vertical",
- "Inset Vertical",
- "The vertical offset (may be negative).",
- INT_MIN,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for inset containers.
-//
-void foobar_inset_container_init( FoobarInsetContainer* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_inset_container_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarInsetContainer* self = (FoobarInsetContainer*)object;
-
- switch ( prop_id )
- {
- case PROP_CHILD:
- g_value_set_object( value, foobar_inset_container_get_child( self ) );
- break;
- case PROP_INSET_HORIZONTAL:
- g_value_set_int( value, foobar_inset_container_get_inset_horizontal( self ) );
- break;
- case PROP_INSET_VERTICAL:
- g_value_set_int( value, foobar_inset_container_get_inset_vertical( 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_inset_container_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarInsetContainer* self = (FoobarInsetContainer*)object;
-
- switch ( prop_id )
- {
- case PROP_CHILD:
- foobar_inset_container_set_child( self, g_value_get_object( value ) );
- break;
- case PROP_INSET_HORIZONTAL:
- foobar_inset_container_set_inset_horizontal( self, g_value_get_int( value ) );
- break;
- case PROP_INSET_VERTICAL:
- foobar_inset_container_set_inset_vertical( self, g_value_get_int( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance de-initialization for inset containers.
-//
-void foobar_inset_container_dispose( GObject* object )
-{
- FoobarInsetContainer* self = (FoobarInsetContainer*)object;
-
- g_clear_pointer( &self->child, gtk_widget_unparent );
-
- G_OBJECT_CLASS( foobar_inset_container_parent_class )->dispose( object );
-}
-
-//
-// Determine the size request mode used to measure the widget.
-//
-// This implementation just forwards the request mode from the child widget.
-//
-GtkSizeRequestMode foobar_inset_container_get_request_mode( GtkWidget* widget )
-{
- FoobarInsetContainer* self = (FoobarInsetContainer*)widget;
- if ( !self->child ) { return GTK_SIZE_REQUEST_CONSTANT_SIZE; }
-
- return gtk_widget_get_request_mode( self->child );
-}
-
-//
-// Compute the requested size of the widget.
-//
-// This implementation uses the size requested by the child widget and then adds the inset to it.
-//
-void foobar_inset_container_measure(
- GtkWidget* widget,
- GtkOrientation orientation,
- int for_size,
- int* minimum,
- int* natural,
- int* minimum_baseline,
- int* natural_baseline )
-{
- FoobarInsetContainer* self = (FoobarInsetContainer*)widget;
- if ( !self->child )
- {
- *minimum = 0;
- *natural = 0;
- *minimum_baseline = 0;
- *natural_baseline = 0;
- return;
- }
-
- // Get the inset for the given orientation and the other one.
-
- gint inset;
- gint other_inset;
- switch ( orientation )
- {
- case GTK_ORIENTATION_HORIZONTAL:
- inset = self->inset_horizontal;
- other_inset = self->inset_vertical;
- break;
- case GTK_ORIENTATION_VERTICAL:
- inset = self->inset_vertical;
- other_inset = self->inset_horizontal;
- break;
- default:
- g_warn_if_reached( );
- return;
- }
-
- // Compute the new size, taking into consideration that the inset might have already been added to measure the other
- // side and needs to be subtracted before passing it to the child widget's measure function.
-
- for_size = MAX( 0, for_size - ( 2 * other_inset ) );
- gtk_widget_measure( self->child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline );
- *minimum = MAX( 0, *minimum + ( 2 * inset ) );
- *natural = MAX( 0, *natural + ( 2 * inset ) );
- if ( orientation == GTK_ORIENTATION_VERTICAL )
- {
- *minimum_baseline = MAX( 0, *minimum_baseline + inset );
- *natural_baseline = MAX( 0, *natural_baseline + inset );
- }
-}
-
-//
-// Arrange the child widget.
-//
-void foobar_inset_container_size_allocate(
- GtkWidget* widget,
- int width,
- int height,
- int baseline )
-{
- FoobarInsetContainer* self = (FoobarInsetContainer*)widget;
- if ( !self->child ) { return; }
-
- GtkAllocation allocation =
- {
- .width = MAX( 0, width - 2 * self->inset_horizontal ),
- .height = MAX( 0, height - 2 * self->inset_vertical ),
- .x = self->inset_horizontal,
- .y = self->inset_vertical,
- };
- gtk_widget_size_allocate( self->child, &allocation, MAX( 0, baseline + self->inset_vertical ) );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new inset container instance.
-//
-GtkWidget* foobar_inset_container_new( void )
-{
- return g_object_new( FOOBAR_TYPE_INSET_CONTAINER, NULL );
-}
-
-//
-// Get the container's child widget.
-//
-GtkWidget* foobar_inset_container_get_child( FoobarInsetContainer* self )
-{
- g_return_val_if_fail( FOOBAR_IS_INSET_CONTAINER( self ), NULL );
- return self->child;
-}
-
-//
-// Get the inset applied to the left and right side of the widget.
-//
-gint foobar_inset_container_get_inset_horizontal( FoobarInsetContainer* self )
-{
- g_return_val_if_fail( FOOBAR_IS_INSET_CONTAINER( self ), 0 );
- return self->inset_horizontal;
-}
-
-//
-// Get the inset applied to the top and bottom side of the widget.
-//
-gint foobar_inset_container_get_inset_vertical( FoobarInsetContainer* self )
-{
- g_return_val_if_fail( FOOBAR_IS_INSET_CONTAINER( self ), 0 );
- return self->inset_vertical;
-}
-
-//
-// Update the container's child widget.
-//
-void foobar_inset_container_set_child(
- FoobarInsetContainer* self,
- GtkWidget* child )
-{
- g_return_if_fail( FOOBAR_IS_INSET_CONTAINER( self ) );
- g_return_if_fail( child == NULL || self->child == child || gtk_widget_get_parent( child ) == NULL );
-
- if ( self->child != child )
- {
- g_clear_pointer( &self->child, gtk_widget_unparent );
-
- if ( child )
- {
- self->child = child;
- gtk_widget_set_parent( child, GTK_WIDGET( self ) );
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CHILD] );
- }
-}
-
-//
-// Update the inset applied to the left and right side of the widget.
-//
-void foobar_inset_container_set_inset_horizontal(
- FoobarInsetContainer* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_INSET_CONTAINER( self ) );
-
- if ( self->inset_horizontal != value )
- {
- self->inset_horizontal = value;
- gtk_widget_queue_resize( GTK_WIDGET( self ) );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_INSET_HORIZONTAL] );
- }
-}
-
-//
-// Update the inset applied to the top and bottom side of the widget.
-//
-void foobar_inset_container_set_inset_vertical(
- FoobarInsetContainer* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_INSET_CONTAINER( self ) );
-
- if ( self->inset_vertical != value )
- {
- self->inset_vertical = value;
- gtk_widget_queue_resize( GTK_WIDGET( self ) );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_INSET_VERTICAL] );
- }
-}
diff --git a/src/widgets/inset-container.h b/src/widgets/inset-container.h
deleted file mode 100644
index 316d2be..0000000
--- a/src/widgets/inset-container.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_INSET_CONTAINER foobar_inset_container_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarInsetContainer, foobar_inset_container, FOOBAR, INSET_CONTAINER, GtkWidget )
-
-GtkWidget* foobar_inset_container_new ( void );
-GtkWidget* foobar_inset_container_get_child ( FoobarInsetContainer* self );
-gint foobar_inset_container_get_inset_horizontal( FoobarInsetContainer* self );
-gint foobar_inset_container_get_inset_vertical ( FoobarInsetContainer* self );
-void foobar_inset_container_set_child ( FoobarInsetContainer* self,
- GtkWidget* value );
-void foobar_inset_container_set_inset_horizontal( FoobarInsetContainer* self,
- gint value );
-void foobar_inset_container_set_inset_vertical ( FoobarInsetContainer* self,
- gint value );
-
-G_END_DECLS
diff --git a/src/widgets/limit-container.c b/src/widgets/limit-container.c
deleted file mode 100644
index 123acef..0000000
--- a/src/widgets/limit-container.c
+++ /dev/null
@@ -1,341 +0,0 @@
-#include "widgets/limit-container.h"
-
-//
-// FoobarLimitContainer:
-//
-// A container which uses the natural size requested by the child as its minimum size, but only up to a certain point.
-//
-// This is useful because gtk_scrolled_window_set_max_content_height doesn't seem to be doing what I understand it to
-// do. This way, we can set propagate_natural_height to TRUE and then limit the height through this container.
-//
-
-struct _FoobarLimitContainer
-{
- GtkWidget parent_instance;
- GtkWidget* child;
- gint max_width;
- gint max_height;
-};
-
-enum
-{
- PROP_CHILD = 1,
- PROP_MAX_WIDTH,
- PROP_MAX_HEIGHT,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_limit_container_class_init ( FoobarLimitContainerClass* klass );
-static void foobar_limit_container_init ( FoobarLimitContainer* self );
-static void foobar_limit_container_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_limit_container_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_limit_container_dispose ( GObject* object );
-static GtkSizeRequestMode foobar_limit_container_get_request_mode( GtkWidget* widget );
-static void foobar_limit_container_measure ( GtkWidget* widget,
- GtkOrientation orientation,
- int for_size,
- int* minimum,
- int* natural,
- int* minimum_baseline,
- int* natural_baseline );
-static void foobar_limit_container_size_allocate ( GtkWidget* widget,
- int width,
- int height,
- int baseline );
-
-G_DEFINE_FINAL_TYPE( FoobarLimitContainer, foobar_limit_container, GTK_TYPE_WIDGET )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Widget Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for limit containers.
-//
-void foobar_limit_container_class_init( FoobarLimitContainerClass* klass )
-{
- GtkWidgetClass* widget_klass = GTK_WIDGET_CLASS( klass );
- widget_klass->get_request_mode = foobar_limit_container_get_request_mode;
- widget_klass->measure = foobar_limit_container_measure;
- widget_klass->size_allocate = foobar_limit_container_size_allocate;
- gtk_widget_class_set_css_name( widget_klass, "limit-container" );
-
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_limit_container_get_property;
- object_klass->set_property = foobar_limit_container_set_property;
- object_klass->dispose = foobar_limit_container_dispose;
-
- props[PROP_CHILD] = g_param_spec_object(
- "child",
- "Child",
- "Child widget for the panel item.",
- GTK_TYPE_WIDGET,
- G_PARAM_READWRITE );
- props[PROP_MAX_WIDTH] = g_param_spec_int(
- "max-width",
- "Max Width",
- "The maximum natural width supported as a minimum size.",
- 0,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- props[PROP_MAX_HEIGHT] = g_param_spec_int(
- "max-height",
- "Max Height",
- "The maximum natural height supported as a minimum size.",
- 0,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for limit containers.
-//
-void foobar_limit_container_init( FoobarLimitContainer* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_limit_container_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarLimitContainer* self = (FoobarLimitContainer*)object;
-
- switch ( prop_id )
- {
- case PROP_CHILD:
- g_value_set_object( value, foobar_limit_container_get_child( self ) );
- break;
- case PROP_MAX_WIDTH:
- g_value_set_int( value, foobar_limit_container_get_max_width( self ) );
- break;
- case PROP_MAX_HEIGHT:
- g_value_set_int( value, foobar_limit_container_get_max_height( 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_limit_container_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarLimitContainer* self = (FoobarLimitContainer*)object;
-
- switch ( prop_id )
- {
- case PROP_CHILD:
- foobar_limit_container_set_child( self, g_value_get_object( value ) );
- break;
- case PROP_MAX_WIDTH:
- foobar_limit_container_set_max_width( self, g_value_get_int( value ) );
- break;
- case PROP_MAX_HEIGHT:
- foobar_limit_container_set_max_height( self, g_value_get_int( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance de-initialization for limit containers.
-//
-void foobar_limit_container_dispose( GObject* object )
-{
- FoobarLimitContainer* self = (FoobarLimitContainer*)object;
-
- g_clear_pointer( &self->child, gtk_widget_unparent );
-
- G_OBJECT_CLASS( foobar_limit_container_parent_class )->dispose( object );
-}
-
-//
-// Determine the size request mode used to measure the widget.
-//
-// This implementation just forwards the request mode from the child widget.
-//
-GtkSizeRequestMode foobar_limit_container_get_request_mode( GtkWidget* widget )
-{
- FoobarLimitContainer* self = (FoobarLimitContainer*)widget;
- if ( !self->child ) { return GTK_SIZE_REQUEST_CONSTANT_SIZE; }
-
- return gtk_widget_get_request_mode( self->child );
-}
-
-//
-// Compute the requested size of the widget.
-//
-void foobar_limit_container_measure(
- GtkWidget* widget,
- GtkOrientation orientation,
- int for_size,
- int* minimum,
- int* natural,
- int* minimum_baseline,
- int* natural_baseline )
-{
- FoobarLimitContainer* self = (FoobarLimitContainer*)widget;
- if ( !self->child )
- {
- *minimum = 0;
- *natural = 0;
- *minimum_baseline = 0;
- *natural_baseline = 0;
- return;
- }
-
- gtk_widget_measure( self->child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline );
- gint natural_minimum = *natural;
- switch ( orientation )
- {
- case GTK_ORIENTATION_HORIZONTAL:
- natural_minimum = MIN( natural_minimum, self->max_width );
- break;
- case GTK_ORIENTATION_VERTICAL:
- natural_minimum = MIN( natural_minimum, self->max_height );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
- *minimum = MAX( *minimum, natural_minimum );
-}
-
-//
-// Arrange the child widget.
-//
-void foobar_limit_container_size_allocate(
- GtkWidget* widget,
- int width,
- int height,
- int baseline )
-{
- FoobarLimitContainer* self = (FoobarLimitContainer*)widget;
- if ( !self->child ) { return; }
-
- gtk_widget_allocate( self->child, width, height, baseline, NULL );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new limit container instance.
-//
-GtkWidget* foobar_limit_container_new( void )
-{
- return g_object_new( FOOBAR_TYPE_LIMIT_CONTAINER, NULL );
-}
-
-//
-// Get the container's child widget.
-//
-GtkWidget* foobar_limit_container_get_child( FoobarLimitContainer* self )
-{
- g_return_val_if_fail( FOOBAR_IS_LIMIT_CONTAINER( self ), NULL );
- return self->child;
-}
-
-//
-// Get the maximum natural width of the child to use as the minimum width for this container.
-//
-gint foobar_limit_container_get_max_width( FoobarLimitContainer* self )
-{
- g_return_val_if_fail( FOOBAR_IS_LIMIT_CONTAINER( self ), 0 );
- return self->max_width;
-}
-
-//
-// Get the maximum natural height of the child to use as the minimum height for this container.
-//
-gint foobar_limit_container_get_max_height( FoobarLimitContainer* self )
-{
- g_return_val_if_fail( FOOBAR_IS_LIMIT_CONTAINER( self ), 0 );
- return self->max_height;
-}
-
-//
-// Update the container's child widget.
-//
-void foobar_limit_container_set_child(
- FoobarLimitContainer* self,
- GtkWidget* child )
-{
- g_return_if_fail( FOOBAR_IS_LIMIT_CONTAINER( self ) );
- g_return_if_fail( child == NULL || self->child == child || gtk_widget_get_parent( child ) == NULL );
-
- if ( self->child != child )
- {
- g_clear_pointer( &self->child, gtk_widget_unparent );
-
- if ( child )
- {
- self->child = child;
- gtk_widget_set_parent( child, GTK_WIDGET( self ) );
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CHILD] );
- }
-}
-
-//
-// Update the maximum natural width of the child to use as the minimum width for this container.
-//
-void foobar_limit_container_set_max_width(
- FoobarLimitContainer* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_LIMIT_CONTAINER( self ) );
- g_return_if_fail( value >= 0 );
-
- if ( self->max_width != value )
- {
- self->max_width = value;
- gtk_widget_queue_resize( GTK_WIDGET( self ) );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_MAX_WIDTH] );
- }
-}
-
-//
-// Update the maximum natural height of the child to use as the minimum height for this container.
-//
-void foobar_limit_container_set_max_height(
- FoobarLimitContainer* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_LIMIT_CONTAINER( self ) );
- g_return_if_fail( value >= 0 );
-
- if ( self->max_height != value )
- {
- self->max_height = value;
- gtk_widget_queue_resize( GTK_WIDGET( self ) );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_MAX_HEIGHT] );
- }
-}
diff --git a/src/widgets/limit-container.h b/src/widgets/limit-container.h
deleted file mode 100644
index cad19aa..0000000
--- a/src/widgets/limit-container.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_LIMIT_CONTAINER foobar_limit_container_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarLimitContainer, foobar_limit_container, FOOBAR, LIMIT_CONTAINER, GtkWidget )
-
-GtkWidget* foobar_limit_container_new ( void );
-GtkWidget* foobar_limit_container_get_child ( FoobarLimitContainer* self );
-gint foobar_limit_container_get_max_width ( FoobarLimitContainer* self );
-gint foobar_limit_container_get_max_height( FoobarLimitContainer* self );
-void foobar_limit_container_set_child ( FoobarLimitContainer* self,
- GtkWidget* value );
-void foobar_limit_container_set_max_width ( FoobarLimitContainer* self,
- gint value );
-void foobar_limit_container_set_max_height( FoobarLimitContainer* self,
- gint value );
-
-G_END_DECLS
diff --git a/src/widgets/meson.build b/src/widgets/meson.build
deleted file mode 100644
index 7aeb3ca..0000000
--- a/src/widgets/meson.build
+++ /dev/null
@@ -1,8 +0,0 @@
-foobar_sources += files(
- 'inset-container.c',
- 'limit-container.c',
- 'notification-widget.c',
-)
-
-subdir('control-center')
-subdir('panel')
diff --git a/src/widgets/notification-widget.c b/src/widgets/notification-widget.c
deleted file mode 100644
index 97cfc22..0000000
--- a/src/widgets/notification-widget.c
+++ /dev/null
@@ -1,821 +0,0 @@
-#include "widgets/notification-widget.h"
-
-//
-// FoobarNotificationCloseAction:
-//
-// The action invoked when the user requests to "close" the notification. The notification is either marked as
-// "dismissed" and kept around or removed from the list.
-//
-
-G_DEFINE_ENUM_TYPE(
- FoobarNotificationCloseAction,
- foobar_notification_close_action,
- G_DEFINE_ENUM_VALUE( FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION_REMOVE, "remove" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION_DISMISS, "dismiss" ) )
-
-//
-// FoobarNotificationWidget:
-//
-// Shared widget for displaying a notification. The widget also manages an inset/margin -- this is done to allow the
-// "close" button to extend into the margin area.
-//
-
-struct _FoobarNotificationWidget
-{
- GtkWidget parent_instance;
- GtkWidget* container;
- GtkWidget* content;
- GtkWidget* close_button;
- FoobarNotification* notification;
- FoobarNotificationCloseAction close_action;
- gchar* time_format;
- gint min_height;
- gint close_button_inset;
- gint inset_start;
- gint inset_end;
- gint inset_top;
- gint inset_bottom;
-};
-
-enum
-{
- PROP_NOTIFICATION = 1,
- PROP_CLOSE_ACTION,
- PROP_TIME_FORMAT,
- PROP_MIN_HEIGHT,
- PROP_CLOSE_BUTTON_INSET,
- PROP_INSET_START,
- PROP_INSET_END,
- PROP_INSET_TOP,
- PROP_INSET_BOTTOM,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_notification_widget_class_init ( FoobarNotificationWidgetClass* klass );
-static void foobar_notification_widget_init ( FoobarNotificationWidget* self );
-static void foobar_notification_widget_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_notification_widget_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_notification_widget_dispose ( GObject* object );
-static void foobar_notification_widget_finalize ( GObject* object );
-static void foobar_notification_widget_handle_enter ( GtkEventControllerMotion* controller,
- gdouble x,
- gdouble y,
- gpointer userdata );
-static void foobar_notification_widget_handle_leave ( GtkEventControllerMotion* controller,
- gpointer userdata );
-static void foobar_notification_widget_handle_close_clicked( GtkButton* button,
- gpointer userdata );
-static gchar* foobar_notification_widget_compute_time_label ( GtkExpression* expression,
- GDateTime* time,
- gchar const* format,
- gpointer userdata );
-static gboolean foobar_notification_widget_compute_icon_visible( GtkExpression* expression,
- GIcon* icon,
- gpointer userdata );
-static gboolean foobar_notification_widget_compute_body_visible( GtkExpression* expression,
- gchar const* body,
- gpointer userdata );
-static void foobar_notification_widget_update_margins ( FoobarNotificationWidget* self );
-
-G_DEFINE_FINAL_TYPE( FoobarNotificationWidget, foobar_notification_widget, GTK_TYPE_WIDGET )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Widget Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for notifications.
-//
-void foobar_notification_widget_class_init( FoobarNotificationWidgetClass* klass )
-{
- GtkWidgetClass* widget_klass = GTK_WIDGET_CLASS( klass );
- gtk_widget_class_set_layout_manager_type( widget_klass, GTK_TYPE_BIN_LAYOUT );
- gtk_widget_class_set_css_name( widget_klass, "notification" );
-
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_notification_widget_get_property;
- object_klass->set_property = foobar_notification_widget_set_property;
- object_klass->dispose = foobar_notification_widget_dispose;
- object_klass->finalize = foobar_notification_widget_finalize;
-
- props[PROP_NOTIFICATION] = g_param_spec_object(
- "notification",
- "Notification",
- "Content to display.",
- FOOBAR_TYPE_NOTIFICATION,
- G_PARAM_READWRITE );
- props[PROP_CLOSE_ACTION] = g_param_spec_enum(
- "close-action",
- "Close Action",
- "Action to execute when the notification is closed.",
- FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION,
- 0,
- G_PARAM_READWRITE );
- props[PROP_TIME_FORMAT] = g_param_spec_string(
- "time-format",
- "Time Format",
- "Time format used to display the notification's timestamp.",
- NULL,
- G_PARAM_READWRITE );
- props[PROP_MIN_HEIGHT] = g_param_spec_int(
- "min-height",
- "Minimum Height",
- "Minimum requested height for the notification itself.",
- 0,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- props[PROP_CLOSE_BUTTON_INSET] = g_param_spec_int(
- "close-button-inset",
- "Close Button Inset",
- "Offset of the close button within the notification (may be negative, possibly increasing the actual inset).",
- INT_MIN,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- props[PROP_INSET_START] = g_param_spec_int(
- "inset-start",
- "Inset Start",
- "Inset for the start edge of the widget.",
- 0,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- props[PROP_INSET_END] = g_param_spec_int(
- "inset-end",
- "Inset End",
- "Inset for the end edge of the widget.",
- 0,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- props[PROP_INSET_TOP] = g_param_spec_int(
- "inset-top",
- "Inset Top",
- "Inset for the top edge of the widget.",
- 0,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- props[PROP_INSET_BOTTOM] = g_param_spec_int(
- "inset-bottom",
- "Inset Bottom",
- "Inset for the bottom edge of the widget.",
- 0,
- INT_MAX,
- 0,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for notifications.
-//
-void foobar_notification_widget_init( FoobarNotificationWidget* self )
-{
- // Set up the main notification content.
-
- GtkWidget* icon = gtk_image_new( );
- gtk_widget_add_css_class( icon, "icon" );
- gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
-
- GtkWidget* title = gtk_label_new( NULL );
- gtk_label_set_justify( GTK_LABEL( title ), GTK_JUSTIFY_LEFT );
- gtk_label_set_ellipsize( GTK_LABEL( title ), PANGO_ELLIPSIZE_END );
- gtk_label_set_wrap( GTK_LABEL( title ), FALSE );
- gtk_label_set_xalign( GTK_LABEL( title ), 0 );
- gtk_widget_add_css_class( title, "title" );
- gtk_widget_set_valign( title, GTK_ALIGN_BASELINE_CENTER );
- gtk_widget_set_hexpand( title, TRUE );
-
- GtkWidget* time = gtk_label_new( NULL );
- gtk_label_set_wrap( GTK_LABEL( time ), FALSE );
- gtk_widget_add_css_class( time, "time" );
- gtk_widget_set_valign( time, GTK_ALIGN_BASELINE_CENTER );
-
- GtkWidget* body = gtk_label_new( NULL );
- gtk_label_set_use_markup( GTK_LABEL( body ), TRUE );
- gtk_label_set_justify( GTK_LABEL( body ), GTK_JUSTIFY_LEFT );
- gtk_label_set_ellipsize( GTK_LABEL( body ), PANGO_ELLIPSIZE_END );
- gtk_label_set_wrap( GTK_LABEL( body ), TRUE );
- gtk_label_set_wrap_mode( GTK_LABEL( body ), PANGO_WRAP_WORD_CHAR );
- // XXX: Increase once layout is fixed in GTK (https://gitlab.gnome.org/GNOME/gtk/-/issues/5885)
- gtk_label_set_lines( GTK_LABEL( body ), 1 );
- gtk_label_set_xalign( GTK_LABEL( body ), 0 );
- gtk_widget_add_css_class( body, "body" );
-
- GtkWidget* header = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_box_append( GTK_BOX( header ), title );
- gtk_box_append( GTK_BOX( header ), time );
-
- GtkWidget* row = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
- gtk_box_append( GTK_BOX( row ), header );
- gtk_box_append( GTK_BOX( row ), body );
- gtk_widget_set_hexpand( row, TRUE );
- gtk_widget_set_valign( row, GTK_ALIGN_CENTER );
-
- self->content = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_box_append( GTK_BOX( self->content ), icon );
- gtk_box_append( GTK_BOX( self->content ), row );
- gtk_widget_add_css_class( self->content, "content" );
-
- // Set up the overlay for the close button.
-
- GtkWidget* close_icon = gtk_image_new_from_icon_name( "fluent-dismiss-symbolic" );
- gtk_image_set_pixel_size( GTK_IMAGE( close_icon ), 12 );
-
- self->close_button = gtk_button_new( );
- gtk_button_set_child( GTK_BUTTON( self->close_button ), close_icon );
- gtk_widget_add_css_class( self->close_button, "close-button" );
- gtk_widget_set_visible( self->close_button, FALSE );
- gtk_widget_set_halign( self->close_button, GTK_ALIGN_END );
- gtk_widget_set_valign( self->close_button, GTK_ALIGN_START );
- g_signal_connect( self->close_button, "clicked", G_CALLBACK( foobar_notification_widget_handle_close_clicked ), self );
-
- GtkEventController* hover_controller = gtk_event_controller_motion_new( );
- g_signal_connect( hover_controller, "enter", G_CALLBACK( foobar_notification_widget_handle_enter ), self );
- g_signal_connect( hover_controller, "leave", G_CALLBACK( foobar_notification_widget_handle_leave ), self );
-
- self->container = gtk_overlay_new( );
- gtk_overlay_set_child( GTK_OVERLAY( self->container ), self->content );
- gtk_overlay_add_overlay( GTK_OVERLAY( self->container ), self->close_button );
- gtk_widget_add_controller( self->container, hover_controller );
- gtk_widget_set_parent( self->container, GTK_WIDGET( self ) );
-
- // Set up bindings.
-
- {
- GtkExpression* notification_expr = gtk_property_expression_new( FOOBAR_TYPE_NOTIFICATION_WIDGET, NULL, "notification" );
- GtkExpression* image_expr = gtk_property_expression_new( FOOBAR_TYPE_NOTIFICATION, notification_expr, "image" );
- gtk_expression_bind( gtk_expression_ref( image_expr ), icon, "gicon", self );
- GtkExpression* visible_params[] = { image_expr };
- GtkExpression* visible_expr = gtk_cclosure_expression_new(
- G_TYPE_BOOLEAN,
- NULL,
- G_N_ELEMENTS( visible_params ),
- visible_params,
- G_CALLBACK( foobar_notification_widget_compute_icon_visible ),
- NULL,
- NULL );
- gtk_expression_bind( visible_expr, icon, "visible", self );
- }
-
- {
- GtkExpression* notification_expr = gtk_property_expression_new( FOOBAR_TYPE_NOTIFICATION_WIDGET, NULL, "notification" );
- GtkExpression* summary_expr = gtk_property_expression_new( FOOBAR_TYPE_NOTIFICATION, notification_expr, "summary" );
- gtk_expression_bind( summary_expr, title, "label", self );
- }
-
- {
- GtkExpression* notification_expr = gtk_property_expression_new( FOOBAR_TYPE_NOTIFICATION_WIDGET, NULL, "notification" );
- GtkExpression* body_expr = gtk_property_expression_new( FOOBAR_TYPE_NOTIFICATION, notification_expr, "body" );
- gtk_expression_bind( gtk_expression_ref( body_expr ), body, "label", self );
- GtkExpression* visible_params[] = { body_expr };
- GtkExpression* visible_expr = gtk_cclosure_expression_new(
- G_TYPE_BOOLEAN,
- NULL,
- G_N_ELEMENTS( visible_params ),
- visible_params,
- G_CALLBACK( foobar_notification_widget_compute_body_visible ),
- NULL,
- NULL );
- gtk_expression_bind( visible_expr, body, "visible", self );
- }
-
- {
- GtkExpression* notification_expr = gtk_property_expression_new( FOOBAR_TYPE_NOTIFICATION_WIDGET, NULL, "notification" );
- GtkExpression* time_expr = gtk_property_expression_new( FOOBAR_TYPE_NOTIFICATION, notification_expr, "time" );
- GtkExpression* format_expr = gtk_property_expression_new( FOOBAR_TYPE_NOTIFICATION_WIDGET, NULL, "time-format" );
- GtkExpression* str_params[] = { time_expr, format_expr };
- GtkExpression* str_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( str_params ),
- str_params,
- G_CALLBACK( foobar_notification_widget_compute_time_label ),
- self,
- NULL );
- gtk_expression_bind( str_expr, time, "label", self );
- }
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_notification_widget_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarNotificationWidget* self = (FoobarNotificationWidget*)object;
-
- switch ( prop_id )
- {
- case PROP_NOTIFICATION:
- g_value_set_object( value, foobar_notification_widget_get_notification( self ) );
- break;
- case PROP_CLOSE_ACTION:
- g_value_set_enum( value, foobar_notification_widget_get_close_action( self ) );
- break;
- case PROP_TIME_FORMAT:
- g_value_set_string( value, foobar_notification_widget_get_time_format( self ) );
- break;
- case PROP_MIN_HEIGHT:
- g_value_set_int( value, foobar_notification_widget_get_min_height( self ) );
- break;
- case PROP_CLOSE_BUTTON_INSET:
- g_value_set_int( value, foobar_notification_widget_get_close_button_inset( self ) );
- break;
- case PROP_INSET_START:
- g_value_set_int( value, foobar_notification_widget_get_inset_start( self ) );
- break;
- case PROP_INSET_END:
- g_value_set_int( value, foobar_notification_widget_get_inset_end( self ) );
- break;
- case PROP_INSET_TOP:
- g_value_set_int( value, foobar_notification_widget_get_inset_top( self ) );
- break;
- case PROP_INSET_BOTTOM:
- g_value_set_int( value, foobar_notification_widget_get_inset_bottom( 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_notification_widget_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarNotificationWidget* self = (FoobarNotificationWidget*)object;
-
- switch ( prop_id )
- {
- case PROP_NOTIFICATION:
- foobar_notification_widget_set_notification( self, g_value_get_object( value ) );
- break;
- case PROP_CLOSE_ACTION:
- foobar_notification_widget_set_close_action( self, g_value_get_enum( value ) );
- break;
- case PROP_TIME_FORMAT:
- foobar_notification_widget_set_time_format( self, g_value_get_string( value ) );
- break;
- case PROP_MIN_HEIGHT:
- foobar_notification_widget_set_min_height( self, g_value_get_int( value ) );
- break;
- case PROP_CLOSE_BUTTON_INSET:
- foobar_notification_widget_set_close_button_inset( self, g_value_get_int( value ) );
- break;
- case PROP_INSET_START:
- foobar_notification_widget_set_inset_start( self, g_value_get_int( value ) );
- break;
- case PROP_INSET_END:
- foobar_notification_widget_set_inset_end( self, g_value_get_int( value ) );
- break;
- case PROP_INSET_TOP:
- foobar_notification_widget_set_inset_top( self, g_value_get_int( value ) );
- break;
- case PROP_INSET_BOTTOM:
- foobar_notification_widget_set_inset_bottom( self, g_value_get_int( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance de-initialization for notifications.
-//
-void foobar_notification_widget_dispose( GObject* object )
-{
- FoobarNotificationWidget* self = (FoobarNotificationWidget*)object;
-
- GtkWidget* child;
- while ( ( child = gtk_widget_get_first_child( GTK_WIDGET( self ) ) ) )
- {
- gtk_widget_unparent( child );
- }
-
- G_OBJECT_CLASS( foobar_notification_widget_parent_class )->dispose( object );
-}
-
-//
-// Instance cleanup for notifications.
-//
-void foobar_notification_widget_finalize( GObject* object )
-{
- FoobarNotificationWidget* self = (FoobarNotificationWidget*)object;
-
- g_clear_object( &self->notification );
- g_clear_pointer( &self->time_format, g_free );
-
- G_OBJECT_CLASS( foobar_notification_widget_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new notification widget instance.
-//
-GtkWidget* foobar_notification_widget_new( void )
-{
- return g_object_new( FOOBAR_TYPE_NOTIFICATION_WIDGET, NULL );
-}
-
-//
-// Get the notification model currently displayed.
-//
-FoobarNotification* foobar_notification_widget_get_notification( FoobarNotificationWidget* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ), NULL );
- return self->notification;
-}
-
-//
-// Get the action invoked when the user wants to "close" a notification.
-//
-FoobarNotificationCloseAction foobar_notification_widget_get_close_action( FoobarNotificationWidget* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ), 0 );
- return self->close_action;
-}
-
-//
-// Get the time format string used to display the notification's timestamp.
-//
-gchar const* foobar_notification_widget_get_time_format( FoobarNotificationWidget* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ), NULL );
- return self->time_format;
-}
-
-//
-// Get the minimum height of the notification (excluding the inset).
-//
-gint foobar_notification_widget_get_min_height( FoobarNotificationWidget* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ), 0 );
- return self->min_height;
-}
-
-//
-// Get the offset of the close button within the notification (may be negative, possibly increasing the actual inset).
-//
-gint foobar_notification_widget_get_close_button_inset( FoobarNotificationWidget* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ), 0 );
- return self->close_button_inset;
-}
-
-//
-// Get the margin for the start edge of the notification.
-//
-gint foobar_notification_widget_get_inset_start( FoobarNotificationWidget* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ), 0 );
- return self->inset_start;
-}
-
-//
-// Get the margin for the end edge of the notification.
-//
-gint foobar_notification_widget_get_inset_end( FoobarNotificationWidget* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ), 0 );
- return self->inset_end;
-}
-
-//
-// Get the margin for the top edge of the notification.
-//
-gint foobar_notification_widget_get_inset_top( FoobarNotificationWidget* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ), 0 );
- return self->inset_top;
-}
-
-//
-// Get the margin for the bottom edge of the notification.
-//
-gint foobar_notification_widget_get_inset_bottom( FoobarNotificationWidget* self )
-{
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ), 0 );
- return self->inset_bottom;
-}
-
-//
-// Update the notification model currently displayed.
-//
-void foobar_notification_widget_set_notification(
- FoobarNotificationWidget* self,
- FoobarNotification* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ) );
-
- if ( self->notification != value )
- {
- g_clear_object( &self->notification );
- if ( value ) { self->notification = g_object_ref( value ); }
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_NOTIFICATION] );
- }
-}
-
-//
-// Update the action invoked when the user wants to "close" a notification.
-//
-void foobar_notification_widget_set_close_action(
- FoobarNotificationWidget* self,
- FoobarNotificationCloseAction value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ) );
-
- if ( self->close_action != value )
- {
- self->close_action = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CLOSE_ACTION] );
- }
-}
-
-//
-// Update the time format string used to display the notification's timestamp.
-//
-void foobar_notification_widget_set_time_format(
- FoobarNotificationWidget* self,
- gchar const* value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ) );
-
- if ( g_strcmp0( self->time_format, value ) )
- {
- g_clear_pointer( &self->time_format, g_free );
- self->time_format = g_strdup( value );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_TIME_FORMAT] );
- }
-}
-
-//
-// Update the minimum height of the notification (excluding the inset).
-//
-void foobar_notification_widget_set_min_height(
- FoobarNotificationWidget* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ) );
-
- value = MAX( value, 0 );
- if ( self->min_height != value )
- {
- self->min_height = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_MIN_HEIGHT] );
- gtk_widget_set_size_request( self->content, -1, self->min_height );
- }
-}
-
-//
-// Update the offset of the close button within the notification (may be negative, possibly increasing the actual inset).
-//
-void foobar_notification_widget_set_close_button_inset(
- FoobarNotificationWidget* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ) );
-
- if ( self->close_button_inset != value )
- {
- self->close_button_inset = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CLOSE_BUTTON_INSET] );
- foobar_notification_widget_update_margins( self );
- }
-}
-
-//
-// Update the margin for the start edge of the notification.
-//
-void foobar_notification_widget_set_inset_start(
- FoobarNotificationWidget* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ) );
-
- value = MAX( value, 0 );
- if ( self->inset_start != value )
- {
- self->inset_start = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_INSET_START] );
- foobar_notification_widget_update_margins( self );
- }
-}
-
-//
-// Update the margin for the end edge of the notification.
-//
-void foobar_notification_widget_set_inset_end(
- FoobarNotificationWidget* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ) );
-
- value = MAX( value, 0 );
- if ( self->inset_end != value )
- {
- self->inset_end = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_INSET_END] );
- foobar_notification_widget_update_margins( self );
- }
-}
-
-//
-// Update the margin for the top edge of the notification.
-//
-void foobar_notification_widget_set_inset_top(
- FoobarNotificationWidget* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ) );
-
- value = MAX( value, 0 );
- if ( self->inset_top != value )
- {
- self->inset_top = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_INSET_TOP] );
- foobar_notification_widget_update_margins( self );
- }
-}
-
-//
-// Update the margin for the bottom edge of the notification.
-//
-void foobar_notification_widget_set_inset_bottom(
- FoobarNotificationWidget* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_NOTIFICATION_WIDGET( self ) );
-
- value = MAX( value, 0 );
- if ( self->inset_bottom != value )
- {
- self->inset_bottom = value;
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_INSET_BOTTOM] );
- foobar_notification_widget_update_margins( self );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when the mouse cursor enters the notification.
-//
-// We will temporarily block the dismissal timeout for the notification and show the close button.
-//
-void foobar_notification_widget_handle_enter(
- GtkEventControllerMotion* controller,
- gdouble x,
- gdouble y,
- gpointer userdata )
-{
- (void)controller;
- (void)x;
- (void)y;
- FoobarNotificationWidget* self = (FoobarNotificationWidget*)userdata;
-
- gtk_widget_set_visible( self->close_button, TRUE );
- if ( self->notification ) { foobar_notification_block_timeout( self->notification ); }
-}
-
-//
-// Called when the mouse cursor leaves the notification.
-//
-// We will unblock the dismissal timeout for the notification and hide the close button.
-//
-void foobar_notification_widget_handle_leave(
- GtkEventControllerMotion* controller,
- gpointer userdata )
-{
- (void)controller;
- FoobarNotificationWidget* self = (FoobarNotificationWidget*)userdata;
-
- gtk_widget_set_visible( self->close_button, FALSE );
- if ( self->notification ) { foobar_notification_resume_timeout( self->notification ); }
-}
-
-//
-// Called when the user has clicked the "close" button.
-//
-// This either marks the notification as dismissed or removes it.
-//
-void foobar_notification_widget_handle_close_clicked(
- GtkButton* button,
- gpointer userdata )
-{
- (void)button;
- FoobarNotificationWidget* self = (FoobarNotificationWidget*)userdata;
-
- if ( self->notification )
- {
- switch ( self->close_action )
- {
- case FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION_REMOVE:
- foobar_notification_close( self->notification );
- break;
- case FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION_DISMISS:
- foobar_notification_dismiss( self->notification );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Value Converters
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Derive the timestamp label value from the notification's timestamp and the selected time format.
-//
-gchar* foobar_notification_widget_compute_time_label(
- GtkExpression* expression,
- GDateTime* time,
- gchar const* format,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- return format != NULL ? g_date_time_format( time, format ) : NULL;
-}
-
-//
-// Derive the visibility of the notification icon from its value.
-//
-gboolean foobar_notification_widget_compute_icon_visible(
- GtkExpression* expression,
- GIcon* icon,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- return icon != NULL;
-}
-
-//
-// Derive the visibility of the notification body label from its value.
-//
-gboolean foobar_notification_widget_compute_body_visible(
- GtkExpression* expression,
- gchar const* body,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- return body != NULL && g_strcmp0( body, "" );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Update the margins of the content widget and close button to the current inset values.
-//
-void foobar_notification_widget_update_margins( FoobarNotificationWidget* self )
-{
- gint inset = MAX( self->close_button_inset, 0 );
- gint overrun = MAX( -self->close_button_inset, 0 );
- gint actual_inset_end = MAX( self->inset_end, overrun );
- gint actual_inset_top = MAX( self->inset_top, overrun );
-
- gtk_widget_set_margin_start( self->container, self->inset_start );
- gtk_widget_set_margin_end( self->container, actual_inset_end - overrun );
- gtk_widget_set_margin_top( self->container, actual_inset_top - overrun );
- gtk_widget_set_margin_bottom( self->container, self->inset_bottom );
-
- gtk_widget_set_margin_end( self->content, overrun );
- gtk_widget_set_margin_top( self->content, overrun );
-
- gtk_widget_set_margin_end( self->close_button, inset );
- gtk_widget_set_margin_top( self->close_button, inset );
-}
diff --git a/src/widgets/notification-widget.h b/src/widgets/notification-widget.h
deleted file mode 100644
index af9de49..0000000
--- a/src/widgets/notification-widget.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-#include "services/notification-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION foobar_notification_close_action_get_type( )
-#define FOOBAR_TYPE_NOTIFICATION_WIDGET foobar_notification_widget_get_type( )
-
-typedef enum
-{
- FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION_REMOVE,
- FOOBAR_TYPE_NOTIFICATION_CLOSE_ACTION_DISMISS,
-} FoobarNotificationCloseAction;
-
-GType foobar_notification_close_action_get_type( void );
-
-G_DECLARE_FINAL_TYPE( FoobarNotificationWidget, foobar_notification_widget, FOOBAR, NOTIFICATION_WIDGET, GtkWidget )
-
-GtkWidget* foobar_notification_widget_new ( void );
-FoobarNotification* foobar_notification_widget_get_notification ( FoobarNotificationWidget* self );
-FoobarNotificationCloseAction foobar_notification_widget_get_close_action ( FoobarNotificationWidget* self );
-gchar const* foobar_notification_widget_get_time_format ( FoobarNotificationWidget* self );
-gint foobar_notification_widget_get_min_height ( FoobarNotificationWidget* self );
-gint foobar_notification_widget_get_close_button_inset( FoobarNotificationWidget* self );
-gint foobar_notification_widget_get_inset_start ( FoobarNotificationWidget* self );
-gint foobar_notification_widget_get_inset_end ( FoobarNotificationWidget* self );
-gint foobar_notification_widget_get_inset_top ( FoobarNotificationWidget* self );
-gint foobar_notification_widget_get_inset_bottom ( FoobarNotificationWidget* self );
-void foobar_notification_widget_set_notification ( FoobarNotificationWidget* self,
- FoobarNotification* value );
-void foobar_notification_widget_set_close_action ( FoobarNotificationWidget* self,
- FoobarNotificationCloseAction value );
-void foobar_notification_widget_set_time_format ( FoobarNotificationWidget* self,
- gchar const* value );
-void foobar_notification_widget_set_min_height ( FoobarNotificationWidget* self,
- gint value );
-void foobar_notification_widget_set_close_button_inset( FoobarNotificationWidget* self,
- gint value );
-void foobar_notification_widget_set_inset_start ( FoobarNotificationWidget* self,
- gint value );
-void foobar_notification_widget_set_inset_end ( FoobarNotificationWidget* self,
- gint value );
-void foobar_notification_widget_set_inset_top ( FoobarNotificationWidget* self,
- gint value );
-void foobar_notification_widget_set_inset_bottom ( FoobarNotificationWidget* self,
- gint value );
-
-G_END_DECLS
diff --git a/src/widgets/panel/meson.build b/src/widgets/panel/meson.build
deleted file mode 100644
index 10afc94..0000000
--- a/src/widgets/panel/meson.build
+++ /dev/null
@@ -1,7 +0,0 @@
-foobar_sources += files(
- 'panel-item.c',
- 'panel-item-icon.c',
- 'panel-item-clock.c',
- 'panel-item-workspaces.c',
- 'panel-item-status.c',
-)
diff --git a/src/widgets/panel/panel-item-clock.c b/src/widgets/panel/panel-item-clock.c
deleted file mode 100644
index 00b2cc5..0000000
--- a/src/widgets/panel/panel-item-clock.c
+++ /dev/null
@@ -1,152 +0,0 @@
-#include "widgets/panel/panel-item-clock.h"
-
-//
-// FoobarPanelItemClock:
-//
-// A panel item displaying the current time in the format configured by the user.
-//
-
-struct _FoobarPanelItemClock
-{
- FoobarPanelItem parent_instance;
- gchar* format;
- FoobarPanelItemAction action;
- GtkWidget* label;
- GtkWidget* button;
- FoobarClockService* clock_service;
-};
-
-static void foobar_panel_item_clock_class_init ( FoobarPanelItemClockClass* klass );
-static void foobar_panel_item_clock_init ( FoobarPanelItemClock* self );
-static void foobar_panel_item_clock_finalize ( GObject* object );
-static void foobar_panel_item_clock_handle_clicked( GtkButton* button,
- gpointer userdata );
-static gchar* foobar_panel_item_clock_compute_label ( GtkExpression* expression,
- GDateTime* time,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarPanelItemClock, foobar_panel_item_clock, FOOBAR_TYPE_PANEL_ITEM )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Panel Item Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for clock panel items.
-//
-void foobar_panel_item_clock_class_init( FoobarPanelItemClockClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->finalize = foobar_panel_item_clock_finalize;
-}
-
-//
-// Instance initialization for clock panel items.
-//
-void foobar_panel_item_clock_init( FoobarPanelItemClock* self )
-{
- self->label = gtk_label_new( NULL );
- gtk_label_set_justify( GTK_LABEL( self->label ), GTK_JUSTIFY_CENTER );
-
- self->button = gtk_button_new( );
- gtk_button_set_has_frame( GTK_BUTTON( self->button ), FALSE );
- gtk_button_set_child( GTK_BUTTON( self->button ), self->label );
- gtk_widget_set_halign( self->button, GTK_ALIGN_CENTER );
- gtk_widget_set_valign( self->button, GTK_ALIGN_CENTER );
- g_signal_connect(
- self->button,
- "clicked",
- G_CALLBACK( foobar_panel_item_clock_handle_clicked ),
- self );
-
- gtk_widget_add_css_class( GTK_WIDGET( self ), "clock" );
- foobar_panel_item_set_child( FOOBAR_PANEL_ITEM( self ), self->button );
-}
-
-//
-// Instance cleanup for clock panel items.
-//
-void foobar_panel_item_clock_finalize( GObject* object )
-{
- FoobarPanelItemClock* self = (FoobarPanelItemClock*)object;
-
- g_clear_pointer( &self->format, g_free );
- g_clear_object( &self->clock_service );
-
- G_OBJECT_CLASS( foobar_panel_item_clock_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new clock panel item with the given configuration.
-//
-FoobarPanelItem* foobar_panel_item_clock_new(
- FoobarPanelItemConfiguration const* config,
- FoobarClockService* clock_service )
-{
- g_return_val_if_fail( config != NULL, NULL );
- g_return_val_if_fail( foobar_panel_item_configuration_get_kind( config ) == FOOBAR_PANEL_ITEM_KIND_CLOCK, NULL );
- g_return_val_if_fail( FOOBAR_IS_CLOCK_SERVICE( clock_service ), NULL );
-
- FoobarPanelItemClock* self = g_object_new( FOOBAR_TYPE_PANEL_ITEM_CLOCK, NULL );
- self->format = g_strdup( foobar_panel_item_clock_configuration_get_format( config ) );
- self->action = foobar_panel_item_configuration_get_action( config );
- self->clock_service = g_object_ref( clock_service );
-
- gtk_widget_set_sensitive( self->button, self->action != FOOBAR_PANEL_ITEM_ACTION_NONE );
-
- {
- GtkExpression* service_expr = gtk_constant_expression_new( FOOBAR_TYPE_CLOCK_SERVICE, self->clock_service );
- GtkExpression* time_expr = gtk_property_expression_new( FOOBAR_TYPE_CLOCK_SERVICE, service_expr, "time" );
- GtkExpression* label_params[] = { time_expr };
- GtkExpression* label_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( label_params ),
- label_params,
- G_CALLBACK( foobar_panel_item_clock_compute_label ),
- self,
- NULL );
- gtk_expression_bind( label_expr, self->label, "label", NULL );
- }
-
- return FOOBAR_PANEL_ITEM( self );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when the button was clicked.
-//
-void foobar_panel_item_clock_handle_clicked(
- GtkButton* button,
- gpointer userdata )
-{
- (void)button;
- FoobarPanelItemClock* self = (FoobarPanelItemClock*)userdata;
-
- foobar_panel_item_invoke_action( FOOBAR_PANEL_ITEM( self ), self->action );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Value Converters
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Derive the label from a timestamp, depending on the configured time format.
-//
-gchar* foobar_panel_item_clock_compute_label(
- GtkExpression* expression,
- GDateTime* time,
- gpointer userdata )
-{
- (void)expression;
- FoobarPanelItemClock* self = (FoobarPanelItemClock*)userdata;
-
- return g_date_time_format( time, self->format );
-}
diff --git a/src/widgets/panel/panel-item-clock.h b/src/widgets/panel/panel-item-clock.h
deleted file mode 100644
index 5ff7e5b..0000000
--- a/src/widgets/panel/panel-item-clock.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include "widgets/panel/panel-item.h"
-#include "services/clock-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_PANEL_ITEM_CLOCK foobar_panel_item_clock_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarPanelItemClock, foobar_panel_item_clock, FOOBAR, PANEL_ITEM_CLOCK, FoobarPanelItem )
-
-FoobarPanelItem* foobar_panel_item_clock_new( FoobarPanelItemConfiguration const* config,
- FoobarClockService* clock_service );
-
-G_END_DECLS
diff --git a/src/widgets/panel/panel-item-icon.c b/src/widgets/panel/panel-item-icon.c
deleted file mode 100644
index 0a3b4e9..0000000
--- a/src/widgets/panel/panel-item-icon.c
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "widgets/panel/panel-item-icon.h"
-
-//
-// FoobarPanelItemIcon:
-//
-// Simple item implementation displaying an icon.
-//
-
-struct _FoobarPanelItemIcon
-{
- FoobarPanelItem parent_instance;
- GtkWidget* button;
- FoobarPanelItemAction action;
-};
-
-static void foobar_panel_item_icon_class_init ( FoobarPanelItemIconClass* klass );
-static void foobar_panel_item_icon_init ( FoobarPanelItemIcon* self );
-static void foobar_panel_item_icon_handle_clicked( GtkButton* button,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarPanelItemIcon, foobar_panel_item_icon, FOOBAR_TYPE_PANEL_ITEM )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Panel Item Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for icon panel items.
-//
-void foobar_panel_item_icon_class_init( FoobarPanelItemIconClass* klass )
-{
- (void)klass;
-}
-
-//
-// Instance initialization for icon panel items.
-//
-void foobar_panel_item_icon_init( FoobarPanelItemIcon* self )
-{
- self->button = gtk_button_new( );
- gtk_button_set_has_frame( GTK_BUTTON( self->button ), FALSE );
- gtk_widget_set_halign( self->button, GTK_ALIGN_CENTER );
- gtk_widget_set_valign( self->button, GTK_ALIGN_CENTER );
- g_signal_connect(
- self->button,
- "clicked",
- G_CALLBACK( foobar_panel_item_icon_handle_clicked ),
- self );
-
- gtk_widget_add_css_class( GTK_WIDGET( self ), "icon" );
- foobar_panel_item_set_child( FOOBAR_PANEL_ITEM( self ), self->button );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new icon panel item with the given configuration.
-//
-FoobarPanelItem* foobar_panel_item_icon_new( FoobarPanelItemConfiguration const* config )
-{
- g_return_val_if_fail( config != NULL, NULL );
- g_return_val_if_fail( foobar_panel_item_configuration_get_kind( config ) == FOOBAR_PANEL_ITEM_KIND_ICON, NULL );
-
- FoobarPanelItemIcon* self = g_object_new( FOOBAR_TYPE_PANEL_ITEM_ICON, NULL );
- self->action = foobar_panel_item_configuration_get_action( config );
-
- gtk_widget_set_sensitive( self->button, self->action != FOOBAR_PANEL_ITEM_ACTION_NONE );
- gtk_button_set_icon_name(
- GTK_BUTTON( self->button ),
- foobar_panel_item_icon_configuration_get_icon_name( config ) );
-
- return FOOBAR_PANEL_ITEM( self );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when the button was clicked.
-//
-void foobar_panel_item_icon_handle_clicked(
- GtkButton* button,
- gpointer userdata )
-{
- (void)button;
- FoobarPanelItemIcon* self = (FoobarPanelItemIcon*)userdata;
-
- foobar_panel_item_invoke_action( FOOBAR_PANEL_ITEM( self ), self->action );
-}
diff --git a/src/widgets/panel/panel-item-icon.h b/src/widgets/panel/panel-item-icon.h
deleted file mode 100644
index 2b4ac1f..0000000
--- a/src/widgets/panel/panel-item-icon.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "widgets/panel/panel-item.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_PANEL_ITEM_ICON foobar_panel_item_icon_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarPanelItemIcon, foobar_panel_item_icon, FOOBAR, PANEL_ITEM_ICON, FoobarPanelItem )
-
-FoobarPanelItem* foobar_panel_item_icon_new( FoobarPanelItemConfiguration const* config );
-
-G_END_DECLS
diff --git a/src/widgets/panel/panel-item-status.c b/src/widgets/panel/panel-item-status.c
deleted file mode 100644
index f06bb68..0000000
--- a/src/widgets/panel/panel-item-status.c
+++ /dev/null
@@ -1,955 +0,0 @@
-#include "widgets/panel/panel-item-status.h"
-
-//
-// FoobarPanelItemStatus:
-//
-// A panel item showing status indicators for various things such as network state, audio volume, screen brightness,
-// battery level, etc.
-//
-
-struct _FoobarPanelItemStatus
-{
- FoobarPanelItem parent_instance;
- FoobarPanelItemAction action;
- GtkWidget* button;
- GtkWidget* layout;
- FoobarBatteryService* battery_service;
- FoobarBrightnessService* brightness_service;
- FoobarAudioService* audio_service;
- FoobarNetworkService* network_service;
- FoobarBluetoothService* bluetooth_service;
- FoobarNotificationService* notification_service;
- GtkWidget* network_icon;
- GtkWidget* network_label;
- GtkExpression* network_icon_expr;
- FoobarNetwork* network;
- gulong network_handler_id;
- gulong network_strength_handler_id;
-};
-
-enum
-{
- PROP_ORIENTATION = 1,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_panel_item_status_class_init ( FoobarPanelItemStatusClass* klass );
-static void foobar_panel_item_status_init ( FoobarPanelItemStatus* self );
-static void foobar_panel_item_status_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_panel_item_status_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_panel_item_status_finalize ( GObject* object );
-static void foobar_panel_item_status_handle_network_change ( FoobarNetworkAdapterWifi* adapter,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_panel_item_status_handle_network_strength_change( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-static void foobar_panel_item_status_handle_clicked ( GtkButton* button,
- gpointer userdata );
-static gboolean foobar_panel_item_status_handle_brightness_scroll ( GtkEventControllerScroll* controller,
- gdouble dx,
- gdouble dy,
- gpointer userdata );
-static gboolean foobar_panel_item_status_handle_audio_scroll ( GtkEventControllerScroll* controller,
- gdouble dx,
- gdouble dy,
- gpointer userdata );
-static gchar* foobar_panel_item_status_compute_network_icon ( GtkExpression* expression,
- FoobarNetworkAdapter* adapter,
- FoobarNetworkAdapterState state,
- gpointer userdata );
-static gchar* foobar_panel_item_status_compute_bluetooth_icon ( GtkExpression* expression,
- gboolean is_enabled,
- guint connected_device_count,
- gpointer userdata );
-static gchar* foobar_panel_item_status_compute_battery_icon ( GtkExpression* expression,
- FoobarBatteryState const* state,
- gpointer userdata );
-static gchar* foobar_panel_item_status_compute_battery_label ( GtkExpression* expression,
- FoobarBatteryState const* state,
- gpointer userdata );
-static gchar* foobar_panel_item_status_compute_brightness_icon ( GtkExpression* expression,
- gint percentage,
- gpointer userdata );
-static gchar* foobar_panel_item_status_compute_brightness_label ( GtkExpression* expression,
- gint percentage,
- gpointer userdata );
-static gchar* foobar_panel_item_status_compute_audio_icon ( GtkExpression* expression,
- gboolean is_available,
- gint volume,
- gpointer userdata );
-static gchar* foobar_panel_item_status_compute_audio_label ( GtkExpression* expression,
- gboolean is_available,
- gint volume,
- gpointer userdata );
-static gchar* foobar_panel_item_status_compute_notifications_icon ( GtkExpression* expression,
- guint count,
- gpointer userdata );
-static GtkWidget* foobar_panel_item_status_create_item ( FoobarPanelItemStatus* self,
- FoobarStatusItem item,
- gboolean show_labels,
- gboolean enable_scrolling );
-static void foobar_panel_item_status_reevaluate_network ( FoobarPanelItemStatus* self );
-
-G_DEFINE_FINAL_TYPE_WITH_CODE(
- FoobarPanelItemStatus,
- foobar_panel_item_status,
- FOOBAR_TYPE_PANEL_ITEM,
- G_IMPLEMENT_INTERFACE( GTK_TYPE_ORIENTABLE, NULL ) )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Panel Item Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for status panel items.
-//
-void foobar_panel_item_status_class_init( FoobarPanelItemStatusClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_panel_item_status_get_property;
- object_klass->set_property = foobar_panel_item_status_set_property;
- object_klass->finalize = foobar_panel_item_status_finalize;
-
- gpointer orientable_iface = g_type_default_interface_peek( GTK_TYPE_ORIENTABLE );
- props[PROP_ORIENTATION] = g_param_spec_override(
- "orientation",
- g_object_interface_find_property( orientable_iface, "orientation" ) );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for status panel items.
-//
-void foobar_panel_item_status_init( FoobarPanelItemStatus* self )
-{
- self->layout = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
-
- self->button = gtk_button_new( );
- gtk_button_set_has_frame( GTK_BUTTON( self->button ), FALSE );
- gtk_button_set_child( GTK_BUTTON( self->button ), self->layout );
- gtk_widget_set_halign( self->button, GTK_ALIGN_CENTER );
- gtk_widget_set_valign( self->button, GTK_ALIGN_CENTER );
- g_signal_connect(
- self->button,
- "clicked",
- G_CALLBACK( foobar_panel_item_status_handle_clicked ),
- self );
-
- gtk_widget_add_css_class( GTK_WIDGET( self ), "status" );
- foobar_panel_item_set_child( FOOBAR_PANEL_ITEM( self ), self->button );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_panel_item_status_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarPanelItemStatus* self = (FoobarPanelItemStatus*)object;
-
- switch ( prop_id )
- {
- case PROP_ORIENTATION:
- g_value_set_enum( value, gtk_orientable_get_orientation( GTK_ORIENTABLE( self->layout ) ) );
- 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_panel_item_status_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarPanelItemStatus* self = (FoobarPanelItemStatus*)object;
-
- switch ( prop_id )
- {
- case PROP_ORIENTATION:
- gtk_orientable_set_orientation( GTK_ORIENTABLE( self->layout ), g_value_get_enum( value ) );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_ORIENTATION] );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for status panel items.
-//
-void foobar_panel_item_status_finalize( GObject* object )
-{
- FoobarPanelItemStatus* self = (FoobarPanelItemStatus*)object;
-
- FoobarNetworkAdapterWifi* adapter = foobar_network_service_get_wifi( self->network_service );
- g_clear_signal_handler( &self->network_handler_id, adapter );
- g_clear_signal_handler( &self->network_strength_handler_id, self->network );
- g_clear_object( &self->network );
- g_clear_object( &self->battery_service );
- g_clear_object( &self->brightness_service );
- g_clear_object( &self->audio_service );
- g_clear_object( &self->network_service );
- g_clear_object( &self->bluetooth_service );
- g_clear_object( &self->notification_service );
- g_clear_pointer( &self->network_icon_expr, gtk_expression_unref );
-
- G_OBJECT_CLASS( foobar_panel_item_status_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new status panel item with the given configuration.
-//
-FoobarPanelItem* foobar_panel_item_status_new(
- FoobarPanelItemConfiguration const* config,
- FoobarBatteryService* battery_service,
- FoobarBrightnessService* brightness_service,
- FoobarAudioService* audio_service,
- FoobarNetworkService* network_service,
- FoobarBluetoothService* bluetooth_service,
- FoobarNotificationService* notification_service )
-{
- g_return_val_if_fail( config != NULL, NULL );
- g_return_val_if_fail( foobar_panel_item_configuration_get_kind( config ) == FOOBAR_PANEL_ITEM_KIND_STATUS, NULL );
- g_return_val_if_fail( FOOBAR_IS_BATTERY_SERVICE( battery_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_BRIGHTNESS_SERVICE( brightness_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( audio_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_NETWORK_SERVICE( network_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_BLUETOOTH_SERVICE( bluetooth_service ), NULL );
- g_return_val_if_fail( FOOBAR_IS_NOTIFICATION_SERVICE( notification_service ), NULL );
-
- FoobarPanelItemStatus* self = g_object_new( FOOBAR_TYPE_PANEL_ITEM_STATUS, NULL );
- self->action = foobar_panel_item_configuration_get_action( config );
- self->battery_service = g_object_ref( battery_service );
- self->brightness_service = g_object_ref( brightness_service );
- self->audio_service = g_object_ref( audio_service );
- self->network_service = g_object_ref( network_service );
- self->bluetooth_service = g_object_ref( bluetooth_service );
- self->notification_service = g_object_ref( notification_service );
-
- gtk_widget_set_sensitive( self->button, self->action != FOOBAR_PANEL_ITEM_ACTION_NONE );
-
- gtk_box_set_spacing( GTK_BOX( self->layout ), foobar_panel_item_status_configuration_get_spacing( config ) );
- gboolean show_labels = foobar_panel_item_status_configuration_get_show_labels( config );
- gboolean enable_scrolling = foobar_panel_item_status_configuration_get_enable_scrolling( config );
- gsize items_count;
- FoobarStatusItem const* items = foobar_panel_item_status_configuration_get_items( config, &items_count );
- for ( gsize i = 0; i < items_count; ++i )
- {
- GtkWidget* widget = foobar_panel_item_status_create_item( self, items[i], show_labels, enable_scrolling );
- if ( widget )
- {
- gtk_widget_set_halign( widget, GTK_ALIGN_CENTER );
- gtk_box_append( GTK_BOX( self->layout ), widget );
- }
- }
-
- return FOOBAR_PANEL_ITEM( self );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called when the wi-fi adapter's active network changes.
-//
-// This is used to then subscribe to changes to the active network's strength. Unfortunately, we can't just use
-// GtkExpressions here because the active network may be NULL in which case the strength expression would fail. If one
-// expression fails to evaluate, then the closure expression used to derive the network icon/label also does not get
-// updated.
-//
-// In addition, this method also updates the network label to show the active network's name or become invisible.
-//
-void foobar_panel_item_status_handle_network_change(
- FoobarNetworkAdapterWifi* adapter,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)pspec;
- FoobarPanelItemStatus* self = (FoobarPanelItemStatus*)userdata;
-
- FoobarNetwork* network = foobar_network_adapter_wifi_get_active( adapter );
- if ( self->network != network )
- {
- g_clear_signal_handler( &self->network_strength_handler_id, self->network );
- g_clear_object( &self->network );
-
- if ( network )
- {
- self->network = g_object_ref( network );
- self->network_strength_handler_id = g_signal_connect(
- self->network,
- "notify::strength",
- G_CALLBACK( foobar_panel_item_status_handle_network_strength_change ),
- self );
- }
-
- if ( self->network_label )
- {
- gtk_label_set_label(
- GTK_LABEL( self->network_label ), self->network ? foobar_network_get_name( self->network ) : NULL );
- gtk_widget_set_visible( GTK_WIDGET( self->network_label ), self->network != NULL );
- }
-
- foobar_panel_item_status_reevaluate_network( self );
- }
-}
-
-//
-// Called when the wi-fi adapter's active network strength changes.
-//
-// This just forces re-evaluation of the network icon expression for reasons mentioned in
-// foobar_panel_item_status_handle_network_change.
-//
-void foobar_panel_item_status_handle_network_strength_change(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- (void)pspec;
- FoobarPanelItemStatus* self = (FoobarPanelItemStatus*)userdata;
- foobar_panel_item_status_reevaluate_network( self );
-}
-
-//
-// Called when the button was clicked.
-//
-void foobar_panel_item_status_handle_clicked(
- GtkButton* button,
- gpointer userdata )
-{
- (void)button;
- FoobarPanelItemStatus* self = (FoobarPanelItemStatus*)userdata;
-
- foobar_panel_item_invoke_action( FOOBAR_PANEL_ITEM( self ), self->action );
-}
-
-//
-// Called when the user has scrolled over the brightness item, used to adjust the brightness level.
-//
-gboolean foobar_panel_item_status_handle_brightness_scroll(
- GtkEventControllerScroll* controller,
- gdouble dx,
- gdouble dy,
- gpointer userdata )
-{
- (void)controller;
- (void)dx;
- FoobarPanelItemStatus* self = (FoobarPanelItemStatus*)userdata;
-
- gint new_brightness = foobar_brightness_service_get_percentage( self->brightness_service ) + (gint)dy;
- foobar_brightness_service_set_percentage( self->brightness_service, new_brightness );
-
- return TRUE;
-}
-
-//
-// Called when the user has scrolled over the audio item, used to adjust the volume.
-//
-gboolean foobar_panel_item_status_handle_audio_scroll(
- GtkEventControllerScroll* controller,
- gdouble dx,
- gdouble dy,
- gpointer userdata )
-{
- (void)controller;
- (void)dx;
- FoobarPanelItemStatus* self = (FoobarPanelItemStatus*)userdata;
-
- FoobarAudioDevice* device = foobar_audio_service_get_default_output( self->audio_service );
- gint new_volume = foobar_audio_device_get_volume( device ) + (gint)dy;
- foobar_audio_device_set_volume( device, new_volume );
-
- return TRUE;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Value Converters
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Derive the network icon from the active network adapter and its state.
-//
-// In addition, this also uses the wi-fi adapter's active network strength which is monitored separately (see
-// foobar_panel_item_status_handle_network_change).
-//
-gchar* foobar_panel_item_status_compute_network_icon(
- GtkExpression* expression,
- FoobarNetworkAdapter* adapter,
- FoobarNetworkAdapterState state,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- if ( FOOBAR_IS_NETWORK_ADAPTER_WIFI( adapter ) )
- {
- if ( state == FOOBAR_NETWORK_ADAPTER_STATE_DISCONNECTED ) { return g_strdup( "fluent-wifi-off-symbolic" ); }
- if ( state != FOOBAR_NETWORK_ADAPTER_STATE_CONNECTED ) { return g_strdup( "fluent-wifi-warning-symbolic" ); }
-
- FoobarNetwork* network = foobar_network_adapter_wifi_get_active( FOOBAR_NETWORK_ADAPTER_WIFI( adapter ) );
- gint strength = network ? foobar_network_get_strength( network ) : 0;
- if ( strength <= 25 ) { return g_strdup( "fluent-wifi-4-symbolic" ); }
- if ( strength <= 50 ) { return g_strdup( "fluent-wifi-3-symbolic" ); }
- if ( strength <= 75 ) { return g_strdup( "fluent-wifi-2-symbolic" ); }
- return g_strdup( "fluent-wifi-1-symbolic" );
- }
- else
- {
- return g_strdup( "fluent-virtual-network-symbolic" );
- }
-}
-
-//
-// Derive the bluetooth icon from the bluetooth adapter's state and the number of currently connected devices.
-//
-gchar* foobar_panel_item_status_compute_bluetooth_icon(
- GtkExpression* expression,
- gboolean is_enabled,
- guint connected_device_count,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- if (is_enabled)
- {
- return ( connected_device_count > 0 )
- ? g_strdup( "fluent-bluetooth-connected-symbolic" )
- : g_strdup( "fluent-bluetooth-symbolic" );
- }
- else
- {
- return g_strdup( "fluent-bluetooth-off-symbolic" );
- }
-}
-
-//
-// Derive the battery icon from the current state structure.
-//
-gchar* foobar_panel_item_status_compute_battery_icon(
- GtkExpression* expression,
- FoobarBatteryState const* state,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- if ( state )
- {
- gint percentage = foobar_battery_state_get_percentage( state );
- if ( foobar_battery_state_is_charging( state ) ) { return g_strdup( "fluent-battery-charge-symbolic" ); }
- if ( percentage <= 5 ) { return g_strdup( "fluent-battery-0-symbolic" ); }
- if ( percentage <= 15 ) { return g_strdup( "fluent-battery-1-symbolic" ); }
- if ( percentage <= 25 ) { return g_strdup( "fluent-battery-2-symbolic" ); }
- if ( percentage <= 35 ) { return g_strdup( "fluent-battery-3-symbolic" ); }
- if ( percentage <= 45 ) { return g_strdup( "fluent-battery-4-symbolic" ); }
- if ( percentage <= 55 ) { return g_strdup( "fluent-battery-5-symbolic" ); }
- if ( percentage <= 65 ) { return g_strdup( "fluent-battery-6-symbolic" ); }
- if ( percentage <= 75 ) { return g_strdup( "fluent-battery-7-symbolic" ); }
- if ( percentage <= 85 ) { return g_strdup( "fluent-battery-8-symbolic" ); }
- if ( percentage <= 95 ) { return g_strdup( "fluent-battery-9-symbolic" ); }
- return g_strdup( "fluent-battery-10-symbolic" );
- }
- else
- {
- return g_strdup( "fluent-plug-connected-symbolic" );
- }
-}
-
-//
-// Derive the battery label from the current state structure, showing the percentage if available.
-//
-gchar* foobar_panel_item_status_compute_battery_label(
- GtkExpression* expression,
- FoobarBatteryState const* state,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- if ( state )
- {
- return g_strdup_printf( "%d%%", foobar_battery_state_get_percentage( state ) );
- }
- else
- {
- return g_strdup( "n/a" );
- }
-}
-
-//
-// Derive the brightness icon from the current percentage value.
-//
-gchar* foobar_panel_item_status_compute_brightness_icon(
- GtkExpression* expression,
- gint percentage,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- if ( percentage <= 50 )
- {
- return g_strdup( "fluent-brightness-low-symbolic" );
- }
- else
- {
- return g_strdup( "fluent-brightness-high-symbolic" );
- }
-}
-
-//
-// Derive the brightness label from the current percentage value.
-//
-gchar* foobar_panel_item_status_compute_brightness_label(
- GtkExpression* expression,
- gint percentage,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- return g_strdup_printf( "%d%%", percentage );
-}
-
-//
-// Derive the audio icon from the current state and volume of the default output device.
-//
-gchar* foobar_panel_item_status_compute_audio_icon(
- GtkExpression* expression,
- gboolean is_available,
- gint volume,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- if ( is_available )
- {
- if ( volume == 0 ) { return g_strdup( "fluent-speaker-mute-symbolic" ); }
- if ( volume <= 33 ) { return g_strdup( "fluent-speaker-0-symbolic" ); }
- if ( volume <= 67 ) { return g_strdup( "fluent-speaker-1-symbolic" ); }
- return g_strdup( "fluent-speaker-2-symbolic" );
- }
- else
- {
- return g_strdup( "fluent-speaker-off-symbolic" );
- }
-}
-
-//
-// Derive the audio label from the current state and volume of the default output device.
-//
-gchar* foobar_panel_item_status_compute_audio_label(
- GtkExpression* expression,
- gboolean is_available,
- gint volume,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- if ( is_available )
- {
- return g_strdup_printf( "%d%%", volume );
- }
- else
- {
- return g_strdup( "n/a" );
- }
-}
-
-//
-// Derive the notification icon from the current number of notifications.
-//
-gchar* foobar_panel_item_status_compute_notifications_icon(
- GtkExpression* expression,
- guint count,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- switch ( count )
- {
- case 0:
- return g_strdup( "fluent-checkmark-circle-symbolic" );
- case 1:
- return g_strdup( "fluent-number-circle-1-symbolic" );
- case 2:
- return g_strdup( "fluent-number-circle-2-symbolic" );
- case 3:
- return g_strdup( "fluent-number-circle-3-symbolic" );
- case 4:
- return g_strdup( "fluent-number-circle-4-symbolic" );
- case 5:
- return g_strdup( "fluent-number-circle-5-symbolic" );
- case 6:
- return g_strdup( "fluent-number-circle-6-symbolic" );
- case 7:
- return g_strdup( "fluent-number-circle-7-symbolic" );
- case 8:
- return g_strdup( "fluent-number-circle-8-symbolic" );
- case 9:
- return g_strdup( "fluent-number-circle-9-symbolic" );
- default:
- return g_strdup( "fluent-more-circle-symbolic" );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new icon displaying the specified status item, optionally adding a label to it (if supported).
-//
-GtkWidget* foobar_panel_item_status_create_item(
- FoobarPanelItemStatus* self,
- FoobarStatusItem item,
- gboolean show_labels,
- gboolean enable_scrolling )
-{
- switch ( item )
- {
- case FOOBAR_STATUS_ITEM_NETWORK:
- {
- GtkWidget* box = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_widget_set_valign( box, GTK_ALIGN_FILL );
-
- GtkExpression* service_expr = gtk_constant_expression_new(
- FOOBAR_TYPE_NETWORK_SERVICE,
- self->network_service );
- GtkExpression* active_expr = gtk_property_expression_new(
- FOOBAR_TYPE_NETWORK_SERVICE,
- service_expr,
- "active" );
- GtkExpression* state_expr = gtk_property_expression_new(
- FOOBAR_TYPE_NETWORK_ADAPTER,
- gtk_expression_ref( active_expr ),
- "state" );
- GtkExpression* icon_params[] = { active_expr, state_expr };
- self->network_icon_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( icon_params ),
- icon_params,
- G_CALLBACK( foobar_panel_item_status_compute_network_icon ),
- self,
- NULL );
- self->network_icon = gtk_image_new( );
- gtk_widget_set_valign( self->network_icon, GTK_ALIGN_CENTER );
- gtk_expression_bind( gtk_expression_ref( self->network_icon_expr ), self->network_icon, "icon-name", NULL );
- gtk_box_append( GTK_BOX( box ), self->network_icon );
-
- if ( show_labels )
- {
- self->network_label = gtk_label_new( NULL );
- gtk_widget_set_valign( self->network_label, GTK_ALIGN_CENTER );
- gtk_label_set_max_width_chars( GTK_LABEL( self->network_label ), 16 );
- gtk_label_set_ellipsize( GTK_LABEL( self->network_label ), PANGO_ELLIPSIZE_END );
- gtk_box_append( GTK_BOX( box ), self->network_label );
- }
-
- // Manually subscribe to network (/strength) because evaluation might fail.
-
- FoobarNetworkAdapterWifi* adapter = foobar_network_service_get_wifi( self->network_service );
- if ( adapter )
- {
- self->network_handler_id = g_signal_connect(
- adapter,
- "notify::active",
- G_CALLBACK( foobar_panel_item_status_handle_network_change ),
- self );
- if ( foobar_network_adapter_wifi_get_active( adapter ) )
- {
- self->network = g_object_ref( foobar_network_adapter_wifi_get_active( adapter ) );
- self->network_strength_handler_id = g_signal_connect(
- self->network,
- "notify::strength",
- G_CALLBACK( foobar_panel_item_status_handle_network_strength_change ),
- self );
- }
- if ( self->network_label )
- {
- gtk_label_set_label(
- GTK_LABEL( self->network_label ),
- self->network ? foobar_network_get_name( self->network ) : NULL );
- gtk_widget_set_visible( GTK_WIDGET( self->network_label ), self->network != NULL );
- }
- }
-
- return box;
- }
- case FOOBAR_STATUS_ITEM_BLUETOOTH:
- {
- GtkWidget* icon = gtk_image_new( );
- gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
-
- GtkExpression* service_expr = gtk_constant_expression_new(
- FOOBAR_TYPE_BLUETOOTH_SERVICE,
- self->bluetooth_service );
- GtkExpression* enabled_expr = gtk_property_expression_new(
- FOOBAR_TYPE_BLUETOOTH_SERVICE,
- gtk_expression_ref( service_expr ),
- "is-enabled" );
- GtkExpression* connected_devices_expr = gtk_property_expression_new(
- FOOBAR_TYPE_BLUETOOTH_SERVICE,
- service_expr,
- "connected-devices" );
- GtkExpression* connected_device_count_expr = gtk_property_expression_new(
- GTK_TYPE_FILTER_LIST_MODEL,
- connected_devices_expr,
- "n-items" );
- GtkExpression* icon_params[] = { enabled_expr, connected_device_count_expr };
- GtkExpression* icon_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( icon_params ),
- icon_params,
- G_CALLBACK( foobar_panel_item_status_compute_bluetooth_icon ),
- NULL,
- NULL );
- gtk_expression_bind( icon_expr, icon, "icon-name", NULL );
-
- return icon;
- }
- case FOOBAR_STATUS_ITEM_BATTERY:
- {
- GtkWidget* box = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_widget_set_valign( box, GTK_ALIGN_FILL );
-
- GtkExpression* service_expr = gtk_constant_expression_new(
- FOOBAR_TYPE_BATTERY_SERVICE,
- self->battery_service );
- GtkExpression* state_expr = gtk_property_expression_new(
- FOOBAR_TYPE_BATTERY_SERVICE,
- service_expr,
- "state" );
-
- GtkExpression* icon_params[] = { state_expr };
- GtkExpression* icon_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( icon_params ),
- icon_params,
- G_CALLBACK( foobar_panel_item_status_compute_battery_icon ),
- NULL,
- NULL );
- GtkWidget* icon = gtk_image_new( );
- gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
- gtk_expression_bind( icon_expr, icon, "icon-name", NULL );
- gtk_box_append( GTK_BOX( box ), icon );
-
- if ( show_labels )
- {
- GtkExpression* label_params[] = { gtk_expression_ref( state_expr ) };
- GtkExpression* label_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( label_params ),
- label_params,
- G_CALLBACK( foobar_panel_item_status_compute_battery_label ),
- NULL,
- NULL );
- GtkWidget* label = gtk_label_new( NULL );
- gtk_widget_set_valign( label, GTK_ALIGN_CENTER );
- gtk_expression_bind( label_expr, label, "label", NULL );
- gtk_box_append( GTK_BOX( box ), label );
- }
-
- return box;
- }
- case FOOBAR_STATUS_ITEM_BRIGHTNESS:
- {
- GtkWidget* box = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_widget_set_valign( box, GTK_ALIGN_FILL );
-
- if ( enable_scrolling )
- {
- GtkEventController* scroll_controller = gtk_event_controller_scroll_new(
- GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE );
- g_signal_connect(
- scroll_controller,
- "scroll",
- G_CALLBACK( foobar_panel_item_status_handle_brightness_scroll ),
- self );
- gtk_widget_add_controller( box, scroll_controller );
- }
-
- GtkExpression* service_expr = gtk_constant_expression_new(
- FOOBAR_TYPE_BRIGHTNESS_SERVICE,
- self->brightness_service );
- GtkExpression* percentage_expr = gtk_property_expression_new(
- FOOBAR_TYPE_BRIGHTNESS_SERVICE,
- service_expr,
- "percentage" );
-
- GtkExpression* icon_params[] = { percentage_expr };
- GtkExpression* icon_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( icon_params ),
- icon_params,
- G_CALLBACK( foobar_panel_item_status_compute_brightness_icon ),
- NULL,
- NULL );
- GtkWidget* icon = gtk_image_new( );
- gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
- gtk_expression_bind( icon_expr, icon, "icon-name", NULL );
- gtk_box_append( GTK_BOX( box ), icon );
-
- if ( show_labels )
- {
- GtkExpression* label_params[] = { gtk_expression_ref( percentage_expr ) };
- GtkExpression* label_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( label_params ),
- label_params,
- G_CALLBACK( foobar_panel_item_status_compute_brightness_label ),
- NULL,
- NULL );
- GtkWidget* label = gtk_label_new( NULL );
- gtk_widget_set_valign( label, GTK_ALIGN_CENTER );
- gtk_expression_bind( label_expr, label, "label", NULL );
- gtk_box_append( GTK_BOX( box ), label );
- }
-
- return box;
- }
- case FOOBAR_STATUS_ITEM_AUDIO:
- {
- GtkWidget* box = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
- gtk_widget_set_valign( box, GTK_ALIGN_FILL );
-
- if ( enable_scrolling )
- {
- GtkEventController* scroll_controller = gtk_event_controller_scroll_new(
- GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE );
- g_signal_connect(
- scroll_controller,
- "scroll",
- G_CALLBACK( foobar_panel_item_status_handle_audio_scroll ),
- self );
- gtk_widget_add_controller( box, scroll_controller );
- }
-
- GtkExpression* device_expr = gtk_constant_expression_new(
- FOOBAR_TYPE_AUDIO_DEVICE,
- foobar_audio_service_get_default_output( self->audio_service ) );
- GtkExpression* available_expr = gtk_property_expression_new(
- FOOBAR_TYPE_AUDIO_DEVICE,
- gtk_expression_ref( device_expr ),
- "is-available" );
- GtkExpression* volume_expr = gtk_property_expression_new(
- FOOBAR_TYPE_AUDIO_DEVICE,
- gtk_expression_ref( device_expr ),
- "volume" );
-
- GtkExpression* icon_params[] = { available_expr, volume_expr };
- GtkExpression* icon_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( icon_params ),
- icon_params,
- G_CALLBACK( foobar_panel_item_status_compute_audio_icon ),
- NULL,
- NULL );
- GtkWidget* icon = gtk_image_new( );
- gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
- gtk_expression_bind( icon_expr, icon, "icon-name", NULL );
- gtk_box_append( GTK_BOX( box ), icon );
-
- if ( show_labels )
- {
- GtkExpression* label_params[] = { gtk_expression_ref( available_expr ), gtk_expression_ref( volume_expr ) };
- GtkExpression* label_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( label_params ),
- label_params,
- G_CALLBACK( foobar_panel_item_status_compute_audio_label ),
- NULL,
- NULL );
- GtkWidget* label = gtk_label_new( NULL );
- gtk_widget_set_valign( label, GTK_ALIGN_CENTER );
- gtk_expression_bind( label_expr, label, "label", NULL );
- gtk_box_append( GTK_BOX( box ), label );
- }
-
- return box;
- }
- case FOOBAR_STATUS_ITEM_NOTIFICATIONS:
- {
- GtkWidget* icon = gtk_image_new( );
- gtk_widget_set_valign( icon, GTK_ALIGN_CENTER );
-
- GtkExpression* list_expr = gtk_constant_expression_new(
- G_TYPE_LIST_MODEL,
- foobar_notification_service_get_notifications( self->notification_service ) );
- GtkExpression* count_expr = gtk_property_expression_new(
- GTK_TYPE_SORT_LIST_MODEL,
- list_expr,
- "n-items" );
- GtkExpression* icon_params[] = { count_expr };
- GtkExpression* icon_expr = gtk_cclosure_expression_new(
- G_TYPE_STRING,
- NULL,
- G_N_ELEMENTS( icon_params ),
- icon_params,
- G_CALLBACK( foobar_panel_item_status_compute_notifications_icon ),
- NULL,
- NULL );
- gtk_expression_bind( icon_expr, icon, "icon-name", NULL );
-
- return icon;
- }
- default:
- g_warn_if_reached( );
- return NULL;
- }
-}
-
-//
-// Force re-evaluation of the network icon expression.
-//
-// This is used whenever the active network or its strength changes.
-//
-void foobar_panel_item_status_reevaluate_network( FoobarPanelItemStatus* self )
-{
- if ( self->network_icon && self->network_icon_expr )
- {
- GValue value = G_VALUE_INIT;
- if ( gtk_expression_evaluate( self->network_icon_expr, NULL, &value ) )
- {
- gtk_image_set_from_icon_name( GTK_IMAGE( self->network_icon ), g_value_get_string( &value ) );
- }
- g_value_unset( &value );
- }
-}
diff --git a/src/widgets/panel/panel-item-status.h b/src/widgets/panel/panel-item-status.h
deleted file mode 100644
index 0f1cfdb..0000000
--- a/src/widgets/panel/panel-item-status.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "widgets/panel/panel-item.h"
-#include "services/battery-service.h"
-#include "services/brightness-service.h"
-#include "services/audio-service.h"
-#include "services/network-service.h"
-#include "services/bluetooth-service.h"
-#include "services/notification-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_PANEL_ITEM_STATUS foobar_panel_item_status_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarPanelItemStatus, foobar_panel_item_status, FOOBAR, PANEL_ITEM_STATUS, FoobarPanelItem )
-
-FoobarPanelItem* foobar_panel_item_status_new( FoobarPanelItemConfiguration const* config,
- FoobarBatteryService* battery_service,
- FoobarBrightnessService* brightness_service,
- FoobarAudioService* audio_service,
- FoobarNetworkService* network_service,
- FoobarBluetoothService* bluetooth_service,
- FoobarNotificationService* notification_service );
-
-G_END_DECLS
diff --git a/src/widgets/panel/panel-item-workspaces.c b/src/widgets/panel/panel-item-workspaces.c
deleted file mode 100644
index dff629b..0000000
--- a/src/widgets/panel/panel-item-workspaces.c
+++ /dev/null
@@ -1,327 +0,0 @@
-#include "widgets/panel/panel-item-workspaces.h"
-#include "widgets/inset-container.h"
-
-//
-// FoobarPanelItemWorkspaces:
-//
-// A panel item showing all workspaces for the current monitor (if multi-monitor mode is enabled) as buttons, indicating
-// the current state of the workspace (i.e., whether it's active, visible, special, etc.).
-//
-
-struct _FoobarPanelItemWorkspaces
-{
- FoobarPanelItem parent_instance;
- GtkWidget* list_view;
- GtkWidget* inset_container;
- FoobarWorkspaceService* workspace_service;
- gint button_size;
- gint spacing;
- GtkFilter* monitor_filter;
- gulong monitor_configuration_handler_id;
-};
-
-enum
-{
- PROP_ORIENTATION = 1,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_panel_item_workspaces_class_init ( FoobarPanelItemWorkspacesClass* klass );
-static void foobar_panel_item_workspaces_init ( FoobarPanelItemWorkspaces* self );
-static void foobar_panel_item_workspaces_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_panel_item_workspaces_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_panel_item_workspaces_finalize ( GObject* object );
-static void foobar_panel_item_workspaces_handle_item_setup ( GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata );
-static void foobar_panel_item_workspaces_handle_item_clicked ( GtkButton* button,
- gpointer userdata );
-static void foobar_panel_item_workspaces_handle_monitor_configuration_changed( FoobarWorkspaceService* service,
- gpointer userdata );
-static gchar** foobar_panel_item_workspaces_compute_css_classes ( GtkExpression* expression,
- FoobarWorkspaceFlags flags,
- gpointer userdata );
-static gboolean foobar_panel_item_workspaces_monitor_filter_func ( gpointer item,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE_WITH_CODE(
- FoobarPanelItemWorkspaces,
- foobar_panel_item_workspaces,
- FOOBAR_TYPE_PANEL_ITEM,
- G_IMPLEMENT_INTERFACE( GTK_TYPE_ORIENTABLE, NULL ) )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Panel Item Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for workspace panel items.
-//
-void foobar_panel_item_workspaces_class_init( FoobarPanelItemWorkspacesClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_panel_item_workspaces_get_property;
- object_klass->set_property = foobar_panel_item_workspaces_set_property;
- object_klass->finalize = foobar_panel_item_workspaces_finalize;
-
- gpointer orientable_iface = g_type_default_interface_peek( GTK_TYPE_ORIENTABLE );
- props[PROP_ORIENTATION] = g_param_spec_override(
- "orientation",
- g_object_interface_find_property( orientable_iface, "orientation" ) );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for workspace panel items.
-//
-void foobar_panel_item_workspaces_init( FoobarPanelItemWorkspaces* self )
-{
- GtkListItemFactory* item_factory = gtk_signal_list_item_factory_new( );
- g_signal_connect( item_factory, "setup", G_CALLBACK( foobar_panel_item_workspaces_handle_item_setup ), self );
-
- self->list_view = gtk_list_view_new( NULL, item_factory );
- gtk_widget_set_halign( self->list_view, GTK_ALIGN_CENTER );
- gtk_widget_set_valign( self->list_view, GTK_ALIGN_CENTER );
- gtk_list_view_set_single_click_activate( GTK_LIST_VIEW( self->list_view ), TRUE );
-
- self->inset_container = foobar_inset_container_new( );
- foobar_inset_container_set_child( FOOBAR_INSET_CONTAINER( self->inset_container ), GTK_WIDGET( self->list_view ) );
-
- gtk_widget_add_css_class( GTK_WIDGET( self ), "workspaces" );
- foobar_panel_item_set_child( FOOBAR_PANEL_ITEM( self ), GTK_WIDGET( self->inset_container ) );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_panel_item_workspaces_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarPanelItemWorkspaces* self = (FoobarPanelItemWorkspaces*)object;
-
- switch ( prop_id )
- {
- case PROP_ORIENTATION:
- g_value_set_enum( value, gtk_orientable_get_orientation( GTK_ORIENTABLE( self->list_view ) ) );
- 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_panel_item_workspaces_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarPanelItemWorkspaces* self = (FoobarPanelItemWorkspaces*)object;
-
- switch ( prop_id )
- {
- case PROP_ORIENTATION:
- gtk_orientable_set_orientation( GTK_ORIENTABLE( self->list_view ), g_value_get_enum( value ) );
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_ORIENTATION] );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for workspace panel items.
-//
-void foobar_panel_item_workspaces_finalize( GObject* object )
-{
- FoobarPanelItemWorkspaces* self = (FoobarPanelItemWorkspaces*)object;
-
- g_clear_signal_handler( &self->monitor_configuration_handler_id, self->workspace_service );
- g_clear_object( &self->workspace_service );
- g_clear_object( &self->monitor_filter );
-
- G_OBJECT_CLASS( foobar_panel_item_workspaces_parent_class )->finalize( object );
-}
-
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new workspaces panel item with the given configuration.
-//
-// If monitor is not NULL, only workspaces on this monitor will be shown.
-//
-FoobarPanelItem* foobar_panel_item_workspaces_new(
- FoobarPanelItemConfiguration const* config,
- GdkMonitor* monitor,
- FoobarWorkspaceService* workspace_service )
-{
- g_return_val_if_fail( config != NULL, NULL );
- g_return_val_if_fail( foobar_panel_item_configuration_get_kind( config ) == FOOBAR_PANEL_ITEM_KIND_WORKSPACES, NULL );
- g_return_val_if_fail( monitor == NULL || GDK_IS_MONITOR( monitor ), NULL );
- g_return_val_if_fail( FOOBAR_IS_WORKSPACE_SERVICE( workspace_service ), NULL );
-
- FoobarPanelItemWorkspaces* self = g_object_new( FOOBAR_TYPE_PANEL_ITEM_WORKSPACES, NULL );
- self->button_size = foobar_panel_item_workspaces_configuration_get_button_size( config );
- self->spacing = foobar_panel_item_workspaces_configuration_get_spacing( config );
- self->workspace_service = g_object_ref( workspace_service );
-
- // Set up the source model, optionally enabling filtering.
-
- GListModel* source_model = g_object_ref( foobar_workspace_service_get_workspaces( self->workspace_service ) );
- if ( monitor )
- {
- GtkCustomFilter* filter = gtk_custom_filter_new(
- foobar_panel_item_workspaces_monitor_filter_func,
- g_object_ref( monitor ),
- g_object_unref );
- self->monitor_filter = GTK_FILTER( filter );
- source_model = G_LIST_MODEL( gtk_filter_list_model_new( source_model, g_object_ref( self->monitor_filter ) ) );
-
- self->monitor_configuration_handler_id = g_signal_connect(
- self->workspace_service,
- "monitor-configuration-changed",
- G_CALLBACK( foobar_panel_item_workspaces_handle_monitor_configuration_changed ),
- self );
- }
- GtkNoSelection* selection_model = gtk_no_selection_new( source_model );
- gtk_list_view_set_model( GTK_LIST_VIEW( self->list_view ), GTK_SELECTION_MODEL( selection_model ) );
-
- // Wrap the list in an inset container to remove the outer margin from the items.
-
- foobar_inset_container_set_inset_horizontal( FOOBAR_INSET_CONTAINER( self->inset_container ), -self->spacing / 2 );
- foobar_inset_container_set_inset_vertical( FOOBAR_INSET_CONTAINER( self->inset_container ), -self->spacing / 2 );
-
- return FOOBAR_PANEL_ITEM( self );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called by the list view to create a widget for displaying a workspace.
-//
-void foobar_panel_item_workspaces_handle_item_setup(
- GtkListItemFactory* factory,
- GtkListItem* list_item,
- gpointer userdata )
-{
- (void)factory;
- FoobarPanelItemWorkspaces* self = (FoobarPanelItemWorkspaces*)userdata;
-
- GtkWidget* button = gtk_button_new( );
- gtk_widget_set_size_request( button, self->button_size, self->button_size );
- gtk_widget_set_margin_top( button, self->spacing / 2 );
- gtk_widget_set_margin_bottom( button, self->spacing / 2 );
- gtk_widget_set_margin_start( button, self->spacing / 2 );
- gtk_widget_set_margin_end( button, self->spacing / 2 );
- g_signal_connect(
- button,
- "clicked",
- G_CALLBACK( foobar_panel_item_workspaces_handle_item_clicked ),
- list_item );
-
- gtk_list_item_set_child( list_item, button );
-
- {
- GtkExpression* item_expr = gtk_property_expression_new( GTK_TYPE_LIST_ITEM, NULL, "item" );
- GtkExpression* active_expr = gtk_property_expression_new( FOOBAR_TYPE_WORKSPACE, item_expr, "flags" );
- GtkExpression* params[] = { active_expr };
- GtkExpression* css_expr = gtk_cclosure_expression_new(
- G_TYPE_STRV,
- NULL,
- G_N_ELEMENTS( params ),
- params,
- G_CALLBACK( foobar_panel_item_workspaces_compute_css_classes ),
- NULL,
- NULL );
- gtk_expression_bind( css_expr, button, "css-classes", list_item );
- }
-}
-
-//
-// Called by the workspace service when the monitor for any workspace has changed.
-//
-// This is needed because the GtkFilterListModel does not track changes to the item properties and needs to be
-// re-evaluated manually.
-//
-void foobar_panel_item_workspaces_handle_monitor_configuration_changed(
- FoobarWorkspaceService* service,
- gpointer userdata )
-{
- (void)service;
- FoobarPanelItemWorkspaces* self = (FoobarPanelItemWorkspaces*)userdata;
-
- gtk_filter_changed( self->monitor_filter, GTK_FILTER_CHANGE_DIFFERENT );
-}
-
-//
-// Called when a workspace button was clicked, activating that workspace.
-//
-void foobar_panel_item_workspaces_handle_item_clicked( GtkButton* button, gpointer userdata )
-{
- (void)button;
- GtkListItem* item = (GtkListItem*)userdata;
-
- FoobarWorkspace* workspace = gtk_list_item_get_item( item );
- if ( workspace ) { foobar_workspace_activate( workspace ); }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Value Converters
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Derive the list of CSS classes for a workspace button from the workspace's current flags.
-//
-gchar** foobar_panel_item_workspaces_compute_css_classes(
- GtkExpression* expression,
- FoobarWorkspaceFlags flags,
- gpointer userdata )
-{
- (void)expression;
- (void)userdata;
-
- GStrvBuilder* builder = g_strv_builder_new( );
- if ( flags & FOOBAR_WORKSPACE_FLAGS_ACTIVE ) { g_strv_builder_add( builder, "active" ); }
- if ( flags & FOOBAR_WORKSPACE_FLAGS_VISIBLE ) { g_strv_builder_add( builder, "visible" ); }
- if ( flags & FOOBAR_WORKSPACE_FLAGS_SPECIAL ) { g_strv_builder_add( builder, "special" ); }
- if ( flags & FOOBAR_WORKSPACE_FLAGS_URGENT ) { g_strv_builder_add( builder, "urgent" ); }
- if ( flags & FOOBAR_WORKSPACE_FLAGS_PERSISTENT ) { g_strv_builder_add( builder, "persistent" ); }
- return g_strv_builder_end( builder );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Check if a workspace is on the panel's monitor.
-//
-gboolean foobar_panel_item_workspaces_monitor_filter_func( gpointer item, gpointer userdata )
-{
- FoobarWorkspace* workspace = (FoobarWorkspace*)item;
- GdkMonitor* monitor = (GdkMonitor*)userdata;
-
- gchar const* expected = gdk_monitor_get_connector( monitor );
- gchar const* actual = foobar_workspace_get_monitor( workspace );
- return !expected || !g_strcmp0( expected, actual );
-}
diff --git a/src/widgets/panel/panel-item-workspaces.h b/src/widgets/panel/panel-item-workspaces.h
deleted file mode 100644
index d179217..0000000
--- a/src/widgets/panel/panel-item-workspaces.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include "widgets/panel/panel-item.h"
-#include "services/workspace-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_PANEL_ITEM_WORKSPACES foobar_panel_item_workspaces_get_type( )
-
-G_DECLARE_FINAL_TYPE( FoobarPanelItemWorkspaces, foobar_panel_item_workspaces, FOOBAR, PANEL_ITEM_WORKSPACES, FoobarPanelItem )
-
-FoobarPanelItem* foobar_panel_item_workspaces_new( FoobarPanelItemConfiguration const* config,
- GdkMonitor* monitor,
- FoobarWorkspaceService* workspace_service );
-
-G_END_DECLS
diff --git a/src/widgets/panel/panel-item.c b/src/widgets/panel/panel-item.c
deleted file mode 100644
index 918d9f5..0000000
--- a/src/widgets/panel/panel-item.c
+++ /dev/null
@@ -1,195 +0,0 @@
-#include "widgets/panel/panel-item.h"
-#include "application.h"
-
-//
-// FoobarPanelItem:
-//
-// Base class for all panel items. This is basically a bin layout where the subclass usually sets the child widget.
-//
-
-typedef struct _FoobarPanelItemPrivate
-{
- GtkWidget* child;
-} FoobarPanelItemPrivate;
-
-enum
-{
- PROP_CHILD = 1,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_panel_item_class_init ( FoobarPanelItemClass* klass );
-static void foobar_panel_item_init ( FoobarPanelItem* self );
-static void foobar_panel_item_get_property( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_panel_item_set_property( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_panel_item_dispose ( GObject* object );
-
-G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE( FoobarPanelItem, foobar_panel_item, GTK_TYPE_WIDGET )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Widget Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for panel items.
-//
-void foobar_panel_item_class_init( FoobarPanelItemClass* klass )
-{
- GtkWidgetClass* widget_klass = GTK_WIDGET_CLASS( klass );
- gtk_widget_class_set_css_name( widget_klass, "panel-item" );
- gtk_widget_class_set_layout_manager_type( widget_klass, GTK_TYPE_BIN_LAYOUT );
-
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_panel_item_get_property;
- object_klass->set_property = foobar_panel_item_set_property;
- object_klass->dispose = foobar_panel_item_dispose;
-
- props[PROP_CHILD] = g_param_spec_object(
- "child",
- "Child",
- "Child widget for the panel item.",
- GTK_TYPE_WIDGET,
- G_PARAM_READWRITE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for panel items.
-//
-void foobar_panel_item_init( FoobarPanelItem* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_panel_item_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarPanelItem* self = (FoobarPanelItem*)object;
-
- switch ( prop_id )
- {
- case PROP_CHILD:
- g_value_set_object( value, foobar_panel_item_get_child( 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_panel_item_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarPanelItem* self = (FoobarPanelItem*)object;
-
- switch ( prop_id )
- {
- case PROP_CHILD:
- foobar_panel_item_set_child( self, g_value_get_object( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance de-initialization for panel items.
-//
-void foobar_panel_item_dispose( GObject* object )
-{
- FoobarPanelItem* self = (FoobarPanelItem*)object;
- FoobarPanelItemPrivate* priv = foobar_panel_item_get_instance_private( self );
-
- g_clear_pointer( &priv->child, gtk_widget_unparent );
-
- G_OBJECT_CLASS( foobar_panel_item_parent_class )->dispose( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Get the child widget for the panel item.
-//
-GtkWidget* foobar_panel_item_get_child( FoobarPanelItem* self )
-{
- g_return_val_if_fail( FOOBAR_IS_PANEL_ITEM( self ), NULL );
- FoobarPanelItemPrivate* priv = foobar_panel_item_get_instance_private( self );
-
- return priv->child;
-}
-
-//
-// Update the child widget for the panel item.
-//
-void foobar_panel_item_set_child(
- FoobarPanelItem* self,
- GtkWidget* child )
-{
- g_return_if_fail( FOOBAR_IS_PANEL_ITEM( self ) );
- FoobarPanelItemPrivate* priv = foobar_panel_item_get_instance_private( self );
- g_return_if_fail( child == NULL || priv->child == child || gtk_widget_get_parent( child ) == NULL );
-
- if ( priv->child != child )
- {
- g_clear_pointer( &priv->child, gtk_widget_unparent );
-
- if ( child )
- {
- priv->child = child;
- gtk_widget_set_parent( child, GTK_WIDGET( self ) );
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), props[PROP_CHILD] );
- }
-}
-
-//
-// Invoke the action for the panel item.
-//
-// This needs to be triggered by a subclass, for example by adding a button to the item. Support for panel item actions
-// is opt-in and subclasses have to explicitly implement it. This is just a helper for implementing the action.
-//
-void foobar_panel_item_invoke_action(
- FoobarPanelItem* self,
- FoobarPanelItemAction action )
-{
- g_return_if_fail( FOOBAR_IS_PANEL_ITEM( self ) );
-
- switch ( action )
- {
- case FOOBAR_PANEL_ITEM_ACTION_NONE:
- break;
- case FOOBAR_PANEL_ITEM_ACTION_LAUNCHER:
- foobar_application_toggle_launcher( FOOBAR_APPLICATION_DEFAULT );
- break;
- case FOOBAR_PANEL_ITEM_ACTION_CONTROL_CENTER:
- foobar_application_toggle_control_center( FOOBAR_APPLICATION_DEFAULT );
- break;
- default:
- g_warn_if_reached( );
- break;
- }
-}
diff --git a/src/widgets/panel/panel-item.h b/src/widgets/panel/panel-item.h
deleted file mode 100644
index 12a7e35..0000000
--- a/src/widgets/panel/panel-item.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include <gtk/gtk.h>
-#include "services/configuration-service.h"
-
-G_BEGIN_DECLS
-
-#define FOOBAR_TYPE_PANEL_ITEM foobar_panel_item_get_type( )
-
-G_DECLARE_DERIVABLE_TYPE( FoobarPanelItem, foobar_panel_item, FOOBAR, PANEL_ITEM, GtkWidget )
-
-struct _FoobarPanelItemClass
-{
- GtkWidgetClass parent_class;
-};
-
-GtkWidget* foobar_panel_item_get_child ( FoobarPanelItem* self );
-void foobar_panel_item_set_child ( FoobarPanelItem* self,
- GtkWidget* child );
-void foobar_panel_item_invoke_action( FoobarPanelItem* self,
- FoobarPanelItemAction action );
-
-G_END_DECLS