commit - d1acfe2d63ab7a6725d3dec52228bab42cbc232a
commit + c243e7413d92458368975c45ee74d5f45aec5337
blob - e538039175d1b279091cc78e378c882c07cefa0f
blob + 711c03363990f9fabb4fefa46e01655a51784b0f
--- bxnotify.c
+++ bxnotify.c
* 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
+#include <time.h>
#include <unistd.h>
-
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xft/Xft.h>
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, ...);
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, ...)
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);
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
* 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 */