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

implement notification time freezing #532

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
8 changes: 8 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ void init_default_style(struct mako_style *style) {
style->actions = true;
style->default_timeout = 0;
style->ignore_timeout = false;
style->freeze = false;

style->colors.background = 0x285577FF;
style->colors.text = 0xFFFFFFFF;
Expand Down Expand Up @@ -301,6 +302,11 @@ bool apply_style(struct mako_style *target, const struct mako_style *style) {
target->spec.ignore_timeout = true;
}

if (style->spec.freeze) {
target->freeze = style->freeze;
target->spec.freeze = true;
}

if (style->spec.colors.background) {
target->colors.background = style->colors.background;
target->spec.colors.background = true;
Expand Down Expand Up @@ -623,6 +629,8 @@ static bool apply_style_option(struct mako_style *style, const char *name,
} else if (strcmp(name, "ignore-timeout") == 0) {
return spec->ignore_timeout =
parse_boolean(value, &style->ignore_timeout);
} else if (strcmp(name, "freeze") == 0) {
return spec->freeze = parse_boolean(value, &style->freeze);
} else if (strcmp(name, "group-by") == 0) {
return spec->group_criteria_spec =
parse_criteria_spec(value, &style->group_criteria_spec);
Expand Down
83 changes: 79 additions & 4 deletions criteria.c
Original file line number Diff line number Diff line change
Expand Up @@ -418,15 +418,56 @@ struct mako_criteria *global_criteria(struct mako_config *config) {
return criteria;
}

// Iterate through `criteria_list`, applying the style from each matching
static void timespec_from_ms(struct timespec *t, long time_ms) {
static const long ms = 1000000;

t->tv_sec = time_ms / 1000;
t->tv_nsec = (time_ms % 1000) * ms;
}

static void timespec_add(struct timespec *t, struct timespec *u) {
static const long s = 1000000000;

t->tv_sec += u->tv_sec;
t->tv_nsec += u->tv_nsec;

if (t->tv_nsec >= s) {
t->tv_nsec -= s;
++t->tv_sec;
}
}

static void timespec_sub(struct timespec *t, struct timespec *u) {
static const long s = 1000000000;

t->tv_sec -= u->tv_sec;
t->tv_nsec += s;
t->tv_nsec -= u->tv_nsec;

if (t->tv_nsec >= s) {
t->tv_nsec -= s;
} else {
--t->tv_sec;
}
}

static void handle_notification_timer(void *data) {
struct mako_notification *notif = data;
struct mako_surface *surface = notif->surface;
notif->timer = NULL;

close_notification(notif, MAKO_NOTIFICATION_CLOSE_EXPIRED, true);
set_dirty(surface);
}

// Iterate through the criteria list, applying the style from each matching
// criteria to `notif`. Returns the number of criteria that matched, or -1 if
// a failure occurs.
ssize_t apply_each_criteria(struct wl_list *criteria_list,
struct mako_notification *notif) {
ssize_t apply_each_criteria(struct mako_state *state, struct mako_notification *notif) {
ssize_t match_count = 0;

struct mako_criteria *criteria;
wl_list_for_each(criteria, criteria_list, link) {
wl_list_for_each(criteria, &state->config.criteria, link) {
if (!match_criteria(criteria, notif)) {
continue;
}
Expand All @@ -447,6 +488,40 @@ ssize_t apply_each_criteria(struct wl_list *criteria_list,
}
}

int32_t expire_timeout = notif->requested_timeout;
if (expire_timeout < 0 || notif->style.ignore_timeout) {
expire_timeout = notif->style.default_timeout;
}
if (notif->frozen != notif->style.freeze) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if (notif->style.freeze) {
notif->froze_at = now;
} else {
timespec_sub(&now, &notif->froze_at);
timespec_add(&notif->at, &now);
}
notif->frozen = notif->style.freeze;
}
if (notif->frozen) {
expire_timeout = 0;
}

if (expire_timeout > 0) {
struct timespec at = notif->at, delta;
timespec_from_ms(&delta, expire_timeout);
timespec_add(&at, &delta);
if (notif->timer) {
notif->timer->at = at;
} else {
notif->timer = add_event_loop_timer(&state->event_loop, &at,
handle_notification_timer, notif);
}
} else if (notif->timer) {
destroy_timer(notif->timer);
notif->timer = NULL;
}

if (!notif->surface) {
notif->surface = create_surface(notif->state, notif->style.output,
notif->style.layer, notif->style.anchor);
Expand Down
2 changes: 1 addition & 1 deletion dbus/mako.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ static void reapply_config(struct mako_state *state) {

finish_style(&notif->style);
init_empty_style(&notif->style);
apply_each_criteria(&state->config.criteria, notif);
apply_each_criteria(state, notif);

// Having to do this for every single notification really hurts... but
// it does do The Right Thing (tm).
Expand Down
21 changes: 1 addition & 20 deletions dbus/xdg.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,6 @@ static int handle_get_capabilities(sd_bus_message *msg, void *data,
return 0;
}

static void handle_notification_timer(void *data) {
struct mako_notification *notif = data;
struct mako_surface *surface = notif->surface;
notif->timer = NULL;

close_notification(notif, MAKO_NOTIFICATION_CLOSE_EXPIRED, true);
set_dirty(surface);
}

static int handle_notify(sd_bus_message *msg, void *data,
sd_bus_error *ret_error) {
struct mako_state *state = data;
Expand Down Expand Up @@ -382,7 +373,7 @@ static int handle_notify(sd_bus_message *msg, void *data,
insert_notification(state, notif);
}

int match_count = apply_each_criteria(&state->config.criteria, notif);
int match_count = apply_each_criteria(state, notif);
if (match_count == -1) {
// We encountered an allocation failure or similar while applying
// criteria. The notification may be partially matched, but the worst
Expand All @@ -398,16 +389,6 @@ static int handle_notify(sd_bus_message *msg, void *data,
return -1;
}

int32_t expire_timeout = notif->requested_timeout;
if (expire_timeout < 0 || notif->style.ignore_timeout) {
expire_timeout = notif->style.default_timeout;
}

if (expire_timeout > 0) {
notif->timer = add_event_loop_timer(&state->event_loop, expire_timeout,
handle_notification_timer, notif);
}

if (notif->style.icons) {
notif->icon = create_icon(notif);
}
Expand Down
6 changes: 6 additions & 0 deletions doc/mako.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,12 @@ associated command-line option.

Default: 0

*freeze*=0|1
Whether to freeze this notification's active timeout, stopping it from
progressing. This can be used for pausing notifications while you're away.

Default: 0

# COLORS

Colors can be specified as _#RRGGBB_ or _#RRGGBBAA_.
Expand Down
22 changes: 3 additions & 19 deletions event-loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,6 @@ void finish_event_loop(struct mako_event_loop *loop) {
}
}

static void timespec_add(struct timespec *t, int delta_ms) {
static const long ms = 1000000, s = 1000000000;

int delta_ms_low = delta_ms % 1000;
int delta_s_high = delta_ms / 1000;

t->tv_sec += delta_s_high;

t->tv_nsec += (long)delta_ms_low * ms;
if (t->tv_nsec >= s) {
t->tv_nsec -= s;
++t->tv_sec;
}
}

static bool timespec_less(struct timespec *t1, struct timespec *t2) {
if (t1->tv_sec != t2->tv_sec) {
return t1->tv_sec < t2->tv_sec;
Expand Down Expand Up @@ -124,7 +109,7 @@ static void update_event_loop_timer(struct mako_event_loop *loop) {
}

struct mako_timer *add_event_loop_timer(struct mako_event_loop *loop,
int delay_ms, mako_event_loop_timer_func_t func, void *data) {
struct timespec *at, mako_event_loop_timer_func_t func, void *data) {
struct mako_timer *timer = calloc(1, sizeof(struct mako_timer));
if (timer == NULL) {
fprintf(stderr, "allocation failed\n");
Expand All @@ -133,10 +118,9 @@ struct mako_timer *add_event_loop_timer(struct mako_event_loop *loop,
timer->event_loop = loop;
timer->func = func;
timer->user_data = data;
wl_list_insert(&loop->timers, &timer->link);
timer->at = *at;

clock_gettime(CLOCK_MONOTONIC, &timer->at);
timespec_add(&timer->at, delay_ms);
wl_list_insert(&loop->timers, &timer->link);

update_event_loop_timer(loop);
return timer;
Expand Down
3 changes: 2 additions & 1 deletion include/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct mako_style_spec {
bool width, height, outer_margin, margin, padding, border_size, border_radius, font,
markup, format, text_alignment, actions, default_timeout, ignore_timeout,
icons, max_icon_size, icon_path, group_criteria_spec, invisible, history,
icon_location, max_visible, layer, output, anchor;
icon_location, max_visible, layer, output, anchor, freeze;
struct {
bool background, text, border, progress;
} colors;
Expand Down Expand Up @@ -76,6 +76,7 @@ struct mako_style {
bool actions;
int default_timeout; // in ms
bool ignore_timeout;
bool freeze;

struct {
uint32_t background;
Expand Down
4 changes: 2 additions & 2 deletions include/criteria.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <wayland-client.h>
#include "config.h"
#include "types.h"
#include "mako.h"

struct mako_config;
struct mako_notification;
Expand Down Expand Up @@ -53,8 +54,7 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria);
bool apply_criteria_field(struct mako_criteria *criteria, char *token);

struct mako_criteria *global_criteria(struct mako_config *config);
ssize_t apply_each_criteria(struct wl_list *criteria_list,
struct mako_notification *notif);
ssize_t apply_each_criteria(struct mako_state *state, struct mako_notification *notif);
struct mako_criteria *create_criteria_from_notification(
struct mako_notification *notif, struct mako_criteria_spec *spec);

Expand Down
2 changes: 1 addition & 1 deletion include/event-loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ bool init_event_loop(struct mako_event_loop *loop, sd_bus *bus,
void finish_event_loop(struct mako_event_loop *loop);
int run_event_loop(struct mako_event_loop *loop);
struct mako_timer *add_event_loop_timer(struct mako_event_loop *loop,
int delay_ms, mako_event_loop_timer_func_t func, void *data);
struct timespec *at, mako_event_loop_timer_func_t func, void *data);

void destroy_timer(struct mako_timer *timer);

Expand Down
3 changes: 3 additions & 0 deletions include/notification.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ struct mako_notification {

struct mako_hotspot hotspot;
struct mako_timer *timer;
struct timespec at;
struct timespec froze_at;
bool frozen;
};

struct mako_action {
Expand Down
3 changes: 3 additions & 0 deletions notification.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ void reset_notification(struct mako_notification *notif) {

destroy_icon(notif->icon);
notif->icon = NULL;

clock_gettime(CLOCK_MONOTONIC, &notif->at);
notif->frozen = false;
}

struct mako_notification *create_notification(struct mako_state *state) {
Expand Down
4 changes: 2 additions & 2 deletions render.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ void render(struct mako_surface *surface, struct pool_buffer *buffer, int scale,
// Immediately before rendering we need to re-match all of the criteria
// so that matches against the anchor and output work even if the
// output was automatically assigned by the compositor.
int rematch_count = apply_each_criteria(&state->config.criteria, notif);
int rematch_count = apply_each_criteria(state, notif);
if (rematch_count == -1) {
// We encountered an allocation failure or similar while applying
// criteria. The notification may be partially matched, but the
Expand Down Expand Up @@ -427,7 +427,7 @@ void render(struct mako_surface *surface, struct pool_buffer *buffer, int scale,
struct mako_notification *hidden_notif = create_notification(state);
hidden_notif->surface = surface;
hidden_notif->hidden = true;
apply_each_criteria(&state->config.criteria, hidden_notif);
apply_each_criteria(state, hidden_notif);

struct mako_style *style = &hidden_notif->style;

Expand Down