diff --git a/meson.build b/meson.build index 021fd92f..a7605801 100644 --- a/meson.build +++ b/meson.build @@ -93,6 +93,9 @@ else config_h.set_quoted('DONATE_LINK', 'https://ko-fi.com/kolunmi') config_h.set_quoted('RELEASE_PAGE', run_command('version.sh', 'get-gh-release').stdout().strip()) + refresh_worker_bin_name = 'bazaar-refresh-worker' + config_h.set_quoted('REFRESH_WORKER_BIN_NAME', refresh_worker_bin_name) + dl_worker_bin_name = 'bazaar-dl-worker' config_h.set_quoted('DL_WORKER_BIN_NAME', dl_worker_bin_name) diff --git a/src/bz-application.c b/src/bz-application.c index a6039574..0c6becfa 100644 --- a/src/bz-application.c +++ b/src/bz-application.c @@ -184,6 +184,9 @@ BZ_DEFINE_DATA ( static DexFuture * init_fiber (GWeakRef *wr); +static DexFuture * +enumerate_disk_entries_fiber (GWeakRef *wr); + static DexFuture * cache_flathub_fiber (GWeakRef *wr); @@ -217,8 +220,8 @@ cache_write_back_finally (DexFuture *future, CacheWriteBackData *data); static DexFuture * -sync_then (DexFuture *future, - GWeakRef *wr); +sync_finally (DexFuture *future, + GWeakRef *wr); static DexFuture * watch_backend_notifs_then_loop_cb (DexFuture *future, @@ -352,6 +355,9 @@ make_sync_future (BzApplication *self); static void finish_with_background_task_label (BzApplication *self); +static void +state_info_set_icon_themes (BzStateInfo *state); + static void bz_application_dispose (GObject *object) { @@ -914,7 +920,23 @@ bz_state_info_get_default (void) app = g_application_get_default (); if G_UNLIKELY (app == NULL) - return NULL; + { + static BzStateInfo *fallback_state = NULL; + + if (g_once_init_enter_pointer (&fallback_state)) + { + g_autoptr (BzStateInfo) tmp = NULL; + + tmp = bz_state_info_new (); + state_info_set_icon_themes (tmp); + + g_once_init_leave_pointer ( + &fallback_state, + g_steal_pointer (&tmp)); + } + + return fallback_state; + } self = (BzApplication *) app; g_assert (BZ_IS_APPLICATION (self)); @@ -930,9 +952,8 @@ init_fiber (GWeakRef *wr) g_autofree char *root_cache_dir = NULL; g_autoptr (GFile) root_cache_dir_file = NULL; g_autoptr (GListModel) repos = NULL; - gboolean has_flathub = FALSE; - gboolean result = FALSE; - g_autoptr (GHashTable) cached_set = NULL; + gboolean has_flathub = FALSE; + gboolean result = FALSE; g_autofree char *flathub_cache = NULL; g_autoptr (GFile) flathub_cache_file = NULL; @@ -1103,74 +1124,14 @@ init_fiber (GWeakRef *wr) } /* Revive old cache from previous Bazaar process */ - cached_set = dex_await_boxed ( - bz_entry_cache_manager_enumerate_disk (self->cache), - &local_error); - if (cached_set != NULL) - { - g_autoptr (GPtrArray) futures = NULL; - GHashTableIter iter = { 0 }; - g_autoptr (GPtrArray) entries = NULL; - - futures = g_ptr_array_new_with_free_func (dex_unref); - - g_hash_table_iter_init (&iter, cached_set); - for (;;) - { - char *checksum = NULL; - - if (!g_hash_table_iter_next ( - &iter, (gpointer *) &checksum, NULL)) - break; - - g_ptr_array_add ( - futures, - bz_entry_cache_manager_get_by_checksum ( - self->cache, checksum)); - } - g_clear_pointer (&cached_set, g_hash_table_unref); - - if (futures->len > 0) - dex_await (dex_future_allv ( - (DexFuture *const *) futures->pdata, - futures->len), - NULL); - - entries = g_ptr_array_new_with_free_func (g_object_unref); - for (guint i = 0; i < futures->len; i++) - { - DexFuture *future = NULL; - const GValue *value = NULL; - - future = g_ptr_array_index (futures, i); - value = dex_future_get_value (future, &local_error); - if (value != NULL) - g_ptr_array_add (entries, g_value_dup_object (value)); - else - { - g_warning ("Unable to retrieve cached entry: %s", local_error->message); - g_clear_error (&local_error); - } - } - - g_ptr_array_sort_values_with_data ( - entries, (GCompareDataFunc) cmp_entry, NULL); - for (guint i = 0; i < entries->len; i++) - { - BzEntry *entry = NULL; - - entry = g_ptr_array_index (entries, i); - fiber_replace_entry (self, entry); - } - - gtk_filter_changed (GTK_FILTER (self->group_filter), GTK_FILTER_CHANGE_LESS_STRICT); - gtk_filter_changed (GTK_FILTER (self->appid_filter), GTK_FILTER_CHANGE_LESS_STRICT); - } - else - { - g_warning ("Unable to enumerate cached entries: %s", local_error->message); - g_clear_error (&local_error); - } + dex_await ( + dex_scheduler_spawn ( + dex_scheduler_get_default (), + bz_get_dex_stack_size (), + (DexFiberFunc) enumerate_disk_entries_fiber, + bz_track_weak (self), + bz_weak_release), + NULL); flathub_cache_file = fiber_dup_flathub_cache_file (&flathub_cache, &local_error); if (flathub_cache_file != NULL) @@ -1231,6 +1192,88 @@ init_fiber (GWeakRef *wr) return dex_future_new_true (); } +static DexFuture * +enumerate_disk_entries_fiber (GWeakRef *wr) +{ + g_autoptr (BzApplication) self = NULL; + g_autoptr (GError) local_error = NULL; + g_autoptr (GHashTable) cached_set = NULL; + g_autoptr (GPtrArray) futures = NULL; + GHashTableIter iter = { 0 }; + g_autoptr (GPtrArray) entries = NULL; + + bz_weak_get_or_return_reject (self, wr); + + cached_set = dex_await_boxed ( + bz_entry_cache_manager_enumerate_disk (self->cache), + &local_error); + if (cached_set == NULL) + { + g_warning ("Unable to enumerate cached entries: %s", local_error->message); + return dex_future_new_for_error (g_steal_pointer (&local_error)); + } + + futures = g_ptr_array_new_with_free_func (dex_unref); + + g_hash_table_iter_init (&iter, cached_set); + for (;;) + { + char *checksum = NULL; + + if (!g_hash_table_iter_next ( + &iter, (gpointer *) &checksum, NULL)) + break; + + g_ptr_array_add ( + futures, + bz_entry_cache_manager_get_by_checksum ( + self->cache, checksum)); + } + g_clear_pointer (&cached_set, g_hash_table_unref); + + if (futures->len > 0) + dex_await (dex_future_allv ( + (DexFuture *const *) futures->pdata, + futures->len), + NULL); + + entries = g_ptr_array_new_with_free_func (g_object_unref); + for (guint i = 0; i < futures->len; i++) + { + DexFuture *future = NULL; + const GValue *value = NULL; + + future = g_ptr_array_index (futures, i); + value = dex_future_get_value (future, &local_error); + if (value != NULL) + g_ptr_array_add (entries, g_value_dup_object (value)); + else + { + g_warning ("Unable to retrieve cached entry: %s", local_error->message); + g_clear_error (&local_error); + } + } + + g_ptr_array_sort_values_with_data ( + entries, (GCompareDataFunc) cmp_entry, NULL); + for (guint i = 0; i < entries->len; i++) + { + BzEntry *entry = NULL; + + entry = g_ptr_array_index (entries, i); + fiber_replace_entry (self, entry); + } + + gtk_filter_changed (GTK_FILTER (self->group_filter), GTK_FILTER_CHANGE_LESS_STRICT); + gtk_filter_changed (GTK_FILTER (self->appid_filter), GTK_FILTER_CHANGE_LESS_STRICT); + + bz_state_info_set_background_task_label (self->state, _ ("Checking for updates…")); + fiber_check_for_updates (self); + finish_with_background_task_label (self); + + return dex_future_new_true (); +} + static DexFuture * cache_flathub_fiber (GWeakRef *wr) { @@ -1832,11 +1875,15 @@ backend_sync_finally (DexFuture *future, bz_weak_get_or_return_reject (self, wr); - bz_state_info_set_online (self->state, dex_future_is_resolved (future)); - bz_state_info_set_syncing (self->state, FALSE); - bz_state_info_set_allow_manual_sync (self->state, TRUE); - - return dex_future_new_true (); + if (dex_future_is_resolved (future)) + return dex_scheduler_spawn ( + dex_scheduler_get_default (), + bz_get_dex_stack_size (), + (DexFiberFunc) enumerate_disk_entries_fiber, + bz_track_weak (self), + bz_weak_release); + else + return dex_ref (future); } static DexFuture * @@ -1896,14 +1943,21 @@ cache_write_back_finally (DexFuture *future, } static DexFuture * -sync_then (DexFuture *future, - GWeakRef *wr) +sync_finally (DexFuture *future, + GWeakRef *wr) { g_autoptr (BzApplication) self = NULL; bz_weak_get_or_return_reject (self, wr); + bz_state_info_set_online (self->state, dex_future_is_resolved (future)); + bz_state_info_set_allow_manual_sync (self->state, TRUE); + bz_state_info_set_busy (self->state, FALSE); + bz_state_info_set_syncing (self->state, FALSE); + finish_with_background_task_label (self); + dex_promise_resolve_boolean (self->ready_to_open_files, TRUE); + return dex_future_new_true (); } @@ -2028,9 +2082,9 @@ fiber_replace_entry (BzApplication *self, if (bz_entry_is_of_kinds (entry, BZ_ENTRY_KIND_APPLICATION)) { - gboolean ignore_eol = FALSE; - const char *runtime_name = NULL; - BzEntry *eol_runtime = NULL; + gboolean ignore_eol = FALSE; + const char *runtime_name = NULL; + g_autoptr (BzEntry) eol_runtime = NULL; BzEntryGroup *group = NULL; GHashTable *ref_to_addon_group_ids = NULL; GPtrArray *pending = NULL; @@ -2039,8 +2093,19 @@ fiber_replace_entry (BzApplication *self, ignore_eol = g_hash_table_contains (self->ignore_eol_set, id); runtime_name = bz_flatpak_entry_get_application_runtime (BZ_FLATPAK_ENTRY (entry)); - if (!ignore_eol && runtime_name != NULL) - eol_runtime = g_hash_table_lookup (self->eol_runtimes, runtime_name); + if (!ignore_eol && + runtime_name != NULL) + { + char *runtime_checksum = NULL; + + runtime_checksum = g_hash_table_lookup (self->eol_runtimes, runtime_name); + if (runtime_checksum != NULL) + eol_runtime = dex_await_object ( + bz_entry_cache_manager_get_by_checksum ( + self->cache, + runtime_checksum), + NULL); + } group = ensure_group_and_add (self, id, entry, eol_runtime, ignore_eol, installed); @@ -2071,7 +2136,7 @@ fiber_replace_entry (BzApplication *self, g_hash_table_replace ( self->eol_runtimes, g_strdup (stripped), - g_object_ref (entry)); + g_strdup (unique_id_checksum)); else g_hash_table_remove (self->eol_runtimes, stripped); } @@ -2876,20 +2941,7 @@ init_service_struct (BzApplication *self, bz_state_info_set_donation_prompt_dismissed (self->state, TRUE); bz_state_info_set_parental_age_rating (self->state, -1); - { - g_autoptr (GtkIconTheme) user_theme = NULL; - g_autoptr (GtkIconTheme) system_theme = NULL; - g_autofree char *user_export_dir = NULL; - - user_theme = gtk_icon_theme_new (); - user_export_dir = g_build_filename (g_get_home_dir (), ".local/share/flatpak/exports/share/icons", NULL); - gtk_icon_theme_add_search_path (user_theme, user_export_dir); - bz_state_info_set_user_icon_theme (self->state, user_theme); - - system_theme = gtk_icon_theme_new (); - gtk_icon_theme_add_search_path (system_theme, "/var/lib/flatpak/exports/share/icons"); - bz_state_info_set_system_icon_theme (self->state, system_theme); - } + state_info_set_icon_themes (self->state); { g_autoptr (GError) bus_error = NULL; @@ -3013,7 +3065,7 @@ init_service_struct (BzApplication *self, self->ids_to_groups = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, g_object_unref); self->eol_runtimes = g_hash_table_new_full ( - g_str_hash, g_str_equal, g_free, g_object_unref); + g_str_hash, g_str_equal, g_free, g_free); self->sys_name_to_addons = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref); self->usr_name_to_addons = g_hash_table_new_full ( @@ -3441,14 +3493,28 @@ validate_group_for_ui (BzApplication *self, static DexFuture * make_sync_future (BzApplication *self) { - g_autoptr (DexFuture) backend_future = NULL; - g_autoptr (DexFuture) flathub_future = NULL; - g_autoptr (DexFuture) ret_future = NULL; + g_autoptr (GError) local_error = NULL; + g_autoptr (GSubprocess) refresh_worker = NULL; + g_autoptr (DexFuture) backend_future = NULL; + g_autoptr (DexFuture) flathub_future = NULL; + g_autoptr (DexFuture) ret_future = NULL; bz_state_info_set_allow_manual_sync (self->state, FALSE); bz_state_info_set_syncing (self->state, TRUE); - backend_future = bz_backend_retrieve_remote_entries (BZ_BACKEND (self->flatpak), NULL); + finish_with_background_task_label (self); + + refresh_worker = g_subprocess_new ( + G_SUBPROCESS_FLAGS_NONE, + &local_error, + REFRESH_WORKER_BIN_NAME, + NULL); + if (refresh_worker == NULL) + g_critical ("FATAL!!! The refresh worker could not be spawned: %s", + local_error->message); + g_assert (refresh_worker != NULL); + + backend_future = dex_subprocess_wait_check (refresh_worker); backend_future = dex_future_finally ( backend_future, (DexFutureCallback) backend_sync_finally, @@ -3466,9 +3532,9 @@ make_sync_future (BzApplication *self) dex_ref (backend_future), dex_ref (flathub_future), NULL); - ret_future = dex_future_then ( + ret_future = dex_future_finally ( ret_future, - (DexFutureCallback) sync_then, + (DexFutureCallback) sync_finally, bz_track_weak (self), bz_weak_release); return g_steal_pointer (&ret_future); } @@ -3486,3 +3552,20 @@ finish_with_background_task_label (BzApplication *self) else bz_state_info_set_background_task_label (self->state, NULL); } + +static void +state_info_set_icon_themes (BzStateInfo *state) +{ + g_autoptr (GtkIconTheme) user_theme = NULL; + g_autoptr (GtkIconTheme) system_theme = NULL; + g_autofree char *user_export_dir = NULL; + + user_theme = gtk_icon_theme_new (); + user_export_dir = g_build_filename (g_get_home_dir (), ".local/share/flatpak/exports/share/icons", NULL); + gtk_icon_theme_add_search_path (user_theme, user_export_dir); + bz_state_info_set_user_icon_theme (state, user_theme); + + system_theme = gtk_icon_theme_new (); + gtk_icon_theme_add_search_path (system_theme, "/var/lib/flatpak/exports/share/icons"); + bz_state_info_set_system_icon_theme (state, system_theme); +} diff --git a/src/bz-io.c b/src/bz-io.c index 76a3c597..0184be48 100644 --- a/src/bz-io.c +++ b/src/bz-io.c @@ -387,14 +387,17 @@ get_all_user_data_ids_fiber (void) char * bz_dup_root_cache_dir (void) { - const char *user_cache = NULL; - const char *id = NULL; + GApplication *application = NULL; + const char *user_cache = NULL; + const char *id = NULL; user_cache = g_get_user_cache_dir (); - id = g_application_get_application_id (g_application_get_default ()); + application = g_application_get_default (); + if (application != NULL) + id = g_application_get_application_id (application); if (id == NULL) - id = "Bazaar"; + id = "io.github.kolunmi.Bazaar"; return g_build_filename (user_cache, id, NULL); } diff --git a/src/meson.build b/src/meson.build index ee8c8634..69f48958 100644 --- a/src/meson.build +++ b/src/meson.build @@ -163,7 +163,6 @@ bz_sources = files( 'bz-world-map.c', 'bz-yaml-parser.c', 'bz-zoom.c', - 'main.c', ) subdir('progress-bar-designs') @@ -359,7 +358,14 @@ bz_sources += gnome.compile_resources('bz-resources', dependencies: [blueprints, release_notes, countries_gvariant], ) -executable('bazaar', bz_sources, gdbus_src, marshalers, +executable('bazaar', bz_sources + ['main.c'], gdbus_src, marshalers, + dependencies: bz_deps, + install: true, +) + +executable(refresh_worker_bin_name, + bz_sources + ['refresh-worker.c'], + gdbus_src, marshalers, dependencies: bz_deps, install: true, ) diff --git a/src/refresh-worker.c b/src/refresh-worker.c new file mode 100644 index 00000000..53d756da --- /dev/null +++ b/src/refresh-worker.c @@ -0,0 +1,153 @@ +/* refresh-worker.c + * + * Copyright 2026 Eva M + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "BAZAAR::REFRESH-WORKER" + +#include "bz-backend-notification.h" +#include "bz-backend.h" +#include "bz-entry-cache-manager.h" +#include "bz-env.h" +#include "bz-flatpak-instance.h" +#include "bz-util.h" + +BZ_DEFINE_DATA ( + main, + Main, + { + GMainLoop *loop; + GIOChannel *stdout_channel; + int rv; + }, + BZ_RELEASE_DATA (loop, g_main_loop_unref); + BZ_RELEASE_DATA (stdout_channel, g_io_channel_unref)); + +static DexFuture * +run (MainData *data); + +int +main (int argc, + char *argv[]) +{ + g_autoptr (GIOChannel) stdout_channel = NULL; + g_autoptr (GMainLoop) main_loop = NULL; + g_autoptr (MainData) data = NULL; + g_autoptr (DexFuture) future = NULL; + + g_log_writer_default_set_use_stderr (TRUE); + dex_init (); + + stdout_channel = g_io_channel_unix_new (STDOUT_FILENO); + g_assert (g_io_channel_set_encoding (stdout_channel, NULL, NULL)); + g_io_channel_set_buffered (stdout_channel, FALSE); + + main_loop = g_main_loop_new (NULL, FALSE); + + data = main_data_new (); + data->loop = g_main_loop_ref (main_loop); + data->stdout_channel = g_io_channel_ref (stdout_channel); + data->rv = EXIT_SUCCESS; + + future = dex_scheduler_spawn ( + dex_scheduler_get_default (), + bz_get_dex_stack_size (), + (DexFiberFunc) run, + main_data_ref (data), main_data_unref); + g_main_loop_run (main_loop); + + return data->rv; +} + +static DexFuture * +run (MainData *data) +{ + gboolean result = FALSE; + g_autoptr (GError) local_error = NULL; + g_autoptr (BzEntryCacheManager) cache = NULL; + g_autoptr (BzFlatpakInstance) flatpak = NULL; + g_autoptr (DexChannel) channel = NULL; + g_autoptr (DexFuture) all_notifs = NULL; + guint n_notifs = 0; + g_autoptr (GPtrArray) write_backs = NULL; + + cache = bz_entry_cache_manager_new (); + + flatpak = dex_await_object ( + bz_flatpak_instance_new (), + &local_error); + if (flatpak == NULL) + goto err; + + channel = bz_backend_create_notification_channel (BZ_BACKEND (flatpak)); + if (channel == NULL) + goto err; + + result = dex_await ( + bz_backend_retrieve_remote_entries ( + BZ_BACKEND (flatpak), NULL), + &local_error); + if (!result) + goto err; + + all_notifs = dex_channel_receive_all (channel); + n_notifs = dex_future_set_get_size (DEX_FUTURE_SET (all_notifs)); + + write_backs = g_ptr_array_new_with_free_func (dex_unref); + for (guint i = 0; i < n_notifs; i++) + { + DexFuture *future = NULL; + g_autoptr (BzBackendNotification) notif = NULL; + BzBackendNotificationKind kind = 0; + + future = dex_future_set_get_future_at ( + DEX_FUTURE_SET (all_notifs), i); + + notif = dex_await_object (dex_ref (future), NULL); + if (notif == NULL) + continue; + + kind = bz_backend_notification_get_kind (notif); + if (kind == BZ_BACKEND_NOTIFICATION_KIND_REPLACE_ENTRY) + { + BzEntry *entry = NULL; + + entry = bz_backend_notification_get_entry (notif); + g_ptr_array_add ( + write_backs, + bz_entry_cache_manager_add (cache, entry)); + } + } + if (write_backs->len > 0) + dex_await ( + dex_future_allv ( + (DexFuture *const *) write_backs->pdata, + write_backs->len), + NULL); + + data->rv = EXIT_SUCCESS; + g_main_loop_quit (data->loop); + return dex_future_new_true (); + +err: + if (local_error != NULL) + g_critical ("Unable to complete refresh: %s", local_error->message); + data->rv = EXIT_FAILURE; + g_main_loop_quit (data->loop); + return dex_future_new_false (); +}