From ed74ab2d27460ec11a29f6d005a003d69104d7c5 Mon Sep 17 00:00:00 2001 From: "jakob.stendahl" Date: Fri, 12 Jul 2024 18:02:47 +0200 Subject: Initial commit --- .clangd | 2 + .gitignore | 3 + Makefile | 28 ++++ snotif.c | 74 +++++++++ snotif.h | 48 ++++++ snotifc.c | 518 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ snotifd.c | 356 ++++++++++++++++++++++++++++++++++++++++++ snotifd.h | 0 term.h | 16 ++ 9 files changed, 1045 insertions(+) create mode 100644 .clangd create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 snotif.c create mode 100644 snotif.h create mode 100644 snotifc.c create mode 100644 snotifd.c create mode 100644 snotifd.h create mode 100644 term.h diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..666e966 --- /dev/null +++ b/.clangd @@ -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 +#include + +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 +#include +#include + +// 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 +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include + +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 diff --git a/term.h b/term.h new file mode 100644 index 0000000..7460982 --- /dev/null +++ b/term.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 -- cgit v1.2.3