Commit Diff


commit - c243e7413d92458368975c45ee74d5f45aec5337
commit + 7b13656e3ef0f51754993932e6c95f5a3d71f894
blob - 711c03363990f9fabb4fefa46e01655a51784b0f
blob + c6c6d155c3a77956ad8ec22c5b60a818ede9a9ed
--- bxnotify.c
+++ bxnotify.c
@@ -19,6 +19,11 @@
 
 #include "config.h"
 
+typedef struct {
+    char **lines;
+    int count;
+} TextBlock;
+
 /* Global Variables */
 static Display *dpy;
 static int screen;
@@ -27,14 +32,18 @@ static XftFont *font;
 static XftDraw *xft_draw;
 static XftColor fg, bg, border;
 static int running = 1;
-static char text[1024];
+static TextBlock tb; 
+static int total_height;
 static time_t start_time;
 
 /* Forward Declarations */
 static void die(const char *fmt, ...);
 static void cleanup(void);
 static void sigusr1(int sig);
-static void setup(void);
+static int measure_text(const char *text);
+static void init_display(void);
+static void calc_layout(void);
+static void setup(int win_h);
 static void readstdin(void);
 static void draw(void);
 static void run(void);
@@ -47,7 +56,10 @@ main(int argc, char *argv[])
     /* TODO: parse cli flags, override config.h */
 
     readstdin();
-    setup();
+    init_display();
+	calc_layout();
+    setup(total_height);
+
     start_time = time(NULL);
     run();
     cleanup();
@@ -55,7 +67,6 @@ main(int argc, char *argv[])
     return 0;
 }
 
-
 static void
 die(const char *fmt, ...)
 {
@@ -69,10 +80,14 @@ die(const char *fmt, ...)
 static void
 cleanup(void)
 {
-    if (draw)   XftDrawDestroy(xft_draw);
-    if (font)   XftFontClose(dpy, font);
-    if (win)    XDestroyWindow(dpy, win);
-    if (dpy)    XCloseDisplay(dpy);
+    for (int i = 0; i < tb.count; i++)
+        free(tb.lines[i]);
+    free(tb.lines);
+
+    if (xft_draw)   XftDrawDestroy(xft_draw);
+    if (font)       XftFontClose(dpy, font);
+    if (win)        XDestroyWindow(dpy, win);
+    if (dpy)        XCloseDisplay(dpy);
 }
 
 static void
@@ -82,110 +97,150 @@ sigusr1(int sig)
     running = 0;
 }
 
+static int
+measure_text(const char *text)
+{
+    XGlyphInfo ext;
+    XftTextExtentsUtf8(dpy, font, (const FcChar8 *)text, strlen(text), &ext);
+    return ext.width;
+}
+
 static void
-setup(void)
+readstdin(void)
 {
-    XSetWindowAttributes swa;
-    int x, y, height;
+    char *line = NULL;
+    size_t linesize = 0;
+    ssize_t len;
 
-    /* open display */
+    tb.lines = NULL;
+    tb.count = 0;
+
+    while ((len = getline(&line, &linesize, stdin)) != -1) {
+        if (len > 0 && line[len - 1] == '\n')
+            line[len - 1] = '\0';
+
+        tb.lines = realloc(tb.lines, (tb.count + 1) * sizeof(char *));
+        if (!tb.lines)
+            die("bxnotify: realloc failed\n");
+
+        tb.lines[tb.count] = strdup(line);
+        if (!tb.lines[tb.count])
+            die("bxnotify: strdup failed\n");
+
+        tb.count++;
+    }
+
+    free(line);
+
+    if (tb.count == 0)
+        die("bxnotify: no input received\n");
+}
+
+static void
+init_display(void)
+{
     if (!(dpy = XOpenDisplay(NULL)))
         die("bxnotify: cannot open display\n");
 
     screen = DefaultScreen(dpy);
 
-    /* load font first — needed for height calculation */
     if (!(font = XftFontOpenName(dpy, screen, FONT)))
         die("bxnotify: cannot load font\n");
+}
 
-    /* auto-height: font ascent + descent + padding top/bottom */
-    height = font->ascent + font->descent + PADDING * 2;
+static void
+calc_layout(void)
+{
+    int line_h = font->ascent + font->descent;
+    int wrapped_count = 0;
+    char **wrapped = NULL;
 
-    /* position: top-right, below bxbar */
+    for (int i = 0; i < tb.count; i++) {
+        char *line = tb.lines[i];
+        
+        while (measure_text(line) > WIN_WIDTH) {
+            /* Find break point */
+            char *p = line + strlen(line);
+            while (p > line && measure_text(line) > WIN_WIDTH) {
+                while (p > line && *p != ' ') p--;
+                if (p == line) break; /* Cannot wrap, forced break */
+                *p = '\0';
+            }
+            /* Add chunk */
+            wrapped = realloc(wrapped, (++wrapped_count) * sizeof(char *));
+            wrapped[wrapped_count - 1] = strdup(line);
+            line = p + 1;
+        }
+        /* Add remainder */
+        wrapped = realloc(wrapped, (++wrapped_count) * sizeof(char *));
+        wrapped[wrapped_count - 1] = strdup(line);
+    }
+
+    /* Update global state */
+    for (int i = 0; i < tb.count; i++) free(tb.lines[i]);
+    free(tb.lines);
+    tb.lines = wrapped;
+    tb.count = wrapped_count;
+    total_height = (tb.count * line_h) + ((tb.count + 1) * PADDING) + (2 * BORDER_WIDTH);
+}
+
+static void
+setup(int win_h)
+{
+    XSetWindowAttributes swa;
+    int x, y;
     int win_w = WIN_WIDTH + 2 * BORDER_WIDTH;
-    int win_h = height + 2 * BORDER_WIDTH;
 
     x = DisplayWidth(dpy, screen) - win_w - WIN_X;
     y = WIN_Y;
 
-    /* create window — override_redirect so bxwm ignores it */
     swa.override_redirect = True;
     swa.background_pixel = bg.pixel;
     swa.border_pixel = 0;
     swa.event_mask = ExposureMask;
 
     win = XCreateWindow(dpy, RootWindow(dpy, screen), x, y,
-                        win_w, win_h, 0, /* border_width = 0 */
+                        win_w, win_h, 0,
                         CopyFromParent, CopyFromParent, CopyFromParent,
                         CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask,
                         &swa);
 
-    /* allocate colors */
-    if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen),
-                           DefaultColormap(dpy, screen), FG_COLOR, &fg))
+    /* Allocate colors and draw context... */
+    if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), FG_COLOR, &fg))
         die("bxnotify: cannot allocate fg color\n");
-    if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen),
-                           DefaultColormap(dpy, screen), BG_COLOR, &bg))
+    if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), BG_COLOR, &bg))
         die("bxnotify: cannot allocate bg color\n");
-    if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen),
-                           DefaultColormap(dpy, screen), BORDER_COLOR, &border))
+    if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), BORDER_COLOR, &border))
         die("bxnotify: cannot allocate border color\n");
 
-    /* create draw context */
-    xft_draw = XftDrawCreate(dpy, win, DefaultVisual(dpy, screen),
-                         DefaultColormap(dpy, screen));
+    xft_draw = XftDrawCreate(dpy, win, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen));
 
     XMapWindow(dpy, win);
 }
 
 static void
-readstdin(void)
-{
-    if (fgets(text, sizeof(text), stdin) == NULL) {
-        die("bxnotify: no input received\n");
-    }
-    /* remove trailing newline */
-    text[strcspn(text, "\n")] = '\0';
-}
-
-static void
 draw(void)
 {
-    int height = font->ascent + font->descent + PADDING * 2;
-    int win_w = WIN_WIDTH + 2 * BORDER_WIDTH;
-    int win_h = height + 2 * BORDER_WIDTH;
-    int x, y;
-    XGlyphInfo ext;
+    int line_h = font->ascent + font->descent;
+    /* Redraw background... */
+    XftDrawRect(xft_draw, &border, 0, 0, WIN_WIDTH + 2 * BORDER_WIDTH, total_height);
+    XftDrawRect(xft_draw, &bg, BORDER_WIDTH, BORDER_WIDTH, WIN_WIDTH, total_height - 2 * BORDER_WIDTH);
 
-    /* draw border (full window) */
-    XftDrawRect(xft_draw, &border, 0, 0, win_w, win_h);
-
-    /* draw background (inner area) */
-    XftDrawRect(xft_draw, &bg, BORDER_WIDTH, BORDER_WIDTH, WIN_WIDTH, height);
-
-    switch (ALIGN) {
-        case 0: /* left */
-            x = PADDING;
-            break;
-        case 1: /* center */
-            XftTextExtentsUtf8(dpy, font, (XftChar8 *)text, strlen(text), &ext);
-            x = (WIN_WIDTH - ext.width) / 2;
-            break;
-        case 2: /* right */
-            XftTextExtentsUtf8(dpy, font, (XftChar8 *)text, strlen(text), &ext);
-            x = WIN_WIDTH - ext.width - PADDING;
-            break;
+    for (int i = 0; i < tb.count; i++) {
+        /* Calculate Y for this specific line */
+        int y = PADDING + BORDER_WIDTH + (i * line_h) + font->ascent;
+        int x;
+        XGlyphInfo ext;
+        XftTextExtentsUtf8(dpy, font, (XftChar8 *)tb.lines[i], strlen(tb.lines[i]), &ext);
+        
+        switch (ALIGN) {
+            case 0: x = BORDER_WIDTH + PADDING; break;
+            case 1: x = BORDER_WIDTH + (WIN_WIDTH - ext.width) / 2; break;
+            case 2: x = BORDER_WIDTH + (WIN_WIDTH - ext.width - PADDING); break;
+            default: x = BORDER_WIDTH + PADDING; break;
+        }
+        XftDrawStringUtf8(xft_draw, &fg, font, x, y, (XftChar8 *)tb.lines[i], strlen(tb.lines[i]));
     }
-
-    y = (height - (font->ascent + font->descent)) / 2 + font->ascent;
-
-    /* offset text into inner content area */
-    x += BORDER_WIDTH;
-    y += BORDER_WIDTH;
-
-    XftDrawStringUtf8(xft_draw, &fg, font, x, y,
-                      (XftChar8 *)text, strlen(text));
-
     XFlush(dpy);
 }
 
@@ -201,7 +256,7 @@ run(void)
                 draw();
         }
 
-        /* check duration timeout */
+        /* Check duration timeout */
         if (DURATION > 0 && (time(NULL) - start_time) >= DURATION)
             running = 0;