Tiling Windowmanager for Wayland
Files | Log | Commits | Refs | README
Author: nyangkosense
Date: 2024-11-04
Subject: fix text sizing, add dynamic width - change readme
commit 0d8ad87829aa175b60e2ac21eea001fd55ab0dc8
Author: nyangkosense <sebastian.michalk@protonmail.com>
Date: Mon Nov 4 10:01:55 2024 +0000
fix text sizing, add dynamic width - change readme
diff --git a/README b/README
index 65a078b..23a9fb4 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
snot - suckless notification daemon
==================================
-snot is a simple notification daemon for Wayland compositors implementing the
+snot is a small, simple notification daemon for Wayland compositors implementing the
layer-shell protocol.
Requirements
@@ -12,6 +12,17 @@ Requirements
* pango
* dbus
+- if you are not on systemd, you might need to manually start a dbus user session:
+
+Start D-Bus session daemon:
+---------------------------
+dbus-daemon --session --address="unix:path=$XDG_RUNTIME_DIR/bus" --nofork --print-address &
+
+Export D-Bus session address for other applications:
+----------------------------------------------------
+export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus
+
+
Installation
-----------
Edit config.mk to match your local setup (snot is installed into
diff --git a/config.def.h b/config.def.h
index f8bc41d..7e301a3 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,25 +1,29 @@
#ifndef CONFIG_H
#define CONFIG_H
+#define NOTIFICATION_WIDTH NOTIFICATION_MIN_WIDTH
+#define NOTIFICATION_HEIGHT NOTIFICATION_MIN_HEIGHT
+
/* appearance */
#define BORDER_WIDTH 2
#define PADDING 15
#define BACKGROUND_COLOR "#222222" /* background color in hex */
#define FOREGROUND_COLOR "#bbbbbb" /* text color in hex */
#define BORDER_COLOR "#005577" /* border color in hex */
-#define FONT "monospace 10" /* font name and size */
+#define FONT "LiberationMono 10" /* font name and size */
/* behavior */
#define DURATION 3000 /* notification display duration in ms */
#define FADE_TIME 200 /* fade animation duration in ms */
#define MAX_NOTIFICATIONS 5 /* maximum number of notifications shown */
-#define NOTIFICATION_WIDTH 300 /* width of notification window */
-#define NOTIFICATION_HEIGHT 100 /* height of notification window */
#define SPACING 10 /* space between notifications */
+#define NOTIFICATION_MIN_WIDTH 300 /* minimum width */
+#define NOTIFICATION_MIN_HEIGHT 50 /* minimum height */
+#define NOTIFICATION_MAX_WIDTH 600 /* prevent notifications from getting too wide */
/* position (0 = top, 1 = bottom) */
#define POSITION 0
/* alignment (0 = left, 1 = center, 2 = right) */
#define ALIGNMENT 2
-#endif /* CONFIG_H */
+#endif /* CONFIG_H */
\ No newline at end of file
diff --git a/dbus.c b/dbus.c
index ad0eb1c..fe8f452 100644
--- a/dbus.c
+++ b/dbus.c
@@ -56,7 +56,7 @@ method_get_server_information(DBusConnection *conn, DBusMessage *msg) {
return DBUS_HANDLER_RESULT_NEED_MEMORY;
const char *name = "snot";
- const char *vendor = "suckless";
+ const char *vendor = "snot";
const char *version = "1.0";
const char *spec_version = "1.2";
diff --git a/dbus.o b/dbus.o
index 0c15bfd..67d122e 100644
Binary files a/dbus.o and b/dbus.o differ
diff --git a/snot.c b/snot.c
index 23d9b89..2439135 100644
--- a/snot.c
+++ b/snot.c
@@ -8,12 +8,14 @@
#include <wayland-client.h>
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
#include "protocols/wlr-layer-shell-unstable-v1-client-protocol.h"
#include "config.h"
#include "snot.h"
#include "dbus.h"
-#include <fcntl.h>
-#include <sys/stat.h>
+
static struct wl_display *display;
static struct wl_registry *registry;
@@ -29,7 +31,7 @@ static void remove_notification(int index);
static void
die(const char *msg) {
- fprintf(stderr, "notifyd: %s\n", msg);
+ fprintf(stderr, "snot: %s\n", msg);
exit(1);
}
@@ -58,22 +60,19 @@ static const struct wl_registry_listener registry_listener = {
.global_remove = registry_global_remove,
};
-
static void
layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
uint32_t serial, uint32_t width, uint32_t height) {
Notification *n = data;
- zwlr_layer_surface_v1_ack_configure(surface, serial);
-
- n->configured = true;
+ printf("Configuring surface with %dx%d\n", width, height);
- n->width = width > 0 ? width : NOTIFICATION_WIDTH;
- n->height = height > 0 ? height : NOTIFICATION_HEIGHT;
-
- printf("Surface configured: %dx%d\n", n->width, n->height);
+ zwlr_layer_surface_v1_ack_configure(surface, serial);
- draw_notification(n);
+ if (!n->configured) {
+ n->configured = true;
+ draw_notification(n);
+ }
}
static void
@@ -92,12 +91,10 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
static void
create_notification_surface(Notification *n) {
+ printf("Creating notification for: '%s' - '%s'\n", n->summary, n->body);
- n->surface = NULL;
- n->layer_surface = NULL;
- n->cairo = NULL;
- n->cairo_surface = NULL;
- n->configured = false;
+ int width = NOTIFICATION_WIDTH;
+ int height = NOTIFICATION_HEIGHT;
n->surface = wl_compositor_create_surface(compositor);
if (!n->surface) {
@@ -115,12 +112,69 @@ create_notification_surface(Notification *n) {
return;
}
+ n->cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ if (cairo_surface_status(n->cairo_surface) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "Failed to create Cairo surface\n");
+ zwlr_layer_surface_v1_destroy(n->layer_surface);
+ wl_surface_destroy(n->surface);
+ return;
+ }
+
+ n->cairo = cairo_create(n->cairo_surface);
+ if (cairo_status(n->cairo) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "Failed to create Cairo context\n");
+ cairo_surface_destroy(n->cairo_surface);
+ zwlr_layer_surface_v1_destroy(n->layer_surface);
+ wl_surface_destroy(n->surface);
+ return;
+ }
+
+ PangoLayout *layout = pango_cairo_create_layout(n->cairo);
+ PangoFontDescription *desc = pango_font_description_from_string(FONT);
+ pango_layout_set_font_description(layout, desc);
+ pango_font_description_free(desc);
+
+ int max_text_width = NOTIFICATION_WIDTH - (2 * PADDING);
+ pango_layout_set_width(layout, max_text_width * PANGO_SCALE);
+ pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
+
+ int text_width, text_height;
+ int total_height = 0;
+
+ if (n->summary) {
+ pango_layout_set_text(layout, n->summary, -1);
+ pango_layout_get_pixel_size(layout, &text_width, &text_height);
+ total_height = text_height;
+ width = MAX(width, text_width + (2 * PADDING));
+ }
+
+ if (n->body) {
+ pango_layout_set_text(layout, n->body, -1);
+ pango_layout_get_pixel_size(layout, &text_width, &text_height);
+ total_height += text_height + PADDING;
+ width = MAX(width, text_width + (2 * PADDING));
+ }
+
+ g_object_unref(layout);
+
+ height = MAX(NOTIFICATION_HEIGHT, total_height + (2 * PADDING));
+ width = MIN(MAX(NOTIFICATION_WIDTH, width), NOTIFICATION_MAX_WIDTH);
+
+ if (width != NOTIFICATION_WIDTH || height != NOTIFICATION_HEIGHT) {
+ cairo_destroy(n->cairo);
+ cairo_surface_destroy(n->cairo_surface);
+
+ n->cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ n->cairo = cairo_create(n->cairo_surface);
+ }
+
+ n->width = width;
+ n->height = height;
+
zwlr_layer_surface_v1_add_listener(n->layer_surface,
&layer_surface_listener, n);
- zwlr_layer_surface_v1_set_size(n->layer_surface,
- NOTIFICATION_WIDTH,
- NOTIFICATION_HEIGHT);
+ zwlr_layer_surface_v1_set_size(n->layer_surface, width, height);
uint32_t anchor = 0;
int margin_top = 0;
@@ -128,32 +182,33 @@ create_notification_surface(Notification *n) {
int margin_bottom = 0;
int margin_left = 0;
- if (POSITION == 0) {
+ if (POSITION == 0) { // top
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
margin_top = SPACING;
- } else {
+ } else { // bottom
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
margin_bottom = SPACING;
}
switch (ALIGNMENT) {
- case 0:
+ case 0: // left
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
margin_left = SPACING;
break;
- case 1:
- anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
+ case 1: // center
+ anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
break;
- case 2:
+ case 2: // right
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
margin_right = SPACING;
break;
}
- int stack_offset = notification_count * (NOTIFICATION_HEIGHT + SPACING);
- if (POSITION == 0) {
+ int stack_offset = notification_count * (height + SPACING);
+ if (POSITION == 0) {
margin_top += stack_offset;
- } else {
+ } else {
margin_bottom += stack_offset;
}
@@ -166,75 +221,79 @@ create_notification_surface(Notification *n) {
zwlr_layer_surface_v1_set_exclusive_zone(n->layer_surface, -1);
- n->cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
- NOTIFICATION_WIDTH,
- NOTIFICATION_HEIGHT);
- if (cairo_surface_status(n->cairo_surface) != CAIRO_STATUS_SUCCESS) {
- fprintf(stderr, "Failed to create Cairo surface\n");
- zwlr_layer_surface_v1_destroy(n->layer_surface);
- wl_surface_destroy(n->surface);
- return;
- }
-
- n->cairo = cairo_create(n->cairo_surface);
- if (cairo_status(n->cairo) != CAIRO_STATUS_SUCCESS) {
- fprintf(stderr, "Failed to create Cairo context\n");
- cairo_surface_destroy(n->cairo_surface);
- zwlr_layer_surface_v1_destroy(n->layer_surface);
- wl_surface_destroy(n->surface);
- return;
- }
+ n->configured = false;
wl_surface_commit(n->surface);
- printf("Notification surface created: pos=%s align=%s offset=%d\n",
+ printf("Notification surface created: pos=%s align=%s size=%dx%d offset=%d\n",
POSITION == 0 ? "top" : "bottom",
ALIGNMENT == 0 ? "left" : (ALIGNMENT == 1 ? "center" : "right"),
- stack_offset);
+ width, height, stack_offset);
}
static void
draw_notification(Notification *n) {
- if (!n->configured) {
- printf("Skipping draw, surface not configured yet\n");
+
+ if (!n->cairo || !n->cairo_surface) {
+ fprintf(stderr, "No cairo surface available\n");
return;
}
-
cairo_t *cr = n->cairo;
- unsigned int r, g, b;
cairo_save(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
cairo_restore(cr);
- sscanf(BACKGROUND_COLOR, "#%02x%02x%02x", &r, &g, &b);
- cairo_set_source_rgba(cr, r/255.0, g/255.0, b/255.0, 0.9);
- cairo_rectangle(cr, 0, 0, n->width, n->height);
- cairo_fill(cr);
+ cairo_set_source_rgba(cr, 0.133, 0.133, 0.133, 0.9);
+ cairo_paint(cr);
- sscanf(BORDER_COLOR, "#%02x%02x%02x", &r, &g, &b);
- cairo_set_source_rgb(cr, r/255.0, g/255.0, b/255.0);
+ cairo_set_source_rgb(cr, 0.0, 0.337, 0.467);
cairo_set_line_width(cr, BORDER_WIDTH);
- cairo_rectangle(cr, BORDER_WIDTH/2.0, BORDER_WIDTH/2.0,
- n->width - BORDER_WIDTH, n->height - BORDER_WIDTH);
+ cairo_rectangle(cr, 0, 0, n->width, n->height);
cairo_stroke(cr);
-
+
PangoLayout *layout = pango_cairo_create_layout(cr);
PangoFontDescription *desc = pango_font_description_from_string(FONT);
pango_layout_set_font_description(layout, desc);
pango_font_description_free(desc);
- sscanf(FOREGROUND_COLOR, "#%02x%02x%02x", &r, &g, &b);
- cairo_set_source_rgb(cr, r/255.0, g/255.0, b/255.0);
+ pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
- pango_layout_set_text(layout, n->summary, -1);
- cairo_move_to(cr, PADDING, PADDING);
- pango_cairo_show_layout(cr, layout);
+ int total_height = 0;
+ int summary_height = 0;
+ int body_height = 0;
+ int text_width;
+
+ pango_layout_set_width(layout, (n->width - 2 * PADDING) * PANGO_SCALE);
+ pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
+
+ if (n->summary) {
+ pango_layout_set_text(layout, n->summary, -1);
+ pango_layout_get_pixel_size(layout, &text_width, &summary_height);
+ total_height += summary_height;
+ }
if (n->body) {
pango_layout_set_text(layout, n->body, -1);
- cairo_move_to(cr, PADDING, PADDING * 2 + 15);
+ pango_layout_get_pixel_size(layout, &text_width, &body_height);
+ total_height += body_height + (PADDING/2);
+ }
+
+ int y_offset = (n->height - total_height) / 2;
+
+ if (n->summary) {
+ printf("Drawing summary: %s\n", n->summary);
+ cairo_set_source_rgb(cr, 0.733, 0.733, 0.733);
+ pango_layout_set_text(layout, n->summary, -1);
+ cairo_move_to(cr, PADDING, y_offset);
+ pango_cairo_show_layout(cr, layout);
+ }
+
+ if (n->body) {
+ printf("Drawing body: %s\n", n->body);
+ pango_layout_set_text(layout, n->body, -1);
+ cairo_move_to(cr, PADDING, y_offset + summary_height + (PADDING/2));
pango_cairo_show_layout(cr, layout);
}
@@ -242,8 +301,8 @@ draw_notification(Notification *n) {
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, n->width);
int size = stride * n->height;
-
- char tmp[] = "/tmp/notifyd-XXXXXX";
+
+ char tmp[] = "/tmp/snot-XXXXXX";
int fd = mkstemp(tmp);
if (fd < 0) {
fprintf(stderr, "Failed to create temporary file\n");
@@ -268,19 +327,20 @@ draw_notification(Notification *n) {
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0,
- n->width, n->height,
- stride,
- WL_SHM_FORMAT_ARGB8888);
-
+ n->width, n->height,
+ stride,
+ WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
close(fd);
-
+
wl_surface_attach(n->surface, buffer, 0, 0);
wl_surface_damage_buffer(n->surface, 0, 0, n->width, n->height);
wl_surface_commit(n->surface);
wl_buffer_destroy(buffer);
munmap(data, size);
+
+ printf("Drawing complete\n");
}
void
@@ -288,7 +348,10 @@ add_notification(const char *summary, const char *body,
const char *app_name, uint32_t replaces_id,
uint32_t expire_timeout) {
Notification *n;
+
+ printf("Received notification: '%s' - '%s'\n", summary, body);
+ /* Handle replacement if applicable */
if (replaces_id > 0) {
for (int i = 0; i < notification_count; i++) {
if (notifications[i].replaces_id == replaces_id) {
@@ -304,18 +367,59 @@ add_notification(const char *summary, const char *body,
if (notification_count >= MAX_NOTIFICATIONS)
return;
n = ¬ifications[notification_count++];
- create_notification_surface(n);
+
+ n->surface = NULL;
+ n->layer_surface = NULL;
+ n->cairo_surface = NULL;
+ n->cairo = NULL;
+ n->configured = false;
+ n->width = NOTIFICATION_WIDTH;
+ n->height = NOTIFICATION_HEIGHT;
replace:
- n->summary = strdup(summary);
+
+ n->summary = summary ? strdup(summary) : NULL;
n->body = body ? strdup(body) : NULL;
- n->app_name = strdup(app_name);
+ n->app_name = app_name ? strdup(app_name) : NULL;
n->replaces_id = replaces_id;
n->expire_timeout = expire_timeout;
n->start_time = time(NULL) * 1000;
n->opacity = 1.0;
- draw_notification(n);
+ create_notification_surface(n);
+}
+
+static void
+calculate_text_dimensions(cairo_t *cr, const char *summary, const char *body,
+ int *width, int *height) {
+ PangoLayout *layout = pango_cairo_create_layout(cr);
+ PangoFontDescription *desc = pango_font_description_from_string(FONT);
+ pango_layout_set_font_description(layout, desc);
+
+ pango_layout_set_width(layout, (NOTIFICATION_WIDTH - 2 * PADDING) * PANGO_SCALE);
+ pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
+
+ int text_width, text_height;
+ int total_height = 0;
+ int max_width = 0;
+
+ pango_layout_set_text(layout, summary, -1);
+ pango_layout_get_pixel_size(layout, &text_width, &text_height);
+ total_height = text_height;
+ max_width = text_width;
+
+ if (body) {
+ pango_layout_set_text(layout, body, -1);
+ pango_layout_get_pixel_size(layout, &text_width, &text_height);
+ total_height += text_height + PADDING;
+ max_width = MAX(max_width, text_width);
+ }
+
+ *width = max_width + (2 * PADDING);
+ *height = total_height + (2 * PADDING);
+
+ g_object_unref(layout);
+ pango_font_description_free(desc);
}
static void
@@ -329,7 +433,7 @@ remove_notification(int index) {
zwlr_layer_surface_v1_destroy(n->layer_surface);
n->layer_surface = NULL;
}
-
+
if (n->surface) {
wl_surface_destroy(n->surface);
n->surface = NULL;
@@ -339,7 +443,7 @@ remove_notification(int index) {
cairo_destroy(n->cairo);
n->cairo = NULL;
}
-
+
if (n->cairo_surface) {
cairo_surface_destroy(n->cairo_surface);
n->cairo_surface = NULL;
@@ -354,26 +458,6 @@ remove_notification(int index) {
}
notification_count--;
-
- for (int i = 0; i < notification_count; i++) {
- if (notifications[i].configured && notifications[i].layer_surface) {
- int stack_offset = i * (NOTIFICATION_HEIGHT + SPACING);
- if (POSITION == 0) {
- zwlr_layer_surface_v1_set_margin(notifications[i].layer_surface,
- SPACING + stack_offset,
- ALIGNMENT == 2 ? SPACING : 0,
- 0,
- ALIGNMENT == 0 ? SPACING : 0);
- } else {
- zwlr_layer_surface_v1_set_margin(notifications[i].layer_surface,
- 0,
- ALIGNMENT == 2 ? SPACING : 0,
- SPACING + stack_offset,
- ALIGNMENT == 0 ? SPACING : 0);
- }
- wl_surface_commit(notifications[i].surface);
- }
- }
}
int
diff --git a/snot.h b/snot.h
index d105bf4..40aba11 100644
--- a/snot.h
+++ b/snot.h
@@ -1,5 +1,5 @@
-#ifndef NOTIFYD_H
-#define NOTIFYD_H
+#ifndef SNOT_H
+#define SNOT_H
#include <wayland-client.h>
#include <cairo/cairo.h>
@@ -21,8 +21,7 @@ typedef struct {
float opacity;
cairo_surface_t *cairo_surface;
cairo_t *cairo;
- struct wl_callback *frame_callback;
- bool configured;
+ bool configured;
} Notification;
void add_notification(const char *summary, const char *body,
diff --git a/snot.o b/snot.o
index 54d44d8..87a32fd 100644
Binary files a/snot.o and b/snot.o differ