gio file monitor doesn't work in cygwin :(

This commit is contained in:
Florian Stinglmayr 2025-05-02 15:47:05 +02:00
parent ca2b771d27
commit 27fcc1cea0
10 changed files with 312 additions and 36 deletions

View File

@ -5,3 +5,4 @@ ADD_DEFINITIONS("-Wall -Werror -std=c99")
ENABLE_TESTING() ENABLE_TESTING()
ADD_SUBDIRECTORY(lib) ADD_SUBDIRECTORY(lib)
ADD_SUBDIRECTORY(edwatch)

17
edwatch/CMakeLists.txt Normal file
View File

@ -0,0 +1,17 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.24)
PROJECT(edterm)
SET(SOURCES
"edwatch.c"
)
INCLUDE_DIRECTORIES(
${GLIB2_INCLUDE_DIRS}
${EDAPI_INCLUDE_DIRS}
)
ADD_EXECUTABLE("edwatch" ${SOURCES})
TARGET_LINK_LIBRARIES(
"edwatch"
"edapi"
)

56
edwatch/edwatch.c Normal file
View File

@ -0,0 +1,56 @@
#include <stdio.h>
#include <gio/gio.h>
#include <edapi/journal/journal.h>
#include <edapi/journal/entry.h>
#include <edapi/journal/file.h>
static EDJournal *journal = NULL;
static GApplication *app = NULL;
static void interrupt(int signal)
{
if (app != NULL) {
g_application_release(app);
}
}
static void new_entry(EDJournal *sender, EDJournalFile *file,
EDJournalEntry *entry, gpointer data)
{
fprintf(stdout, "new event!\n");
}
static void activate(GApplication *self, gpointer data)
{
gchar *location = NULL;
GError *error = NULL;
journal = ed_journal_new_with_location(location, &error);
if (journal == NULL) {
fprintf(stderr, "failed to open journal: %s\n",
error->message);
g_application_quit(self);
return;
}
g_signal_connect(journal, "new-entry", G_CALLBACK(new_entry), NULL);
g_application_hold(self);
}
int main(int ac, char **av)
{
app = g_application_new("org.nola.edwatch", 0);
if (app == NULL) {
return 3;
}
signal(SIGINT, interrupt);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
g_application_run(app, 0, NULL);
return 0;
}

View File

@ -18,6 +18,9 @@ SET(SOURCES
"src/journal/journal.c" "src/journal/journal.c"
) )
SET(EDAPI_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include"
CACHE STRING "EDAPI lib include dir")
INCLUDE_DIRECTORIES( INCLUDE_DIRECTORIES(
"include" "include"
${GIO2_INCLUDE_DIRS} ${GIO2_INCLUDE_DIRS}

View File

@ -33,10 +33,20 @@ EDErrorCode ed_journal_file_parse_filename(
* *
* To fully load all entries call ed_journal_file_load(). * To fully load all entries call ed_journal_file_load().
*/ */
EDErrorCode ed_journal_file_open(EDJournalFile *file, EDErrorCode ed_journal_file_peek(EDJournalFile *file,
char const *filename, char const *filename,
GError **error); GError **error);
/**
* Opens the file and seeks to its end. If any new entries are
* added by the game then ed_journal_file_read() will return new
* entries.
*/
EDErrorCode ed_journal_file_open_tail(EDJournalFile *self);
void ed_journal_file_close(EDJournalFile *self);
EDJournalEntry *ed_journal_file_read(EDJournalFile *self, GError **error);
/** /**
* Load all entries from this given journal. * Load all entries from this given journal.
*/ */

View File

@ -13,12 +13,16 @@ G_DECLARE_FINAL_TYPE(
#define ED_TYPE_JOURNAL ed_journal_get_type() #define ED_TYPE_JOURNAL ed_journal_get_type()
gchar *ed_journal_determine_location(void);
EDJournal *ed_journal_new(void); EDJournal *ed_journal_new(void);
EDJournal *ed_journal_new_with_location(gchar const *dir); EDJournal *ed_journal_new_with_location(gchar const *dir, GError **error);
gchar const *ed_journal_get_location(EDJournal *j); gchar const *ed_journal_get_location(EDJournal *j);
EDErrorCode ed_journal_set_location(EDJournal *j, gchar const *dir);
void ed_journal_close(EDJournal *self);
EDErrorCode ed_journal_open(EDJournal *j, gchar const *dir, GError **error);
/** /**
* Get a list of files. Files are sorted in ascending order, with * Get a list of files. Files are sorted in ascending order, with

View File

@ -8,6 +8,7 @@
typedef struct { typedef struct {
gchar *filename; gchar *filename;
GFile *file;
gchar *datetime; gchar *datetime;
GDateTime *timestamp; GDateTime *timestamp;
gint part; gint part;
@ -15,6 +16,9 @@ typedef struct {
EDCommander *commander; EDCommander *commander;
gchar *gameversion; gchar *gameversion;
GDataInputStream *reader;
GFileInputStream *stream;
EDJournalEntry *first; EDJournalEntry *first;
EDJournalEntry *last; EDJournalEntry *last;
} EDJournalFilePrivate; } EDJournalFilePrivate;
@ -40,8 +44,13 @@ static void ed_journal_file_dispose(GObject *obj)
EDJournalFile *self = ED_JOURNALFILE(obj); EDJournalFile *self = ED_JOURNALFILE(obj);
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self); EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
g_clear_object(&p->file);
g_clear_object(&p->reader);
g_clear_object(&p->stream);
g_clear_object(&p->first); g_clear_object(&p->first);
g_clear_object(&p->last); g_clear_object(&p->last);
g_clear_object(&p->commander); g_clear_object(&p->commander);
G_OBJECT_CLASS(ed_journal_file_parent_class)->dispose(obj); G_OBJECT_CLASS(ed_journal_file_parent_class)->dispose(obj);
@ -215,6 +224,7 @@ static EDErrorCode ed_journal_file_parse_(EDJournalFile *self,
g_free(p->filename); g_free(p->filename);
p->filename = g_strdup(filename); p->filename = g_strdup(filename);
p->file = g_file_new_for_path(p->filename);
ret = ed_error_success; ret = ed_error_success;
@ -257,7 +267,6 @@ static EDErrorCode ed_journal_file_read_first_(EDJournalFile *self)
EDErrorCode ret = ed_error_internal; EDErrorCode ret = ed_error_internal;
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self); EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
GFile *file = NULL;
GFileInputStream *stream = NULL; GFileInputStream *stream = NULL;
GDataInputStream *reader = NULL; GDataInputStream *reader = NULL;
EDJournalEntry *entry = NULL; EDJournalEntry *entry = NULL;
@ -265,10 +274,7 @@ static EDErrorCode ed_journal_file_read_first_(EDJournalFile *self)
char *line = NULL; char *line = NULL;
gsize linelen = 0; gsize linelen = 0;
file = g_file_new_for_path(p->filename); stream = g_file_read(p->file, NULL, NULL);
goto_if_true(file == NULL, done);
stream = g_file_read(file, NULL, NULL);
if (stream == NULL) { if (stream == NULL) {
goto done; goto done;
} }
@ -319,7 +325,6 @@ done:
g_clear_object(&reader); g_clear_object(&reader);
g_clear_object(&stream); g_clear_object(&stream);
g_clear_object(&file);
return ret; return ret;
} }
@ -524,7 +529,7 @@ done:
return ret; return ret;
} }
EDErrorCode ed_journal_file_open(EDJournalFile *file, EDErrorCode ed_journal_file_peek(EDJournalFile *file,
char const *filename, char const *filename,
GError **error) GError **error)
{ {
@ -640,3 +645,78 @@ gboolean ed_journal_file_has_entries_in_range(EDJournalFile *self,
return FALSE; return FALSE;
} }
EDJournalEntry *ed_journal_file_read(EDJournalFile *self, GError **error)
{
return_if_true(self == NULL, NULL);
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
GError *tmp = NULL;
if (p->reader == NULL) {
return NULL;
}
gchar *line = NULL;
gsize linelen = 0;
line = g_data_input_stream_read_line_utf8(
p->reader, &linelen, NULL, &tmp);
if (line == NULL) {
g_propagate_error(error, tmp);
return NULL;
}
g_strchomp(line);
if (strlen(line) <= 0) {
g_free(line);
return NULL;
}
EDJournalEntry *entry = ed_journal_entry_new_parse(line, &tmp);
g_free(line);
if (entry == NULL) {
g_propagate_error(error, tmp);
return NULL;
}
return entry;
}
void ed_journal_file_close(EDJournalFile *self)
{
return_if_true(self == NULL,);
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
g_clear_object(&p->stream);
g_clear_object(&p->reader);
}
EDErrorCode ed_journal_file_open_tail(EDJournalFile *self)
{
return_if_true(self == NULL, ed_error_args);
EDJournalFilePrivate *p = ed_journal_file_get_instance_private(self);
EDErrorCode ret = ed_error_internal;
p->stream = g_file_read(p->file, NULL, NULL);
goto_if_true(p->stream == NULL, done);
p->reader = g_data_input_stream_new(G_INPUT_STREAM(p->stream));
goto_if_true(p->reader == NULL, done);
if (!g_seekable_seek(G_SEEKABLE(p->stream), 0, G_SEEK_END, NULL, NULL)) {
goto done;
}
ret = ed_error_success;
done:
if (ED_ERROR(ret)) {
g_clear_object(&p->stream);
g_clear_object(&p->reader);
}
return ret;
}

View File

@ -2,9 +2,14 @@
#include <edapi/journal/file.h> #include <edapi/journal/file.h>
#include <edapi/util.h> #include <edapi/util.h>
#include <gio/gio.h>
#include <stdio.h>
typedef struct { typedef struct {
gchar *location; gchar *location;
GFile *loc;
GList *files; GList *files;
GFileMonitor *monitor;
} EDJournalPrivate; } EDJournalPrivate;
struct _EDJournal { struct _EDJournal {
@ -23,6 +28,10 @@ G_DEFINE_TYPE_EXTENDED(
G_ADD_PRIVATE(EDJournal) G_ADD_PRIVATE(EDJournal)
); );
static void ed_journal_changed(GFileMonitor *self, GFile *file, GFile *other,
GFileMonitorEvent event,
gpointer user);
static void ed_journal_finalize(GObject *obj) static void ed_journal_finalize(GObject *obj)
{ {
EDJournal *self = ED_JOURNAL(obj); EDJournal *self = ED_JOURNAL(obj);
@ -34,12 +43,34 @@ static void ed_journal_finalize(GObject *obj)
G_OBJECT_CLASS(ed_journal_parent_class)->finalize(obj); G_OBJECT_CLASS(ed_journal_parent_class)->finalize(obj);
} }
static void ed_journal_class_init(EDJournalClass *klass) static void ed_journal_dispose(GObject *obj)
{ {
G_OBJECT_CLASS(klass)->finalize = ed_journal_finalize; EDJournal *self = ED_JOURNAL(obj);
EDJournalPrivate *p = ed_journal_get_instance_private(self);
g_clear_object(&p->loc);
g_clear_object(&p->monitor);
G_OBJECT_CLASS(ed_journal_parent_class)->dispose(obj);
} }
static EDErrorCode ed_journal_determine_location(EDJournal *self) static void ed_journal_class_init(EDJournalClass *klass)
{
g_signal_new("new-entry",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0, NULL, NULL, NULL,
G_TYPE_NONE,
2,
ED_TYPE_JOURNALFILE,
ED_TYPE_JOURNALENTRY
);
G_OBJECT_CLASS(klass)->finalize = ed_journal_finalize;
G_OBJECT_CLASS(klass)->dispose = ed_journal_dispose;
}
gchar *ed_journal_determine_location(void)
{ {
gchar *location = NULL; gchar *location = NULL;
gchar *tmp = NULL; gchar *tmp = NULL;
@ -67,22 +98,19 @@ static EDErrorCode ed_journal_determine_location(EDJournal *self)
} }
if (S_EMPTY(location)) { if (S_EMPTY(location)) {
return ed_error_invalid; return NULL;
} }
if (!g_file_test(location, G_FILE_TEST_IS_DIR)) { if (!g_file_test(location, G_FILE_TEST_IS_DIR)) {
g_free(location); g_free(location);
return ed_error_invalid; return NULL;
} }
ed_journal_set_location(self, location); return location;
return ed_error_success;
} }
static void ed_journal_init(EDJournal *self) static void ed_journal_init(EDJournal *self)
{ {
ed_journal_determine_location(self);
} }
EDJournal *ed_journal_new(void) EDJournal *ed_journal_new(void)
@ -90,19 +118,33 @@ EDJournal *ed_journal_new(void)
return g_object_new(ED_TYPE_JOURNAL, NULL); return g_object_new(ED_TYPE_JOURNAL, NULL);
} }
EDJournal *ed_journal_new_with_location(gchar const *dir) EDJournal *ed_journal_new_with_location(gchar const *dir, GError **error)
{ {
EDJournal *j = NULL; EDJournal *j = NULL;
EDErrorCode ret = ed_error_success; EDErrorCode ret = ed_error_success;
GError *tmp = NULL;
if (dir == NULL) {
gchar *loc = ed_journal_determine_location();
return_if_true(loc == NULL, NULL);
j = ed_journal_new_with_location(loc, error);
g_free(loc);
return j;
}
j = ed_journal_new(); j = ed_journal_new();
return_if_true(j == NULL, NULL); return_if_true(j == NULL, NULL);
ret = ed_journal_set_location(j, dir); if (!S_EMPTY(dir)) {
ret = ed_journal_open(j, dir, &tmp);
if (ED_ERROR(ret)) { if (ED_ERROR(ret)) {
g_propagate_error(error, tmp);
g_clear_object(&j); g_clear_object(&j);
return NULL; return NULL;
} }
}
return j; return j;
} }
@ -143,7 +185,7 @@ static void ed_journal_load_files(EDJournal *self)
journalfile = ed_journal_file_new(); journalfile = ed_journal_file_new();
goto_if_true(journalfile == NULL, next); goto_if_true(journalfile == NULL, next);
ret = ed_journal_file_open(journalfile, full, NULL); ret = ed_journal_file_peek(journalfile, full, NULL);
if (ED_SUCCESS(ret)) { if (ED_SUCCESS(ret)) {
p->files = g_list_append(p->files, journalfile); p->files = g_list_append(p->files, journalfile);
} }
@ -164,6 +206,40 @@ done:
} }
} }
static EDErrorCode ed_journal_watch(EDJournal *self, GError **error)
{
return_if_true(self == NULL, ed_error_args);
EDJournalPrivate *p = ed_journal_get_instance_private(self);
GError *tmp = NULL;
g_clear_object(&p->monitor);
p->monitor = g_file_monitor_directory(p->loc, 0, NULL, &tmp);
if (p->monitor == NULL) {
g_propagate_error(error, tmp);
return ed_error_internal;
}
g_signal_connect(
p->monitor, "changed", G_CALLBACK(ed_journal_changed), self);
return ed_error_success;
}
static void ed_journal_changed(GFileMonitor *mon, GFile *file, GFile *other,
GFileMonitorEvent event,
gpointer user)
{
EDJournal *self = ED_JOURNAL(user);
if (event == G_FILE_MONITOR_EVENT_CHANGED) {
printf("file changed!\n");
}
(void)self;
}
gchar const *ed_journal_get_location(EDJournal *self) gchar const *ed_journal_get_location(EDJournal *self)
{ {
return_if_true(self == NULL, NULL); return_if_true(self == NULL, NULL);
@ -171,11 +247,30 @@ gchar const *ed_journal_get_location(EDJournal *self)
return p->location; return p->location;
} }
EDErrorCode ed_journal_set_location(EDJournal *self, gchar const *dir) void ed_journal_close(EDJournal *self)
{
return_if_true(self == NULL,);
EDJournalPrivate *p = ed_journal_get_instance_private(self);
g_free(p->location);
p->location = NULL;
g_clear_object(&p->loc);
g_clear_object(&p->monitor);
g_list_free_full(p->files, g_object_unref);
p->files = NULL;
}
EDErrorCode ed_journal_open(EDJournal *self, gchar const *dir, GError **error)
{ {
return_if_true(self == NULL || dir == NULL, ed_error_args); return_if_true(self == NULL || dir == NULL, ed_error_args);
EDJournalPrivate *p = ed_journal_get_instance_private(self); EDJournalPrivate *p = ed_journal_get_instance_private(self);
EDErrorCode ret = 0;
GError *tmp = NULL;
if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) { if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) {
return ed_error_invalid; return ed_error_invalid;
@ -184,8 +279,18 @@ EDErrorCode ed_journal_set_location(EDJournal *self, gchar const *dir)
g_free(p->location); g_free(p->location);
p->location = g_strdup(dir); p->location = g_strdup(dir);
g_clear_object(&p->loc);
p->loc = g_file_new_for_path(dir);
ed_journal_load_files(self); ed_journal_load_files(self);
ret = ed_journal_watch(self, &tmp);
if (ED_ERROR(ret)) {
g_propagate_error(error, tmp);
ed_journal_close(self);
return ed_error_internal;
}
return ed_error_success; return ed_error_success;
} }

View File

@ -16,7 +16,7 @@ static void test_new_filename(void **state)
assert_non_null(file); assert_non_null(file);
ret = ed_journal_file_open(file, filename, &error); ret = ed_journal_file_peek(file, filename, &error);
assert_int_equal(ret, ed_error_success); assert_int_equal(ret, ed_error_success);
assert_null(error); assert_null(error);
@ -33,7 +33,7 @@ static void test_old_filename(void **state)
assert_non_null(file); assert_non_null(file);
ret = ed_journal_file_open(file, filename, &error); ret = ed_journal_file_peek(file, filename, &error);
assert_int_equal(ret, ed_error_success); assert_int_equal(ret, ed_error_success);
assert_null(error); assert_null(error);
@ -50,7 +50,7 @@ static void test_new_datetime(void **state)
assert_non_null(file); assert_non_null(file);
ret = ed_journal_file_open(file, filename, &error); ret = ed_journal_file_peek(file, filename, &error);
assert_int_equal(ret, ed_error_success); assert_int_equal(ret, ed_error_success);
assert_null(error); assert_null(error);
@ -79,7 +79,7 @@ static void test_old_datetime(void **state)
assert_non_null(file); assert_non_null(file);
ret = ed_journal_file_open(file, filename, &error); ret = ed_journal_file_peek(file, filename, &error);
assert_int_equal(ret, ed_error_success); assert_int_equal(ret, ed_error_success);
assert_null(error); assert_null(error);
@ -109,7 +109,7 @@ static void test_valid_load(void **state)
file = ed_journal_file_new(); file = ed_journal_file_new();
assert_non_null(file); assert_non_null(file);
ret = ed_journal_file_open(file, filename, &error); ret = ed_journal_file_peek(file, filename, &error);
assert_null(error); assert_null(error);
assert_int_equal(ret, ed_error_success); assert_int_equal(ret, ed_error_success);
@ -128,7 +128,7 @@ static void test_valid_peek(void **state)
file = ed_journal_file_new(); file = ed_journal_file_new();
assert_non_null(file); assert_non_null(file);
ret = ed_journal_file_open(file, filename, &error); ret = ed_journal_file_peek(file, filename, &error);
assert_null(error); assert_null(error);
assert_int_equal(ret, ed_error_success); assert_int_equal(ret, ed_error_success);
@ -161,7 +161,7 @@ static void test_first_last(void **state)
file = ed_journal_file_new(); file = ed_journal_file_new();
assert_non_null(file); assert_non_null(file);
ret = ed_journal_file_open(file, filename, &error); ret = ed_journal_file_peek(file, filename, &error);
assert_null(error); assert_null(error);
assert_int_equal(ret, ed_error_success); assert_int_equal(ret, ed_error_success);
@ -193,7 +193,7 @@ static void test_in_range(void **state)
file = ed_journal_file_new(); file = ed_journal_file_new();
assert_non_null(file); assert_non_null(file);
ret = ed_journal_file_open(file, filename, &error); ret = ed_journal_file_peek(file, filename, &error);
assert_null(error); assert_null(error);
assert_int_equal(ret, ed_error_success); assert_int_equal(ret, ed_error_success);

View File

@ -14,7 +14,7 @@ static void test_userprofile_location(void **state)
{ {
setenv("USERPROFILE", "./", 1); setenv("USERPROFILE", "./", 1);
EDJournal *journal = ed_journal_new(); EDJournal *journal = ed_journal_new_with_location(NULL, NULL);
assert_non_null(journal); assert_non_null(journal);
gchar const *location = ed_journal_get_location(journal); gchar const *location = ed_journal_get_location(journal);
@ -27,7 +27,7 @@ static void test_file_sorting(void **state)
{ {
setenv("USERPROFILE", "./", 1); setenv("USERPROFILE", "./", 1);
EDJournal *journal = ed_journal_new(); EDJournal *journal = ed_journal_new_with_location(NULL, NULL);
assert_non_null(journal); assert_non_null(journal);
gchar const *location = ed_journal_get_location(journal); gchar const *location = ed_journal_get_location(journal);