diff options
author | jakob.stendahl <jakob.stendahl@infomedia.dk> | 2024-07-12 18:02:47 +0200 |
---|---|---|
committer | jakob.stendahl <jakob.stendahl@infomedia.dk> | 2024-07-12 18:02:47 +0200 |
commit | ed74ab2d27460ec11a29f6d005a003d69104d7c5 (patch) | |
tree | 0ca5295ed6ab640db1d6d5dd4b7fc0d79210ccc5 | |
download | simple-notification-daemon-ed74ab2d27460ec11a29f6d005a003d69104d7c5.tar.gz simple-notification-daemon-ed74ab2d27460ec11a29f6d005a003d69104d7c5.zip |
Initial commit
-rw-r--r-- | .clangd | 2 | ||||
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 28 | ||||
-rw-r--r-- | snotif.c | 74 | ||||
-rw-r--r-- | snotif.h | 48 | ||||
-rw-r--r-- | snotifc.c | 518 | ||||
-rw-r--r-- | snotifd.c | 356 | ||||
-rw-r--r-- | snotifd.h | 0 | ||||
-rw-r--r-- | term.h | 16 |
9 files changed, 1045 insertions, 0 deletions
@@ -0,0 +1,2 @@ +CompileFlags: + Add: [-I/usr/include/dbus-1.0, -I/usr/lib64/dbus-1.0/include, -ldbus-1] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9f5782 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +snotifd +snotifc diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..38c3773 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +GCC := gcc +CFLAGS_DBUS := $(shell pkg-config --cflags --libs dbus-1) +CFLAGS = -g -Wall -O ${CFLAGS_DBUS} + +DEST = + +.PHONY: clean + +all: snotifd snotifc +o_files := $(patsubst %.c,%.o,$(wildcard *.c)) + +%: %.c + ${GCC} $< -o $@ -c ${CFLAGS} + +snotifd: ${o_files} + ${GCC} -o $@ $@.o snotif.o ${CFLAGS} + +snotifc: ${o_files} + ${GCC} -o $@ $@.o snotif.o ${CFLAGS} -lncursesw + +install: snotifc snotifd + install snotifd ${DEST}/usr/local/bin/snotifd + install snotifc ${DEST}/usr/local/bin/snotifc + +clean: + rm -f *.o + rm -f snotifc + rm -f snotifd diff --git a/snotif.c b/snotif.c new file mode 100644 index 0000000..8d914e5 --- /dev/null +++ b/snotif.c @@ -0,0 +1,74 @@ +#include "snotif.h" +#include <stdio.h> +#include <stdlib.h> + +char* strnotify_params(const struct NotifyParams* params) { + char time[80] = ""; + if (params->time) + strftime(time, 80, "%R:%S", localtime(¶ms->time)); + static char res[180]; + sprintf( + res, + "%s [%s] (timeout: %d, replaces: %u) %s\n", + time, params->app_name, params->expire_timeout, params->replaces_id, params->summary + ); + return res; +} + +void notifs_list_init(struct NotifsList** list) { + (*list) = malloc(sizeof(struct NotifsList)); + (*list)->list_size = 0; + (*list)->element_count = 0; + +#define INITIAL_SIZE 8 + (*list)->list = calloc(INITIAL_SIZE, sizeof(struct NotifyParams*)); + if ((*list)->list == NULL) { + fprintf(stderr, "Failed to allocate memory for notification log!\n"); + exit(1); + } + (*list)->list_size = INITIAL_SIZE; +} + +void notifs_list_set(struct NotifsList* list, struct NotifyParams* notif) { + while (notif->id > list->list_size) { + unsigned int new_size = list->list_size * 2; + struct NotifyParams** temp = realloc(list->list, new_size * sizeof(struct NotifyParams*)); + if (!temp) { + fprintf(stderr, "Failed to reallocate memory for notification log!\n"); + exit(1); + } + list->list = temp; + for (unsigned int i = list->list_size; i < new_size; i++) { + list->list[i] = NULL; + } + list->list_size = new_size; + } + + if (notif->id > list->element_count) { + list->element_count = notif->id; + } + list->list[notif->id - 1] = notif; +} + +int notifs_list_set_seen(struct NotifsList* list, unsigned int id, bool read) { + if (id > list-> list_size) + return 1; + list->list[id]->seen = read; + return 0; +} + +void notifs_list_free(struct NotifsList* list) { + for (unsigned int i = 0; i < list->element_count; i++) { + if (list->list[i]->app_name) + free(list->list[i]->app_name); + if (list->list[i]->app_icon) + free(list->list[i]->app_icon); + if (list->list[i]->summary) + free(list->list[i]->summary); + if (list->list[i]->body) + free(list->list[i]->body); + free(list->list[i]); + } + free(list->list); + free(list); +} diff --git a/snotif.h b/snotif.h new file mode 100644 index 0000000..dd52ac4 --- /dev/null +++ b/snotif.h @@ -0,0 +1,48 @@ +#ifndef SNOTIF_H +#define SNOTIF_H + +#include <dbus/dbus.h> +#include <stdbool.h> +#include <time.h> + +// Constansts for the DBus interactions +#define DBUS_NOTIF_INTERFACE "org.freedesktop.Notifications" +#define DBUS_CLIENT_INTERFACE "snotifd.Notifications" +#define DBUS_CLIENT_OBJECT "/snotifd/Notifications" +#define DBUS_METHOD_GETLIST "GetNotifications" +#define DBUS_METHOD_GETUNSEENCOUNT "GetUnseenCount" +#define DBUS_METHOD_SETSEEN "SetSeen" + +// Curses related constants +#define CPAIR_SEL 132 +#define CPAIR_B 4 + +// Extended struct from the specification at: +// https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html +struct NotifyParams { + unsigned int id; + char* app_name; + dbus_uint32_t replaces_id; + char* app_icon; + char* summary; + char* body; + dbus_int32_t expire_timeout; + time_t time; + bool seen; +}; + +// Convenient struct for keeping a list of notifications +struct NotifsList { + struct NotifyParams** list; + size_t element_count; + size_t list_size; +}; + +// Convenient methods for working with a list of notifications +char* strnotify_params(const struct NotifyParams* params); +void notifs_list_init(struct NotifsList** list); +void notifs_list_set(struct NotifsList* list, struct NotifyParams* notif); +void notifs_list_free(struct NotifsList* list); +int notifs_list_set_seen(struct NotifsList* list, unsigned int id, bool read); + +#endif diff --git a/snotifc.c b/snotifc.c new file mode 100644 index 0000000..ebda38a --- /dev/null +++ b/snotifc.c @@ -0,0 +1,518 @@ +#include "snotif.h" +#include "term.h" +#include <locale.h> +#include <ncurses.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void print_unseen_count(DBusConnection* conn) { + DBusMessage* msg; + DBusMessageIter args; + DBusPendingCall* pending; + + msg = dbus_message_new_method_call( + DBUS_CLIENT_INTERFACE, + DBUS_CLIENT_OBJECT, + DBUS_CLIENT_INTERFACE, + DBUS_METHOD_GETUNSEENCOUNT + ); + + dbus_message_iter_init_append(msg, &args); + + if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + + dbus_connection_flush(conn); + dbus_message_unref(msg); + + dbus_pending_call_block(pending); + msg = dbus_pending_call_steal_reply(pending); + if (msg == NULL) { + fprintf(stderr, "Reply Null\n"); exit(1); + } + dbus_pending_call_unref(pending); + + unsigned int count; + if (!dbus_message_iter_init(msg, &args)) + fprintf(stderr, "Message has no arguments!\n"); + else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args)) + fprintf(stderr, "Argument is not uint32!\n"); + else + dbus_message_iter_get_basic(&args, &count); + printf("%d", count); + fflush(stdout); + + dbus_message_unref(msg); +} + +void set_notif_seen(DBusConnection* conn, uint id, bool seen) { + DBusMessage* msg; + DBusMessageIter args; + DBusPendingCall* pending; + + msg = dbus_message_new_method_call( + DBUS_CLIENT_INTERFACE, + DBUS_CLIENT_OBJECT, + DBUS_CLIENT_INTERFACE, + DBUS_METHOD_SETSEEN + ); + + dbus_message_iter_init_append(msg, &args); + if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &id)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + + unsigned int _seen = seen; + if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_BOOLEAN, &_seen)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + + if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + + dbus_connection_flush(conn); + dbus_message_unref(msg); + + dbus_pending_call_block(pending); + msg = dbus_pending_call_steal_reply(pending); + if (msg == NULL) { + fprintf(stderr, "Reply Null\n"); exit(1); + } + dbus_pending_call_unref(pending); + + dbus_message_unref(msg); +} + +void print_notification_list(DBusConnection* conn) { + DBusMessage* msg; + DBusMessageIter args; + DBusPendingCall* pending; + + msg = dbus_message_new_method_call( + DBUS_CLIENT_INTERFACE, + DBUS_CLIENT_OBJECT, + DBUS_CLIENT_INTERFACE, + DBUS_METHOD_GETLIST + ); + + dbus_message_iter_init_append(msg, &args); + + if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + + dbus_connection_flush(conn); + dbus_message_unref(msg); + + dbus_pending_call_block(pending); + msg = dbus_pending_call_steal_reply(pending); + if (msg == NULL) { + fprintf(stderr, "Reply Null\n"); exit(1); + } + dbus_pending_call_unref(pending); + + DBusMessageIter array_iter, struct_iter; + if (!dbus_message_iter_init(msg, &args)) + fprintf(stderr, "Message has no arguments!\n"); + else { + if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&args)) { + dbus_message_iter_recurse(&args, &array_iter); + + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRUCT) { + dbus_message_iter_recurse(&array_iter, &struct_iter); + + unsigned int id; + const char* app_name; + const char* summary; + const char* body; + + dbus_message_iter_get_basic(&struct_iter, &id); + dbus_message_iter_next(&struct_iter); + dbus_message_iter_get_basic(&struct_iter, &app_name); + dbus_message_iter_next(&struct_iter); + dbus_message_iter_get_basic(&struct_iter, &summary); + dbus_message_iter_next(&struct_iter); + dbus_message_iter_get_basic(&struct_iter, &body); + dbus_message_iter_next(&struct_iter); + + //printf("Received Struct: { id: %d, app_name: %s, summary: %s, body: %s }\n", id, app_name, summary, body); + printf(KITAL"%10s"KNRM" ┃ "KMAG"%s"KNRM"\n", app_name, summary); + + dbus_message_iter_next(&array_iter); + } + } + } + + char* response = NULL; + if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &response, DBUS_TYPE_INVALID)) { + printf("Received: %s\n", response); + } + dbus_message_unref(msg); +} + +void add_to_notiflist(struct NotifsList* notifs, DBusMessageIter* struct_iter) { + struct NotifyParams* notif = malloc(sizeof(struct NotifyParams)); + if (!notif) { + fprintf(stderr, "Failed to allocate memory for notification parameters!\n"); + exit(1); + } + notif->app_icon = NULL; + notif->time = 0; + + const char* tmp_str; + dbus_message_iter_get_basic(struct_iter, ¬if->id); + + dbus_message_iter_next(struct_iter); + dbus_message_iter_get_basic(struct_iter, &tmp_str); + notif->app_name = strdup(tmp_str); + + dbus_message_iter_next(struct_iter); + dbus_message_iter_get_basic(struct_iter, &tmp_str); + notif->summary = strdup(tmp_str); + + dbus_message_iter_next(struct_iter); + dbus_message_iter_get_basic(struct_iter, &tmp_str); + notif->body = strdup(tmp_str); + + dbus_message_iter_next(struct_iter); + dbus_message_iter_get_basic(struct_iter, ¬if->time); + + dbus_message_iter_next(struct_iter); + dbus_message_iter_get_basic(struct_iter, ¬if->seen); + + notifs_list_set(notifs, notif); +} + +void get_notification_list(DBusConnection* conn, struct NotifsList* notifs) { + DBusMessage* msg; + DBusMessageIter args; + DBusPendingCall* pending; + + msg = dbus_message_new_method_call( + DBUS_CLIENT_INTERFACE, + DBUS_CLIENT_OBJECT, + DBUS_CLIENT_INTERFACE, + DBUS_METHOD_GETLIST + ); + + dbus_message_iter_init_append(msg, &args); + + if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + + dbus_connection_flush(conn); + dbus_message_unref(msg); + + dbus_pending_call_block(pending); + msg = dbus_pending_call_steal_reply(pending); + if (msg == NULL) { + fprintf(stderr, "Reply Null\n"); exit(1); + } + dbus_pending_call_unref(pending); + + DBusMessageIter array_iter, struct_iter; + if (dbus_message_iter_init(msg, &args) && dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_ARRAY) { + dbus_message_iter_recurse(&args, &array_iter); + + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRUCT) { + dbus_message_iter_recurse(&array_iter, &struct_iter); + add_to_notiflist(notifs, &struct_iter); + dbus_message_iter_next(&array_iter); + } + } + dbus_message_unref(msg); +} + +void init_dbus(DBusConnection** conn) { + DBusError err; + dbus_error_init(&err); + *conn = dbus_bus_get(DBUS_BUS_SESSION, &err); + + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Connection Error: %s\n", err.message); + dbus_error_free(&err); + exit(EXIT_FAILURE); + } +} + +void curses_init() { + initscr(); + cbreak(); + noecho(); + curs_set(0); + keypad(stdscr, TRUE); + start_color(); + use_default_colors(); + + init_pair(2, COLOR_BLACK, COLOR_CYAN); + init_pair(3, COLOR_YELLOW, COLOR_BLUE); + + init_pair(CPAIR_B, -1, -1); + init_pair(CPAIR_B + 1, COLOR_YELLOW, -1); + init_pair(CPAIR_B + 2, COLOR_MAGENTA, -1); + init_pair(CPAIR_B + 3, COLOR_WHITE, -1); + init_pair(CPAIR_B | CPAIR_SEL, -1, COLOR_CYAN); + init_pair((CPAIR_B + 1) | CPAIR_SEL, COLOR_YELLOW, COLOR_CYAN); + init_pair((CPAIR_B + 2) | CPAIR_SEL, COLOR_MAGENTA, COLOR_CYAN); + init_pair((CPAIR_B + 3) | CPAIR_SEL, COLOR_WHITE, COLOR_CYAN); +} + +void curses_display_menu(int c_id) { + mvprintw(LINES - 1, 0, " F7"); + attron(COLOR_PAIR(2)); + printw("Toogle read"); + attroff(COLOR_PAIR(2)); + + printw(" F8"); + attron(COLOR_PAIR(2)); + printw("Mark all read"); + attroff(COLOR_PAIR(2)); + + printw("F10"); + attron(COLOR_PAIR(2)); + printw("Quit"); + attroff(COLOR_PAIR(2)); + + chgat(-1, A_NORMAL, 2, NULL); + + attron(COLOR_PAIR(2)); + mvprintw(LINES-1, COLS-3, "%3d", c_id); + attroff(COLOR_PAIR(2)); +} + +void curses_display_notifs(struct NotifsList* notifs, int start, int selected) { + int w_app_name = 0; + int w = 0; + + for (size_t i = 0; i < notifs->element_count; i++) { + if (!notifs->list[i]) + continue; + + w = strlen(notifs->list[i]->app_name); + if (notifs->list[i]->app_name && w > w_app_name) + w_app_name = w; + } + + const int items = (notifs->element_count > LINES) ? LINES : notifs->element_count; + int n_printed = 0; + size_t ri; + + struct NotifyParams* notif; + for (size_t i = 0; i < items; i++) { + ri = notifs->element_count - i - 1; + if (!notifs->list[ri]) + continue; + notif = notifs->list[ri]; + + char time[10] = ""; + if (notif->time) + strftime(time, 10, "%R:%S", localtime(¬if->time)); + + short extra_bits = (i == selected) ? CPAIR_SEL : 0; + attron(COLOR_PAIR(CPAIR_B | extra_bits)); + if (notif->seen) { + attron(COLOR_PAIR((CPAIR_B + 3) | extra_bits)); + attron(A_ITALIC); + mvprintw(i, 0, " %s", time); + attroff(A_ITALIC); + printw(" │ "); + attron(A_ITALIC); + printw("%*s ", w_app_name, notif->app_name); + attroff(A_ITALIC); + printw(" │ "); + attron(A_ITALIC); + printw("%s", notif->summary); + attroff(A_ITALIC); + } else { + attron(A_ITALIC); + mvprintw(i, 0, " %s", time); + attroff(A_ITALIC); + printw(" │ "); + attron(COLOR_PAIR((CPAIR_B + 2) | extra_bits)); + printw("%*s", w_app_name, notif->app_name); + attroff(COLOR_PAIR((CPAIR_B + 2) | extra_bits)); + attron(COLOR_PAIR(CPAIR_B | extra_bits)); + printw(" │ "); + printw("%s", notif->summary); + } + attroff(COLOR_PAIR(CPAIR_B | extra_bits)); + chgat(-1, A_NORMAL, CPAIR_B | extra_bits, NULL); + + n_printed++; + } +} + +void show_full_notif(int id, struct NotifsList* notifs) { + if (id >= notifs->element_count || !notifs->list[id]) + return; + + int width = COLS; + int height = LINES - 1; + int start_x = 0; + int start_y = 0; + +#define MAX_WIDTH 80 + if (width > MAX_WIDTH) { + width = MAX_WIDTH; + start_x = ((COLS - 1) / 2) - (MAX_WIDTH / 2); + } +#define MAX_HEIGHT 20 + if (height > MAX_HEIGHT) { + height = MAX_HEIGHT; + start_y = ((LINES - 1) / 2) - (MAX_HEIGHT / 2); + } + + WINDOW *win = newwin(height, width, start_y, start_x); + WINDOW *win_content = newwin(height-2, width-2, start_y+1, start_x+1); + while (1) { + box(win, 0, 0); + + wattron(win, COLOR_PAIR(2)); + mvwprintw(win, 0, 1, "%s", notifs->list[id]->app_name); + mvwprintw(win, height-1, 1, "q: close"); + wattroff(win, COLOR_PAIR(2)); + + mvwprintw(win_content, 1, 1, "%s", notifs->list[id]->summary); + + for (size_t i = 0; i < width - 2; i++) { + mvwprintw(win_content, 2, i, "─"); + } + + mvwprintw(win_content, 3, 1, "%s", notifs->list[id]->body); + + wrefresh(win); + wrefresh(win_content); + refresh(); + + int ch = getch(); + if (ch == 'q' || ch == KEY_F(10)) + break; + } + + werase(win_content); + wrefresh(win_content); + delwin(win_content); + werase(win); + wrefresh(win); + delwin(win); +} + +void curses_handle_input(DBusConnection* conn, int ch, int* start, int* selected, struct NotifsList* notifs) { + unsigned int id = notifs->element_count - (*selected) - 1; + switch (ch) { + case KEY_DOWN: + case 'j': + if ((*selected) < notifs->element_count-1) { + (*selected)++; + } + break; + case KEY_UP: + case 'k': + if ((*selected) > 0) { + (*selected)--; + } + break; + case KEY_ENTER: + case '\n': + case '\r': + case KEY_F(2): + show_full_notif(id, notifs); + break; + case KEY_F(7): + set_notif_seen(conn, id, !(notifs->list[id]->seen)); + break; + case KEY_F(8): + for (size_t i = 0; i < notifs->element_count; i++) { + if (notifs->list[id] || !notifs->list[id]->seen) { + set_notif_seen(conn, i, true); + } + } + break; + } +} + +void update_notif_list(DBusConnection* conn, struct NotifsList** notifs, int* unread_count) { + struct NotifsList* new_notifs = NULL; + notifs_list_init(&new_notifs); + get_notification_list(conn, new_notifs); + + notifs_list_free(*notifs); + (*notifs) = new_notifs; + + (*unread_count) = 0; + for (unsigned int i = 0; i < (*notifs)->element_count; i++) { + if ((*notifs)->list[i] && !(*notifs)->list[i]->seen) + (*unread_count)++; + } +} + +void run_tui_client(DBusConnection* conn) { + int ch; + int start = 0; + int selected = 0; + int unread_count = 0; + + setlocale(LC_ALL, ""); + curses_init(); + + struct NotifsList* notifs = NULL; + notifs_list_init(¬ifs); + + while (1) { + timeout(500); + ch = getch(); + + if (ch == KEY_F(10) || ch == 'q') { + break; + } + + curses_handle_input(conn, ch, &start, &selected, notifs); + update_notif_list(conn, ¬ifs, &unread_count); + curses_display_notifs(notifs, start, selected); + curses_display_menu(unread_count); + refresh(); + } + + endwin(); + notifs_list_free(notifs); +} + +void usage(char **argv) { + printf("Usage: %s [unread|ls]\n", argv[0]); +} + +int main(int argc, char **argv) { + DBusConnection* conn; + init_dbus(&conn); + + if (argc == 1) { + run_tui_client(conn); + exit(0); + } + +#define UNREAD "unread" +#define LS "ls" + + if (!strncmp(argv[1], UNREAD, strlen(UNREAD))) + print_unseen_count(conn); + else if (!strncmp(argv[1], LS, strlen(LS))) + print_notification_list(conn); + else { + usage(argv); + exit(1); + } + + dbus_connection_unref(conn); + return 0; +} diff --git a/snotifd.c b/snotifd.c new file mode 100644 index 0000000..d60f9f4 --- /dev/null +++ b/snotifd.c @@ -0,0 +1,356 @@ +#include "snotif.h" +#include <dbus/dbus.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +struct NotifsList* notifs; + +void print_notification_log() { + printf("LOG:\n"); + for (size_t i = 0; i < notifs->element_count; i++) { + if (notifs->list[i]) { + printf("[%zu] %s", i, strnotify_params(notifs->list[i])); + } + } +} + +unsigned int notifid(struct NotifyParams* notify_params) { + if (!notify_params->replaces_id || notify_params->replaces_id > notifs->element_count) { + notifs->element_count++; + return notifs->element_count; + } + return notify_params->replaces_id; +} + +void reply_to_notify_method_call(DBusMessage* msg, DBusConnection* conn, struct NotifyParams* notify_params) { + DBusMessage* reply; + DBusMessageIter args; + dbus_uint32_t serial = 0; + + reply = dbus_message_new_method_return(msg); + + dbus_message_iter_init_append(reply, &args); + + unsigned int nid = notify_params->id; + if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &nid)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + + if (!dbus_connection_send(conn, reply, &serial)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + dbus_connection_flush(conn); + + dbus_message_unref(reply); + printf("> [%s] notification ID %i\n", notify_params->app_name, notify_params->id); +} + +void handle_notify_method_call(DBusMessage* msg, DBusConnection* conn) { + //const char* sender = dbus_message_get_sender(msg); + + struct NotifyParams* notify_params = malloc(sizeof(struct NotifyParams)); + if (!notify_params) { + fprintf(stderr, "Failed to allocate memory for notification parameters!\n"); + exit(1); + } + + notify_params->time = time(NULL); + notify_params->app_name = ""; + notify_params->app_icon = ""; + notify_params->summary = ""; + notify_params->body = ""; + notify_params->expire_timeout = -1; + notify_params->seen = false; + + const char* tmp_str; + DBusMessageIter args; + dbus_message_iter_init(msg, &args); + size_t msg_i = 0; + do { + switch (msg_i) { + case 0: + dbus_message_iter_get_basic(&args, &tmp_str); + notify_params->app_name = strdup(tmp_str); + break; + case 1: + dbus_message_iter_get_basic(&args, ¬ify_params->replaces_id); + break; + case 2: + dbus_message_iter_get_basic(&args, &tmp_str); + notify_params->app_icon = strdup(tmp_str); + break; + case 3: + dbus_message_iter_get_basic(&args, &tmp_str); + notify_params->summary = strdup(tmp_str); + break; + case 4: + dbus_message_iter_get_basic(&args, &tmp_str); + notify_params->body = strdup(tmp_str); + break; + case 7: + dbus_message_iter_get_basic(&args, ¬ify_params->expire_timeout); + break; + } + + msg_i++; + } while (dbus_message_iter_next(&args)); + notify_params->id = notifid(notify_params); + + printf("< %s", strnotify_params(notify_params)); + reply_to_notify_method_call(msg, conn, notify_params); + notifs_list_set(notifs, notify_params); +} + +void reply_to_get_capabilities_method_call(DBusMessage* msg, DBusConnection* conn) { + DBusMessage* reply; + DBusMessageIter args, array_iter; + dbus_uint32_t serial = 0; + + reply = dbus_message_new_method_return(msg); + + dbus_message_iter_init_append(reply, &args); + dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "s", &array_iter); + + const char* capabilities[] = {"body", "body-hyperlinks", "body-images", "body-markup"}; + for (int i = 0; i < 4; ++i) { + if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &capabilities[i])) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + } + + dbus_message_iter_close_container(&args, &array_iter); + + if (!dbus_connection_send(conn, reply, &serial)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + dbus_connection_flush(conn); + + dbus_message_unref(reply); +} + +void reply_to_get_server_information_method_call(DBusMessage* msg, DBusConnection* conn) { + DBusMessage* reply; + DBusMessageIter args; + dbus_uint32_t serial = 0; + + reply = dbus_message_new_method_return(msg); + + dbus_message_iter_init_append(reply, &args); + const char* name = "NotifdbNotificationServer"; + const char* vendor = "jakobst1n"; + const char* version = "1.0"; + const char* spec_version = "1.2"; + + if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &name) || + !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &vendor) || + !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &version) || + !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &spec_version)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + + if (!dbus_connection_send(conn, reply, &serial)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + dbus_connection_flush(conn); + + dbus_message_unref(reply); +} + +void reply_to_get_notification_list(DBusMessage* msg, DBusConnection* conn) { + DBusMessage* reply; + DBusMessageIter args, array_iter, struct_iter; + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &args); + + dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, + DBUS_STRUCT_BEGIN_CHAR_AS_STRING + DBUS_TYPE_UINT32_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_UINT32_AS_STRING + DBUS_TYPE_BOOLEAN_AS_STRING + DBUS_STRUCT_END_CHAR_AS_STRING, + &array_iter); + + for (size_t i = 0; i < notifs->element_count; i++) { + if (notifs->list[i]) { + dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); + dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, ¬ifs->list[i]->id); + dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, ¬ifs->list[i]->app_name); + dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, ¬ifs->list[i]->summary); + dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, ¬ifs->list[i]->body); + dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, ¬ifs->list[i]->time); + dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BOOLEAN, ¬ifs->list[i]->seen); + dbus_message_iter_close_container(&array_iter, &struct_iter); + } + } + dbus_message_iter_close_container(&args, &array_iter); + + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); +} + +void reply_to_get_unseen_count(DBusMessage* msg, DBusConnection* conn) { + DBusMessage* reply; + DBusMessageIter args; + + unsigned int unseen_count = 0; + for (size_t i = 0; i < notifs->element_count; i++) { + if (notifs->list[i] && !notifs->list[i]->seen) + unseen_count++; + } + + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &args); + if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &unseen_count)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(1); + } + + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); +} + +void reply_to_set_seen(DBusMessage* msg, DBusConnection* conn) { + DBusMessage* reply; + DBusMessageIter args; + + int notif_id = -1; + unsigned int seen = false; + + if (!dbus_message_iter_init(msg, &args)) { + fprintf(stderr, "Message has no arguments!\n"); + return; + } + + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_UINT32) { + fprintf(stderr, "First argument is not an integer!\n"); + return; + } + + dbus_message_iter_get_basic(&args, ¬if_id); + + if (!dbus_message_iter_next(&args)) { + fprintf(stderr, "Message has too few arguments!\n"); + return; + } + + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_BOOLEAN) { + fprintf(stderr, "Second argument is not an boolean!\n"); + return; + } + dbus_message_iter_get_basic(&args, &seen); + + printf("< Received notif_id: %d, seen: %d\n", notif_id, seen); + printf("< Current notification_id: %lu\n", notifs->element_count); + + if (notif_id < notifs->element_count) { + printf("< Setting notification_log[%d]->seen to %d\n", notif_id, seen); + notifs->list[notif_id]->seen = seen; + } else { + fprintf(stderr, "Invalid notif_id: %d (max: %lu)\n", notif_id, notifs->element_count - 1); + } + + reply = dbus_message_new_method_return(msg); + + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); +} + + +void init_dbus(DBusConnection** conn) { + DBusError err; + dbus_error_init(&err); + + *conn = dbus_bus_get(DBUS_BUS_SESSION, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Connection Error (%s)\n", err.message); + dbus_error_free(&err); + } + if (NULL == conn) { + fprintf(stderr, "Failed to connect to the session bus\n"); + exit(1); + } + printf("Connected to the DBus session bus\n"); + + int ret = dbus_bus_request_name(*conn, DBUS_NOTIF_INTERFACE, DBUS_NAME_FLAG_REPLACE_EXISTING, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Name Error (%s)\n", err.message); + dbus_error_free(&err); + } + if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { + fprintf(stderr, "Failed to request name\n"); + exit(1); + } + printf("DBus name \"%s\" requested successfully\n", DBUS_NOTIF_INTERFACE); + + ret = dbus_bus_request_name(*conn, DBUS_CLIENT_INTERFACE, DBUS_NAME_FLAG_REPLACE_EXISTING, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "Name Error (%s)\n", err.message); + dbus_error_free(&err); + } + if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { + fprintf(stderr, "Failed to request name\n"); + exit(1); + } + printf("DBus name \"%s\" requested successfully\n", DBUS_CLIENT_INTERFACE); +} + +void handle_dbus_message(DBusConnection* conn, DBusMessage* msg) { + if (NULL == msg) { + return; + } + + if (dbus_message_is_method_call(msg, DBUS_NOTIF_INTERFACE, "Notify")) { + handle_notify_method_call(msg, conn); + } else if (dbus_message_is_method_call(msg, DBUS_NOTIF_INTERFACE, "GetCapabilities")) { + reply_to_get_capabilities_method_call(msg, conn); + } else if (dbus_message_is_method_call(msg, DBUS_NOTIF_INTERFACE, "GetServerInformation")) { + reply_to_get_server_information_method_call(msg, conn); + } else if (dbus_message_is_method_call(msg, DBUS_CLIENT_INTERFACE, DBUS_METHOD_GETLIST)) { + reply_to_get_notification_list(msg, conn); + } else if (dbus_message_is_method_call(msg, DBUS_CLIENT_INTERFACE, DBUS_METHOD_GETUNSEENCOUNT)) { + reply_to_get_unseen_count(msg, conn); + } else if (dbus_message_is_method_call(msg, DBUS_CLIENT_INTERFACE, DBUS_METHOD_SETSEEN)) { + reply_to_set_seen(msg, conn); + } + + dbus_message_unref(msg); +} + +void listen_dbus() { + DBusConnection* conn; + DBusMessage* msg; + init_dbus(&conn); + + while (1) { + dbus_connection_read_write(conn, 0); + msg = dbus_connection_pop_message(conn); + + if (NULL == msg) { + usleep(10000); + continue; + } + + handle_dbus_message(conn, msg); + } +} + +int main(int argc, char **argv) { + printf("Starting simple-notification-daemon\n"); + notifs_list_init(¬ifs); + listen_dbus(); + return 0; +} diff --git a/snotifd.h b/snotifd.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/snotifd.h @@ -0,0 +1,16 @@ +#ifndef TERM_H +#define TERM_H + +#define KDIM "\x1B[2m" +#define KITAL "\x1B[3m" + +#define KNRM "\x1B[0m" +#define KRED "\x1B[31m" +#define KGRN "\x1B[32m" +#define KYEL "\x1B[33m" +#define KBLU "\x1B[34m" +#define KMAG "\x1B[35m" +#define KCYN "\x1B[36m" +#define KWHT "\x1B[37m" + +#endif |