2wmw

Tiling Windowmanager for Wayland

Files | Log | Commits | Refs | README


0d8ad87

Author: nyangkosense

Date: 2024-11-04

Subject: fix text sizing, add dynamic width - change readme

Diff

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 = &notifications[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