diff --git a/src/fbd-event.c b/src/fbd-event.c index 917b5e2..a257034 100644 --- a/src/fbd-event.c +++ b/src/fbd-event.c @@ -343,8 +343,8 @@ fbd_event_get_timeout (FbdEvent *self) void fbd_event_add_feedback (FbdEvent *self, FbdFeedbackBase *feedback) { - self->feedbacks = g_slist_prepend (self->feedbacks, g_object_ref(feedback)); - g_object_set_data (G_OBJECT (feedback), "event-id", GUINT_TO_POINTER(self->id)); + self->feedbacks = g_slist_prepend (self->feedbacks, g_object_ref (feedback)); + g_object_set_data (G_OBJECT (feedback), "event-id", GUINT_TO_POINTER (self->id)); g_signal_connect_object (feedback, "ended", (GCallback) on_fb_ended, @@ -361,10 +361,26 @@ fbd_event_get_feedbacks (FbdEvent *self) int fbd_event_remove_feedback (FbdEvent *self, FbdFeedbackBase *feedback) { + guint len; + g_return_val_if_fail (FBD_IS_EVENT (self), 0); + if (!self->feedbacks) + return 0; + + if (g_slist_index (self->feedbacks, feedback) >= 0) { + g_signal_handlers_disconnect_by_data (feedback, self); + /* Drop our reference, feedbacks end themselves when unref'ed */ + g_object_unref (feedback); + } + self->feedbacks = g_slist_remove (self->feedbacks, feedback); - return g_slist_length (self->feedbacks); + + len = g_slist_length (self->feedbacks); + if (!len) + check_ended (self); + + return len; } /** @@ -414,6 +430,52 @@ fbd_event_end_feedbacks (FbdEvent *self) g_slist_foreach (self->feedbacks, (GFunc)fbd_feedback_end, NULL); } +/** + * fbd_event_end_feedbacks_by_level: + * @self: The Event + * @level: The profile level + * + * End all feedback strictly above the given profile level. + */ +void +fbd_event_end_feedbacks_by_level (FbdEvent *self, guint level) +{ + g_autoptr (GSList) feedbacks = NULL; + guint num = 0; + + g_return_if_fail (FBD_IS_EVENT (self)); + /* Copy the list as we will remove feedbacks from self->feedbacks */ + feedbacks = g_slist_copy (fbd_event_get_feedbacks (self)); + + for (GSList *l = feedbacks; l; l = l->next) { + FbdFeedbackBase *fb = FBD_FEEDBACK_BASE (l->data); + guint event_level; + + event_level = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (fb), "fbd-level")); + if (event_level > level) + num++; + } + + if (num == 0) + return; + + g_debug ("Will end %d feedbacks of event %s", num, fbd_event_get_event (self)); + /* Set 'explicit' if we end all remaining feedbacks */ + if (num == g_slist_length (self->feedbacks)) + fbd_event_set_end_reason (self, FBD_EVENT_END_REASON_EXPLICIT); + + for (GSList *l = feedbacks; l; l = l->next) { + FbdFeedbackBase *fb = FBD_FEEDBACK_BASE (l->data); + guint event_level; + + event_level = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (fb), "fbd-level")); + if (event_level > level) { + fbd_event_remove_feedback (self, fb); + fbd_feedback_end (fb); + } + } +} + /** * fbd_event_get_feedbacks_ended: * @self: The Event diff --git a/src/fbd-event.h b/src/fbd-event.h index f7844c2..b023217 100644 --- a/src/fbd-event.h +++ b/src/fbd-event.h @@ -47,6 +47,7 @@ int fbd_event_remove_feedback (FbdEvent *self, FbdFeedbackBase *feedback); void fbd_event_run_feedbacks (FbdEvent *self); void fbd_event_end_feedbacks (FbdEvent *self); +void fbd_event_end_feedbacks_by_level (FbdEvent *self, guint level); gboolean fbd_event_get_feedbacks_ended (FbdEvent *self); const char *fbd_event_get_sender (FbdEvent *self); diff --git a/src/fbd-feedback-manager.c b/src/fbd-feedback-manager.c index 2188969..8a647de 100644 --- a/src/fbd-feedback-manager.c +++ b/src/fbd-feedback-manager.c @@ -201,8 +201,6 @@ on_profile_changed (FbdFeedbackManager *self, GParamSpec *psepc, gpointer unused if (!fbd_feedback_manager_set_profile (self, pname)) g_warning ("Invalid profile '%s'", pname); - - /* TODO: end running feedbacks that aren't allowed in new profile immediately */ } static void @@ -418,7 +416,7 @@ fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object, feedbacks = fbd_feedback_theme_lookup_feedback (self->theme, level, event); if (feedbacks) { for (l = feedbacks; l; l = l->next) { - FbdFeedbackBase *fb = l->data; + FbdFeedbackBase *fb = FBD_FEEDBACK_BASE (l->data); if (fbd_feedback_is_available (FBD_FEEDBACK_BASE (fb))) { fbd_event_add_feedback (event, fb); @@ -626,6 +624,20 @@ fbd_feedback_manager_load_theme (FbdFeedbackManager *self) } } + +static void +cancel_running (FbdFeedbackManager *self) +{ + g_autoptr (GList) running = g_hash_table_get_values (self->events); + + for (GList *l = running; l ; l = l->next) { + FbdEvent *event = FBD_EVENT (l->data); + + fbd_event_end_feedbacks_by_level (event, self->level); + } +} + + gboolean fbd_feedback_manager_set_profile (FbdFeedbackManager *self, const gchar *profile) { @@ -645,5 +657,8 @@ fbd_feedback_manager_set_profile (FbdFeedbackManager *self, const gchar *profile self->level = level; lfb_gdbus_feedback_set_profile (LFB_GDBUS_FEEDBACK (self), profile); g_settings_set_string (self->settings, FEEDBACKD_KEY_PROFILE, profile); + + cancel_running (self); + return TRUE; } diff --git a/src/fbd-feedback-theme.c b/src/fbd-feedback-theme.c index e416f89..2e89fcc 100644 --- a/src/fbd-feedback-theme.c +++ b/src/fbd-feedback-theme.c @@ -354,7 +354,8 @@ fbd_feedback_theme_lookup_feedback (FbdFeedbackTheme *self, FbdFeedbackBase *feedback = fbd_feedback_profile_get_feedback (profile, fbd_event_get_event (event)); if (feedback) { - feedbacks = g_slist_prepend (feedbacks, g_object_ref(feedback)); + g_object_set_data (G_OBJECT (feedback), "fbd-level", GUINT_TO_POINTER (i)); + feedbacks = g_slist_prepend (feedbacks, g_object_ref (feedback)); } } diff --git a/src/fbd-feedback-theme.h b/src/fbd-feedback-theme.h index 3485eb0..57f23cc 100644 --- a/src/fbd-feedback-theme.h +++ b/src/fbd-feedback-theme.h @@ -25,15 +25,15 @@ void fbd_feedback_theme_update (FbdFeedbackTheme *self, FbdFeedba const char *fbd_feedback_theme_get_name (FbdFeedbackTheme *self); void fbd_feedback_theme_set_name (FbdFeedbackTheme *self, const char *name); -void fbd_feedback_theme_set_parent_name (FbdFeedbackTheme *self, - const char *parent_name); +void fbd_feedback_theme_set_parent_name (FbdFeedbackTheme *self, + const char *parent_name); const char *fbd_feedback_theme_get_parent_name (FbdFeedbackTheme *self); void fbd_feedback_theme_add_profile (FbdFeedbackTheme *self, FbdFeedbackProfile *profile); FbdFeedbackProfile *fbd_feedback_theme_get_profile (FbdFeedbackTheme *self, const char *name); -GSList *fbd_feedback_theme_lookup_feedback (FbdFeedbackTheme *self, - FbdFeedbackProfileLevel profile, - FbdEvent *event); +GSList *fbd_feedback_theme_lookup_feedback (FbdFeedbackTheme *self, + FbdFeedbackProfileLevel profile, + FbdEvent *event); G_END_DECLS diff --git a/tests/test-fbd-event.c b/tests/test-fbd-event.c index cac2c89..7fbad07 100644 --- a/tests/test-fbd-event.c +++ b/tests/test-fbd-event.c @@ -1,5 +1,7 @@ /* * Copyright (C) 2020 Purism SPC + * 2023-2024 The Phosh Developers + * * SPDX-License-Identifier: GPL-3.0+ * Author: Guido Günther */ @@ -12,7 +14,7 @@ static void test_fbd_event (void) { - g_autoptr(FbdEvent) event = NULL; + g_autoptr (FbdEvent) event = NULL; g_autofree gchar *appid = NULL; g_autofree gchar *name = NULL; g_autofree gchar *sender = NULL; @@ -50,9 +52,9 @@ static void test_fbd_event_feedback (void) { GSList *feedbacks; - g_autoptr(FbdEvent) event = NULL; - g_autoptr(FbdFeedbackDummy) feedback1 = NULL; - g_autoptr(FbdFeedbackDummy) feedback2 = NULL; + FbdEvent *event = NULL; + FbdFeedbackDummy *feedback1 = NULL; + FbdFeedbackDummy *feedback2 = NULL; event = fbd_event_new (1, TEST_APP_ID, TEST_EVENT, -1, NULL); feedback1 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY, NULL); @@ -61,21 +63,21 @@ test_fbd_event_feedback (void) feedbacks = fbd_event_get_feedbacks (event); g_assert_cmpint (g_slist_length (feedbacks), ==, 0); - fbd_event_add_feedback (event, FBD_FEEDBACK_BASE(feedback1)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback1)); feedbacks = fbd_event_get_feedbacks (event); g_assert_cmpint (g_slist_length (feedbacks), ==, 1); /* Remove non existing feedback */ - fbd_event_remove_feedback (event, FBD_FEEDBACK_BASE(feedback2)); + fbd_event_remove_feedback (event, FBD_FEEDBACK_BASE (feedback2)); feedbacks = fbd_event_get_feedbacks (event); g_assert_cmpint (g_slist_length (feedbacks), ==, 1); - fbd_event_remove_feedback (event, FBD_FEEDBACK_BASE(feedback1)); + fbd_event_remove_feedback (event, FBD_FEEDBACK_BASE (feedback1)); feedbacks = fbd_event_get_feedbacks (event); g_assert_cmpint (g_slist_length (feedbacks), ==, 0); - fbd_event_add_feedback (event, FBD_FEEDBACK_BASE(feedback1)); - fbd_event_add_feedback (event, FBD_FEEDBACK_BASE(feedback2)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback1)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback2)); feedbacks = fbd_event_get_feedbacks (event); g_assert_cmpint (g_slist_length (feedbacks), ==, 2); @@ -83,6 +85,11 @@ test_fbd_event_feedback (void) fbd_event_end_feedbacks (event); /* Dummy feedback ends immediately */ g_assert_true (fbd_event_get_feedbacks_ended (event)); + + /* event holds a ref on the feedbacks so finalize it first */ + g_assert_finalize_object (event); + g_assert_finalize_object (feedback2); + g_assert_finalize_object (feedback1); } static void @@ -96,16 +103,17 @@ on_feedbacks_ended (FbdEvent *event, gboolean *ended) static void test_fbd_event_feedback_ended (void) { - g_autoptr(FbdEvent) event = NULL; - g_autoptr(FbdFeedbackDummy) feedback1 = NULL; - g_autoptr(FbdFeedbackDummy) feedback2 = NULL; + g_autoptr (FbdEvent) event = NULL; + g_autoptr (FbdFeedbackDummy) feedback1 = NULL; + g_autoptr (FbdFeedbackDummy) feedback2 = NULL; gboolean ended = FALSE; event = fbd_event_new (1, TEST_APP_ID, TEST_EVENT, FBD_EVENT_TIMEOUT_ONESHOT, NULL); feedback1 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY, NULL); - fbd_event_add_feedback (event, FBD_FEEDBACK_BASE(feedback1)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback1)); + feedback2 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY, NULL); - fbd_event_add_feedback (event, FBD_FEEDBACK_BASE(feedback2)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback2)); g_signal_connect (event, "feedbacks-ended", (GCallback)on_feedbacks_ended, &ended); @@ -114,19 +122,65 @@ test_fbd_event_feedback_ended (void) g_assert_true (ended); } + +static void +test_fbd_event_feedback_end_by_level (void) +{ + FbdEvent *event; + FbdFeedbackDummy *feedback1, *feedback2; + gboolean ended; + + feedback1 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY, NULL); + g_object_set_data (G_OBJECT (feedback1), "fbd-level", GUINT_TO_POINTER (10)); + + feedback2 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY, NULL); + g_object_set_data (G_OBJECT (feedback2), "fbd-level", GUINT_TO_POINTER (5)); + + event = fbd_event_new (1, TEST_APP_ID, TEST_EVENT, FBD_EVENT_TIMEOUT_ONESHOT, NULL); + g_signal_connect (event, "feedbacks-ended", + (GCallback)on_feedbacks_ended, &ended); + + /* End all feedback at once */ + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback1)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback2)); + fbd_event_end_feedbacks_by_level (event, 3); + g_assert_cmpint (g_slist_length (fbd_event_get_feedbacks (event)), ==, 0); + g_assert_cmpint (fbd_event_get_end_reason (event), ==, FBD_EVENT_END_REASON_EXPLICIT); + g_assert_true (ended); + + /* End feedback one by one */ + ended = FALSE; + fbd_event_set_end_reason (event, FBD_EVENT_END_REASON_NATURAL); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback1)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback2)); + fbd_event_end_feedbacks_by_level (event, 7); + g_assert_cmpint (g_slist_length (fbd_event_get_feedbacks (event)), ==, 1); + g_assert_cmpint (fbd_event_get_end_reason (event), ==, FBD_EVENT_END_REASON_NATURAL); + g_assert_false (ended); + fbd_event_end_feedbacks_by_level (event, 4); + g_assert_cmpint (g_slist_length (fbd_event_get_feedbacks (event)), ==, 0); + g_assert_cmpint (fbd_event_get_end_reason (event), ==, FBD_EVENT_END_REASON_EXPLICIT); + g_assert_true (ended); + + g_assert_finalize_object (event); + g_assert_finalize_object (feedback2); + g_assert_finalize_object (feedback1); +} + + static void test_fbd_event_feedback_loop (void) { - g_autoptr(FbdEvent) event = NULL; - g_autoptr(FbdFeedbackDummy) feedback1 = NULL; - g_autoptr(FbdFeedbackDummy) feedback2 = NULL; + g_autoptr (FbdEvent) event = NULL; + g_autoptr (FbdFeedbackDummy) feedback1 = NULL; + g_autoptr (FbdFeedbackDummy) feedback2 = NULL; gboolean ended = FALSE; event = fbd_event_new (1, TEST_APP_ID, TEST_EVENT, FBD_EVENT_TIMEOUT_LOOP, NULL); feedback1 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY, NULL); - fbd_event_add_feedback (event, FBD_FEEDBACK_BASE(feedback1)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback1)); feedback2 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY, NULL); - fbd_event_add_feedback (event, FBD_FEEDBACK_BASE(feedback2)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback2)); g_signal_connect (event, "feedbacks-ended", (GCallback)on_feedbacks_ended, &ended); @@ -139,16 +193,16 @@ test_fbd_event_feedback_loop (void) static void test_fbd_event_feedback_timeout (void) { - g_autoptr(FbdEvent) event = NULL; - g_autoptr(FbdFeedbackDummy) feedback1 = NULL; - g_autoptr(FbdFeedbackDummy) feedback2 = NULL; + g_autoptr (FbdEvent) event = NULL; + g_autoptr (FbdFeedbackDummy) feedback1 = NULL; + g_autoptr (FbdFeedbackDummy) feedback2 = NULL; gboolean ended = FALSE; event = fbd_event_new (1, TEST_APP_ID, TEST_EVENT, 1, NULL); feedback1 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY, NULL); - fbd_event_add_feedback (event, FBD_FEEDBACK_BASE(feedback1)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback1)); feedback2 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY, NULL); - fbd_event_add_feedback (event, FBD_FEEDBACK_BASE(feedback2)); + fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (feedback2)); g_signal_connect (event, "feedbacks-ended", (GCallback)on_feedbacks_ended, &ended); @@ -163,11 +217,13 @@ main (gint argc, gchar *argv[]) { g_test_init (&argc, &argv, NULL); - g_test_add_func("/feedbackd/fbd/event/new", test_fbd_event); - g_test_add_func("/feedbackd/fbd/event/feedbacks/props", test_fbd_event_feedback); - g_test_add_func("/feedbackd/fbd/event/feedbacks/ended", test_fbd_event_feedback_ended); - g_test_add_func("/feedbackd/fbd/event/feedbacks/loop", test_fbd_event_feedback_loop); - g_test_add_func("/feedbackd/fbd/event/feedbacks/timeout", test_fbd_event_feedback_timeout); + g_test_add_func ("/feedbackd/fbd/event/new", test_fbd_event); + g_test_add_func ("/feedbackd/fbd/event/feedbacks/props", test_fbd_event_feedback); + g_test_add_func ("/feedbackd/fbd/event/feedbacks/ended", test_fbd_event_feedback_ended); + g_test_add_func ("/feedbackd/fbd/event/feedbacks/end_by_level", + test_fbd_event_feedback_end_by_level); + g_test_add_func ("/feedbackd/fbd/event/feedbacks/loop", test_fbd_event_feedback_loop); + g_test_add_func ("/feedbackd/fbd/event/feedbacks/timeout", test_fbd_event_feedback_timeout); - return g_test_run(); + return g_test_run (); }