Files
feedbackd/src/fbd-feedback-theme.c
T
Guido Günther e10b2cc150 feedback-theme: Allow to update theme
New profiles are added, existing profiles updated. This will
allow to merge themes.
2022-12-08 10:05:47 +01:00

360 lines
10 KiB
C

/*
* Copyright (C) 2020 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*/
#define G_LOG_DOMAIN "fbd-feedback-theme"
#include "fbd-feedback-base.h"
#include "fbd-feedback-dummy.h"
#include "fbd-feedback-sound.h"
#include "fbd-feedback-theme.h"
#include "fbd-feedback-vibra.h"
#include "fbd-feedback-profile.h"
#include <json-glib/json-glib.h>
enum {
PROP_0,
PROP_NAME,
PROP_PROFILES,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
typedef struct _FbdFeedbackTheme {
GObject parent;
char *name;
GHashTable *profiles;
} FbdFeedbackTheme;
static void json_serializable_iface_init (JsonSerializableIface *iface);
G_DEFINE_TYPE_WITH_CODE (FbdFeedbackTheme, fbd_feedback_theme, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE,
json_serializable_iface_init));
static JsonNode *
fbd_theme_serializable_serialize_property (JsonSerializable *serializable,
const gchar *property_name,
const GValue *value,
GParamSpec *pspec)
{
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (serializable);
JsonNode *node = NULL;
if (g_strcmp0 (property_name, "profiles") == 0) {
GHashTableIter iter;
gpointer key;
FbdFeedbackProfile *profile;
JsonArray *array = json_array_sized_new (FBD_FEEDBACK_PROFILE_N_PROFILES);
g_hash_table_iter_init (&iter, self->profiles);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *) &profile)) {
json_array_add_element (array, json_gobject_serialize (G_OBJECT(profile)));
}
node = json_node_init_array (json_node_alloc (), array);
} else {
node = json_serializable_default_serialize_property (serializable,
property_name,
value,
pspec);
}
return node;
}
static gboolean
fbd_theme_serializable_deserialize_property (JsonSerializable *serializable,
const gchar *property_name,
GValue *value,
GParamSpec *pspec,
JsonNode *property_node)
{
if (g_strcmp0 (property_name, "profiles") == 0) {
if (JSON_NODE_TYPE (property_node) == JSON_NODE_NULL) {
g_value_set_pointer (value, NULL);
return TRUE;
} else if (JSON_NODE_TYPE (property_node) == JSON_NODE_ARRAY) {
JsonArray *array = json_node_get_array (property_node);
guint i, array_len = json_array_get_length (array);
GHashTable *profiles = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
(GDestroyNotify)g_object_unref);
for (i = 0; i < array_len; i++) {
JsonNode *element_node = json_array_get_element (array, i);
FbdFeedbackProfile *profile;
gchar *name;
if (JSON_NODE_HOLDS_OBJECT (element_node)) {
profile = FBD_FEEDBACK_PROFILE (json_gobject_deserialize (FBD_TYPE_FEEDBACK_PROFILE,
element_node));
name = g_strdup (fbd_feedback_profile_get_name (profile));
g_hash_table_insert (profiles, name, profile);
} else {
return FALSE;
}
}
g_value_set_boxed (value, profiles);
return TRUE;
}
return FALSE;
} else {
return json_serializable_default_deserialize_property (serializable,
property_name,
value,
pspec,
property_node);
}
return FALSE;
}
static void
fbd_feedback_theme_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (object);
switch (property_id) {
case PROP_NAME:
fbd_feedback_theme_set_name (self, g_value_get_string (value));
break;
case PROP_PROFILES:
if (self->profiles)
g_hash_table_unref (self->profiles);
self->profiles = g_value_get_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
fbd_feedback_theme_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (object);
switch (property_id) {
case PROP_NAME:
g_value_set_string (value, fbd_feedback_theme_get_name (self));
break;
case PROP_PROFILES:
g_value_set_boxed (value, self->profiles);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
fbd_feedback_theme_dispose (GObject *object)
{
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (object);
g_clear_pointer (&self->profiles, g_hash_table_unref);
G_OBJECT_CLASS (fbd_feedback_theme_parent_class)->dispose (object);
}
static void
fbd_feedback_theme_finalize (GObject *object)
{
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (object);
g_clear_pointer (&self->name, g_free);
G_OBJECT_CLASS (fbd_feedback_theme_parent_class)->finalize (object);
}
static void
json_serializable_iface_init (JsonSerializableIface *iface)
{
iface->serialize_property = fbd_theme_serializable_serialize_property;
iface->deserialize_property = fbd_theme_serializable_deserialize_property;
}
static void
fbd_feedback_theme_class_init (FbdFeedbackThemeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = fbd_feedback_theme_set_property;
object_class->get_property = fbd_feedback_theme_get_property;
object_class->dispose = fbd_feedback_theme_dispose;
object_class->finalize = fbd_feedback_theme_finalize;
props[PROP_NAME] =
g_param_spec_string (
"name",
"Name",
"The theme name",
NULL,
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_PROFILES] =
g_param_spec_boxed (
"profiles",
"Profiles",
"The feedback profiles",
G_TYPE_HASH_TABLE,
/* Can't be CONSTRUCT_ONLY since json-glib can't handle it */
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
}
static void
fbd_feedback_theme_init (FbdFeedbackTheme *self)
{
self->profiles = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
(GDestroyNotify)g_object_unref);
}
FbdFeedbackTheme *
fbd_feedback_theme_new (const gchar *name)
{
return FBD_FEEDBACK_THEME (g_object_new (FBD_TYPE_FEEDBACK_THEME,
"name", name,
NULL));
}
FbdFeedbackTheme *
fbd_feedback_theme_new_from_data (const gchar *data, GError **error)
{
g_autoptr (JsonNode) node = json_from_string(data, error);
if (!node)
return NULL;
return FBD_FEEDBACK_THEME (json_gobject_deserialize (FBD_TYPE_FEEDBACK_THEME, node));
}
FbdFeedbackTheme *
fbd_feedback_theme_new_from_file (const gchar *filename, GError **error)
{
g_autofree char *data = NULL;
if (!g_file_get_contents (filename, &data, NULL, error))
return NULL;
return fbd_feedback_theme_new_from_data (data, error);
}
void
fbd_feedback_theme_set_name (FbdFeedbackTheme *self, const char *name)
{
g_return_if_fail (FBD_IS_FEEDBACK_THEME (self));
if (g_strcmp0 (self->name, name) == 0)
return;
g_free (self->name);
self->name = g_strdup (name);
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_NAME]);
}
const char *
fbd_feedback_theme_get_name (FbdFeedbackTheme *self)
{
g_return_val_if_fail (FBD_IS_FEEDBACK_THEME (self), "");
return self->name;
}
void
fbd_feedback_theme_add_profile (FbdFeedbackTheme *self, FbdFeedbackProfile *profile)
{
gchar *name;
g_return_if_fail (FBD_IS_FEEDBACK_THEME (self));
g_return_if_fail (FBD_IS_FEEDBACK_PROFILE (profile));
name = g_strdup (fbd_feedback_profile_get_name (profile));
g_hash_table_insert (self->profiles, name, g_object_ref (profile));
}
FbdFeedbackProfile *
fbd_feedback_theme_get_profile (FbdFeedbackTheme *self, const char *name)
{
g_return_val_if_fail (FBD_IS_FEEDBACK_THEME (self), NULL);
return g_hash_table_lookup (self->profiles, name);
}
GSList *
fbd_feedback_theme_lookup_feedback (FbdFeedbackTheme *self,
FbdFeedbackProfileLevel level,
FbdEvent *event)
{
GSList *feedbacks = NULL;
for (int i = level; i >= FBD_FEEDBACK_PROFILE_LEVEL_SILENT; i--) {
const char *profile_name = fbd_feedback_profile_level_to_string (i);
FbdFeedbackProfile *profile = fbd_feedback_theme_get_profile (self, profile_name);
FbdFeedbackBase *feedback = fbd_feedback_profile_get_feedback (profile,
fbd_event_get_event (event));
if (feedback) {
feedbacks = g_slist_prepend (feedbacks, g_object_ref(feedback));
}
}
if (!g_slist_length (feedbacks))
g_debug ("No feedback for event %s", fbd_event_get_event (event));
return feedbacks;
}
/**
* fbd_feedback_theme_update:
* @self: The feedback theme that should be updated
* @new: The feedback theme we want to take profiles and feedbacks new
*
* Merges two feedback themes. Feedbacks and profiles are read new
* the `new` theme. If feedback already exists in the same profile
* of `self` it is overwritten with the feedback in `new`.
*/
void
fbd_feedback_theme_update (FbdFeedbackTheme *self, FbdFeedbackTheme *new)
{
GHashTableIter iter;
const char *profile_name;
FbdFeedbackProfile *profile;
g_return_if_fail (FBD_IS_FEEDBACK_THEME (self));
g_return_if_fail (FBD_IS_FEEDBACK_THEME (new));
fbd_feedback_theme_set_name (self, fbd_feedback_theme_get_name (new));
g_hash_table_iter_init (&iter, new->profiles);
while (g_hash_table_iter_next (&iter, (gpointer)&profile_name, (gpointer)&profile)) {
FbdFeedbackProfile *current;
current = g_hash_table_lookup (self->profiles, profile_name);
if (current == NULL) {
current = fbd_feedback_profile_new (profile_name);
g_hash_table_insert (self->profiles,
g_strdup (profile_name),
current);
}
fbd_feedback_profile_update (current, profile);
}
}