Recursively load themes
Themes (other than the default theme) can specify a `parent-name` in the json file. This theme will be used as a base for the theme. Events and profiles of the children override those in the parents. For this we move the lookup code into it's separate class and also add tests. Closes: #38
This commit is contained in:
+20
-78
@@ -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,11 @@
|
||||
|
||||
#define FEEDBACKD_SCHEMA_ID "org.sigxcpu.feedbackd"
|
||||
#define FEEDBACKD_KEY_PROFILE "profile"
|
||||
#define FEEDBACKD_THEME_VAR "FEEDBACK_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
|
||||
@@ -430,65 +431,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)
|
||||
@@ -599,31 +541,31 @@ 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;
|
||||
|
||||
// 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 ();
|
||||
expander = fbd_theme_expander_new ((const char *const *)compatibles,
|
||||
g_getenv (FEEDBACKD_THEME_VAR));
|
||||
theme = fbd_theme_expander_load_theme_files (expander, &err);
|
||||
|
||||
// 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);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* 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 DEVICE_TREE_PATH "/sys/firmware/devicetree/base/compatible"
|
||||
#define DEVICE_NAME_MAX 1024
|
||||
|
||||
#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_FILE,
|
||||
PROP_COMPATIBLES,
|
||||
PROP_LAST_PROP
|
||||
};
|
||||
static GParamSpec *props[PROP_LAST_PROP];
|
||||
|
||||
struct _FbdThemeExpander {
|
||||
GObject parent;
|
||||
|
||||
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_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_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_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_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-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_file)
|
||||
{
|
||||
return FBD_THEME_EXPANDER (g_object_new (FBD_TYPE_THEME_EXPANDER,
|
||||
"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, DEFAULT_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);
|
||||
|
||||
return g_steal_pointer (&merged);
|
||||
}
|
||||
|
||||
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,28 @@
|
||||
/*
|
||||
* 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_file);
|
||||
FbdFeedbackTheme *fbd_theme_expander_load_theme_files (FbdThemeExpander *self,
|
||||
GError **err);
|
||||
const char *fbd_theme_expander_get_theme_file (FbdThemeExpander *self);
|
||||
const char * const *fbd_theme_expander_get_compatibles (FbdThemeExpander *self);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -13,6 +13,7 @@ G_BEGIN_DECLS
|
||||
|
||||
typedef enum {
|
||||
FBD_ERROR_FAILED = 0,
|
||||
FBD_ERROR_THEME_EXPAND = 1,
|
||||
} FbdError;
|
||||
|
||||
GQuark fbd_error_quark (void);
|
||||
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+7
-3
@@ -11,7 +11,7 @@ test_env.set('MALLOC_CHECK_', '2')
|
||||
test_env.set('XDG_CONFIG_HOME', meson.current_source_dir())
|
||||
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,10 +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_DATA_DIR="@0@"'.format(join_paths(meson.current_source_dir(), 'data')),
|
||||
]
|
||||
]
|
||||
|
||||
test_fbd_link_args = [
|
||||
'-fPIC',
|
||||
@@ -69,6 +72,7 @@ fbd_tests = [
|
||||
'fbd-feedback-profile',
|
||||
'fbd-feedback-theme',
|
||||
'fbd-event',
|
||||
'fbd-theme-expander',
|
||||
]
|
||||
|
||||
foreach test : fbd_tests
|
||||
@@ -79,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
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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_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);
|
||||
theme = fbd_theme_expander_load_theme_files (expander, &err);
|
||||
compatibles2 = fbd_theme_expander_get_compatibles (expander);
|
||||
g_assert_cmpstrv (compatibles2, compatibles);
|
||||
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);
|
||||
compatibles2 = fbd_theme_expander_get_compatibles (expander);
|
||||
g_assert_cmpstrv (compatibles2, compatibles);
|
||||
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);
|
||||
compatibles2 = fbd_theme_expander_get_compatibles (expander);
|
||||
g_assert_cmpstrv (compatibles2, compatibles);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
Reference in New Issue
Block a user