commit c243e7413d92458368975c45ee74d5f45aec5337 from: Brett Fisher date: Sun May 24 06:29:13 2026 UTC bxnotify.c, config.def.h: Initialize lightweight notification management. - Implement basic windowing with Xft and custom border rendering. - Add signal handling for lifecycle management (SIGUSR1). - Implement stdin reading for flexible shell integration. - Add auto-layout height and configurable alignment. - Incorporate posix-compliant sleep and error reporting. commit - d1acfe2d63ab7a6725d3dec52228bab42cbc232a commit + c243e7413d92458368975c45ee74d5f45aec5337 blob - e538039175d1b279091cc78e378c882c07cefa0f blob + 711c03363990f9fabb4fefa46e01655a51784b0f --- bxnotify.c +++ bxnotify.c @@ -4,19 +4,15 @@ * See LICENSE.md and README.md for details. */ -/* - * bxnotify.c - * - * A very basic X notification tool, a.k.a. Brett's X notifier. - * See LICENSE.md and README.md for details. - */ +#define _POSIX_C_SOURCE 200809L +#include #include #include #include #include +#include #include - #include #include #include @@ -28,10 +24,11 @@ static Display *dpy; static int screen; static Window win; static XftFont *font; -static XftDraw *draw; +static XftDraw *xft_draw; static XftColor fg, bg, border; static int running = 1; static char text[1024]; +static time_t start_time; /* Forward Declarations */ static void die(const char *fmt, ...); @@ -45,17 +42,19 @@ static void run(void); int main(int argc, char *argv[]) { + signal(SIGUSR1, sigusr1); + /* TODO: parse cli flags, override config.h */ readstdin(); setup(); + start_time = time(NULL); run(); cleanup(); return 0; } -/* implementations */ static void die(const char *fmt, ...) @@ -70,7 +69,7 @@ die(const char *fmt, ...) static void cleanup(void) { - if (draw) XftDrawDestroy(draw); + if (draw) XftDrawDestroy(xft_draw); if (font) XftFontClose(dpy, font); if (win) XDestroyWindow(dpy, win); if (dpy) XCloseDisplay(dpy); @@ -86,24 +85,129 @@ sigusr1(int sig) static void setup(void) { - /* TODO: open display, create window, allocate colors, font */ + XSetWindowAttributes swa; + int x, y, height; + + /* open display */ + 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; + + /* position: top-right, below bxbar */ + 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 */ + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask, + &swa); + + /* allocate colors */ + 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)) + die("bxnotify: cannot allocate bg color\n"); + 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)); + + XMapWindow(dpy, win); } static void readstdin(void) { - /* TODO: read from stdin into text[], handle EOF */ + 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) { - /* TODO: clear, render text, calculate auto-height */ + 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; + + /* 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; + } + + 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); } static void run(void) { - /* TODO: event loop with timeout, handle Expose and SIGUSR1 */ + XEvent ev; + + while (running) { + if (XPending(dpy)) { + XNextEvent(dpy, &ev); + if (ev.type == Expose) + draw(); + } + + /* check duration timeout */ + if (DURATION > 0 && (time(NULL) - start_time) >= DURATION) + running = 0; + + struct timespec ts = {0, 10000000}; /* 10 milliseconds */ + nanosleep(&ts, NULL); + + } } blob - 2081eb4e59110ab690aadabb746ffbc8d873a4b5 blob + 41948ebe97c4a51534ff71c4bd789dff0584823d --- config.def.h +++ config.def.h @@ -4,21 +4,15 @@ * See LICENSE.md and README.md for details. */ -/* - * config.def.h for bxnotify - * - * A very basic X notification tool, a.k.a. Brett's X notifier. - * See LICENSE.md and README.md for details. - */ - #define WIN_WIDTH 480 #define WIN_X 0 /* Offset from right edge of screen */ -#define WIN_Y 36 /* Below bxbar (BAR_HEIGHT 32 + PADDING 4) */ +#define WIN_Y 33 /* Below bxbar (BAR_HEIGHT 32 + PADDING 4) */ #define PADDING 6 #define BORDER_WIDTH 2 #define BORDER_COLOR "#555555" #define FG_COLOR "#ffffff" #define BG_COLOR "#000000" #define FONT "monospace:size=14" +#define ALIGN 0 /* 0 = left, 1 = center, 2 = right */ #define DURATION 5 /* Seconds, 0 = persist until dismissed */