diff --git a/Makefile b/Makefile index 3225e36..383b953 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ CFLAGS = -Wall -Wextra -pedantic -lX11 -lXft -I/usr/include/freetype2 -pthread +CFLAGS += -lrt PREFIX ?= /usr/local CC ?= cc diff --git a/herbe.c b/herbe.c index 51d3990..6279a59 100644 --- a/herbe.c +++ b/herbe.c @@ -1,13 +1,19 @@ -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include #include -#include -#include -#include +#include +#include +#include +#include #include "config.h" @@ -16,9 +22,19 @@ #define EXIT_DISMISS 2 Display *display; +XftFont *font; Window window; +int num_of_lines; +char **lines; int exit_code = EXIT_DISMISS; +struct mq_object { + pid_t pid; + long timestamp; + char buffer[1024]; +}; +long lastTimestamp; + static void die(const char *format, ...) { va_list ap; @@ -70,6 +86,81 @@ int get_max_len(char *string, XftFont *font, int max_text_width) return ++eol; } +void freeLines() { + if(lines) { + for (int i = 0; i < num_of_lines; i++) + free(lines[i]); + free(lines); + } +} + +void constructLines(char* strList[], int numberOfStrings) { + freeLines(); + int max_text_width = width - 2 * padding; + num_of_lines = 0; + int lines_size = 5; + lines = malloc(lines_size * sizeof(char *)); + if (!lines) + die("malloc failed"); + + for (int i = 0; i < numberOfStrings; i++) + { + for (unsigned int eol = get_max_len(strList[i], font, max_text_width); eol; strList[i] += eol, num_of_lines++, eol = get_max_len(strList[i], font, max_text_width)) + { + if (lines_size <= num_of_lines) + { + lines = realloc(lines, (lines_size += 5) * sizeof(char *)); + if (!lines) + die("realloc failed"); + } + lines[num_of_lines] = malloc((eol + 1) * sizeof(char)); + if (!lines[num_of_lines]) + die("malloc failed"); + + strncpy(lines[num_of_lines], strList[i], eol); + lines[num_of_lines][eol] = '\0'; + } + } +} + +void reload(union sigval sv); +void readAllEvents(mqd_t mqd) { + + struct sigevent event = {.sigev_notify=SIGEV_THREAD, .sigev_signo=SIGHUP, .sigev_value.sival_int=mqd, .sigev_notify_function=reload}; + if(mq_notify(mqd, &event) == -1) { + perror("mq_notify failed"); + exit(1); + } + struct mq_object object; + while(1) { + int ret = mq_receive(mqd, (char*)&object, sizeof(object), NULL); + if(ret==-1) { + if(errno == EAGAIN) + return; + perror("mq_receive"); + exit(1); + } + if(object.timestamp && lastTimestamp > object.timestamp) + return; + if(object.timestamp) + lastTimestamp = object.timestamp; + char *buffer = object.buffer; + + constructLines(&buffer, 1); + kill(object.pid, SIGTERM); + } +} +void reload(union sigval sv) { + // we've already timed out + if(alarm(duration) == 0) + return; + readAllEvents(sv.sival_int); + XEvent event; + event.type = Expose; + XSendEvent(display, window, 0, 0, &event); + XFlush(display); +} + void expire(int sig) { XEvent event; @@ -79,6 +170,11 @@ void expire(int sig) XFlush(display); } + +void exitSuccess() { + exit(0); +} + int main(int argc, char *argv[]) { if (argc == 1) @@ -87,6 +183,44 @@ int main(int argc, char *argv[]) die("Usage: %s body", argv[0]); } + const char* id =getenv("HERBE_ID"); + mqd_t mqd=-1; + if(id) { + struct mq_attr attr = { .mq_maxmsg = 10, .mq_msgsize = sizeof(struct mq_object) }; + mqd = mq_open(id, O_RDWR|O_CREAT|O_NONBLOCK, 0722, &attr); + if(mqd==-1){ + perror("mq_open"); + die("mq_open"); + } + while (1) { + if(flock(mqd, LOCK_EX|LOCK_NB) == 0) { + // if we get the lock, register for events + break; + } + if(errno != EWOULDBLOCK) { + perror("flock"); + exit(1); + } + // someone else is listening for events + char* ts_str = getenv("NOTIFICATION_ID"); + lastTimestamp = ts_str?atol(ts_str):0; + struct mq_object object = {getpid(), lastTimestamp, {0}}; + char *buffer=object.buffer; + for(int i=1;i