Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Main loop: rework timer logic #1308

Merged
merged 1 commit into from
Apr 14, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 58 additions & 23 deletions src/dunst.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,29 @@ struct dunst_status dunst_status_get(void)
/* misc functions */
static gboolean run(void *data);

/**
* The reason for which the run function was invoked.
* - DUNST_TIMER: the timer until the next event in queue has expired (routine
* wakeup)
* - DUNST_WAKEUP: an external event (eg. new notification) triggered a call to
* wake_up (unscheduled wakeup)
*/
enum dunst_run_reason {
DUNST_TIMER,
DUNST_WAKEUP,
};

const char* dunst_run_reason_str(enum dunst_run_reason reason) {
switch(reason) {
case DUNST_TIMER:
return "DUNST_TIMER";
case DUNST_WAKEUP:
return "DUNST_WAKEUP";
default:
return "BAD VALUE";
}
}

void wake_up(void)
{
// If wake_up is being called before the output has been setup we should
Expand All @@ -75,23 +98,52 @@ void wake_up(void)
}

LOG_D("Waking up");
run(GINT_TO_POINTER(1));
run(GINT_TO_POINTER(DUNST_WAKEUP));
}

static gboolean run(void *data)
{
static gint64 next_timeout = 0;
/* Timer gestion
* =============
*
* - At any time (except transiently during the execution of `run`), at
* most one glib timeout source (or "timer" here) exists. If `run`
* was invoked by a timer, it will be deleted upon return, as the
* function always returns G_SOURCE_REMOVE.
* - Furthermore, if next_timeout_id is not null, a timer with this
* glib source id exists and is running. As a consequence,
* - if reason is DUNST_TIMER, it is the timer that triggered the
* current call to run;
* - if reason is DUNST_WAKEUP, this timer was scheduled some time in
* the future (or in the past, but not yet executed my the main loop,
* which is equivalent for our purpose).
*
* Thus, in each call to run,
* - if reason is DUNST_WAKEUP and next_timeout_id != 0, we delete this
* timer -- we now have more recent information on which we can
* decide of a (maybe) better timeout.
* - in any case, we reset next_timeout_id to 0.
* - if there is any event to be run in the future, we set a new timer
* to this time, and update next_timeout_id accordingly.
*/

static guint next_timeout_id = 0;
int reason = GPOINTER_TO_INT(data);
enum dunst_run_reason reason = GPOINTER_TO_INT(data);

LOG_D("RUN, reason %i", reason);
LOG_D("RUN, reason %i: %s", reason, dunst_run_reason_str(reason));
gint64 now = time_monotonic_now();

dunst_status(S_FULLSCREEN, output->have_fullscreen_window());
dunst_status(S_IDLE, output->is_idle());

queues_update(status, now);

if(reason == DUNST_WAKEUP && next_timeout_id != 0) {
zappolowski marked this conversation as resolved.
Show resolved Hide resolved
// Delete the upcoming timer
g_source_remove(next_timeout_id);
}
next_timeout_id = 0;

if (!queues_length_displayed()) {
output->win_hide(win);
return G_SOURCE_REMOVE;
Expand All @@ -101,8 +153,6 @@ static gboolean run(void *data)
draw();
output->win_show(win);

bool timeout_set = true;

gint64 timeout_at = queues_get_next_datachange(now);
if (timeout_at != -1) {
// Previous computations may have taken time, update `now`
Expand All @@ -114,22 +164,7 @@ static gboolean run(void *data)

LOG_D("Sleeping for %li ms", sleep/1000);

if (sleep >= 0) {
if (reason == 0 || next_timeout < now || timeout_at < next_timeout) {
if (next_timeout != 0) {
g_source_remove(next_timeout_id);
}
timeout_set = true;
next_timeout_id = g_timeout_add(sleep/1000, run, NULL);
next_timeout = timeout_at;
}
}
}

if(!timeout_set) {
// If no new timeout was set this time, clear up te timer id
next_timeout = 0;
next_timeout_id = 0;
next_timeout_id = g_timeout_add(sleep/1000, run, NULL);
}

/* If the execution got triggered by g_timeout_add,
Expand Down Expand Up @@ -240,7 +275,7 @@ int dunst_main(int argc, char *argv[])
}

setup_done = true;
run(NULL);
run(GINT_TO_POINTER(DUNST_TIMER)); // The first run() is a scheduled one
g_main_loop_run(mainloop);
g_clear_pointer(&mainloop, g_main_loop_unref);

Expand Down