Merge branch 'custom-sound' into 'main'
Allow to use custom sound files Closes #81 See merge request Librem5/feedbackd!150
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
(c-file-style . "linux")
|
||||
(indent-tabs-mode . nil)
|
||||
(c-basic-offset . 2)
|
||||
(display-fill-column-indicator-column . 100)
|
||||
))
|
||||
(setq auto-mode-alist (cons '("\\.ui$" . nxml-mode) auto-mode-alist))
|
||||
(nxml-mode . (
|
||||
|
||||
+13
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
* 2023-2024 The Phosh Developers
|
||||
* 2023-2025 The Phosh Developers
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
@@ -60,7 +60,11 @@ on_user_input (GIOChannel *channel, GIOCondition cond, LfbEvent *event)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
trigger_event (const char *name, const gchar *profile, gboolean important, gint timeout)
|
||||
trigger_event (const char *name,
|
||||
const char *profile,
|
||||
gboolean important,
|
||||
gint timeout,
|
||||
const char *sound_file)
|
||||
{
|
||||
g_autoptr (GError) err = NULL;
|
||||
g_autoptr (LfbEvent) event = NULL;
|
||||
@@ -79,6 +83,9 @@ trigger_event (const char *name, const gchar *profile, gboolean important, gint
|
||||
if (important)
|
||||
lfb_event_set_important (event, TRUE);
|
||||
|
||||
if (sound_file)
|
||||
lfb_event_set_sound_file(event, sound_file);
|
||||
|
||||
g_signal_connect (event, "feedback-ended", (GCallback)on_feedback_ended, &success);
|
||||
if (!lfb_event_trigger_feedback (event, &err)) {
|
||||
g_print ("Failed to report event: %s\n", err->message);
|
||||
@@ -140,6 +147,7 @@ main (int argc, char *argv[0])
|
||||
g_autoptr (GError) err = NULL;
|
||||
g_autofree char *profile = NULL;
|
||||
g_autofree char *app_id = NULL;
|
||||
g_autofree char *sound_file = NULL;
|
||||
const char *name = NULL;
|
||||
gboolean success, important = FALSE;
|
||||
int watch = 30;
|
||||
@@ -157,6 +165,8 @@ main (int argc, char *argv[0])
|
||||
"How long to watch for feedback longest", NULL},
|
||||
{"app-id", 'A', 0, G_OPTION_ARG_STRING, &app_id,
|
||||
"Override used application id"},
|
||||
{"sound-file", 'S', 0, G_OPTION_ARG_STRING, &sound_file,
|
||||
"Override the sound effect used by a file"},
|
||||
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -183,7 +193,7 @@ main (int argc, char *argv[0])
|
||||
name = g_strdup (DEFAULT_EVENT);
|
||||
|
||||
g_timeout_add_seconds (watch, (GSourceFunc)on_watch_expired, NULL);
|
||||
success = trigger_event (name, profile, important, timeout);
|
||||
success = trigger_event (name, profile, important, timeout, sound_file);
|
||||
}
|
||||
|
||||
lfb_uninit ();
|
||||
|
||||
@@ -26,7 +26,11 @@
|
||||
Together with the 'profile' hint this allows to trigger feedback for events
|
||||
that would otherwise be disabled. A typical use case is an alarm clock. Note
|
||||
that the feedback daemon (depending on it's configuration) might ignore this flag.
|
||||
@timeout: When the feedbacks for this event should end latest in seconds. The special values '-1' (just run each feedback once) and '0' (endless loop) are also supported.
|
||||
- sound-file: A custom sound file to play. This file will be used instead of any
|
||||
sound event specified in the "full" profile. The sound will only be played if
|
||||
appropriate for the feedback level of the event.
|
||||
@timeout: When the feedbacks for this event should end latest in seconds. The special
|
||||
values '-1' (just run each feedback once) and '0' (endless loop) are also supported.
|
||||
@id: Event id for future reference
|
||||
|
||||
Give user feedback for an event by triggering feedbacks
|
||||
|
||||
Vendored
+6
@@ -1,3 +1,9 @@
|
||||
feedbackd (0.7.0~dev) UNRELEASED; urgency=medium
|
||||
|
||||
* Unreleased
|
||||
|
||||
-- Guido Günther <agx@sigxcpu.org> Sun, 26 Jan 2025 14:07:50 +0100
|
||||
|
||||
feedbackd (0.6.0) experimental; urgency=medium
|
||||
|
||||
[ Guido Günther ]
|
||||
|
||||
Vendored
+2
@@ -10,6 +10,7 @@ libfeedback-0.0.so.0 libfeedback-0.0-0 #MINVER#
|
||||
lfb_event_get_event@LIBFEEDBACK_0_0_0 0.1.1
|
||||
lfb_event_get_feedback_profile@LIBFEEDBACK_0_0_0 0.1.1
|
||||
lfb_event_get_important@LIBFEEDBACK_0_0_0 0.2.1
|
||||
lfb_event_get_sound_file@LIBFEEDBACK_0_0_0 0.7.0~
|
||||
lfb_event_get_state@LIBFEEDBACK_0_0_0 0.1.1
|
||||
lfb_event_get_timeout@LIBFEEDBACK_0_0_0 0.1.1
|
||||
lfb_event_get_type@LIBFEEDBACK_0_0_0 0.1.1
|
||||
@@ -17,6 +18,7 @@ libfeedback-0.0.so.0 libfeedback-0.0-0 #MINVER#
|
||||
lfb_event_set_app_id@LIBFEEDBACK_0_0_0 0.2.0
|
||||
lfb_event_set_feedback_profile@LIBFEEDBACK_0_0_0 0.1.1
|
||||
lfb_event_set_important@LIBFEEDBACK_0_0_0 0.2.1
|
||||
lfb_event_set_sound_file@LIBFEEDBACK_0_0_0 0.7.0~
|
||||
lfb_event_set_timeout@LIBFEEDBACK_0_0_0 0.1.1
|
||||
lfb_event_state_get_type@LIBFEEDBACK_0_0_0 0.1.1
|
||||
lfb_event_trigger_feedback@LIBFEEDBACK_0_0_0 0.1.1
|
||||
|
||||
+77
-7
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
* 2024-2025 The Phosh Developers
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
*/
|
||||
@@ -81,6 +83,7 @@ enum {
|
||||
PROP_FEEDBACK_PROFILE,
|
||||
PROP_IMPORTANT,
|
||||
PROP_APP_ID,
|
||||
PROP_SOUND_FILE,
|
||||
PROP_LAST_PROP,
|
||||
};
|
||||
static GParamSpec *props[PROP_LAST_PROP];
|
||||
@@ -99,6 +102,7 @@ typedef struct _LfbEvent {
|
||||
gchar *profile;
|
||||
gboolean important;
|
||||
char *app_id;
|
||||
char *sound_file;
|
||||
|
||||
guint id;
|
||||
LfbEventState state;
|
||||
@@ -139,10 +143,18 @@ build_hints (LfbEvent *self)
|
||||
GVariantBuilder hints_builder;
|
||||
|
||||
g_variant_builder_init (&hints_builder, G_VARIANT_TYPE ("a{sv}"));
|
||||
if (self->profile)
|
||||
g_variant_builder_add (&hints_builder, "{sv}", "profile", g_variant_new_string (self->profile));
|
||||
if (self->important)
|
||||
g_variant_builder_add (&hints_builder, "{sv}", "important", g_variant_new_boolean (self->important));
|
||||
if (self->profile) {
|
||||
g_variant_builder_add (&hints_builder, "{sv}", "profile",
|
||||
g_variant_new_string (self->profile));
|
||||
}
|
||||
if (self->important) {
|
||||
g_variant_builder_add (&hints_builder, "{sv}", "important",
|
||||
g_variant_new_boolean (self->important));
|
||||
}
|
||||
if (self->sound_file) {
|
||||
g_variant_builder_add (&hints_builder, "{sv}", "sound-file",
|
||||
g_variant_new_string (self->sound_file));
|
||||
}
|
||||
return g_variant_builder_end (&hints_builder);
|
||||
}
|
||||
|
||||
@@ -233,6 +245,9 @@ lfb_event_set_property (GObject *object,
|
||||
case PROP_APP_ID:
|
||||
lfb_event_set_app_id (self, g_value_get_string (value));
|
||||
break;
|
||||
case PROP_SOUND_FILE:
|
||||
lfb_event_set_sound_file (self, g_value_get_string (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -264,6 +279,9 @@ lfb_event_get_property (GObject *object,
|
||||
case PROP_APP_ID:
|
||||
g_value_set_string (value, lfb_event_get_app_id (self));
|
||||
break;
|
||||
case PROP_SOUND_FILE:
|
||||
g_value_set_string (value, lfb_event_get_sound_file (self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -278,6 +296,7 @@ lfb_event_finalize (GObject *object)
|
||||
/* Signal handler is disconnected automatically due to g_signal_connect_object */
|
||||
self->handler_id = 0;
|
||||
|
||||
g_clear_pointer (&self->sound_file, g_free);
|
||||
g_clear_pointer (&self->event, g_free);
|
||||
g_clear_pointer (&self->profile, g_free);
|
||||
g_clear_pointer (&self->app_id, g_free);
|
||||
@@ -340,7 +359,6 @@ lfb_event_class_init (LfbEventClass *klass)
|
||||
LFB_TYPE_EVENT_END_REASON,
|
||||
LFB_EVENT_END_REASON_NATURAL,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* LfbEvent:feedback-profile:
|
||||
*
|
||||
@@ -354,7 +372,6 @@ lfb_event_class_init (LfbEventClass *klass)
|
||||
"Feedback profile to use for this event",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* LfbEvent:important:
|
||||
*
|
||||
@@ -368,7 +385,6 @@ lfb_event_class_init (LfbEventClass *klass)
|
||||
"Whether to flags this event as important",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* LfbEvent:app-id:
|
||||
*
|
||||
@@ -382,6 +398,19 @@ lfb_event_class_init (LfbEventClass *klass)
|
||||
"The Application id to use for this event",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
|
||||
/**
|
||||
* LfbEvent:sound-file:
|
||||
*
|
||||
* A custom sound-file to play. See [method@LfbEvent.set_sound_file]
|
||||
* for details.
|
||||
*/
|
||||
props[PROP_SOUND_FILE] =
|
||||
g_param_spec_string (
|
||||
"sound-file",
|
||||
"Sound file",
|
||||
"Custom sound file to play",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
|
||||
|
||||
@@ -863,3 +892,44 @@ lfb_event_get_app_id (LfbEvent *self)
|
||||
|
||||
return self->app_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_set_sound_file:
|
||||
* @self: The event
|
||||
* @sound_file: The sound file
|
||||
*
|
||||
* Tells the feedback server to use the given sound file if a sound is
|
||||
* to be played. It will only be used if sound is suitable for the
|
||||
* events feedback level.
|
||||
*
|
||||
* A value of %NULL (the default) lets the server pick the sound
|
||||
* based on the event.
|
||||
*/
|
||||
void
|
||||
lfb_event_set_sound_file (LfbEvent *self, const gchar *sound_file)
|
||||
{
|
||||
g_return_if_fail (LFB_IS_EVENT (self));
|
||||
|
||||
if (!g_strcmp0 (self->sound_file, sound_file))
|
||||
return;
|
||||
|
||||
g_free (self->sound_file);
|
||||
self->sound_file = g_strdup (sound_file);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SOUND_FILE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_get_sound_file:
|
||||
* @self: The event
|
||||
*
|
||||
* Returns the sound file for this event if set.
|
||||
*
|
||||
* Returns:(transfer none): The set sound file for this event or %NULL.
|
||||
*/
|
||||
const char *
|
||||
lfb_event_get_sound_file (LfbEvent *self)
|
||||
{
|
||||
g_return_val_if_fail (LFB_IS_EVENT (self), NULL);
|
||||
|
||||
return self->sound_file;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ gboolean lfb_event_get_important (LfbEvent *self);
|
||||
void lfb_event_set_app_id (LfbEvent *self, const char *app_id);
|
||||
const char *lfb_event_get_app_id (LfbEvent *self);
|
||||
const char *lfb_event_get_event (LfbEvent *self);
|
||||
void lfb_event_set_sound_file (LfbEvent *self, const char *sound_file);
|
||||
const char *lfb_event_get_sound_file (LfbEvent *self);
|
||||
|
||||
LfbEventState lfb_event_get_state (LfbEvent *self);
|
||||
LfbEventEndReason lfb_event_get_end_reason (LfbEvent *self);
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
project('feedbackd', 'c',
|
||||
version : '0.6.0',
|
||||
version : '0.7.0-dev',
|
||||
license : 'LGPLv3+',
|
||||
meson_version : '>= 1.0.0',
|
||||
default_options : [
|
||||
|
||||
+26
-12
@@ -176,14 +176,16 @@ on_sound_play_finished_callback (GSoundContext *ctx,
|
||||
g_autoptr (GError) err = NULL;
|
||||
|
||||
if (!gsound_context_play_full_finish (ctx, res, &err)) {
|
||||
const char *sound = fbd_feedback_sound_get_file_name (data->feedback);
|
||||
if (!sound)
|
||||
sound = fbd_feedback_sound_get_effect (data->feedback);
|
||||
|
||||
if (err->domain == GSOUND_ERROR && err->code == GSOUND_ERROR_NOTFOUND) {
|
||||
g_debug ("Failed to find sound '%s'", fbd_feedback_sound_get_effect (data->feedback));
|
||||
g_debug ("Failed to find sound '%s'", sound);
|
||||
} else if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||||
g_debug ("Sound '%s' cancelled", fbd_feedback_sound_get_effect (data->feedback));
|
||||
g_debug ("Sound '%s' cancelled", sound);
|
||||
} else {
|
||||
g_warning ("Failed to play sound '%s': %s",
|
||||
fbd_feedback_sound_get_effect (data->feedback),
|
||||
err->message);
|
||||
g_warning ("Failed to play sound '%s': %s", sound, err->message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +202,7 @@ gboolean
|
||||
fbd_dev_sound_play (FbdDevSound *self, FbdFeedbackSound *feedback, FbdDevSoundPlayedCallback callback)
|
||||
{
|
||||
FbdAsyncData *data;
|
||||
const char *filename;
|
||||
|
||||
g_return_val_if_fail (FBD_IS_DEV_SOUND (self), FALSE);
|
||||
g_return_val_if_fail (GSOUND_IS_CONTEXT (self->ctx), FALSE);
|
||||
@@ -209,13 +212,24 @@ fbd_dev_sound_play (FbdDevSound *self, FbdFeedbackSound *feedback, FbdDevSoundPl
|
||||
if (!g_hash_table_insert (self->playbacks, feedback, data))
|
||||
g_warning ("Feedback %p already present", feedback);
|
||||
|
||||
gsound_context_play_full (self->ctx, data->cancel,
|
||||
(GAsyncReadyCallback) on_sound_play_finished_callback,
|
||||
data,
|
||||
GSOUND_ATTR_EVENT_ID, fbd_feedback_sound_get_effect (feedback),
|
||||
GSOUND_ATTR_EVENT_DESCRIPTION, "Feedbackd sound feedback",
|
||||
GSOUND_ATTR_MEDIA_ROLE, "event",
|
||||
NULL);
|
||||
filename = fbd_feedback_sound_get_file_name (feedback);
|
||||
if (filename) {
|
||||
gsound_context_play_full (self->ctx, data->cancel,
|
||||
(GAsyncReadyCallback) on_sound_play_finished_callback,
|
||||
data,
|
||||
GSOUND_ATTR_MEDIA_FILENAME, filename,
|
||||
GSOUND_ATTR_EVENT_DESCRIPTION, "Feedbackd custom sound feedback",
|
||||
GSOUND_ATTR_MEDIA_ROLE, "event",
|
||||
NULL);
|
||||
} else {
|
||||
gsound_context_play_full (self->ctx, data->cancel,
|
||||
(GAsyncReadyCallback) on_sound_play_finished_callback,
|
||||
data,
|
||||
GSOUND_ATTR_EVENT_ID, fbd_feedback_sound_get_effect (feedback),
|
||||
GSOUND_ATTR_EVENT_DESCRIPTION, "Feedbackd sound feedback",
|
||||
GSOUND_ATTR_MEDIA_ROLE, "event",
|
||||
NULL);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
+72
-34
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
* 2024-2025 The Phosh Developers
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
*/
|
||||
@@ -340,9 +342,12 @@ get_max_level (FbdFeedbackProfileLevel global_level,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_hints (GVariant *hints, FbdFeedbackProfileLevel *level, gboolean *hint_important)
|
||||
parse_hints (GVariant *hints,
|
||||
FbdFeedbackProfileLevel *level,
|
||||
gboolean *hint_important,
|
||||
char **hint_sound_file)
|
||||
{
|
||||
const gchar *profile;
|
||||
const gchar *profile, *sound_file;
|
||||
gboolean found, important;
|
||||
g_auto (GVariantDict) dict = G_VARIANT_DICT_INIT (NULL);
|
||||
|
||||
@@ -356,9 +361,68 @@ parse_hints (GVariant *hints, FbdFeedbackProfileLevel *level, gboolean *hint_imp
|
||||
if (hint_important && found)
|
||||
*hint_important = important;
|
||||
|
||||
found = g_variant_dict_lookup (&dict, "sound-file", "&s", &sound_file);
|
||||
if (hint_sound_file && found)
|
||||
*hint_sound_file = g_strdup (sound_file);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* add_event_feedbacks:
|
||||
*
|
||||
* Add the suitable feedacks to the event.
|
||||
*
|
||||
* Returns: `TRUE` if at least on feedback was added.
|
||||
*/
|
||||
static gboolean
|
||||
add_event_feedbacks (FbdFeedbackManager *self,
|
||||
FbdEvent *event,
|
||||
GSList *feedbacks,
|
||||
FbdFeedbackProfileLevel level,
|
||||
const char *sound_file)
|
||||
{
|
||||
gboolean has_vibra = FALSE, has_sound = FALSE;
|
||||
|
||||
/* Synthesize sound event for custom sound */
|
||||
if (sound_file && level >= FBD_FEEDBACK_PROFILE_LEVEL_FULL) {
|
||||
g_autoptr (FbdFeedbackSound) sound = NULL;
|
||||
|
||||
g_debug ("Using custom sound event '%s'", sound_file);
|
||||
sound = fbd_feedback_sound_new_from_file_name (sound_file);
|
||||
|
||||
fbd_event_add_feedback (event, FBD_FEEDBACK_BASE (sound));
|
||||
has_sound = TRUE;
|
||||
}
|
||||
|
||||
for (GSList *l = feedbacks; l; l = l->next) {
|
||||
FbdFeedbackBase *fb = FBD_FEEDBACK_BASE (l->data);
|
||||
|
||||
if (!fbd_feedback_is_available (FBD_FEEDBACK_BASE (fb)))
|
||||
continue;
|
||||
|
||||
if (FBD_IS_FEEDBACK_SOUND (fb) && has_sound)
|
||||
continue;
|
||||
|
||||
/* Handle one haptic feedback at a time. In practice haptics can handle multiple
|
||||
* patterns but none of the devices supports this atm */
|
||||
/* TODO: should respect priorities */
|
||||
if (FBD_IS_FEEDBACK_VIBRA (fb)) {
|
||||
if (fbd_dev_vibra_is_busy (self->vibra))
|
||||
continue;
|
||||
has_vibra = TRUE;
|
||||
}
|
||||
|
||||
/* Events take priority over the haptic interface */
|
||||
if (has_vibra)
|
||||
fbd_haptic_manager_end_feedback (self->haptic_manager);
|
||||
|
||||
fbd_event_add_feedback (event, fb);
|
||||
}
|
||||
|
||||
return (fbd_event_get_feedbacks (event) != NULL);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object,
|
||||
@@ -370,12 +434,13 @@ fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object,
|
||||
{
|
||||
FbdFeedbackManager *self;
|
||||
FbdEvent *event;
|
||||
GSList *feedbacks, *l;
|
||||
GSList *feedbacks;
|
||||
guint event_id;
|
||||
const gchar *sender;
|
||||
FbdFeedbackProfileLevel level, hint_level = FBD_FEEDBACK_PROFILE_LEVEL_FULL;
|
||||
gboolean found_fb = FALSE;
|
||||
gboolean hint_important = FALSE;
|
||||
g_autofree char *sound_file = NULL;
|
||||
|
||||
sender = g_dbus_method_invocation_get_sender (invocation);
|
||||
g_debug ("Event '%s' for '%s' from %s", arg_event, arg_app_id, sender);
|
||||
@@ -399,7 +464,7 @@ fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!parse_hints (arg_hints, &hint_level, &hint_important)) {
|
||||
if (!parse_hints (arg_hints, &hint_level, &hint_important, &sound_file)) {
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid hints");
|
||||
@@ -415,38 +480,11 @@ fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object,
|
||||
g_hash_table_insert (self->events, GUINT_TO_POINTER (event_id), event);
|
||||
|
||||
level = fbd_feedback_manager_get_effective_level (self, arg_app_id, hint_level, hint_important);
|
||||
|
||||
feedbacks = fbd_feedback_theme_lookup_feedback (self->theme, level, event);
|
||||
|
||||
if (feedbacks) {
|
||||
gboolean has_vibra = FALSE;
|
||||
|
||||
for (l = feedbacks; l; l = l->next) {
|
||||
FbdFeedbackBase *fb = FBD_FEEDBACK_BASE (l->data);
|
||||
|
||||
if (fbd_feedback_is_available (FBD_FEEDBACK_BASE (fb))) {
|
||||
/* Handle one haptic feedback at a time. In practice haptics can handle multiple
|
||||
* patterns but none of the devices supports this atm */
|
||||
/* TODO: should respect priorities */
|
||||
if (FBD_IS_FEEDBACK_VIBRA (fb)) {
|
||||
if (fbd_dev_vibra_is_busy (self->vibra))
|
||||
continue;
|
||||
has_vibra = TRUE;
|
||||
}
|
||||
|
||||
fbd_event_add_feedback (event, fb);
|
||||
|
||||
/* Events take priority over the haptic interface */
|
||||
if (has_vibra)
|
||||
fbd_haptic_manager_end_feedback (self->haptic_manager);
|
||||
|
||||
found_fb = TRUE;
|
||||
}
|
||||
}
|
||||
found_fb = add_event_feedbacks (self, event, feedbacks, level, sound_file);
|
||||
if (feedbacks)
|
||||
g_slist_free_full (feedbacks, g_object_unref);
|
||||
} else {
|
||||
/* No feedbacks found at all */
|
||||
found_fb = FALSE;
|
||||
}
|
||||
|
||||
lfb_gdbus_feedback_complete_trigger_feedback (object, invocation, event_id);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_EFFECT,
|
||||
PROP_FILE_NAME,
|
||||
PROP_LAST_PROP,
|
||||
};
|
||||
static GParamSpec *props[PROP_LAST_PROP];
|
||||
@@ -30,7 +31,8 @@ static GParamSpec *props[PROP_LAST_PROP];
|
||||
typedef struct _FbdFeedbackSound {
|
||||
FbdFeedbackBase parent;
|
||||
|
||||
gchar *effect;
|
||||
char *effect;
|
||||
char *file_name;
|
||||
} FbdFeedbackSound;
|
||||
|
||||
G_DEFINE_TYPE (FbdFeedbackSound, fbd_feedback_sound, FBD_TYPE_FEEDBACK_BASE);
|
||||
@@ -86,6 +88,9 @@ fbd_feedback_sound_set_property (GObject *object,
|
||||
case PROP_EFFECT:
|
||||
self->effect = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_FILE_NAME:
|
||||
self->file_name = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -104,6 +109,9 @@ fbd_feedback_sound_get_property (GObject *object,
|
||||
case PROP_EFFECT:
|
||||
g_value_set_string (value, fbd_feedback_sound_get_effect (self));
|
||||
break;
|
||||
case PROP_FILE_NAME:
|
||||
g_value_set_string (value, fbd_feedback_sound_get_file_name (self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -116,6 +124,7 @@ fbd_feedback_sound_finalize (GObject *object)
|
||||
FbdFeedbackSound *self = FBD_FEEDBACK_SOUND (object);
|
||||
|
||||
g_clear_pointer (&self->effect, g_free);
|
||||
g_clear_pointer (&self->file_name, g_free);
|
||||
|
||||
G_OBJECT_CLASS (fbd_feedback_sound_parent_class)->finalize (object);
|
||||
}
|
||||
@@ -142,6 +151,11 @@ fbd_feedback_sound_class_init (FbdFeedbackSoundClass *klass)
|
||||
"",
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
props[PROP_FILE_NAME] =
|
||||
g_param_spec_string ("file-name", "", "",
|
||||
NULL,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
|
||||
}
|
||||
|
||||
@@ -150,7 +164,22 @@ fbd_feedback_sound_init (FbdFeedbackSound *self)
|
||||
{
|
||||
}
|
||||
|
||||
const char *fbd_feedback_sound_get_effect (FbdFeedbackSound *self)
|
||||
|
||||
FbdFeedbackSound *
|
||||
fbd_feedback_sound_new_from_file_name (const char *filename)
|
||||
{
|
||||
return g_object_new (FBD_TYPE_FEEDBACK_SOUND, "file-name", filename, NULL);
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
fbd_feedback_sound_get_effect (FbdFeedbackSound *self)
|
||||
{
|
||||
return self->effect;
|
||||
}
|
||||
|
||||
const char *
|
||||
fbd_feedback_sound_get_file_name (FbdFeedbackSound *self)
|
||||
{
|
||||
return self->file_name;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ G_BEGIN_DECLS
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FbdFeedbackSound, fbd_feedback_sound, FBD, FEEDBACK_SOUND, FbdFeedbackBase);
|
||||
|
||||
const char *fbd_feedback_sound_get_effect (FbdFeedbackSound *self);
|
||||
FbdFeedbackSound *fbd_feedback_sound_new_from_file_name (const char *filename);
|
||||
const char *fbd_feedback_sound_get_effect (FbdFeedbackSound *self);
|
||||
const char *fbd_feedback_sound_get_file_name (FbdFeedbackSound *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -15,6 +15,7 @@ test_lfb_event_props (void)
|
||||
g_autofree gchar *evname = NULL;
|
||||
g_autofree gchar *profile = NULL;
|
||||
g_autofree gchar *app_id = NULL;
|
||||
g_autofree gchar *sound_file = NULL;
|
||||
gboolean important;
|
||||
gint timeout;
|
||||
|
||||
@@ -29,12 +30,14 @@ test_lfb_event_props (void)
|
||||
"feedback-profile", &profile,
|
||||
"app-id", &app_id,
|
||||
"important", &important,
|
||||
"sound-file", &sound_file,
|
||||
NULL);
|
||||
g_assert_cmpstr (evname, ==, "window-close");
|
||||
g_assert_cmpint (timeout, ==, -1);
|
||||
g_assert_null (profile);
|
||||
g_assert_false (important);
|
||||
g_assert_null (app_id);
|
||||
g_assert_null (sound_file);
|
||||
|
||||
g_assert_cmpint (lfb_event_get_end_reason (event), ==, LFB_EVENT_END_REASON_NATURAL);
|
||||
g_assert_cmpint (lfb_event_get_state (event), ==, LFB_EVENT_STATE_NONE);
|
||||
@@ -49,6 +52,11 @@ test_lfb_event_props (void)
|
||||
g_assert_cmpstr (app_id, ==, "com.example.foo");
|
||||
g_assert_cmpstr (lfb_event_get_app_id (event), ==, "com.example.foo");
|
||||
|
||||
g_object_set (event, "sound-file", "/does/not/exist", NULL);
|
||||
g_object_get (event, "sound-file", &sound_file, NULL);
|
||||
g_assert_cmpstr (sound_file, ==, "/does/not/exist");
|
||||
g_assert_cmpstr (lfb_event_get_sound_file (event), ==, "/does/not/exist");
|
||||
|
||||
lfb_uninit ();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user