Merge branch 'merge-themes' into 'main'
Implement recursive theme expansion and custom theme loading Closes #38 See merge request Librem5/feedbackd!93
This commit is contained in:
@@ -11,5 +11,9 @@
|
||||
(css-mode . (
|
||||
(css-indent-offset . 2)
|
||||
))
|
||||
(js-mode . (
|
||||
(indent-tabs-mode . nil)
|
||||
(js-indent-level . 2)
|
||||
))
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ stages:
|
||||
- export DEBIAN_FRONTEND=noninteractive
|
||||
- apt-get -y update
|
||||
- apt-get -y build-dep .
|
||||
- apt-get -y install gcovr
|
||||
- apt-get -y install gcovr git
|
||||
|
||||
.image: &image
|
||||
image:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "subprojects/gmobile"]
|
||||
path = subprojects/gmobile
|
||||
url = https://gitlab.gnome.org/guidog/gmobile.git
|
||||
@@ -79,17 +79,45 @@ app that triggered it crashes.
|
||||
|
||||
## Feedback theme
|
||||
Events are then mapped to a specific type of feedback (sound, led, vibra) via a
|
||||
device specific theme (since devices have different capabilities).
|
||||
device specific theme - since devices have different capabilities and
|
||||
different users different needs.
|
||||
|
||||
Feedbackd is shipped with a default theme `default.json`.
|
||||
You can add your own themes in one of two ways:
|
||||
You can add your own themes in multiple ways:
|
||||
|
||||
1. By exporting an environment variable `FEEDBACK_THEME` with a path to a
|
||||
valid theme file (not recommended, use for testing only), or
|
||||
2. By creating a theme file under `$XDG_CONFIG_HOME/feedbackd/themes/default.json`.
|
||||
If `XDG_CONFIG_HOME` environment variable is not set or empty, it will
|
||||
default to `$HOME/.config`, or
|
||||
3. By adding your theme file to one of the folders in the `XDG_DATA_DIRS`
|
||||
3. By creating a theme file under `$XDG_CONFIG_HOME/feedbackd/themes/custom.json`.
|
||||
You only specify the values you want to change in that theme and add an entry
|
||||
```json
|
||||
{
|
||||
"name: "custom"
|
||||
"parent-theme": "default"
|
||||
"profiles" : [
|
||||
...(entries you want to change go here)...
|
||||
]
|
||||
}
|
||||
```
|
||||
next to the `name` entry in. This has the upside that your theme
|
||||
gets way smaller and that new entries added to the default theme
|
||||
will automatically be used by your theme too. See
|
||||
[here](./tests/data/user-config/feedbackd/themes/custom.json) for
|
||||
an example. Once you have the file in place, tell feedbackd the
|
||||
them you want to use:
|
||||
|
||||
gsettings set org.sigxcpu.feedbackd theme custom
|
||||
|
||||
When you want to go back to the default theme just do:
|
||||
|
||||
gsettings reset org.sigxcpu.feedbackd theme
|
||||
|
||||
Note that you can name your theme as you wish but avoid theme names
|
||||
starting with `__` or `$` as this namespace is reserved.
|
||||
|
||||
4. By adding your theme file to one of the folders in the `XDG_DATA_DIRS`
|
||||
environment variable, appended with `feedbackd/themes/`. This folder isn't
|
||||
created automatically, so you have to create it yourself. Here's an example:
|
||||
```bash
|
||||
|
||||
@@ -14,6 +14,15 @@
|
||||
level given on events to the user.
|
||||
</description>
|
||||
</key>
|
||||
|
||||
<key name="theme" type="s">
|
||||
<default>'default'</default>
|
||||
<summary>The feedback theme to use</summary>
|
||||
<description>
|
||||
The feedback theme determines which feedbacks are triggered when
|
||||
feedbackd receives an event.
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.sigxcpu.feedbackd.application">
|
||||
|
||||
@@ -14,6 +14,13 @@ gio_unix = dependency('gio-unix-2.0', version: '>=2.50.0')
|
||||
glib = dependency('glib-2.0', version: '>=2.50.0')
|
||||
if get_option('daemon')
|
||||
gobject = dependency('gobject-2.0', version: '>=2.50.0')
|
||||
gmobile_subp = subproject('gmobile',
|
||||
default_options: [
|
||||
'examples=false',
|
||||
'gtk_doc=false',
|
||||
'tests=false',
|
||||
])
|
||||
gmobile = gmobile_subp.get_variable('libgmobile_dep')
|
||||
gsound = dependency('gsound')
|
||||
gudev = dependency('gudev-1.0', version: '>=232')
|
||||
json_glib = dependency('json-glib-1.0')
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Guido Günther
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
*/
|
||||
|
||||
#include "fbd.h"
|
||||
|
||||
GQuark
|
||||
fbd_error_quark (void)
|
||||
{
|
||||
static GQuark quark = 0;
|
||||
|
||||
if (!quark)
|
||||
quark = g_quark_from_static_string("fbd");
|
||||
|
||||
return quark;
|
||||
}
|
||||
@@ -126,3 +126,11 @@ static void
|
||||
fbd_feedback_dummy_init (FbdFeedbackDummy *self)
|
||||
{
|
||||
}
|
||||
|
||||
guint
|
||||
fbd_feedback_dummy_get_duration (FbdFeedbackDummy *self)
|
||||
{
|
||||
g_return_val_if_fail (FBD_IS_FEEDBACK_DUMMY (self), 0);
|
||||
|
||||
return self->duration;
|
||||
}
|
||||
|
||||
@@ -13,4 +13,6 @@ G_BEGIN_DECLS
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FbdFeedbackDummy, fbd_feedback_dummy, FBD, FEEDBACK_DUMMY, FbdFeedbackBase);
|
||||
|
||||
guint fbd_feedback_dummy_get_duration (FbdFeedbackDummy *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
+35
-83
@@ -14,6 +14,10 @@
|
||||
#include "fbd-feedback-vibra.h"
|
||||
#include "fbd-feedback-manager.h"
|
||||
#include "fbd-feedback-theme.h"
|
||||
#include "fbd-theme-expander.h"
|
||||
|
||||
#define GMOBILE_USE_UNSTABLE_API
|
||||
#include <gmobile.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib-unix.h>
|
||||
@@ -21,14 +25,12 @@
|
||||
|
||||
#define FEEDBACKD_SCHEMA_ID "org.sigxcpu.feedbackd"
|
||||
#define FEEDBACKD_KEY_PROFILE "profile"
|
||||
#define FEEDBACKD_THEME_VAR "FEEDBACK_THEME"
|
||||
#define FEEDBACKD_KEY_THEME "theme"
|
||||
|
||||
#define APP_SCHEMA FEEDBACKD_SCHEMA_ID ".application"
|
||||
#define APP_PREFIX "/org/sigxcpu/feedbackd/application/"
|
||||
|
||||
#define DEVICE_TREE_PATH "/sys/firmware/devicetree/base/compatible"
|
||||
#define DEVICE_NAME_MAX 1024
|
||||
|
||||
#define FEEDBACKD_THEME_VAR "FEEDBACK_THEME"
|
||||
|
||||
/**
|
||||
* SECTION:fbd-feedback-manager
|
||||
@@ -212,10 +214,15 @@ on_feedbackd_setting_changed (FbdFeedbackManager *self,
|
||||
|
||||
g_return_if_fail (FBD_IS_FEEDBACK_MANAGER (self));
|
||||
g_return_if_fail (G_IS_SETTINGS (settings));
|
||||
g_return_if_fail (!g_strcmp0 (key, FEEDBACKD_KEY_PROFILE));
|
||||
|
||||
profile = g_settings_get_string (settings, key);
|
||||
fbd_feedback_manager_set_profile (self, profile);
|
||||
if (g_str_equal (key, FEEDBACKD_KEY_PROFILE)) {
|
||||
profile = g_settings_get_string (settings, key);
|
||||
fbd_feedback_manager_set_profile (self, profile);
|
||||
} else if (g_str_equal (key, FEEDBACKD_KEY_THEME)) {
|
||||
fbd_feedback_manager_load_theme (self);
|
||||
} else {
|
||||
g_critical ("Unknown settings key '%s'", key);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -430,65 +437,6 @@ fbd_feedback_manager_handle_end_feedback (LfbGdbusFeedback *object,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
find_themefile (void)
|
||||
{
|
||||
gint i = 0;
|
||||
gsize len;
|
||||
const gchar *comp;
|
||||
|
||||
g_autoptr (GError) err = NULL;
|
||||
g_autofree gchar *user_config_path = NULL;
|
||||
gchar **xdg_data_dirs = (gchar **) g_get_system_data_dirs ();
|
||||
g_autofree gchar *compatibles = NULL;
|
||||
|
||||
// First look for a default file under $XDG_DATA_HOME
|
||||
user_config_path = g_build_filename (g_get_user_config_dir (), "feedbackd",
|
||||
"themes", "default.json", NULL);
|
||||
if (g_file_test (user_config_path, (G_FILE_TEST_EXISTS))) {
|
||||
g_debug ("Found user themefile at: %s", user_config_path);
|
||||
return g_steal_pointer (&user_config_path);
|
||||
}
|
||||
|
||||
// Try to read the device name
|
||||
if (g_file_test (DEVICE_TREE_PATH, (G_FILE_TEST_EXISTS))) {
|
||||
g_debug ("Found device tree device compatible at %s", DEVICE_TREE_PATH);
|
||||
|
||||
// Check if feedbackd has a proper config available this device
|
||||
if (!g_file_get_contents (DEVICE_TREE_PATH, &compatibles, &len, &err))
|
||||
g_warning ("Unable to read: %s", err->message);
|
||||
|
||||
comp = compatibles;
|
||||
while (comp - compatibles < len) {
|
||||
|
||||
// Iterate over $XDG_DATA_DIRS
|
||||
for (i = 0; i < g_strv_length (xdg_data_dirs); i++) {
|
||||
g_autofree gchar *config_path = NULL;
|
||||
g_autofree gchar *theme_file_name = NULL;
|
||||
|
||||
// We leave it to g_build_filename to add/remove erroneous path separators
|
||||
theme_file_name = g_strconcat (comp, ".json", NULL);
|
||||
config_path = g_build_filename (xdg_data_dirs[i], "feedbackd", "themes",
|
||||
theme_file_name, NULL);
|
||||
g_debug ("Searching for device specific themefile in %s", config_path);
|
||||
|
||||
// Check if file exist
|
||||
if (g_file_test (config_path, (G_FILE_TEST_EXISTS))) {
|
||||
g_debug ("Found themefile for this device at: %s", config_path);
|
||||
return g_steal_pointer (&config_path);
|
||||
}
|
||||
}
|
||||
|
||||
// Next compatible
|
||||
comp = strchr (comp, 0);
|
||||
comp++;
|
||||
}
|
||||
}else {
|
||||
g_debug ("Device tree path does not exist: %s", DEVICE_TREE_PATH);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
fbd_feedback_manager_constructed (GObject *object)
|
||||
@@ -497,11 +445,11 @@ fbd_feedback_manager_constructed (GObject *object)
|
||||
|
||||
G_OBJECT_CLASS (fbd_feedback_manager_parent_class)->constructed (object);
|
||||
|
||||
fbd_feedback_manager_load_theme(self);
|
||||
|
||||
g_signal_connect (self, "notify::profile", (GCallback)on_profile_changed, NULL);
|
||||
|
||||
self->settings = g_settings_new (FEEDBACKD_SCHEMA_ID);
|
||||
g_signal_connect_swapped (self->settings, "changed::" FEEDBACKD_KEY_THEME,
|
||||
G_CALLBACK (on_feedbackd_setting_changed), self);
|
||||
g_signal_connect_swapped (self->settings, "changed::" FEEDBACKD_KEY_PROFILE,
|
||||
G_CALLBACK (on_feedbackd_setting_changed), self);
|
||||
on_feedbackd_setting_changed (self, FEEDBACKD_KEY_PROFILE, self->settings);
|
||||
@@ -599,31 +547,35 @@ fbd_feedback_manager_get_dev_leds (FbdFeedbackManager *self)
|
||||
return self->leds;
|
||||
}
|
||||
|
||||
void fbd_feedback_manager_load_theme (FbdFeedbackManager *self) {
|
||||
g_autoptr (GError) err = NULL;
|
||||
void
|
||||
fbd_feedback_manager_load_theme (FbdFeedbackManager *self)
|
||||
{
|
||||
g_autoptr (FbdThemeExpander) expander = NULL;
|
||||
g_autoptr (FbdFeedbackTheme) theme = NULL;
|
||||
const gchar *themefile;
|
||||
g_autoptr (GError) err = NULL;
|
||||
g_auto (GStrv) compatibles = NULL;
|
||||
g_autofree char *theme_name = NULL;
|
||||
const char *theme_file = g_getenv (FEEDBACKD_THEME_VAR);
|
||||
|
||||
// Overide themefile with environment variable if requested
|
||||
themefile = g_getenv (FEEDBACKD_THEME_VAR);
|
||||
compatibles = gm_devicetree_get_compatibles (NULL, &err);
|
||||
if (compatibles == NULL && err) {
|
||||
g_debug ("Failed to get compatibles: %s", err->message);
|
||||
g_clear_error (&err);
|
||||
}
|
||||
|
||||
// Search for device-specific configuration
|
||||
if (!themefile)
|
||||
themefile = find_themefile ();
|
||||
if (theme_file == NULL)
|
||||
theme_name = g_settings_get_string (self->settings, FEEDBACKD_KEY_THEME);
|
||||
|
||||
// Fallback to default configuration if needed
|
||||
if (!themefile)
|
||||
themefile = FEEDBACKD_THEME_DIR "/default.json";
|
||||
g_info ("Using themefile: %s", themefile);
|
||||
|
||||
theme = fbd_feedback_theme_new_from_file (themefile, &err);
|
||||
expander = fbd_theme_expander_new ((const char *const *)compatibles,
|
||||
theme_name, theme_file);
|
||||
theme = fbd_theme_expander_load_theme_files (expander, &err);
|
||||
if (theme) {
|
||||
g_set_object(&self->theme, theme);
|
||||
} else {
|
||||
if (self->theme)
|
||||
g_warning ("Failed to reload theme: %s", err->message);
|
||||
else
|
||||
g_error ("Failed to load theme: %s", err->message); // No point to carry on
|
||||
g_error ("Failed to load any theme: %s", err->message); // No point to carry on
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ typedef struct _FbdFeedbackProfile {
|
||||
GObject parent;
|
||||
|
||||
gchar *name;
|
||||
GHashTable *feedbacks;
|
||||
GHashTable *feedbacks; /* key: event name, value: feedback */
|
||||
} FbdFeedbackProfile;
|
||||
|
||||
static void json_serializable_iface_init (JsonSerializableIface *iface);
|
||||
@@ -341,3 +341,33 @@ fbd_feedback_profile_level_to_string (FbdFeedbackProfileLevel level)
|
||||
return "full";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fbd_profile_update:
|
||||
* @self: The profile that should be updated
|
||||
* @new: The profile theme we want to take new feedbacks from
|
||||
*
|
||||
* Updates a profile. Feedbacks are read from the `new` profile. If
|
||||
* feedback already exists in `self` it is overwritten with the feedback
|
||||
* from `new`.
|
||||
*
|
||||
* It is not allowed to update a profile with a profile of a different name.
|
||||
*/
|
||||
void
|
||||
fbd_feedback_profile_update (FbdFeedbackProfile *self, FbdFeedbackProfile *new)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
const char *event_name;
|
||||
FbdFeedbackBase *fb;
|
||||
|
||||
g_return_if_fail (FBD_IS_FEEDBACK_PROFILE (self));
|
||||
g_return_if_fail (FBD_IS_FEEDBACK_PROFILE (new));
|
||||
|
||||
g_return_if_fail (g_str_equal (fbd_feedback_profile_get_name (self),
|
||||
fbd_feedback_profile_get_name (new)));
|
||||
|
||||
g_hash_table_iter_init (&iter, new->feedbacks);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer)&event_name, (gpointer)&fb)) {
|
||||
g_hash_table_insert (self->feedbacks, g_strdup (event_name), g_object_ref (fb));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ typedef enum _FbdFeedbackProfileLevel {
|
||||
G_DECLARE_FINAL_TYPE (FbdFeedbackProfile, fbd_feedback_profile, FBD, FEEDBACK_PROFILE, GObject);
|
||||
|
||||
FbdFeedbackProfile *fbd_feedback_profile_new (const gchar *name);
|
||||
void fbd_feedback_profile_update (FbdFeedbackProfile *self,
|
||||
FbdFeedbackProfile *new);
|
||||
const gchar *fbd_feedback_profile_get_name (FbdFeedbackProfile *self);
|
||||
void fbd_feedback_profile_add_feedback (FbdFeedbackProfile *self,
|
||||
FbdFeedbackBase *feedback);
|
||||
|
||||
+107
-25
@@ -18,6 +18,7 @@
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_NAME,
|
||||
PROP_PARENT_NAME,
|
||||
PROP_PROFILES,
|
||||
PROP_LAST_PROP,
|
||||
};
|
||||
@@ -27,6 +28,8 @@ typedef struct _FbdFeedbackTheme {
|
||||
GObject parent;
|
||||
|
||||
char *name;
|
||||
char *parent_name;
|
||||
|
||||
GHashTable *profiles;
|
||||
} FbdFeedbackTheme;
|
||||
|
||||
@@ -115,16 +118,18 @@ fbd_theme_serializable_deserialize_property (JsonSerializable *serializable,
|
||||
|
||||
static void
|
||||
fbd_feedback_theme_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_NAME:
|
||||
g_free (self->name);
|
||||
self->name = g_value_dup_string (value);
|
||||
fbd_feedback_theme_set_name (self, g_value_get_string (value));
|
||||
break;
|
||||
case PROP_PARENT_NAME:
|
||||
fbd_feedback_theme_set_parent_name (self, g_value_get_string (value));
|
||||
break;
|
||||
case PROP_PROFILES:
|
||||
if (self->profiles)
|
||||
@@ -140,15 +145,18 @@ fbd_feedback_theme_set_property (GObject *object,
|
||||
|
||||
static void
|
||||
fbd_feedback_theme_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, self->name);
|
||||
g_value_set_string (value, fbd_feedback_theme_get_name (self));
|
||||
break;
|
||||
case PROP_PARENT_NAME:
|
||||
g_value_set_string (value, fbd_feedback_theme_get_parent_name (self));
|
||||
break;
|
||||
case PROP_PROFILES:
|
||||
g_value_set_boxed (value, self->profiles);
|
||||
@@ -186,20 +194,6 @@ json_serializable_iface_init (JsonSerializableIface *iface)
|
||||
iface->deserialize_property = fbd_theme_serializable_deserialize_property;
|
||||
}
|
||||
|
||||
static void
|
||||
fbd_feedback_theme_constructed (GObject *object)
|
||||
{
|
||||
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (object);
|
||||
|
||||
G_OBJECT_CLASS (fbd_feedback_theme_parent_class)->constructed (object);
|
||||
|
||||
self->profiles = g_hash_table_new_full (g_str_hash,
|
||||
g_str_equal,
|
||||
g_free,
|
||||
(GDestroyNotify)g_object_unref);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_feedback_theme_class_init (FbdFeedbackThemeClass *klass)
|
||||
{
|
||||
@@ -208,7 +202,6 @@ fbd_feedback_theme_class_init (FbdFeedbackThemeClass *klass)
|
||||
object_class->set_property = fbd_feedback_theme_set_property;
|
||||
object_class->get_property = fbd_feedback_theme_get_property;
|
||||
|
||||
object_class->constructed = fbd_feedback_theme_constructed;
|
||||
object_class->dispose = fbd_feedback_theme_dispose;
|
||||
object_class->finalize = fbd_feedback_theme_finalize;
|
||||
|
||||
@@ -218,7 +211,15 @@ fbd_feedback_theme_class_init (FbdFeedbackThemeClass *klass)
|
||||
"Name",
|
||||
"The theme name",
|
||||
NULL,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
props[PROP_PARENT_NAME] =
|
||||
g_param_spec_string (
|
||||
"parent-name",
|
||||
"Parent theme Name",
|
||||
"The parent theme name",
|
||||
NULL,
|
||||
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
props[PROP_PROFILES] =
|
||||
g_param_spec_boxed (
|
||||
@@ -235,6 +236,10 @@ fbd_feedback_theme_class_init (FbdFeedbackThemeClass *klass)
|
||||
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 *
|
||||
@@ -268,6 +273,20 @@ fbd_feedback_theme_new_from_file (const gchar *filename, GError **error)
|
||||
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)
|
||||
{
|
||||
@@ -276,6 +295,31 @@ fbd_feedback_theme_get_name (FbdFeedbackTheme *self)
|
||||
return self->name;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fbd_feedback_theme_set_parent_name (FbdFeedbackTheme *self, const char *parent_name)
|
||||
{
|
||||
g_return_if_fail (FBD_IS_FEEDBACK_THEME (self));
|
||||
|
||||
if (g_strcmp0 (self->parent_name, parent_name) == 0)
|
||||
return;
|
||||
|
||||
g_free (self->parent_name);
|
||||
self->parent_name = g_strdup (parent_name);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_PARENT_NAME]);
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
fbd_feedback_theme_get_parent_name (FbdFeedbackTheme *self)
|
||||
{
|
||||
g_return_val_if_fail (FBD_IS_FEEDBACK_THEME (self), "");
|
||||
|
||||
return self->parent_name;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fbd_feedback_theme_add_profile (FbdFeedbackTheme *self, FbdFeedbackProfile *profile)
|
||||
{
|
||||
@@ -317,3 +361,41 @@ fbd_feedback_theme_lookup_feedback (FbdFeedbackTheme *self,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,13 @@ G_DECLARE_FINAL_TYPE (FbdFeedbackTheme, fbd_feedback_theme, FBD, FEEDBACK_THEME,
|
||||
FbdFeedbackTheme *fbd_feedback_theme_new (const char *name);
|
||||
FbdFeedbackTheme *fbd_feedback_theme_new_from_data (const gchar *data, GError **error);
|
||||
FbdFeedbackTheme *fbd_feedback_theme_new_from_file (const gchar *filename, GError **error);
|
||||
void fbd_feedback_theme_update (FbdFeedbackTheme *self, FbdFeedbackTheme *from);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
@@ -147,7 +147,8 @@ fbd_feedback_vibra_init (FbdFeedbackVibra *self)
|
||||
{
|
||||
}
|
||||
|
||||
guint fbd_feedback_vibra_get_duration (FbdFeedbackVibra *self)
|
||||
guint
|
||||
fbd_feedback_vibra_get_duration (FbdFeedbackVibra *self)
|
||||
{
|
||||
FbdFeedbackVibraPrivate *priv;
|
||||
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Guido Günther
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "fbd-theme-expander"
|
||||
|
||||
#include "fbd.h"
|
||||
#include "fbd-feedback-theme.h"
|
||||
#include "fbd-theme-expander.h"
|
||||
|
||||
#define DEFAULT_THEME_NAME "default"
|
||||
#define DEVICE_THEME_NAME "$device"
|
||||
|
||||
#define MAX_THEME_DEPTH 10
|
||||
|
||||
/**
|
||||
* SECTION:theme-expander
|
||||
* @short_description: Feedback theme expander
|
||||
* @Title: FbdThemeExpander
|
||||
*
|
||||
* The theme expander reads themes from disks and expands references
|
||||
* to other themes
|
||||
*/
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_THEME_NAME,
|
||||
PROP_THEME_FILE,
|
||||
PROP_COMPATIBLES,
|
||||
PROP_LAST_PROP
|
||||
};
|
||||
static GParamSpec *props[PROP_LAST_PROP];
|
||||
|
||||
struct _FbdThemeExpander {
|
||||
GObject parent;
|
||||
|
||||
char *theme_name;
|
||||
char *theme_file;
|
||||
gboolean device_theme_loaded;
|
||||
GStrv compatibles;
|
||||
};
|
||||
G_DEFINE_TYPE (FbdThemeExpander, fbd_theme_expander, G_TYPE_OBJECT)
|
||||
|
||||
|
||||
static void
|
||||
fbd_theme_expander_set_compatibles (FbdThemeExpander *self, const char * const * compatibles)
|
||||
{
|
||||
g_return_if_fail (FBD_IS_THEME_EXPANDER (self));
|
||||
|
||||
g_strfreev (self->compatibles);
|
||||
self->compatibles = g_strdupv ((GStrv)compatibles);
|
||||
|
||||
/* Make sure we reload the device theme */
|
||||
self->device_theme_loaded = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
fbd_theme_expander_set_theme_name (FbdThemeExpander *self, const char *theme_name)
|
||||
{
|
||||
g_return_if_fail (FBD_IS_THEME_EXPANDER (self));
|
||||
|
||||
g_free (self->theme_name);
|
||||
if (theme_name)
|
||||
self->theme_name = g_strdup (theme_name);
|
||||
else
|
||||
self->theme_name = g_strdup (DEFAULT_THEME_NAME);
|
||||
|
||||
/* Make sure we reload the device theme */
|
||||
self->device_theme_loaded = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
fbd_theme_expander_set_theme_file (FbdThemeExpander *self, const char *theme_file)
|
||||
{
|
||||
g_return_if_fail (FBD_IS_THEME_EXPANDER (self));
|
||||
|
||||
g_free (self->theme_file);
|
||||
self->theme_file = g_strdup (theme_file);
|
||||
|
||||
/* Make sure we reload the device theme */
|
||||
self->device_theme_loaded = FALSE;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
fbd_theme_expander_find_theme_in_xdg_data (const char *theme_name)
|
||||
{
|
||||
const char * const *xdg_data_dirs = g_get_system_data_dirs ();
|
||||
g_autofree char *theme_file_name = g_strconcat (theme_name, ".json", NULL);
|
||||
|
||||
for (int i = 0; xdg_data_dirs[i] != NULL; i++) {
|
||||
g_autofree char *theme_path = NULL;
|
||||
|
||||
theme_path = g_build_filename (xdg_data_dirs[i], "feedbackd", "themes",
|
||||
theme_file_name, NULL);
|
||||
g_debug ("Looking for theme file at %s", theme_path);
|
||||
|
||||
/* Check if file exist */
|
||||
if (g_file_test (theme_path, (G_FILE_TEST_EXISTS))) {
|
||||
g_info ("Loading theme file at '%s'", theme_path);
|
||||
return g_steal_pointer (&theme_path);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
fbd_theme_expander_find_device_theme_path (FbdThemeExpander *self, const char *theme_name)
|
||||
{
|
||||
/* Device specific lookup only for default theme */
|
||||
if (g_str_equal (theme_name, DEVICE_THEME_NAME) == FALSE &&
|
||||
g_str_equal (theme_name, DEFAULT_THEME_NAME) == FALSE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->device_theme_loaded)
|
||||
return NULL;
|
||||
|
||||
self->device_theme_loaded = TRUE;
|
||||
|
||||
/* no compatibles found */
|
||||
if (self->compatibles == NULL)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < g_strv_length (self->compatibles); i++) {
|
||||
const char *compatible = self->compatibles[i];
|
||||
g_autofree char *theme_path = NULL;
|
||||
|
||||
theme_path = fbd_theme_expander_find_theme_in_xdg_data (compatible);
|
||||
if (theme_path) {
|
||||
g_info ("Loading themefile for compatible '%s' at: %s", compatible, theme_path);
|
||||
return g_steal_pointer (&theme_path);
|
||||
}
|
||||
}
|
||||
|
||||
g_debug ("No device theme found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
fbd_theme_expander_find_user_theme_path (const char *theme_name)
|
||||
{
|
||||
g_autofree char *filename = g_strdup_printf ("%s.json", theme_name);
|
||||
g_autofree char *user_config_path = NULL;
|
||||
|
||||
user_config_path = g_build_filename (g_get_user_config_dir (), "feedbackd",
|
||||
"themes", filename, NULL);
|
||||
if (g_file_test (user_config_path, (G_FILE_TEST_EXISTS))) {
|
||||
g_info ("Found theme file at: %s", user_config_path);
|
||||
return g_steal_pointer (&user_config_path);
|
||||
}
|
||||
|
||||
g_debug ("No user theme found for '%s'", theme_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
fbd_theme_expander_find_theme_path (FbdThemeExpander *self, const char *theme_name)
|
||||
{
|
||||
g_autofree char *file_name = NULL;
|
||||
char *theme_path;
|
||||
|
||||
g_assert (theme_name);
|
||||
|
||||
theme_path = fbd_theme_expander_find_user_theme_path (theme_name);
|
||||
if (theme_path)
|
||||
return theme_path;
|
||||
|
||||
theme_path = fbd_theme_expander_find_device_theme_path (self, theme_name);
|
||||
if (theme_path)
|
||||
return theme_path;
|
||||
|
||||
if (g_str_equal (theme_name, DEFAULT_THEME_NAME) == FALSE)
|
||||
g_critical ("Theme '%s' not found, falling back to default theme", theme_name);
|
||||
|
||||
theme_path = fbd_theme_expander_find_theme_in_xdg_data (DEFAULT_THEME_NAME);
|
||||
if (theme_path)
|
||||
return theme_path;
|
||||
|
||||
/* Last resort is shipped default config */
|
||||
file_name = g_strdup_printf ("%s.json", theme_name);
|
||||
g_info ("Using theme_file: %s", file_name);
|
||||
return g_build_filename (FEEDBACKD_THEME_DIR, file_name, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_theme_expander_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FbdThemeExpander *self = FBD_THEME_EXPANDER (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_THEME_NAME:
|
||||
fbd_theme_expander_set_theme_name (self, g_value_get_string (value));
|
||||
break;
|
||||
case PROP_THEME_FILE:
|
||||
fbd_theme_expander_set_theme_file (self, g_value_get_string (value));
|
||||
break;
|
||||
case PROP_COMPATIBLES:
|
||||
fbd_theme_expander_set_compatibles (self, g_value_get_boxed (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_theme_expander_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FbdThemeExpander *self = FBD_THEME_EXPANDER (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_THEME_NAME:
|
||||
g_value_set_string (value, fbd_theme_expander_get_theme_name (self));
|
||||
break;
|
||||
case PROP_THEME_FILE:
|
||||
g_value_set_string (value, fbd_theme_expander_get_theme_file (self));
|
||||
break;
|
||||
case PROP_COMPATIBLES:
|
||||
g_value_set_boxed (value, fbd_theme_expander_get_compatibles (self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_theme_expander_finalize (GObject *object)
|
||||
{
|
||||
FbdThemeExpander *self = FBD_THEME_EXPANDER(object);
|
||||
|
||||
g_clear_pointer (&self->theme_name, g_free);
|
||||
g_clear_pointer (&self->theme_file, g_free);
|
||||
g_clear_pointer (&self->compatibles, g_strfreev);
|
||||
|
||||
G_OBJECT_CLASS (fbd_theme_expander_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_theme_expander_class_init (FbdThemeExpanderClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->get_property = fbd_theme_expander_get_property;
|
||||
object_class->set_property = fbd_theme_expander_set_property;
|
||||
object_class->finalize = fbd_theme_expander_finalize;
|
||||
|
||||
/**
|
||||
* FbdThemeExpander:theme-name:
|
||||
*
|
||||
* Specifies the theme to expand. Assumes `default` if unset.
|
||||
*
|
||||
* The `compatibles` defines which device specific theme bits are loaded.
|
||||
*/
|
||||
props[PROP_THEME_NAME] =
|
||||
g_param_spec_string ("theme-name", "", "",
|
||||
DEFAULT_THEME_NAME,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY |
|
||||
G_PARAM_STATIC_STRINGS);
|
||||
/**
|
||||
* FbdThemeExpander:theme-file:
|
||||
*
|
||||
* Specifies the theme-file to load an expand. Takes preference over `theme`.
|
||||
*/
|
||||
props[PROP_THEME_FILE] =
|
||||
g_param_spec_string ("theme-file", "", "",
|
||||
NULL,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY |
|
||||
G_PARAM_STATIC_STRINGS);
|
||||
/**
|
||||
* FbdThemeExpander:compatibles:
|
||||
*
|
||||
* Specifies the device types this device is compatible with. The
|
||||
* device compatbiles to look for. When unset the value is retrieved
|
||||
* from /sysfs.
|
||||
*
|
||||
* The `compatibles` defines which device specific theme bits are loaded.
|
||||
*/
|
||||
props[PROP_COMPATIBLES] =
|
||||
g_param_spec_boxed ("compatibles", "", "",
|
||||
G_TYPE_STRV,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_theme_expander_init (FbdThemeExpander *self)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
FbdThemeExpander *
|
||||
fbd_theme_expander_new (const char * const *compatibles,
|
||||
const char *theme_name,
|
||||
const char *theme_file)
|
||||
{
|
||||
return FBD_THEME_EXPANDER (g_object_new (FBD_TYPE_THEME_EXPANDER,
|
||||
"theme-name", theme_name,
|
||||
"theme-file", theme_file,
|
||||
"compatibles", compatibles,
|
||||
NULL));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
update_theme (gpointer data, gpointer user_data)
|
||||
{
|
||||
FbdFeedbackTheme *theme = FBD_FEEDBACK_THEME (data);
|
||||
FbdFeedbackTheme *merged = FBD_FEEDBACK_THEME (user_data);
|
||||
|
||||
g_assert (FBD_IS_FEEDBACK_THEME (theme));
|
||||
g_assert (FBD_IS_FEEDBACK_THEME (merged));
|
||||
|
||||
fbd_feedback_theme_update (merged, theme);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fbd_theme_expander_load_theme_files:
|
||||
* @self: The theme expander
|
||||
* @err: return location for error or %NULL
|
||||
*
|
||||
* Parses a theme recursively taking the theme's `parent-name` relations into
|
||||
* account as well as the expander's list of `compatibles`.
|
||||
*
|
||||
* Returns: (transfer full)(allow-none): The parsed theme or %NULL on error
|
||||
*/
|
||||
FbdFeedbackTheme *
|
||||
fbd_theme_expander_load_theme_files (FbdThemeExpander *self, GError **err)
|
||||
{
|
||||
g_autoqueue (FbdFeedbackTheme) queue = g_queue_new ();
|
||||
g_autoptr (FbdFeedbackTheme) merged = fbd_feedback_theme_new ("merged-theme");
|
||||
g_autoptr (FbdFeedbackTheme) theme = NULL;
|
||||
g_autofree char *theme_file = NULL;
|
||||
guint len = 0;
|
||||
|
||||
g_return_val_if_fail (FBD_IS_THEME_EXPANDER (self), NULL);
|
||||
g_return_val_if_fail (err == NULL || *err == NULL, NULL);
|
||||
|
||||
if (self->theme_file) {
|
||||
theme_file = g_strdup (self->theme_file);
|
||||
} else {
|
||||
theme_file = fbd_theme_expander_find_theme_path (self, self->theme_name);
|
||||
if (g_strcmp0 (self->theme_file, theme_file)) {
|
||||
self->theme_file = g_steal_pointer (&theme_file);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_THEME_FILE]);
|
||||
}
|
||||
}
|
||||
|
||||
theme = fbd_feedback_theme_new_from_file (self->theme_file, err);
|
||||
if (theme == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Build a list of themes */
|
||||
while (TRUE) {
|
||||
g_autofree char *parent_path = NULL;
|
||||
const char *parent_name, *theme_name;
|
||||
|
||||
if (len > MAX_THEME_DEPTH) {
|
||||
g_set_error (err, fbd_error_quark(), FBD_ERROR_THEME_EXPAND, "Theme depth exceeded");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
theme_name = fbd_feedback_theme_get_name (theme);
|
||||
if (theme_name == NULL || theme_name[0] == '\0') {
|
||||
g_set_error (err, fbd_error_quark(), FBD_ERROR_THEME_EXPAND,
|
||||
"Theme name of %s can't be empty", self->theme_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parent_name = fbd_feedback_theme_get_parent_name (theme);
|
||||
|
||||
if (parent_name &&
|
||||
g_str_equal (fbd_feedback_theme_get_name (theme), DEFAULT_THEME_NAME)) {
|
||||
g_set_error (err, fbd_error_quark(), FBD_ERROR_THEME_EXPAND,
|
||||
"Default theme can't specify a parent");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parent_name = fbd_feedback_theme_get_parent_name (theme);
|
||||
|
||||
g_queue_push_head (queue, g_steal_pointer (&theme));
|
||||
|
||||
if (parent_name == NULL)
|
||||
break;
|
||||
|
||||
parent_path = fbd_theme_expander_find_theme_path (self, parent_name);
|
||||
theme = fbd_feedback_theme_new_from_file (parent_path, err);
|
||||
if (theme == NULL)
|
||||
return NULL;
|
||||
|
||||
len++;
|
||||
}
|
||||
|
||||
/* Merge themes bottom to top */
|
||||
g_queue_foreach (queue, update_theme, merged);
|
||||
|
||||
fbd_feedback_theme_set_name (merged, self->theme_name);
|
||||
return g_steal_pointer (&merged);
|
||||
}
|
||||
|
||||
const char *
|
||||
fbd_theme_expander_get_theme_name (FbdThemeExpander *self)
|
||||
{
|
||||
g_return_val_if_fail (FBD_IS_THEME_EXPANDER (self), NULL);
|
||||
|
||||
return self->theme_name;
|
||||
|
||||
}
|
||||
|
||||
const char *
|
||||
fbd_theme_expander_get_theme_file (FbdThemeExpander *self)
|
||||
{
|
||||
g_return_val_if_fail (FBD_IS_THEME_EXPANDER (self), NULL);
|
||||
|
||||
return self->theme_file;
|
||||
}
|
||||
|
||||
const char * const *
|
||||
fbd_theme_expander_get_compatibles (FbdThemeExpander *self)
|
||||
{
|
||||
g_return_val_if_fail (FBD_IS_THEME_EXPANDER (self), NULL);
|
||||
|
||||
return (const char * const*)self->compatibles;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Guido Günther
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fbd-feedback-theme.h"
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FBD_TYPE_THEME_EXPANDER (fbd_theme_expander_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FbdThemeExpander, fbd_theme_expander, FBD, THEME_EXPANDER, GObject)
|
||||
|
||||
|
||||
FbdThemeExpander *fbd_theme_expander_new (const char * const *compatibles,
|
||||
const char *theme_name,
|
||||
const char *theme_file);
|
||||
FbdFeedbackTheme *fbd_theme_expander_load_theme_files (FbdThemeExpander *self,
|
||||
GError **err);
|
||||
const char *fbd_theme_expander_get_theme_name (FbdThemeExpander *self);
|
||||
const char *fbd_theme_expander_get_theme_file (FbdThemeExpander *self);
|
||||
const char * const *fbd_theme_expander_get_compatibles (FbdThemeExpander *self);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -17,19 +17,6 @@
|
||||
|
||||
static GMainLoop *loop;
|
||||
|
||||
|
||||
GQuark
|
||||
fbd_error_quark (void)
|
||||
{
|
||||
static GQuark quark = 0;
|
||||
|
||||
if (!quark)
|
||||
quark = g_quark_from_static_string("fbd");
|
||||
|
||||
return quark;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
quit_cb (gpointer user_data)
|
||||
{
|
||||
@@ -105,6 +92,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
g_autoptr(GError) err = NULL;
|
||||
g_autoptr(GOptionContext) opt_context = NULL;
|
||||
g_autoptr (FbdFeedbackManager) manager = NULL;
|
||||
|
||||
opt_context = g_option_context_new ("- A daemon to trigger event feedback");
|
||||
if (!g_option_context_parse (opt_context, &argc, &argv, &err)) {
|
||||
@@ -113,6 +101,9 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
manager = fbd_feedback_manager_get_default ();
|
||||
fbd_feedback_manager_load_theme (manager);
|
||||
|
||||
g_unix_signal_add (SIGTERM, quit_cb, NULL);
|
||||
g_unix_signal_add (SIGINT, quit_cb, NULL);
|
||||
g_unix_signal_add (SIGHUP, reload_cb, NULL);
|
||||
@@ -130,6 +121,4 @@ int main(int argc, char *argv[])
|
||||
|
||||
g_main_loop_run (loop);
|
||||
g_main_loop_unref (loop);
|
||||
|
||||
g_object_unref (fbd_feedback_manager_get_default ());
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ G_BEGIN_DECLS
|
||||
|
||||
typedef enum {
|
||||
FBD_ERROR_FAILED = 0,
|
||||
FBD_ERROR_THEME_EXPAND = 1,
|
||||
} FbdError;
|
||||
|
||||
GQuark fbd_error_quark (void);
|
||||
|
||||
+3
-1
@@ -12,7 +12,7 @@ fbd_enum_sources = gnome.mkenums_simple('fbd-enums',
|
||||
sources = [
|
||||
generated_dbus_sources,
|
||||
fbd_enum_sources,
|
||||
'fbd.c',
|
||||
'fbd-error.c',
|
||||
'fbd-dev-vibra.c',
|
||||
'fbd-dev-sound.c',
|
||||
'fbd-dev-leds.c',
|
||||
@@ -27,6 +27,7 @@ sources = [
|
||||
'fbd-feedback-vibra.c',
|
||||
'fbd-feedback-vibra-periodic.c',
|
||||
'fbd-feedback-vibra-rumble.c',
|
||||
'fbd-theme-expander.c',
|
||||
'fbd-udev.c',
|
||||
]
|
||||
|
||||
@@ -34,6 +35,7 @@ fbd_deps = [
|
||||
gio,
|
||||
gio_unix,
|
||||
glib,
|
||||
gmobile,
|
||||
gobject,
|
||||
gsound,
|
||||
gudev,
|
||||
|
||||
Submodule
+1
Submodule subprojects/gmobile added at 1039e78081
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name" : "base",
|
||||
"profiles" : [
|
||||
{
|
||||
"name" : "full",
|
||||
"feedbacks" : [
|
||||
{
|
||||
"type" : "Dummy",
|
||||
"event-name" : "test-dummy-00",
|
||||
"duration" : 0x01
|
||||
},
|
||||
{
|
||||
"type" : "Dummy",
|
||||
"event-name" : "test-dummy-01",
|
||||
"duration" : 0x02
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "quiet",
|
||||
"feedbacks" : [
|
||||
{
|
||||
"type" : "Dummy",
|
||||
"event-name" : "test-dummy-00",
|
||||
"duration" : 0x03
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name" : "test",
|
||||
"profiles" : [
|
||||
{
|
||||
"name" : "full",
|
||||
"feedbacks" : [
|
||||
{
|
||||
"type" : "Dummy",
|
||||
"event-name" : "test-dummy-00",
|
||||
"duration" : 0x10
|
||||
},
|
||||
{
|
||||
"type" : "Dummy",
|
||||
"event-name" : "test-dummy-10",
|
||||
"duration" : 0x20
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "quiet",
|
||||
"feedbacks" : [
|
||||
{
|
||||
"type" : "Dummy",
|
||||
"event-name" : "test-dummy-10",
|
||||
"duration" : 0x30
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "silent",
|
||||
"feedbacks" : [
|
||||
{
|
||||
"type" : "Dummy",
|
||||
"event-name" : "test-dummy-10",
|
||||
"duration" : 0x40
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name" : "custom",
|
||||
"parent-name": "default",
|
||||
"profiles" : [
|
||||
{
|
||||
"name" : "full",
|
||||
"feedbacks" : [
|
||||
{
|
||||
"event-name" : "test-dummy-0",
|
||||
"type" : "Dummy",
|
||||
"duration" : 0x10
|
||||
},
|
||||
{
|
||||
"event-name" : "test-dummy-2",
|
||||
"type" : "Dummy",
|
||||
"duration" : 0x30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name" : "$device",
|
||||
"parent-name": "default"
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name" : "default",
|
||||
"profiles" : [
|
||||
{
|
||||
"name" : "full",
|
||||
"feedbacks" : [
|
||||
{
|
||||
"event-name" : "test-dummy-0",
|
||||
"type" : "Dummy"
|
||||
},
|
||||
{
|
||||
"event-name" : "test-dummy-1",
|
||||
"type" : "Dummy"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "quiet",
|
||||
"feedbacks" : [
|
||||
{
|
||||
"event-name" : "test-dummy-0",
|
||||
"type" : "Dummy"
|
||||
},
|
||||
{
|
||||
"event-name" : "test-dummy-1",
|
||||
"type" : "Dummy"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name" : "$device",
|
||||
"parent-name": "default",
|
||||
"profiles" : [
|
||||
{
|
||||
"name" : "full",
|
||||
"feedbacks" : [
|
||||
{
|
||||
"event-name" : "test-dummy-0",
|
||||
"type" : "Dummy",
|
||||
"duration" : 0x10
|
||||
},
|
||||
{
|
||||
"event-name" : "test-dummy-2",
|
||||
"type" : "Dummy",
|
||||
"duration" : 0x20
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+10
-5
@@ -8,10 +8,10 @@ test_env.set('GSETTINGS_BACKEND', 'memory')
|
||||
test_env.set('GSETTINGS_SCHEMA_DIR', '@0@/data'.format(meson.project_build_root()))
|
||||
test_env.set('PYTHONDONTWRITEBYTECODE', 'yes')
|
||||
test_env.set('MALLOC_CHECK_', '2')
|
||||
test_env.set('XDG_CONFIG_HOME', meson.current_source_dir())
|
||||
test_env.set('XDG_CONFIG_HOME', meson.current_source_dir() / 'data' / 'user-config')
|
||||
test_env.set('XDG_CONFIG_DIRS', meson.current_source_dir())
|
||||
|
||||
# Shared lfbrary Unit tests
|
||||
# Shared library Unit tests
|
||||
|
||||
test_lfb_cflags = [
|
||||
'-DTEST_APP_ID="org.sigxcpu.feedbackd_test"',
|
||||
@@ -52,9 +52,13 @@ endforeach
|
||||
# Daemon Unit tests
|
||||
if get_option('daemon')
|
||||
|
||||
test_env_fbd = test_env
|
||||
test_env_fbd.set('XDG_DATA_DIRS', meson.current_source_dir() / 'data' / 'xdg-data')
|
||||
|
||||
test_fbd_cflags = [
|
||||
'-DTEST_APP_ID="org.sigxcpu.feedbackd_test"',
|
||||
]
|
||||
'-DTEST_APP_ID="org.sigxcpu.feedbackd_test"',
|
||||
'-DTEST_DATA_DIR="@0@"'.format(join_paths(meson.current_source_dir(), 'data')),
|
||||
]
|
||||
|
||||
test_fbd_link_args = [
|
||||
'-fPIC',
|
||||
@@ -68,6 +72,7 @@ fbd_tests = [
|
||||
'fbd-feedback-profile',
|
||||
'fbd-feedback-theme',
|
||||
'fbd-event',
|
||||
'fbd-theme-expander',
|
||||
]
|
||||
|
||||
foreach test : fbd_tests
|
||||
@@ -78,7 +83,7 @@ foreach test : fbd_tests
|
||||
link_args : test_fbd_link_args,
|
||||
include_directories : fbd_inc,
|
||||
dependencies : test_fbd_deps)
|
||||
test(test, t, env : test_env)
|
||||
test(test, t, env : test_env_fbd)
|
||||
endforeach
|
||||
|
||||
|
||||
|
||||
@@ -81,6 +81,49 @@ test_fbd_feedback_profile_parse (void)
|
||||
g_assert_true (FBD_IS_FEEDBACK_VIBRA(fb));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_fbd_feedback_profile_update (void)
|
||||
{
|
||||
FbdFeedbackBase *fb;
|
||||
FbdFeedbackProfile *a = fbd_feedback_profile_new (PROFILE_NAME);
|
||||
FbdFeedbackProfile *b = fbd_feedback_profile_new (PROFILE_NAME);
|
||||
|
||||
g_autoptr (FbdFeedbackDummy) fb_a_1 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY,
|
||||
"event-name", "a-1",
|
||||
NULL);
|
||||
g_autoptr (FbdFeedbackDummy) fb_a_2 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY,
|
||||
"event-name", "a-2",
|
||||
NULL);
|
||||
/* New event */
|
||||
g_autoptr (FbdFeedbackDummy) fb_b_1 = g_object_new (FBD_TYPE_FEEDBACK_DUMMY,
|
||||
"event-name", "b-1",
|
||||
NULL);
|
||||
/* overwrite existing event */
|
||||
g_autoptr (FbdFeedbackDummy) fb_b_2 = g_object_new (FBD_TYPE_FEEDBACK_VIBRA,
|
||||
"event-name", "a-2",
|
||||
NULL);
|
||||
|
||||
fbd_feedback_profile_add_feedback (a, FBD_FEEDBACK_BASE(fb_a_1));
|
||||
fbd_feedback_profile_add_feedback (a, FBD_FEEDBACK_BASE(fb_a_2));
|
||||
|
||||
fbd_feedback_profile_add_feedback (b, FBD_FEEDBACK_BASE(fb_b_1));
|
||||
fbd_feedback_profile_add_feedback (b, FBD_FEEDBACK_BASE(fb_b_2));
|
||||
|
||||
fbd_feedback_profile_update (a,b);
|
||||
|
||||
fb = fbd_feedback_profile_get_feedback (a, "a-1");
|
||||
g_assert_true (FBD_IS_FEEDBACK_DUMMY(fb));
|
||||
fb = fbd_feedback_profile_get_feedback (a, "a-2");
|
||||
g_assert_true (FBD_IS_FEEDBACK_VIBRA(fb));
|
||||
fb = fbd_feedback_profile_get_feedback (a, "b-1");
|
||||
g_assert_true (FBD_IS_FEEDBACK_DUMMY(fb));
|
||||
|
||||
g_assert_finalize_object (b);
|
||||
g_assert_finalize_object (a);
|
||||
}
|
||||
|
||||
|
||||
gint
|
||||
main (gint argc, gchar *argv[])
|
||||
{
|
||||
@@ -89,6 +132,7 @@ main (gint argc, gchar *argv[])
|
||||
g_test_add_func("/feedbackd/fbd/feedback-profile/name", test_fbd_feedback_profile_name);
|
||||
g_test_add_func("/feedbackd/fbd/feedback-profile/feedbacks", test_fbd_feedback_profile_feedbacks);
|
||||
g_test_add_func("/feedbackd/fbd/feedback-profile/parse", test_fbd_feedback_profile_parse);
|
||||
g_test_add_func("/feedbackd/fbd/feedback-profile/update", test_fbd_feedback_profile_update);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ test_fbd_feedback_theme_parse (void)
|
||||
const char *json =" "
|
||||
"{ "
|
||||
" \"name\" : \"test\", "
|
||||
" \"parent-name\" : \"parent-test\", "
|
||||
" \"profiles\" : [ "
|
||||
" { "
|
||||
" \"name\" : \"full\", "
|
||||
@@ -97,10 +98,18 @@ test_fbd_feedback_theme_parse (void)
|
||||
g_autoptr (GError) err = NULL;
|
||||
g_autoptr (FbdFeedbackTheme) theme = NULL;
|
||||
FbdFeedbackProfile *profile;
|
||||
const char *name;
|
||||
|
||||
theme = fbd_feedback_theme_new_from_data (json, &err);
|
||||
g_assert_no_error (err);
|
||||
g_assert_nonnull (theme);
|
||||
|
||||
name = fbd_feedback_theme_get_name (theme);
|
||||
g_assert_cmpstr ("test", ==, name);
|
||||
|
||||
name = fbd_feedback_theme_get_parent_name (theme);
|
||||
g_assert_cmpstr ("parent-test", ==, name);
|
||||
|
||||
profile = fbd_feedback_theme_get_profile (theme, "full");
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
g_assert_cmpstr ("full", ==,
|
||||
@@ -111,6 +120,71 @@ test_fbd_feedback_theme_parse (void)
|
||||
fbd_feedback_profile_get_name (profile));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_fbd_feedback_theme_update (void)
|
||||
{
|
||||
g_autoptr (GError) err = NULL;
|
||||
FbdFeedbackTheme *theme, *from;
|
||||
FbdFeedbackProfile *profile;
|
||||
FbdFeedbackBase *fb;
|
||||
const char *name;
|
||||
|
||||
/* test-dummy-1x feedbacks */
|
||||
from = fbd_feedback_theme_new_from_file (TEST_DATA_DIR "/parent/test.json", &err);
|
||||
g_assert_no_error (err);
|
||||
g_assert_true (FBD_IS_FEEDBACK_THEME (from));
|
||||
|
||||
/* test-dummy-0{0,1} feedbacks */
|
||||
theme = fbd_feedback_theme_new_from_file (TEST_DATA_DIR "/parent/base.json", &err);
|
||||
g_assert_no_error (err);
|
||||
g_assert_true (FBD_IS_FEEDBACK_THEME (theme));
|
||||
|
||||
name = fbd_feedback_theme_get_name (from);
|
||||
g_assert_cmpstr ("test", ==, name);
|
||||
|
||||
name = fbd_feedback_theme_get_name (theme);
|
||||
g_assert_cmpstr ("base", ==, name);
|
||||
|
||||
/* `theme` is lacking "silent" while `from` has it */
|
||||
profile = fbd_feedback_theme_get_profile (theme, "silent");
|
||||
g_assert_null (profile);
|
||||
profile = fbd_feedback_theme_get_profile (from, "silent");
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
|
||||
fbd_feedback_theme_update (theme, from);
|
||||
|
||||
/* After merging `theme` has a silent profile too */
|
||||
profile = fbd_feedback_theme_get_profile (theme, "silent");
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
g_assert_cmpstr ("silent", ==, fbd_feedback_profile_get_name (profile));
|
||||
|
||||
/* Name updated */
|
||||
name = fbd_feedback_theme_get_name (theme);
|
||||
g_assert_cmpstr ("test", ==, name);
|
||||
|
||||
/* test-dummy-10 added */
|
||||
profile = fbd_feedback_theme_get_profile (theme, "full");
|
||||
fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-10");
|
||||
g_assert_cmpstr ("test-dummy-10", ==, fbd_feedback_get_event_name (fb));
|
||||
profile = fbd_feedback_theme_get_profile (theme, "quiet");
|
||||
fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-10");
|
||||
g_assert_cmpstr ("test-dummy-10", ==, fbd_feedback_get_event_name (fb));
|
||||
profile = fbd_feedback_theme_get_profile (theme, "silent");
|
||||
fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-10");
|
||||
g_assert_cmpstr ("test-dummy-10", ==, fbd_feedback_get_event_name (fb));
|
||||
|
||||
/* test-dummy-00 updated */
|
||||
profile = fbd_feedback_theme_get_profile (theme, "full");
|
||||
fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-00");
|
||||
g_assert_cmpstr ("test-dummy-00", ==, fbd_feedback_get_event_name (fb));
|
||||
g_assert_cmpint (fbd_feedback_dummy_get_duration (FBD_FEEDBACK_DUMMY (fb)), ==, 0x10);
|
||||
|
||||
g_assert_finalize_object (from);
|
||||
g_assert_finalize_object (theme);
|
||||
}
|
||||
|
||||
|
||||
gint
|
||||
main (gint argc, gchar *argv[])
|
||||
{
|
||||
@@ -119,6 +193,7 @@ main (gint argc, gchar *argv[])
|
||||
g_test_add_func("/feedbackd/fbd/feedback-theme/name", test_fbd_feedback_theme_name);
|
||||
g_test_add_func("/feedbackd/fbd/feedback-theme/profiles", test_fbd_feedback_theme_profiles);
|
||||
g_test_add_func("/feedbackd/fbd/feedback-theme/parse", test_fbd_feedback_theme_parse);
|
||||
g_test_add_func("/feedbackd/fbd/feedback-theme/update", test_fbd_feedback_theme_update);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Guido Günther <agx@sigxcpu.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*/
|
||||
|
||||
#include "fbd-feedback-dummy.h"
|
||||
#include "fbd-theme-expander.h"
|
||||
|
||||
#include <json-glib/json-glib.h>
|
||||
|
||||
|
||||
static void
|
||||
test_fbd_theme_expander_object (void)
|
||||
{
|
||||
g_autoptr (FbdThemeExpander) expander = g_object_new (FBD_TYPE_THEME_EXPANDER, NULL);
|
||||
|
||||
g_assert_cmpstr ("default", ==, fbd_theme_expander_get_theme_name (expander));
|
||||
g_assert_null (fbd_theme_expander_get_theme_file (expander));
|
||||
g_assert_null (fbd_theme_expander_get_compatibles (expander));
|
||||
}
|
||||
|
||||
static void
|
||||
test_fbd_theme_expander_device (void)
|
||||
{
|
||||
g_autoptr (GError) err = NULL;
|
||||
const char *compatibles[] = { NULL, NULL };
|
||||
FbdFeedbackProfile *profile;
|
||||
FbdThemeExpander *expander;
|
||||
FbdFeedbackTheme *theme;
|
||||
FbdFeedbackBase *fb;
|
||||
const char *const *compatibles2;
|
||||
|
||||
/* just the default profile */
|
||||
compatibles[0] = "doesnotexist";
|
||||
expander = fbd_theme_expander_new (compatibles, NULL, NULL);
|
||||
g_assert_cmpstr ("default", ==, fbd_theme_expander_get_theme_name (expander));
|
||||
|
||||
theme = fbd_theme_expander_load_theme_files (expander, &err);
|
||||
compatibles2 = fbd_theme_expander_get_compatibles (expander);
|
||||
#if GLIB_CHECK_VERSION(2, 68, 0)
|
||||
g_assert_cmpstrv (compatibles2, compatibles);
|
||||
#endif
|
||||
g_assert_no_error (err);
|
||||
profile = fbd_feedback_theme_get_profile (theme, "silent");
|
||||
g_assert_null (profile);
|
||||
profile = fbd_feedback_theme_get_profile (theme, "quiet");
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
profile = fbd_feedback_theme_get_profile (theme, "full");
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
g_assert_finalize_object (theme);
|
||||
g_assert_finalize_object (expander);
|
||||
|
||||
/* Empty device theme that just chains up */
|
||||
compatibles[0] = "chainup";
|
||||
expander = fbd_theme_expander_new (compatibles, NULL, NULL);
|
||||
compatibles2 = fbd_theme_expander_get_compatibles (expander);
|
||||
#if GLIB_CHECK_VERSION(2, 68, 0)
|
||||
g_assert_cmpstrv (compatibles2, compatibles);
|
||||
#endif
|
||||
theme = fbd_theme_expander_load_theme_files (expander, &err);
|
||||
g_assert_no_error (err);
|
||||
profile = fbd_feedback_theme_get_profile (theme, "silent");
|
||||
g_assert_null (profile);
|
||||
profile = fbd_feedback_theme_get_profile (theme, "quiet");
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
profile = fbd_feedback_theme_get_profile (theme, "full");
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
g_assert_finalize_object (theme);
|
||||
g_assert_finalize_object (expander);
|
||||
|
||||
/* Replace an existing event */
|
||||
compatibles[0] = "replace";
|
||||
expander = fbd_theme_expander_new (compatibles, NULL, NULL);
|
||||
compatibles2 = fbd_theme_expander_get_compatibles (expander);
|
||||
#if GLIB_CHECK_VERSION(2, 68, 0)
|
||||
g_assert_cmpstrv (compatibles2, compatibles);
|
||||
#endif
|
||||
theme = fbd_theme_expander_load_theme_files (expander, &err);
|
||||
g_assert_no_error (err);
|
||||
profile = fbd_feedback_theme_get_profile (theme, "full");
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-0");
|
||||
g_assert_cmpstr ("test-dummy-0", ==, fbd_feedback_get_event_name (fb));
|
||||
g_assert_cmpint (fbd_feedback_dummy_get_duration (FBD_FEEDBACK_DUMMY (fb)), ==, 0x10);
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-2");
|
||||
g_assert_cmpstr ("test-dummy-2", ==, fbd_feedback_get_event_name (fb));
|
||||
g_assert_cmpint (fbd_feedback_dummy_get_duration (FBD_FEEDBACK_DUMMY (fb)), ==, 0x20);
|
||||
g_assert_finalize_object (theme);
|
||||
g_assert_finalize_object (expander);
|
||||
}
|
||||
|
||||
static void
|
||||
test_fbd_theme_expander_custom (void)
|
||||
{
|
||||
g_autoptr (GError) err = NULL;
|
||||
const char *compatibles[] = { NULL, NULL };
|
||||
FbdFeedbackProfile *profile;
|
||||
FbdThemeExpander *expander;
|
||||
FbdFeedbackTheme *theme;
|
||||
FbdFeedbackBase *fb;
|
||||
|
||||
/* Load a custom theme that overrides a single event on top of the `replace` from above */
|
||||
compatibles[0] = "replace";
|
||||
expander = fbd_theme_expander_new (compatibles, "custom", NULL);
|
||||
theme = fbd_theme_expander_load_theme_files (expander, &err);
|
||||
g_assert_no_error (err);
|
||||
profile = fbd_feedback_theme_get_profile (theme, "full");
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-0");
|
||||
g_assert_cmpstr ("test-dummy-0", ==, fbd_feedback_get_event_name (fb));
|
||||
g_assert_cmpint (fbd_feedback_dummy_get_duration (FBD_FEEDBACK_DUMMY (fb)), ==, 0x10);
|
||||
g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile));
|
||||
fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-2");
|
||||
g_assert_cmpstr ("test-dummy-2", ==, fbd_feedback_get_event_name (fb));
|
||||
g_assert_cmpint (fbd_feedback_dummy_get_duration (FBD_FEEDBACK_DUMMY (fb)), ==, 0x30);
|
||||
g_assert_finalize_object (theme);
|
||||
g_assert_finalize_object (expander);
|
||||
}
|
||||
|
||||
gint
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func("/feedbackd/fbd/theme-expander/object", test_fbd_theme_expander_object);
|
||||
g_test_add_func("/feedbackd/fbd/theme-expander/device", test_fbd_theme_expander_device);
|
||||
g_test_add_func("/feedbackd/fbd/theme-expander/custom", test_fbd_theme_expander_custom);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
Reference in New Issue
Block a user