feat: add files monitor

FilesMonitor allows shell to monitor changes on a set of files.
This allows the shell to react in realtime any changes related
to its configurations such as: themes, keyboard, setting, etc.
This commit is contained in:
DanyLE 2025-06-14 23:08:11 +02:00
parent e598847994
commit 224252807a
6 changed files with 184 additions and 6 deletions

View File

@ -63,6 +63,7 @@ base = [
'src/input.c', 'src/input.c',
'src/virtual-keyboard.c', 'src/virtual-keyboard.c',
'src/widgets/virtual-keyboard-widgets.c', 'src/widgets/virtual-keyboard-widgets.c',
'src/files-monitor.c',
wayland_targets wayland_targets
] ]

14
src/files-monior.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef DIYA_FILES_MONITOR_H
#define DIYA_FILES_MONITOR_H
#include "base.h"
#define DIYA_TYPE_FILES_MONITOR (diya_files_monitor_get_type ())
G_DECLARE_FINAL_TYPE (DiyaFilesMonitor, diya_files_monitor, DIYA, FILES_MONITOR, DiyaObject)
DiyaFilesMonitor* diya_files_monitor_new();
gboolean diya_files_monitor_watch(DiyaFilesMonitor* monitor, const gchar* file, GCallback callback, gpointer userdata);
void diya_files_monitor_unwatch(DiyaFilesMonitor* monitor, const gchar* file);
#endif

83
src/files-monitor.c Normal file
View File

@ -0,0 +1,83 @@
#include <glib-unix.h>
#include <gio/gio.h>
#include <stdbool.h>
#include "files-monior.h"
struct _DiyaFilesMonitor
{
DiyaObject parent;
GHashTable *watch_table;
};
G_DEFINE_FINAL_TYPE(DiyaFilesMonitor, diya_files_monitor, DIYA_TYPE_OBJECT)
static void diya_files_monitor_dispose(GObject *object)
{
DiyaFilesMonitor *self = DIYA_FILES_MONITOR(object);
g_debug("diya_files_monitor_dispose: %s", diya_object_to_string(object));
g_hash_table_destroy(self->watch_table);
G_OBJECT_CLASS(diya_files_monitor_parent_class)->dispose(object);
}
static void diya_files_monitor_init(DiyaFilesMonitor *self)
{
self->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
}
static const gchar *diya_files_monitor_to_string(DiyaObject *object)
{
(void) object;
return "Diya Files Monitor";
}
static void diya_files_monitor_class_init(DiyaFilesMonitorClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(class);
DiyaObjectClass *base_class = DIYA_OBJECT_CLASS(class);
gobject_class->dispose = diya_files_monitor_dispose;
base_class->to_string = diya_files_monitor_to_string;
}
DiyaFilesMonitor* diya_files_monitor_new()
{
DiyaFilesMonitor* self = DIYA_FILES_MONITOR(g_object_new(DIYA_TYPE_FILES_MONITOR, NULL));
return self;
}
gboolean diya_files_monitor_watch(DiyaFilesMonitor* self, const gchar* path, GCallback callback, gpointer userdata)
{
GFileMonitor* fm = NULL;
if(!g_hash_table_contains(self->watch_table, path))
{
GError *err = NULL;
GFile* file = g_file_new_for_path(path);
fm = g_file_monitor(file, G_FILE_MONITOR_WATCH_MOVES, NULL, &err);
g_object_unref(file);
if (err)
{
g_error("Unable to watch file %s: %s", path, err->message);
g_error_free(err);
return false;
}
g_info("DiyaFilesMonitor: watch new file: %s", path);
g_hash_table_insert(self->watch_table, (gpointer)g_strdup(path), fm);
}
else
{
g_info("File %s is already watched, register callback handle to the existing watcher", path);
fm = g_hash_table_lookup(self->watch_table, path);
}
g_signal_connect(G_OBJECT(fm), "changed", callback, userdata);
return true;
}
void diya_files_monitor_unwatch(DiyaFilesMonitor* self, const gchar* path)
{
if(!g_hash_table_contains(self->watch_table, path))
{
g_hash_table_remove(self->watch_table, (gconstpointer)path);
}
}

View File

@ -1,6 +1,6 @@
#ifndef DIYA_INPUT_H #ifndef DIYA_INPUT_H
#define DIYA_INPUT_H #define DIYA_INPUT_H
#include "base.h"
#include "shell.h" #include "shell.h"
#define DIYA_TYPE_INPUT (diya_input_get_type ()) #define DIYA_TYPE_INPUT (diya_input_get_type ())

View File

@ -3,6 +3,10 @@
#include "wayland.h" #include "wayland.h"
#include "virtual-keyboard.h" #include "virtual-keyboard.h"
#include "input.h" #include "input.h"
#include "files-monior.h"
#define diya_shell_config_css_file(priv) (g_strconcat(g_get_user_config_dir(), "/diya/themes/", priv->name, ".css", NULL))
enum enum
{ {
NO_PROP, NO_PROP,
@ -23,6 +27,7 @@ typedef struct _DiyaShellPrivate
GHashTable *vkbs; GHashTable *vkbs;
GtkCssProvider *css_provider; GtkCssProvider *css_provider;
DiyaInput *input; DiyaInput *input;
DiyaFilesMonitor *files_watchdog;
} DiyaShellPrivate; } DiyaShellPrivate;
G_DEFINE_TYPE_WITH_PRIVATE(DiyaShell, diya_shell, DIYA_TYPE_OBJECT); G_DEFINE_TYPE_WITH_PRIVATE(DiyaShell, diya_shell, DIYA_TYPE_OBJECT);
@ -50,6 +55,11 @@ static void diya_shell_dispose(GObject *object)
g_object_unref(priv->input); g_object_unref(priv->input);
} }
g_hash_table_destroy(priv->vkbs); g_hash_table_destroy(priv->vkbs);
if (priv->files_watchdog)
{
g_object_unref(priv->files_watchdog);
priv->files_watchdog = NULL;
}
G_OBJECT_CLASS(diya_shell_parent_class)->dispose(object); G_OBJECT_CLASS(diya_shell_parent_class)->dispose(object);
if (priv->app) if (priv->app)
{ {
@ -63,10 +73,10 @@ static void diya_shell_reload(DiyaShell *shell)
GBytes *bytes = NULL; GBytes *bytes = NULL;
gchar *css_string = NULL; gchar *css_string = NULL;
DiyaShellPrivate *priv = diya_shell_get_instance_private(shell); DiyaShellPrivate *priv = diya_shell_get_instance_private(shell);
gchar *css_file = g_strconcat(g_get_user_config_dir(), "/diya/themes/", priv->name, ".css", NULL); gchar *css_file = diya_shell_config_css_file(priv);
g_debug("diya_shell_reload: Looking for css file: %s", css_file); g_debug("diya_shell_reload: Looking for css file: %s", css_file);
g_file_get_contents(css_file, &css_string, NULL, &err); g_file_get_contents(css_file, &css_string, NULL, &err);
free(css_file); g_free(css_file);
if (err != NULL) if (err != NULL)
{ {
g_warning("diya_shell_reload: Unable to load CSS from file: %s", err->message); g_warning("diya_shell_reload: Unable to load CSS from file: %s", err->message);
@ -114,6 +124,38 @@ static void diya_shell_reload(DiyaShell *shell)
} }
} }
void on_diya_shell_theme_file_changed(GFileMonitor *monitor, GFile *file, GFile *other, GFileMonitorEvent evtype, gpointer user_data)
{
(void)monitor;
DiyaShell *shell = user_data;
char *fpath = g_file_get_path(file);
char *opath = NULL;
if (other)
{
opath = g_file_get_path(other);
}
switch (evtype)
{
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
case G_FILE_MONITOR_EVENT_DELETED:
case G_FILE_MONITOR_EVENT_RENAMED:
case G_FILE_MONITOR_EVENT_MOVED_IN:
case G_FILE_MONITOR_EVENT_MOVED_OUT:
//case G_FILE_MONITOR_EVENT_CHANGED:
//case G_FILE_MONITOR_EVENT_CREATED:
diya_shell_reload(shell);
break;
default:
g_debug("%s event %x", fpath, evtype);
break;
}
if (opath)
{
g_free(opath);
}
g_free(fpath);
}
static void on_gtk_app_startup(GtkApplication *app, void *data) static void on_gtk_app_startup(GtkApplication *app, void *data)
{ {
(void)app; (void)app;
@ -121,6 +163,9 @@ static void on_gtk_app_startup(GtkApplication *app, void *data)
DiyaShellPrivate *priv = diya_shell_get_instance_private(shell); DiyaShellPrivate *priv = diya_shell_get_instance_private(shell);
priv->wayland = diya_wayland_new(DIYA_SHELL(shell)); priv->wayland = diya_wayland_new(DIYA_SHELL(shell));
DiyaShellClass *class = DIYA_SHELL_GET_CLASS(shell); DiyaShellClass *class = DIYA_SHELL_GET_CLASS(shell);
gchar *css_file = diya_shell_config_css_file(priv);
diya_shell_watch_file(shell, css_file, G_CALLBACK(on_diya_shell_theme_file_changed), (gpointer)shell);
g_free(css_file);
diya_shell_reload(shell); diya_shell_reload(shell);
if (class->startup_handle) if (class->startup_handle)
{ {
@ -225,9 +270,10 @@ static void diya_shell_init(DiyaShell *self)
priv->wayland = NULL; priv->wayland = NULL;
priv->app = NULL; priv->app = NULL;
priv->name = NULL; priv->name = NULL;
priv->vkbs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref); priv->vkbs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
priv->css_provider = NULL; priv->css_provider = NULL;
priv->input = NULL; priv->input = NULL;
priv->files_watchdog = NULL;
} }
DiyaWayland *diya_shell_get_wayland(DiyaShell *shell) DiyaWayland *diya_shell_get_wayland(DiyaShell *shell)
@ -284,6 +330,19 @@ int diya_shell_run(DiyaShell *shell, int argc, char **argv)
DiyaShellPrivate *priv = diya_shell_get_instance_private(shell); DiyaShellPrivate *priv = diya_shell_get_instance_private(shell);
assert(GTK_IS_APPLICATION(priv->app)); assert(GTK_IS_APPLICATION(priv->app));
GtkApplication *app = priv->app; GtkApplication *app = priv->app;
/**
* create shell config dir if not exists
*/
// theme dir
gchar* path = g_strconcat(g_get_user_config_dir(), "/diya/themes/", NULL);
g_mkdir_with_parents((const gchar*)path,0755);
g_free(path);
// keymap dir
path = g_strconcat(g_get_user_config_dir(), "/diya/xkb/", NULL);
g_mkdir_with_parents((const gchar*)path,0755);
g_free(path);
int status = g_application_run(G_APPLICATION(app), argc, argv); int status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app); g_object_unref(app);
return status; return status;
@ -310,7 +369,7 @@ DiyaVirtualKeyboard *diya_shell_get_virtual_keyboard(DiyaShell *shell, const gch
if (vkb) if (vkb)
{ {
g_debug("diya_shell_get_virtual_keyboard: add new keyboard %s to cache", name); g_debug("diya_shell_get_virtual_keyboard: add new keyboard %s to cache", name);
g_hash_table_insert(priv->vkbs, (gpointer)name, vkb); g_hash_table_insert(priv->vkbs, (gpointer)g_strdup(name), vkb);
} }
} }
free(keymap_file); free(keymap_file);
@ -323,7 +382,7 @@ DiyaVirtualKeyboard *diya_shell_get_virtual_keyboard(DiyaShell *shell, const gch
if (!g_hash_table_contains(priv->vkbs, "default")) if (!g_hash_table_contains(priv->vkbs, "default"))
{ {
g_debug("Add new keyboard instance to cache"); g_debug("Add new keyboard instance to cache");
g_hash_table_insert(priv->vkbs, "default", diya_virtual_keyboard_new(shell, NULL)); g_hash_table_insert(priv->vkbs, (gpointer)g_strdup("default"), diya_virtual_keyboard_new(shell, NULL));
} }
vkb = g_hash_table_lookup(priv->vkbs, "default"); vkb = g_hash_table_lookup(priv->vkbs, "default");
} }
@ -339,3 +398,22 @@ void diya_shell_monitor_input(DiyaShell *self)
} }
priv->input = diya_input_new(self); priv->input = diya_input_new(self);
} }
gboolean diya_shell_watch_file(DiyaShell *self, const gchar *file, GCallback callback, gpointer userdata)
{
DiyaShellPrivate *priv = diya_shell_get_instance_private(self);
if (!priv->files_watchdog)
{
priv->files_watchdog = diya_files_monitor_new();
}
return diya_files_monitor_watch(priv->files_watchdog, file, callback, userdata);
}
void diya_shell_unwatch_file(DiyaShell *self, const gchar *file)
{
DiyaShellPrivate *priv = diya_shell_get_instance_private(self);
if (priv->files_watchdog)
{
diya_files_monitor_unwatch(priv->files_watchdog, file);
}
}

View File

@ -34,6 +34,8 @@ GtkApplication* diya_shell_get_application(DiyaShell* shell);
const char* diya_shell_get_name(DiyaShell* shell); const char* diya_shell_get_name(DiyaShell* shell);
DiyaVirtualKeyboard* diya_shell_get_virtual_keyboard(DiyaShell* shell, const gchar* name); DiyaVirtualKeyboard* diya_shell_get_virtual_keyboard(DiyaShell* shell, const gchar* name);
void diya_shell_monitor_input(DiyaShell* shell); void diya_shell_monitor_input(DiyaShell* shell);
gboolean diya_shell_watch_file(DiyaShell* shell, const gchar* file, GCallback callback, gpointer userdata);
void diya_shell_unwatch_file(DiyaShell* shell, const gchar* file);
int diya_shell_run(DiyaShell* shell, int argc, char **argv); int diya_shell_run(DiyaShell* shell, int argc, char **argv);
#endif #endif