Compare commits
2 Commits
3dd4e69235
...
86b0579cb7
Author | SHA1 | Date | |
---|---|---|---|
86b0579cb7 | |||
27a63e3414 |
103
wdmtg.c
103
wdmtg.c
@ -101,6 +101,7 @@ event_free(struct event *self)
|
|||||||
struct {
|
struct {
|
||||||
GAsyncQueue *queue; // Async queue of `struct event`
|
GAsyncQueue *queue; // Async queue of `struct event`
|
||||||
sqlite3 *db; // Event database
|
sqlite3 *db; // Event database
|
||||||
|
sqlite3_stmt *add_event; // Prepared statement: add event
|
||||||
} g;
|
} g;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -243,6 +244,41 @@ x_text_property(xcb_window_t window, xcb_atom_t property)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Async Queue Source ------------------------------------------------------
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
async_queue_source_prepare(G_GNUC_UNUSED GSource *source,
|
||||||
|
G_GNUC_UNUSED gint *timeout_)
|
||||||
|
{
|
||||||
|
return g_async_queue_length(g.queue) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
async_queue_source_dispatch(G_GNUC_UNUSED GSource *source,
|
||||||
|
GSourceFunc callback, gpointer user_data)
|
||||||
|
{
|
||||||
|
// I don't want to call it once per message, prefer batch processing
|
||||||
|
if (callback)
|
||||||
|
return callback(user_data);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSource *
|
||||||
|
async_queue_source_new(void)
|
||||||
|
{
|
||||||
|
static GSourceFuncs funcs = {
|
||||||
|
.prepare = async_queue_source_prepare,
|
||||||
|
.check = NULL,
|
||||||
|
.dispatch = async_queue_source_dispatch,
|
||||||
|
.finalize = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
GSource *source = g_source_new(&funcs, sizeof *source);
|
||||||
|
g_source_set_name(source, "AsyncQueueSource");
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Generator ---------------------------------------------------------------
|
// --- Generator ---------------------------------------------------------------
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -252,6 +288,9 @@ push_event(void) {
|
|||||||
event->title = g_strdup(gen.current_title);
|
event->title = g_strdup(gen.current_title);
|
||||||
event->idle = gen.current_idle;
|
event->idle = gen.current_idle;
|
||||||
g_async_queue_push(g.queue, event);
|
g_async_queue_push(g.queue, event);
|
||||||
|
|
||||||
|
// This is the best thing GLib exposes (GWakeUp is internal)
|
||||||
|
g_main_context_wakeup(g_main_context_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
@ -309,11 +348,8 @@ update_current_window(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
free(gpr);
|
free(gpr);
|
||||||
if (update_window_title(new_title)) {
|
if (update_window_title(new_title))
|
||||||
printf("Window changed: %s\n",
|
|
||||||
gen.current_title ? gen.current_title : "(none)");
|
|
||||||
push_event();
|
push_event();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -324,11 +360,9 @@ on_x_property_notify(const xcb_property_notify_event_t *ev)
|
|||||||
update_current_window();
|
update_current_window();
|
||||||
} else if (ev->window == gen.current_window &&
|
} else if (ev->window == gen.current_window &&
|
||||||
ev->atom == gen.atom_net_wm_name) {
|
ev->atom == gen.atom_net_wm_name) {
|
||||||
if (update_window_title(x_window_title(ev->window))) {
|
if (update_window_title(x_window_title(ev->window)))
|
||||||
printf("Title changed: %s\n", gen.current_title);
|
|
||||||
push_event();
|
push_event();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -357,7 +391,6 @@ static void
|
|||||||
on_x_alarm_notify(const xcb_sync_alarm_notify_event_t *ev)
|
on_x_alarm_notify(const xcb_sync_alarm_notify_event_t *ev)
|
||||||
{
|
{
|
||||||
if (ev->alarm == gen.idle_alarm_inactive) {
|
if (ev->alarm == gen.idle_alarm_inactive) {
|
||||||
printf("User is inactive\n");
|
|
||||||
gen.current_idle = true;
|
gen.current_idle = true;
|
||||||
push_event();
|
push_event();
|
||||||
|
|
||||||
@ -369,7 +402,6 @@ on_x_alarm_notify(const xcb_sync_alarm_notify_event_t *ev)
|
|||||||
set_idle_alarm(&gen.idle_alarm_active,
|
set_idle_alarm(&gen.idle_alarm_active,
|
||||||
XCB_SYNC_TESTTYPE_NEGATIVE_COMPARISON, minus_one);
|
XCB_SYNC_TESTTYPE_NEGATIVE_COMPARISON, minus_one);
|
||||||
} else if (ev->alarm == gen.idle_alarm_active) {
|
} else if (ev->alarm == gen.idle_alarm_active) {
|
||||||
printf("User is active\n");
|
|
||||||
gen.current_idle = false;
|
gen.current_idle = false;
|
||||||
push_event();
|
push_event();
|
||||||
|
|
||||||
@ -378,7 +410,7 @@ on_x_alarm_notify(const xcb_sync_alarm_notify_event_t *ev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static void
|
||||||
process_x11_event(xcb_generic_event_t *ev)
|
process_x11_event(xcb_generic_event_t *ev)
|
||||||
{
|
{
|
||||||
int event_code = ev->response_type & 0x7f;
|
int event_code = ev->response_type & 0x7f;
|
||||||
@ -508,6 +540,44 @@ generator_cleanup(void)
|
|||||||
|
|
||||||
// --- Main --------------------------------------------------------------------
|
// --- Main --------------------------------------------------------------------
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_queue_incoming(G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
char *errmsg = NULL;
|
||||||
|
if ((rc = sqlite3_exec(g.db, "BEGIN", NULL, NULL, &errmsg))) {
|
||||||
|
g_printerr("DB BEGIN error: %s\n", errmsg);
|
||||||
|
free(errmsg);
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: there should ideally be a limit to how many things can end up
|
||||||
|
// in a transaction at once (the amount of dequeues here)
|
||||||
|
struct event *event = NULL;
|
||||||
|
while ((event = g_async_queue_try_pop(g.queue))) {
|
||||||
|
printf("Event: ts: %ld, title: %s, idle: %d\n",
|
||||||
|
event->timestamp, event->title ?: "(none)", event->idle);
|
||||||
|
|
||||||
|
if ((rc = sqlite3_bind_int64(g.add_event, 1, event->timestamp)) ||
|
||||||
|
// FIXME: it will fail on NULL titles
|
||||||
|
(rc = sqlite3_bind_text(g.add_event, 2, event->title, -1,
|
||||||
|
SQLITE_STATIC)) ||
|
||||||
|
(rc = sqlite3_bind_int(g.add_event, 3, event->idle)))
|
||||||
|
g_printerr("DB bind error: %s\n", sqlite3_errmsg(g.db));
|
||||||
|
|
||||||
|
if (((rc = sqlite3_step(g.add_event)) && rc != SQLITE_DONE) ||
|
||||||
|
(rc = sqlite3_reset(g.add_event)))
|
||||||
|
g_printerr("DB write error: %s\n", sqlite3_errmsg(g.db));
|
||||||
|
|
||||||
|
event_free(event);
|
||||||
|
}
|
||||||
|
if ((rc = sqlite3_exec(g.db, "COMMIT", NULL, NULL, &errmsg))) {
|
||||||
|
g_printerr("DB commit error: %s\n", errmsg);
|
||||||
|
free(errmsg);
|
||||||
|
}
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
static sqlite3 *
|
static sqlite3 *
|
||||||
database_init(const gchar *db_path)
|
database_init(const gchar *db_path)
|
||||||
{
|
{
|
||||||
@ -552,6 +622,11 @@ database_init(const gchar *db_path)
|
|||||||
}
|
}
|
||||||
if ((rc = sqlite3_exec(db, "COMMIT", NULL, NULL, &errmsg)))
|
if ((rc = sqlite3_exec(db, "COMMIT", NULL, NULL, &errmsg)))
|
||||||
exit_fatal("%s: %s", db_path, errmsg);
|
exit_fatal("%s: %s", db_path, errmsg);
|
||||||
|
|
||||||
|
if ((rc = sqlite3_prepare_v2(db,
|
||||||
|
"INSERT INTO events (timestamp, title, idle) VALUES (?, ?, ?)", -1,
|
||||||
|
&g.add_event, NULL)))
|
||||||
|
exit_fatal("%s: %s", db_path, sqlite3_errmsg(db));
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,12 +717,12 @@ main(int argc, char *argv[])
|
|||||||
g.db = database_init(db_path);
|
g.db = database_init(db_path);
|
||||||
g_free(db_path);
|
g_free(db_path);
|
||||||
|
|
||||||
// TODO: somehow read events from the async queue
|
GSource *queue_source = async_queue_source_new();
|
||||||
// TODO: how in the name of fuck would our custom source wake up a sleeping
|
g_source_set_callback(queue_source, on_queue_incoming, NULL, NULL);
|
||||||
// main loop? There is g_main_context_wakeup() but...
|
g_source_attach(queue_source, g_main_context_default());
|
||||||
// - GWakeUp is internal, apparently
|
|
||||||
|
|
||||||
// TODO: listen for connections on the control socket
|
// TODO: listen for connections on the control socket
|
||||||
|
// - just show/map the window when anything connects at all
|
||||||
|
|
||||||
WdmtgWindow *window = wdmtg_window_new_with_db(g.db);
|
WdmtgWindow *window = wdmtg_window_new_with_db(g.db);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user