Skip to content
Open
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
9 changes: 9 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ void init_default_style(struct mako_style *style) {
style->actions = true;
style->default_timeout = 0;
style->ignore_timeout = false;
style->timeout_nodismiss = false;

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

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

if (style->spec.colors.background) {
target->colors.background = style->colors.background;
target->spec.colors.background = true;
Expand Down Expand Up @@ -642,6 +648,9 @@ 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, "timeout-nodismiss") == 0) {
return spec->timeout_nodismiss =
parse_boolean(value, &style->timeout_nodismiss);
} else if (strcmp(name, "group-by") == 0) {
return spec->group_criteria_spec =
parse_criteria_spec(value, &style->group_criteria_spec);
Expand Down
3 changes: 2 additions & 1 deletion contrib/completions/bash/mako
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ _mako()
'--sort'
'--default-timeout'
'--ignore-timeout'
'--timeout-nodismiss'
'--output'
'--layer'
'--anchor'
Expand All @@ -44,7 +45,7 @@ _mako()
COMPREPLY=($(compgen -f -- "$cur"))
return
;;
--icons|--markup|--actions|--history|--ignore-timeout)
--icons|--markup|--actions|--history|--ignore-timeout|--timeout-nodismiss)
COMPREPLY=($(compgen -W "0 1" -- "$cur"))
return
;;
Expand Down
5 changes: 5 additions & 0 deletions contrib/completions/bash/makoctl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ _makoctl()
'menu'
'list'
'history'
'mark-read'
'reload'
'mode'
'help'
Expand Down Expand Up @@ -41,6 +42,10 @@ _makoctl()
COMPREPLY=($(compgen -W "-a -r -t -s" -- "$cur"))
return
;;
mark-read)
COMPREPLY=($(compgen -W "-a --all -n" -- "$cur"))
return
;;
esac

if [[ "${COMP_WORDS[COMP_CWORD-3]}" == "menu" ]]; then
Expand Down
1 change: 1 addition & 0 deletions contrib/completions/fish/mako.fish
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ complete -c mako -l history -d 'Add expired notifications to history' -xa "1 0"
complete -c mako -l sort -d 'Set notification sorting method' -x
complete -c mako -l default-timeout -d 'Notification timeout in ms' -x
complete -c mako -l ignore-timeout -d 'Enable notification timeout or not' -xa "1 0"
complete -c mako -l timeout-nodismiss -d 'Keep timed-out notifications as unread in history' -xa "1 0"
complete -c mako -l output -d 'Show notifications on this output' -xa '(complete_outputs)'
complete -c mako -l layer -d 'Show notifications on this layer' -x
complete -c mako -l anchor -d 'Position on output to put notifications' -x
Expand Down
5 changes: 4 additions & 1 deletion contrib/completions/fish/makoctl.fish
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
function __fish_makoctl_complete_no_subcommand
for i in (commandline -opc)
if contains -- $i dismiss restore invoke menu list reload mode help
if contains -- $i dismiss restore invoke menu list history mark-read reload mode help
return 1
end
end
Expand All @@ -24,6 +24,9 @@ complete -c makoctl -n '__fish_seen_subcommand_from dismiss' -s n -d "Dismiss th
complete -c makoctl -n '__fish_seen_subcommand_from invoke' -s n -d "Invoke an action on the notification with the given id" -x
complete -c makoctl -n '__fish_seen_subcommand_from menu' -s n -d "Use a program to select one action on the notification with the given id" -x
complete -c makoctl -n '__fish_seen_subcommand_from menu' -a "(__fish_complete_command)" -x
complete -c makoctl -n '__fish_makoctl_complete_no_subcommand' -a mark-read -d 'Mark history notifications as read' -x
complete -c makoctl -n '__fish_seen_subcommand_from mark-read' -s a -l all -d "Mark all as read" -x
complete -c makoctl -n '__fish_seen_subcommand_from mark-read' -s n -d "Mark notification with given id as read" -x
complete -c makoctl -n '__fish_seen_subcommand_from mode' -s a -d "Add mode" -x
complete -c makoctl -n '__fish_seen_subcommand_from mode' -s r -d "Remove mode" -x
complete -c makoctl -n '__fish_seen_subcommand_from mode' -s t -d "Toggle mode" -x
Expand Down
1 change: 1 addition & 0 deletions contrib/completions/zsh/_mako
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ _arguments \
'--history[Add expired notification to history.]:history:' \
'--default-timeout[Default timeout in milliseconds.]:timeout (ms):' \
'--ignore-timeout[If set, mako will ignore the expire timeout sent by notifications and use the one provided by default-timeout instead.]:Use default timeout:(0 1)' \
'--timeout-nodismiss[Keep timed-out notifications as unread in history.]:timeout nodismiss:(0 1)' \
'--output[Show notifications on this output.]:name:' \
'--layer[Arrange notifications at this layer.]:layer:(background bottom top overlay)' \
'--anchor[Position on output to put notifications.]:position:(top-right bottom-right bottom-center bottom-left top-left top-center center-right center-left center)' \
Expand Down
6 changes: 6 additions & 0 deletions contrib/completions/zsh/_makoctl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ makoctl_cmds=(
'menu:Use a program to select one action to be invoked on the notification'
'list:Retrieve a list of current notifications'
'history:Retrieve a list of dismissed notifications'
'mark-read:Mark history notifications as read'
'reload:Reload the configuration file'
'mode:List, activate, or deactivate modes'
'help:Show help message and quit'
Expand Down Expand Up @@ -40,6 +41,11 @@ else
'-n[Use a program to select one action on the notification with the given id]:id:' \
'*:prog and args:_command_names -e'
;;
mark-read)
_arguments -s \
'(-a --all)'{-a,--all}'[Mark all as read]' \
'-n[Mark notification with given id as read]:id:'
;;
mode)
_arguments -s \
'*-a[Add mode]:mode:' \
Expand Down
66 changes: 66 additions & 0 deletions dbus/mako.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ static int handle_restore_action(sd_bus_message *msg, void *data,
struct mako_notification *notif =
wl_container_of(state->history.next, notif, link);
wl_list_remove(&notif->link);
notif->unread = false;

finish_style(&notif->style);
init_empty_style(&notif->style);
Expand All @@ -140,6 +141,62 @@ static int handle_restore_action(sd_bus_message *msg, void *data,
return sd_bus_reply_method_return(msg, "");
}

static int handle_mark_read(sd_bus_message *msg, void *data,
sd_bus_error *ret_error) {
struct mako_state *state = data;

uint32_t id = 0;
int all = 0;

int ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
if (ret < 0) {
return ret;
}

while (true) {
ret = sd_bus_message_enter_container(msg, 'e', "sv");
if (ret < 0) {
return ret;
} else if (ret == 0) {
break;
}

const char *key = NULL;
ret = sd_bus_message_read(msg, "s", &key);
if (ret < 0) {
return ret;
}

if (strcmp(key, "id") == 0) {
ret = sd_bus_message_read(msg, "v", "u", &id);
} else if (strcmp(key, "all") == 0) {
ret = sd_bus_message_read(msg, "v", "b", &all);
} else {
ret = sd_bus_message_skip(msg, "v");
}
if (ret < 0) {
return ret;
}

ret = sd_bus_message_exit_container(msg);
if (ret < 0) {
return ret;
}
}

struct mako_notification *notif;
wl_list_for_each(notif, &state->history, link) {
if (all || notif->id == id) {
notif->unread = false;
if (!all) {
break;
}
}
}

return sd_bus_reply_method_return(msg, "");
}

static int handle_list_for_each(sd_bus_message *reply, struct wl_list *list) {

int ret = sd_bus_message_open_container(reply, 'a', "a{sv}");
Expand Down Expand Up @@ -202,6 +259,14 @@ static int handle_list_for_each(sd_bus_message *reply, struct wl_list *list) {
return ret;
}

if (notif->unread) {
ret = sd_bus_message_append(reply, "{sv}", "unread",
"b", (int)notif->unread);
if (ret < 0) {
return ret;
}
}

ret = sd_bus_message_open_container(reply, 'e', "sv");
if (ret < 0) {
return ret;
Expand Down Expand Up @@ -501,6 +566,7 @@ static const sd_bus_vtable service_vtable[] = {
SD_BUS_METHOD("RestoreNotification", "", "", handle_restore_action, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListNotifications", "", "aa{sv}", handle_list_notifications, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListHistory", "", "aa{sv}", handle_list_history, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MarkRead", "a{sv}", "", handle_mark_read, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reload", "", "", handle_reload, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetMode", "s", "", handle_set_mode, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListModes", "", "as", handle_list_modes, SD_BUS_VTABLE_UNPRIVILEGED),
Expand Down
9 changes: 9 additions & 0 deletions doc/mako.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,15 @@ Default when grouped: (%g) <b>%s</b>\\n%b

Default: 0

*timeout-nodismiss*=0|1
If set, notifications that expire via timeout will be moved to history
with an _unread_ flag. Notifications dismissed explicitly by the user
(e.g. via click or _makoctl dismiss_) will not have this flag. The flag
is visible in _makoctl history -j_ output and can be cleared with
_makoctl mark-read_.

Default: 0

*group-by*=_field[,field,...]_
A comma-separated list of criteria fields that will be compared to other
visible notifications to determine if this one should form a group with
Expand Down
12 changes: 12 additions & 0 deletions doc/makoctl.1.scd
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ Sends IPC commands to the running mako daemon via dbus.
*-j*
Use JSON output.

*mark-read* [-a|--all] [-n <id>]
Marks notifications in history as read, clearing the _unread_ flag set by
_timeout-nodismiss_.

Options:

*-a, --all*
Mark all history notifications as read.

*-n* <id>
Mark the notification with the given id as read.

*reload*
Reloads the configuration file.

Expand Down
4 changes: 3 additions & 1 deletion include/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ enum mako_icon_location {
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, icon_border_radius, group_criteria_spec, invisible, history,
timeout_nodismiss, icons, max_icon_size, icon_path, icon_border_radius,
group_criteria_spec, invisible, history,
icon_location, max_visible, layer, output, anchor;
struct {
bool background, text, border, progress;
Expand Down Expand Up @@ -77,6 +78,7 @@ struct mako_style {
bool actions;
int default_timeout; // in ms
bool ignore_timeout;
bool timeout_nodismiss;

struct {
uint32_t background;
Expand Down
1 change: 1 addition & 0 deletions include/notification.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct mako_notification {
int group_index;
int group_count;
bool hidden;
bool unread;

char *app_name;
char *app_icon;
Expand Down
83 changes: 82 additions & 1 deletion makoctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ struct notification_metadata {
uint32_t id;
const char *app_name, *app_icon, *category, *desktop_entry, *summary, *body;
uint8_t urgency;
int unread;
char **actions;
};

Expand Down Expand Up @@ -404,7 +405,11 @@ static void print_notification_as_json(const struct notification_metadata *notif
print_json_str(title);
}
}
printf("}\n");
printf("}");
if (notif->unread) {
printf(",\n \"unread\": true");
}
printf("\n");
printf(" }");
}

Expand Down Expand Up @@ -442,6 +447,8 @@ static int print_notification(sd_bus_message *reply, bool json) {
ret = sd_bus_message_read(reply, "v", "s", &notif.desktop_entry);
} else if (strcmp(key, "urgency") == 0) {
ret = sd_bus_message_read(reply, "v", "y", &notif.urgency);
} else if (strcmp(key, "unread") == 0) {
ret = sd_bus_message_read(reply, "v", "b", &notif.unread);
} else {
ret = sd_bus_message_skip(reply, "v");
}
Expand Down Expand Up @@ -526,6 +533,76 @@ static int print_notification_list(sd_bus_message *reply, int argc, char *argv[]
return sd_bus_message_exit_container(reply);
}

static int run_mark_read(sd_bus *bus, int argc, char *argv[]) {
uint32_t id = 0;
bool all = false;
while (true) {
const struct option options[] = {
{ "all", no_argument, 0, 'a' },
{0},
};
int opt = getopt_long(argc, argv, "an:", options, NULL);
if (opt == -1) {
break;
}

switch (opt) {
case 'a':
all = true;
break;
case 'n':;
int ret = parse_uint32(&id, optarg);
if (ret < 0) {
log_neg_errno(ret, "invalid notification ID");
return 1;
}
break;
default:
return -EINVAL;
}
}

if (all && id != 0) {
fprintf(stderr, "-n cannot be used with -a\n");
return -EINVAL;
}

if (!all && id == 0) {
fprintf(stderr, "must specify -n <id> or -a\n");
return -EINVAL;
}

sd_bus_message *msg = NULL;
int ret = new_method_call(bus, &msg, "MarkRead");
if (ret < 0) {
return ret;
}

ret = sd_bus_message_open_container(msg, 'a', "{sv}");
if (ret < 0) {
return ret;
}

ret = sd_bus_message_append(msg, "{sv}", "id", "u", id);
if (ret < 0) {
return ret;
}

ret = sd_bus_message_append(msg, "{sv}", "all", "b", (int)all);
if (ret < 0) {
return ret;
}

ret = sd_bus_message_close_container(msg);
if (ret < 0) {
return ret;
}

ret = call(bus, msg, NULL);
sd_bus_message_unref(msg);
return ret;
}

static int run_history(sd_bus *bus, int argc, char *argv[]) {
sd_bus_message *reply = NULL;
int ret = call_method(bus, "ListHistory", &reply, "");
Expand Down Expand Up @@ -932,6 +1009,8 @@ static const char usage[] =
" notification if none is given\n"
" list [-j] List notifications\n"
" history [-j] List history\n"
" mark-read [-n id] Mark a history notification as read\n"
" [-a|--all] Mark all history notifications as read\n"
" reload Reload the configuration file\n"
" mode List modes\n"
" mode [-a mode]... [-r mode]... Add/remove modes\n"
Expand Down Expand Up @@ -975,6 +1054,8 @@ int main(int argc, char *argv[]) {
ret = run_mode(bus, cmd_argc, cmd_argv);
} else if (strcmp(cmd, "reload") == 0) {
ret = call_method(bus, "Reload", NULL, "");
} else if (strcmp(cmd, "mark-read") == 0) {
ret = run_mark_read(bus, cmd_argc, cmd_argv);
} else if (strcmp(cmd, "restore") == 0) {
ret = call_method(bus, "RestoreNotification", NULL, "");
} else {
Expand Down
Loading