commit fnott for openSUSE:Factory
Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package fnott for openSUSE:Factory checked in at 2024-08-03 20:04:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/fnott (Old) and /work/SRC/openSUSE:Factory/.fnott.new.7232 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "fnott" Sat Aug 3 20:04:38 2024 rev:10 rq:1191228 version:1.7.0 Changes: -------- --- /work/SRC/openSUSE:Factory/fnott/fnott.changes 2024-07-17 15:15:16.226329966 +0200 +++ /work/SRC/openSUSE:Factory/.fnott.new.7232/fnott.changes 2024-08-03 20:04:42.555475424 +0200 @@ -1,0 +2,45 @@ +Fri Aug 2 14:59:54 UTC 2024 - Soc Virnyl Estela <obs@uncomfyhalomacro.pl> + +- Specfile cleanup + * %config macro for config file + +------------------------------------------------------------------- +Fri Aug 2 14:54:43 UTC 2024 - Soc Virnyl Estela <obs@uncomfyhalomacro.pl> + +- Set c11 to leap only. confusing workaround + +------------------------------------------------------------------- +Fri Aug 2 14:48:58 UTC 2024 - Soc Virnyl Estela <obs@uncomfyhalomacro.pl> + +- Update to version 1.7.0: + * Log output now respects the [`NO_COLOR`](http://no-color.org/) + environment variable. + * `border-radius` configuration option (yes, this means fnott now + supports rounded corners). + * Support for linking against a system provided nanosvg library. See + the new `-Dsystem-nanosvg` meson option. Defaults to `disabled` + (i.e. use the bundled version). + * Support for the `x-canonical-private-synchronous` hint. + * XDG activation support; when triggering an action, fnott attempts to + retrieve an XDG activation token. This will only succeed if the + cursor is inside the notification window. The token is then + signaled over the D-Bus _Notifications_ interface. + * `fnottctl dismiss-with-default-action`. + * Implemented the `org.freedesktop.DBus.Introspectable` + interface. This fixes an issue where e.g. `gdbus` was not able to + create, or close, notifications. + * Left clicking a notification now triggers the default action, if + any, in addition to dismissing the notification. Right click to + dismiss the notification without trigger the default action. + * `STRING:image-path` hint that points to either a non-existing file, + or an invalid image, will now be ignored (instead of removing the + notification's icon). + * All notifications are now dismissed + (i.e. `org.freedesktop.Notifications.NotificationClosed` is + signaled) when fnott exits. + * `reason` in the `NotificationClosed` signal being off-by-one. + * Icons loaded via `image-data` hints being too dark. + * Not all data being read from the action selection helper, under + certain circumstances. + +------------------------------------------------------------------- Old: ---- fnott-1.6.0.tar.gz fnott-1.6.0.tar.gz.sig New: ---- fnott-1.7.0.tar.gz fnott-1.7.0.tar.gz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ fnott.spec ++++++ --- /var/tmp/diff_new_pack.eBABfj/_old 2024-08-03 20:04:44.491554964 +0200 +++ /var/tmp/diff_new_pack.eBABfj/_new 2024-08-03 20:04:44.491554964 +0200 @@ -17,7 +17,7 @@ Name: fnott -Version: 1.6.0 +Version: 1.7.0 Release: 0 Summary: Lightweight notification daemon for Wayland License: MIT @@ -65,7 +65,7 @@ %build %meson \ -%if 0%{?sle_version} == 150400 && 0%{?is_opensuse} +%if 0%{?suse_version} <= 1600 && 0%{?is_opensuse} -Dc_std=c11 \ %endif -Db_lto=true @@ -88,7 +88,7 @@ %doc CHANGELOG.md %dir %{_sysconfdir}/xdg/%{name}/ -%{_sysconfdir}/xdg/%{name}/fnott.ini +%config(noreplace) %{_sysconfdir}/xdg/%{name}/fnott.ini %{_datadir}/applications/fnott.desktop %dir %{_datadir}/dbus-1 ++++++ fnott-1.6.0.tar.gz -> fnott-1.7.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/3rd-party/nanosvg/src/nanosvg.h new/fnott-1.7.0/3rd-party/nanosvg/src/nanosvg.h --- old/fnott-1.6.0/3rd-party/nanosvg/src/nanosvg.h 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/3rd-party/nanosvg/src/nanosvg.h 2024-08-02 11:48:03.000000000 +0200 @@ -1653,7 +1653,7 @@ static void nsvg__parseTransform(float* xform, const char* str) { - float t[6]; + float t[6] = {0}; int len; nsvg__xformIdentity(xform); while (*str) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/CHANGELOG.md new/fnott-1.7.0/CHANGELOG.md --- old/fnott-1.6.0/CHANGELOG.md 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/CHANGELOG.md 2024-08-02 11:48:03.000000000 +0200 @@ -1,5 +1,6 @@ # Changelog +* [1.7.0](#1-7-0) * [1.6.0](#1-6-0) * [1.5.0](#1-5-0) * [1.4.1](#1-4-1) @@ -14,6 +15,55 @@ * [1.0.0](#1-0-0) +## 1.7.0 + +### Added + +* Log output now respects the [`NO_COLOR`](http://no-color.org/) + environment variable. +* `border-radius` configuration option (yes, this means fnott now + supports rounded corners). +* Support for linking against a system provided nanosvg library. See + the new `-Dsystem-nanosvg` meson option. Defaults to `disabled` + (i.e. use the bundled version). +* Support for the `x-canonical-private-synchronous` hint. +* XDG activation support; when triggering an action, fnott attempts to + retrieve an XDG activation token. This will only succeed if the + cursor is inside the notification window. The token is then + signaled over the D-Bus _Notifications_ interface. +* `fnottctl dismiss-with-default-action`. +* Implemented the `org.freedesktop.DBus.Introspectable` + interface. This fixes an issue where e.g. `gdbus` was not able to + create, or close, notifications. + + +### Changed + +* Left clicking a notification now triggers the default action, if + any, in addition to dismissing the notification. Right click to + dismiss the notification without trigger the default action. +* `STRING:image-path` hint that points to either a non-existing file, + or an invalid image, will now be ignored (instead of removing the + notification's icon). +* All notifications are now dismissed + (i.e. `org.freedesktop.Notifications.NotificationClosed` is + signaled) when fnott exits. + + +### Fixed + +* `reason` in the `NotificationClosed` signal being off-by-one. +* Icons loaded via `image-data` hints being too dark. +* Not all data being read from the action selection helper, under + certain circumstances. + + +### Contributors + +* Evyatar Stalinsky +* ldev + + ## 1.6.0 ### Added diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/PKGBUILD new/fnott-1.7.0/PKGBUILD --- old/fnott-1.6.0/PKGBUILD 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/PKGBUILD 2024-08-02 11:48:03.000000000 +0200 @@ -1,5 +1,5 @@ pkgname=fnott -pkgver=1.6.0 +pkgver=1.7.0 pkgrel=1 pkgdesc="Lightweight notification daemon for Wayland" arch=('x86_64' 'aarch64') @@ -7,7 +7,7 @@ license=(mit) makedepends=('meson' 'ninja' 'scdoc' 'tllist>=1.0.1') depends=( - 'wayland' 'pixman' 'libpng' 'dbus' + 'wayland' 'pixman' 'libpng' 'dbus' 'nanosvg' 'fcft>=3.0.0' 'fcft<4.0.0') source=() changelog=CHANGELOG.md @@ -18,7 +18,7 @@ } build() { - meson --prefix=/usr --buildtype=release --wrap-mode=nofallback -Db_lto=true .. + meson --prefix=/usr --buildtype=release --wrap-mode=nofallback -Db_lto=true -Dsystem-nanosvg=enabled .. ninja } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/config.c new/fnott-1.7.0/config.c --- old/fnott-1.6.0/config.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/config.c 2024-08-02 11:48:03.000000000 +0200 @@ -301,6 +301,16 @@ conf->border.color = color; } + else if (strcmp(key, "border-radius") == 0) { + unsigned long rad; + if (!str_to_ulong(value, 10, &rad)) { + LOG_ERR("%s:%u: invalid border-radius (expected an integer): %s", + path, lineno, value); + return false; + } + conf->border.radius = rad; + } + else if (strcmp(key, "border-size") == 0) { unsigned long sz; if (!str_to_ulong(value, 10, &sz)) { @@ -709,6 +719,18 @@ conf->by_urgency[i].border.color = color; } + else if (strcmp(key, "border-radius") == 0) { + unsigned long rad; + if (!str_to_ulong(value, 10, &rad)) { + LOG_ERR("%s:%u: invalid border-radius (expected an integer): %s", + path, lineno, value); + return false; + } + + for (int i = 0; i < 3; i++) + conf->by_urgency[i].border.radius = rad; + } + else if (strcmp(key, "border-size") == 0) { unsigned long sz; if (!str_to_ulong(value, 10, &sz)) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/config.h new/fnott-1.7.0/config.h --- old/fnott-1.6.0/config.h 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/config.h 2024-08-02 11:48:03.000000000 +0200 @@ -28,6 +28,7 @@ struct { pixman_color_t color; int size; + int radius; } border; struct { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/ctrl-protocol.h new/fnott-1.7.0/ctrl-protocol.h --- old/fnott-1.6.0/ctrl-protocol.h 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/ctrl-protocol.h 2024-08-02 11:48:03.000000000 +0200 @@ -8,6 +8,7 @@ CTRL_UNPAUSE, CTRL_DISMISS_BY_ID, CTRL_DISMISS_ALL, CTRL_ACTIONS_BY_ID, + CTRL_DISMISS_WITH_DEFAULT_ACTION_BY_ID, }; struct ctrl_request { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/ctrl.c new/fnott-1.7.0/ctrl.c --- old/fnott-1.6.0/ctrl.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/ctrl.c 2024-08-02 11:48:03.000000000 +0200 @@ -80,7 +80,7 @@ }; static void -actions_complete(uint32_t notif_id, const char *action_id, void *data) +actions_complete(struct notif *notif, const char *action_id, void *data) { struct actions_cb_data *info = data; struct ctrl *ctrl = info->ctrl; @@ -93,8 +93,7 @@ if (action_id == NULL) goto out; - reply.result = dbus_signal_action(info->ctrl->bus, notif_id, action_id) - ? CTRL_OK : CTRL_ERROR; + reply.result = notif_signal_action(notif, action_id) ? CTRL_OK : CTRL_ERROR; out: send_reply(fd, &reply); @@ -198,6 +197,23 @@ * response before sending reply to fnottctl */ goto no_reply; } + break; + + case CTRL_DISMISS_WITH_DEFAULT_ACTION_BY_ID: { + LOG_DBG("client: FD=%d, dismiss-with-default-action: %u", fd, client->recv.cmd.id); + + struct notif *notif = + notif_mgr_get_notif(ctrl->notif_mgr, client->recv.cmd.id); + + if (notif != NULL) { + notif_signal_action(notif, "default"); + reply.result = + notif_mgr_dismiss_id(ctrl->notif_mgr, client->recv.cmd.id) + ? CTRL_OK : CTRL_INVALID_ID; + } else + reply.result = CTRL_INVALID_ID; + break; + } } if (!send_reply(fd, &reply)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/dbus.c new/fnott-1.7.0/dbus.c --- old/fnott-1.6.0/dbus.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/dbus.c 2024-08-02 11:48:03.000000000 +0200 @@ -56,8 +56,8 @@ if (!dbus_connection_send(bus->conn, reply, NULL)) goto err; - if (dbus_connection_has_messages_to_send(bus->conn)) - fdm_event_add(bus->fdm, bus->bus_fd, EPOLLOUT); + dbus_connection_flush(bus->conn); + assert(!dbus_connection_has_messages_to_send(bus->conn)); ret = true; err: @@ -81,13 +81,14 @@ dbus_message_iter_append_basic(&arr, DBUS_TYPE_STRING, &(const char *){"body-markup"}); dbus_message_iter_append_basic(&arr, DBUS_TYPE_STRING, &(const char *){"actions"}); dbus_message_iter_append_basic(&arr, DBUS_TYPE_STRING, &(const char *){"icon-static"}); + dbus_message_iter_append_basic(&arr, DBUS_TYPE_STRING, &(const char *){"x-canonical-private-synchronous"}); dbus_message_iter_close_container(&iter, &arr); if (!dbus_connection_send(bus->conn, reply, NULL)) goto err; - if (dbus_connection_has_messages_to_send(bus->conn)) - fdm_event_add(bus->fdm, bus->bus_fd, EPOLLOUT); + dbus_connection_flush(bus->conn); + assert(!dbus_connection_has_messages_to_send(bus->conn)); ret = true; err: @@ -121,7 +122,7 @@ if (notif_mgr_is_paused(bus->notif_mgr)) { LOG_DBG("paused, refusing to notify"); } else { - char *app_name, *app_icon, *summary, *body; + char *app_name, *app_icon, *summary, *body, *sync_tag = NULL; enum urgency urgency = URGENCY_NORMAL; int8_t progress_percent = -1; @@ -253,8 +254,6 @@ dbus_message_iter_recurse(&entry_iter, &value_iter); dbus_message_iter_next(&entry_iter); - LOG_DBG("hint: %s", name); - if (strcmp(name, "urgency") == 0) { if (dbus_message_iter_get_arg_type(&value_iter) != DBUS_TYPE_BYTE) goto err; @@ -267,6 +266,14 @@ urgency = level; } + else if (strcmp(name, "x-canonical-private-synchronous") == 0) { + if (dbus_message_iter_get_arg_type(&value_iter) != DBUS_TYPE_STRING) + goto err; + + dbus_message_iter_get_basic(&value_iter, &sync_tag); + LOG_DBG("x-canonical-private-synchronous: %s", sync_tag); + } + else if (strcmp(name, "value") == 0) { if (dbus_message_iter_get_arg_type(&value_iter) != DBUS_TYPE_INT32) goto err; @@ -298,13 +305,17 @@ image_path = path; } - if (pix != NULL) { - free(pixman_image_get_data(pix)); - pixman_image_unref(pix); - pix = NULL; - } + pixman_image_t *image = icon_load( + image_path, bus->conf->max_icon_size, bus->icon_theme); - pix = icon_load(image_path, bus->conf->max_icon_size, bus->icon_theme); + if (image != NULL) { + if (pix != NULL) { + free(pixman_image_get_data(pix)); + pixman_image_unref(pix); + } + + pix = image; + } free(scheme); free(host); @@ -397,9 +408,38 @@ pix = NULL; } + /* pixman expects pre-multiplied alpha */ + if (format == PIXMAN_a8b8g8r8) { + for (int i = 0; i < height; i++) { + uint32_t *p = (uint32_t *)&image_data[i * stride]; + for (int j = 0; j < width; j++, p++) { + uint8_t a = (*p >> 24) & 0xff; + uint8_t b = (*p >> 16) & 0xff; + uint8_t g = (*p >> 8) & 0xff; + uint8_t r = (*p >> 0) & 0xff; + + if (a == 0xff) + continue; + + if (a == 0) { + r = g = b = 0; + } else { + r = r * a / 0xff; + g = g * a / 0xff; + b = b * a / 0xff; + } + + *p = (uint32_t)a << 24 | b << 16 | g << 8 | r; + } + } + } + pix = pixman_image_create_bits_no_clear( format, width, height, (uint32_t *)image_data, stride); } + } else { + LOG_DBG("hint: %s unrecognized, ignoring", name); + } } @@ -412,7 +452,7 @@ dbus_message_iter_get_basic(&args_iter, &timeout_ms); LOG_DBG("timeout = %dms", timeout_ms); - notif = notif_mgr_create_notif(bus->notif_mgr, replaces_id); + notif = notif_mgr_create_notif(bus->notif_mgr, replaces_id, sync_tag); if (notif == NULL) goto err; @@ -448,8 +488,8 @@ if (!dbus_connection_send(bus->conn, reply, NULL)) goto err; - if (dbus_connection_has_messages_to_send(bus->conn)) - fdm_event_add(bus->fdm, bus->bus_fd, EPOLLOUT); + dbus_connection_flush(bus->conn); + assert(!dbus_connection_has_messages_to_send(bus->conn)); ret = true; goto out; @@ -506,8 +546,84 @@ if (!dbus_connection_send(bus->conn, reply, NULL)) goto err; - if (dbus_connection_has_messages_to_send(bus->conn)) - fdm_event_add(bus->fdm, bus->bus_fd, EPOLLOUT); + dbus_connection_flush(bus->conn); + assert(!dbus_connection_has_messages_to_send(bus->conn)); + ret = true; + +err: + dbus_message_unref(reply); + return ret; +} + +static bool +introspect(struct dbus *bus, DBusMessage *msg) +{ + const char *data = + "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" + " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" + "<node name=\"/org/freedesktop/Notifications\">\n" + " <interface name=\"org.freedesktop.Notifications\">\n" + + " <method name=\"Notify\">\n" + " <arg name=\"id\" type=\"u\" direction=\"out\"/>\n" + " <arg name=\"app_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"replaces_id\" type=\"u\" direction=\"in\"/>\n" + " <arg name=\"app_icon\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"summary\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"body\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"actions\" type=\"as\" direction=\"in\"/>\n" + " <arg name=\"hints\" type=\"a{sv}\" direction=\"in\"/>\n" + " <arg name=\"expire_timeout\" type=\"i\" direction=\"in\"/>\n" + " </method>\n" + + " <method name=\"CloseNotification\">\n" + " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" + " </method>\n" + + " <method name=\"GetServerInformation\">\n" + " <arg name=\"name\" type=\"s\" direction=\"out\"/>\n" + " <arg name=\"vendor\" type=\"s\" direction=\"out\"/>\n" + " <arg name=\"version\" type=\"s\" direction=\"out\"/>\n" + " <arg name=\"spec_version\" type=\"s\" direction=\"out\"/>\n" + " </method>\n" + + " <method name=\"GetCapabilities\">\n" + " <arg name=\"capabilities\" type=\"as\" direction=\"out\"/>\n" + " </method>\n" + + " <signal name=\"NotificationClosed\">\n" + " <arg name=\"id\" type=\"u\"/>\n" + " <arg name=\"reason\" type=\"u\"/>\n" + " </signal>\n" + + " <signal name=\"ActionInvoked\">\n" + " <arg name=\"id\" type=\"u\"/>\n" + " <arg name=\"action_key\" type=\"s\"/>\n" + " </signal>\n" + + " <signal name=\"ActivationToken\">\n" + " <arg name=\"id\" type=\"u\"/>\n" + " <arg name=\"activation_token\" type=\"s\"/>\n" + " </signal>\n" + + " </interface>\n" + "</node>\n"; + + bool ret = false; + DBusMessage *reply = dbus_message_new_method_return(msg); + + if (reply == NULL) + goto err; + + DBusMessageIter args; + dbus_message_iter_init_append(reply, &args); + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &data); + + if (!dbus_connection_send(bus->conn, reply, NULL)) + goto err; + + dbus_connection_flush(bus->conn); + assert(!dbus_connection_has_messages_to_send(bus->conn)); ret = true; err: @@ -520,21 +636,27 @@ { struct dbus *bus = data; - const char *iface __attribute__((unused)) = dbus_message_get_interface(msg); + const char *iface = dbus_message_get_interface(msg); const char *member = dbus_message_get_member(msg); LOG_DBG("%s:%s", iface, member); static const struct { + const char *iface; const char *name; bool (*handler)(struct dbus *bus, DBusMessage *msg); } handlers[] = { - {"GetServerInformation", &get_server_information}, - {"GetCapabilities", &get_capabilities}, - {"Notify", ¬ify}, - {"CloseNotification", &close_notification}, + /* Don't forget to update introspect() when adding methods or signals */ + {"org.freedesktop.DBus.Introspectable", "Introspect", &introspect}, + + {"org.freedesktop.Notifications", "GetServerInformation", &get_server_information}, + {"org.freedesktop.Notifications", "GetCapabilities", &get_capabilities}, + {"org.freedesktop.Notifications", "Notify", ¬ify}, + {"org.freedesktop.Notifications", "CloseNotification", &close_notification}, }; for (size_t i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) { + if (strcmp(handlers[i].iface, iface) != 0) + continue; if (strcmp(handlers[i].name, member) != 0) continue; @@ -562,28 +684,50 @@ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &(dbus_uint32_t){id}); dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &(dbus_uint32_t){reason}); dbus_connection_send(bus->conn, signal, NULL); + dbus_connection_flush(bus->conn); + assert(!dbus_connection_has_messages_to_send(bus->conn)); dbus_message_unref(signal); - if (dbus_connection_has_messages_to_send(bus->conn)) - fdm_event_add(bus->fdm, bus->bus_fd, EPOLLOUT); return true; } bool dbus_signal_expired(struct dbus *bus, uint32_t id) { - return signal_notification_closed(bus, id, 0); + return signal_notification_closed(bus, id, 1); } bool dbus_signal_dismissed(struct dbus *bus, uint32_t id) { - return signal_notification_closed(bus, id, 1); + return signal_notification_closed(bus, id, 2); } bool dbus_signal_closed(struct dbus *bus, uint32_t id) { - return signal_notification_closed(bus, id, 2); + return signal_notification_closed(bus, id, 3); +} + +bool +dbus_signal_token(struct dbus *bus, uint32_t id, const char *token) +{ + DBusMessage *signal = dbus_message_new_signal( + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + "ActivationToken"); + + if (signal == NULL) + return false; + + DBusMessageIter iter; + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &(dbus_uint32_t){id}); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &token); + dbus_connection_send(bus->conn, signal, NULL); + dbus_connection_flush(bus->conn); + assert(!dbus_connection_has_messages_to_send(bus->conn)); + dbus_message_unref(signal); + return true; } bool @@ -602,9 +746,9 @@ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &(dbus_uint32_t){id}); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &action_id); dbus_connection_send(bus->conn, signal, NULL); + dbus_connection_flush(bus->conn); + assert(!dbus_connection_has_messages_to_send(bus->conn)); dbus_message_unref(signal); - if (dbus_connection_has_messages_to_send(bus->conn)) - fdm_event_add(bus->fdm, bus->bus_fd, EPOLLOUT); return true; } @@ -614,23 +758,11 @@ bool ret = false; struct dbus *bus = data; - bool had_outgoing = dbus_connection_has_messages_to_send(bus->conn); - - /* A sending function added EPOLLOUT when it wasn't necessary */ - if ((events & EPOLLOUT) && !had_outgoing) { - LOG_WARN("EPOLLOUT set, but no outgoing messages"); - fdm_event_del(bus->fdm, bus->bus_fd, EPOLLOUT); - } - if (!dbus_connection_read_write(bus->conn, 0)) { LOG_ERRNO("failed to read/write dbus connection"); goto err; } - /* Remove EPOLLOUT when no longer needed */ - if (had_outgoing && !dbus_connection_has_messages_to_send(bus->conn)) - fdm_event_del(bus->fdm, bus->bus_fd, EPOLLOUT); - while (dbus_connection_dispatch(bus->conn) != DBUS_DISPATCH_COMPLETE) ; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/dbus.h new/fnott-1.7.0/dbus.h --- old/fnott-1.6.0/dbus.h 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/dbus.h 2024-08-02 11:48:03.000000000 +0200 @@ -17,6 +17,7 @@ bool dbus_signal_expired(struct dbus *bus, uint32_t id); bool dbus_signal_dismissed(struct dbus *bus, uint32_t id); bool dbus_signal_closed(struct dbus *bus, uint32_t id); +bool dbus_signal_token(struct dbus *bus, uint32_t id, const char *token); bool dbus_signal_action(struct dbus *bus, uint32_t id, const char *action_id); int dbus_poll_fd(const struct dbus *bus); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/doc/fnott.ini.5.scd new/fnott-1.7.0/doc/fnott.ini.5.scd --- old/fnott-1.6.0/doc/fnott.ini.5.scd 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/doc/fnott.ini.5.scd 2024-08-02 11:48:03.000000000 +0200 @@ -154,6 +154,9 @@ Border color of the notification, in RGBA format. Default: _909090ff_. +*border-radius* + Corner radius on the border in pixels. Default: _0_. + *border-size* Border size, in pixels. Default: _1_. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/doc/fnottctl.1.scd new/fnott-1.7.0/doc/fnottctl.1.scd --- old/fnott-1.6.0/doc/fnottctl.1.scd 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/doc/fnottctl.1.scd 2024-08-02 11:48:03.000000000 +0200 @@ -5,6 +5,7 @@ # SYNOPSIS *fnottctl* *dismiss* [_id_]++ +*fnottctl* *dismiss-with-default-action* [_id_]++ *fnottctl* *actions* [_id_]++ *fnottctl* *list*++ *fnottctl* *pause*++ @@ -28,12 +29,18 @@ The most common operation is *fnottctl dismiss*. This will dismiss the highest priority notification. You might want to bind this to a -keyboard shortcut in your Wayland compositor configuration. +keyboard shortcut in your Wayland compositor configuration. This is +the same as right clicking the notification. To see, and select between, actions associated with the notification, use *fnottctl actions*. This requires a dmenu-like utility to have been configured in *fnott.ini*(5). +Finally, you can trigger the notification's default action (an action +named *default*) and dismiss it simultaneously with +*dismiss-with-default-action*. This is the same as left clicking the +notification. + You can optionally specify a notification ID, to dismiss (or show actions for) a specific notification instead of the highest priority one. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/fnott.ini new/fnott-1.7.0/fnott.ini --- old/fnott-1.6.0/fnott.ini 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/fnott.ini 2024-08-02 11:48:03.000000000 +0200 @@ -23,6 +23,7 @@ # background=3f5f3fff # border-color=909090ff +# border-radius=0 # border-size=1 # padding-vertical=20 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/fnottctl.c new/fnott-1.7.0/fnottctl.c --- old/fnott-1.6.0/fnottctl.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/fnottctl.c 2024-08-02 11:48:03.000000000 +0200 @@ -18,7 +18,7 @@ static void print_usage(const char *prog) { - printf("Usage: %s dismiss | actions [<id>]\n" + printf("Usage: %s dismiss | actions | dismiss-with-default-action [<id>]\n" " %s list | pause | unpause | quit\n" " %s --version\n" "\n" @@ -86,6 +86,8 @@ ? CTRL_DISMISS_ALL : CTRL_DISMISS_BY_ID; } else if (strcmp(cmd_word, "actions") == 0) cmd_type = CTRL_ACTIONS_BY_ID; + else if (strcmp(cmd_word, "dismiss-with-default-action") == 0) + cmd_type = CTRL_DISMISS_WITH_DEFAULT_ACTION_BY_ID; else if (strcmp(cmd_word, "list") == 0) cmd_type = CTRL_LIST; else if (strcmp(cmd_word, "pause") == 0) @@ -102,6 +104,7 @@ switch (cmd_type) { case CTRL_DISMISS_BY_ID: case CTRL_ACTIONS_BY_ID: + case CTRL_DISMISS_WITH_DEFAULT_ACTION_BY_ID: if (have_id) { char *end = NULL; errno = 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/icon.c new/fnott-1.7.0/icon.c --- old/fnott-1.6.0/icon.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/icon.c 2024-08-02 11:48:03.000000000 +0200 @@ -13,8 +13,8 @@ #include <tllist.h> -#include <nanosvg.h> -#include <nanosvgrast.h> +#include <nanosvg/nanosvg.h> +#include <nanosvg/nanosvgrast.h> #define LOG_MODULE "icon" #define LOG_ENABLE_DBG 0 @@ -326,8 +326,8 @@ if (strcasecmp(key, "inherits") == 0) { char *ctx = NULL; - for (const char *theme_name = strtok_r(value, ",", &ctx); - theme_name != NULL; theme_name = strtok_r(NULL, ",", &ctx)) + for (const char *theme_name = strtok_r(value, ", ", &ctx); + theme_name != NULL; theme_name = strtok_r(NULL, ", ", &ctx)) { tll_push_back(*themes_to_load, strdup(theme_name)); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/log.c new/fnott-1.7.0/log.c --- old/fnott-1.6.0/log.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/log.c 2024-08-02 11:48:03.000000000 +0200 @@ -30,7 +30,15 @@ [LOG_CLASS_DEBUG] = LOG_DEBUG, }; - colorize = _colorize == LOG_COLORIZE_NEVER ? false : _colorize == LOG_COLORIZE_ALWAYS ? true : isatty(STDERR_FILENO); + /* Don't use colors if NO_COLOR is defined and not empty */ + const char *no_color_str = getenv("NO_COLOR"); + const bool no_color = no_color_str != NULL && no_color_str[0] != '\0'; + + colorize = _colorize == LOG_COLORIZE_NEVER + ? false + : _colorize == LOG_COLORIZE_ALWAYS + ? true + : !no_color && isatty(STDERR_FILENO); do_syslog = _do_syslog; if (do_syslog) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/meson.build new/fnott-1.7.0/meson.build --- old/fnott-1.6.0/meson.build 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/meson.build 2024-08-02 11:48:03.000000000 +0200 @@ -1,5 +1,5 @@ project('fnott', 'c', - version: '1.6.0', + version: '1.7.0', license: 'MIT', meson_version: '>=0.59.0', default_options: [ @@ -60,6 +60,8 @@ wayland_cursor = dependency('wayland-cursor') dbus = dependency('dbus-1') fontconfig = dependency('fontconfig') +system_nanosvg = cc.find_library('nanosvg', required: get_option('system-nanosvg')) +system_nanosvgrast = cc.find_library('nanosvgrast', required: get_option('system-nanosvg')) tllist = dependency('tllist', version: '>=1.0.1', fallback: 'tllist') fcft = dependency('fcft', version: ['>=3.0.0', '<4.0.0'], fallback: 'fcft') @@ -80,6 +82,7 @@ wayland_protocols_datadir / 'staging/fractional-scale/fractional-scale-v1.xml', wayland_protocols_datadir / 'unstable/tablet/tablet-unstable-v2.xml', # required by cursor-shape-v1 wayland_protocols_datadir / 'staging/cursor-shape/cursor-shape-v1.xml', + wayland_protocols_datadir / 'staging/xdg-activation/xdg-activation-v1.xml', ] wl_proto_headers = [] @@ -106,11 +109,19 @@ output: 'version.h', command: [env, 'LC_ALL=C', generate_version_sh, meson.project_version(), '@CURRENT_SOURCE_DIR@', '@OUTPUT@']) -nanosvg = declare_dependency( - sources: ['nanosvg.c', '3rd-party/nanosvg/src/nanosvg.h', - 'nanosvgrast.c', '3rd-party/nanosvg/src/nanosvgrast.h'], - include_directories: '3rd-party/nanosvg/src', - dependencies: math) +if system_nanosvg.found() and system_nanosvgrast.found() + nanosvg = declare_dependency( + dependencies: [system_nanosvg, system_nanosvgrast, math] + ) + svg_lib = 'nanosvg (system)' +else + nanosvg = declare_dependency( + sources: ['nanosvg.c', '3rd-party/nanosvg/src/nanosvg.h', + 'nanosvgrast.c', '3rd-party/nanosvg/src/nanosvgrast.h'], + include_directories: '.', + dependencies: math) + svg_lib = 'nanosvg (bundled)' +endif executable( 'fnott', @@ -162,6 +173,7 @@ summary( { + 'SVG': svg_lib, 'Documentation': scdoc.found(), } ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/meson_options.txt new/fnott-1.7.0/meson_options.txt --- old/fnott-1.6.0/meson_options.txt 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/meson_options.txt 2024-08-02 11:48:03.000000000 +0200 @@ -1,5 +1,6 @@ option('docs', type: 'feature', description: 'Build and install documentation (man pages, example fnott.ini, readme, license etc).') - +option('system-nanosvg', type: 'feature', value: 'disabled', + description: 'use system\'s nanosvg instead of the bundled version') option('systemd-units-dir', type: 'string', value: '', description: 'Where to install the systemd service files (absolute path). Default: ${systemduserunitdir}') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/nanosvg/nanosvg.h new/fnott-1.7.0/nanosvg/nanosvg.h --- old/fnott-1.6.0/nanosvg/nanosvg.h 1970-01-01 01:00:00.000000000 +0100 +++ new/fnott-1.7.0/nanosvg/nanosvg.h 2024-08-02 11:48:03.000000000 +0200 @@ -0,0 +1 @@ +#include <3rd-party/nanosvg/src/nanosvg.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/nanosvg/nanosvgrast.h new/fnott-1.7.0/nanosvg/nanosvgrast.h --- old/fnott-1.6.0/nanosvg/nanosvgrast.h 1970-01-01 01:00:00.000000000 +0100 +++ new/fnott-1.7.0/nanosvg/nanosvgrast.h 2024-08-02 11:48:03.000000000 +0200 @@ -0,0 +1 @@ +#include <3rd-party/nanosvg/src/nanosvgrast.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/nanosvg.c new/fnott-1.7.0/nanosvg.c --- old/fnott-1.6.0/nanosvg.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/nanosvg.c 2024-08-02 11:48:03.000000000 +0200 @@ -3,4 +3,4 @@ #include <math.h> #define NANOSVG_ALL_COLOR_KEYWORDS #define NANOSVG_IMPLEMENTATION -#include <nanosvg.h> +#include <3rd-party/nanosvg/src/nanosvg.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/nanosvgrast.c new/fnott-1.7.0/nanosvgrast.c --- old/fnott-1.6.0/nanosvgrast.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/nanosvgrast.c 2024-08-02 11:48:03.000000000 +0200 @@ -1,6 +1,6 @@ #include <stdlib.h> #include <string.h> -#include <nanosvg.h> +#include <3rd-party/nanosvg/src/nanosvg.h> #define NANOSVGRAST_IMPLEMENTATION -#include <nanosvgrast.h> +#include <3rd-party/nanosvg/src/nanosvgrast.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/notification.c new/fnott-1.7.0/notification.c --- old/fnott-1.6.0/notification.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/notification.c 2024-08-02 11:48:03.000000000 +0200 @@ -76,11 +76,12 @@ bool is_configured; uint32_t id; - enum urgency urgency; + char *synchronous_tag; /* x-canonical-private-synchronous */ char32_t *app; char32_t *summary; char32_t *body; + enum urgency urgency; tll(struct action) actions; int8_t progress; @@ -180,6 +181,8 @@ regfree(&mgr->html_entity_re); + notif_mgr_dismiss_all(mgr); + tll_foreach(mgr->notifs, it) notif_destroy(it->item); tll_free(mgr->notifs); @@ -401,6 +404,23 @@ } struct notif * +notif_mgr_get_notif_for_sync_tag(struct notif_mgr *mgr, const char *tag) +{ + if (tag == NULL) + return NULL; + + tll_foreach(mgr->notifs, it) { + if (it->item->synchronous_tag == NULL) + continue; + + if (strcmp(it->item->synchronous_tag, tag) == 0) + return it->item; + } + + return NULL; +} + +struct notif * notif_mgr_get_notif_for_surface(struct notif_mgr *mgr, const struct wl_surface *surface) { @@ -415,10 +435,17 @@ /* Instantiates a new notification. You *must* call * notif_mgr_refresh() "soon" (after configuring the notification). */ struct notif * -notif_mgr_create_notif(struct notif_mgr *mgr, uint32_t replaces_id) +notif_mgr_create_notif(struct notif_mgr *mgr, uint32_t replaces_id, + const char *sync_tag) { int notif_id; + { + struct notif *old_notif = notif_mgr_get_notif_for_sync_tag(mgr, sync_tag); + if (old_notif != NULL) + return old_notif; + } + if (replaces_id != 0) { struct notif *old_notif = notif_mgr_get_notif(mgr, replaces_id); if (old_notif != NULL) @@ -432,10 +459,11 @@ *notif = (struct notif) { .mgr = mgr, .id = notif_id, - .urgency = URGENCY_NORMAL, + .synchronous_tag = sync_tag != NULL ? strdup(sync_tag) : NULL, .app = c32dup(U""), .summary = c32dup(U""), .body = c32dup(U""), + .urgency = URGENCY_NORMAL, .actions = tll_init(), .timeout_ms = -1, /* -1, up to us, 0 - never expire */ .timeout_fd = -1, @@ -529,6 +557,7 @@ tll_remove(notif->text_run_cache, it); } + free(notif->synchronous_tag); free(notif->app); free(notif->summary); free(notif->body); @@ -1712,6 +1741,33 @@ return ret; } +static inline void +fill_rounded_rectangle(pixman_op_t op, pixman_image_t* dest, + const pixman_color_t* color, int16_t x, int16_t y, + uint16_t width, uint16_t height, uint16_t radius) +{ + int rect_count = ( radius + radius ) + 1; + pixman_rectangle16_t rects[rect_count]; + + for (int i = 0; i <= radius; i++){ + uint16_t ydist = radius - i; + uint16_t curve = sqrt(radius * radius - ydist * ydist); + + rects[i] = (pixman_rectangle16_t){ + x + radius - curve, y + i, width - radius * 2 + curve * 2, 1 + }; + + rects[radius + i] = (pixman_rectangle16_t){ + x + radius - curve, y + height - i, width - radius * 2 + curve * 2, 1 + }; + } + + rects[(radius * 2)] = (pixman_rectangle16_t){ + x, y + radius, width, height + 1 - radius * 2 + }; + pixman_image_fill_rectangles(op, dest, color,rect_count, rects); +} + static int notif_show(struct notif *notif, int y) { @@ -1881,30 +1937,71 @@ roundf(conf->margins.horizontal / scale)); /* left */ struct buffer *buf = wayl_get_buffer(wayl, width, height); - const int brd_sz = urgency->border.size;; + const int brd_sz = urgency->border.size; + const int brd_rad = min(min(urgency->border.radius, buf->width*0.5), buf->height*0.5); pixman_region32_t clip; pixman_region32_init_rect(&clip, 0, 0, width, height); pixman_image_set_clip_region32(buf->pix, &clip); pixman_region32_fini(&clip); - /* Border */ - pixman_image_fill_rectangles( - PIXMAN_OP_SRC, buf->pix, &urgency->border.color, - 4, (pixman_rectangle16_t []){ - {0, 0, buf->width, brd_sz}, /* top */ - {buf->width - brd_sz, 0, brd_sz, buf->height}, /* right */ - {0, buf->height - brd_sz, buf->width, brd_sz}, /* bottom */ - {0, 0, brd_sz, buf->height}, /* left */ - }); - - /* Background */ - pixman_image_fill_rectangles( - PIXMAN_OP_SRC, buf->pix, &urgency->bg, - 1, &(pixman_rectangle16_t){ - brd_sz, brd_sz, - buf->width - 2 * brd_sz, buf->height - 2 * brd_sz} - ); + if (brd_rad == 0){ + /* Border */ + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, buf->pix, &urgency->border.color, + 4, (pixman_rectangle16_t []){ + {0, 0, buf->width, brd_sz}, /* top */ + {buf->width - brd_sz, 0, brd_sz, buf->height}, /* right */ + {0, buf->height - brd_sz, buf->width, brd_sz}, /* bottom */ + {0, 0, brd_sz, buf->height}, /* left */ + }); + + /* Background */ + pixman_image_fill_rectangles( + PIXMAN_OP_SRC, buf->pix, &urgency->bg, + 1, &(pixman_rectangle16_t){ + brd_sz, brd_sz, + buf->width - 2 * brd_sz, buf->height - 2 * brd_sz} + ); + } else { + const int msaa_scale = 2; + const double brd_sz_scaled = brd_sz * msaa_scale; + const double brd_rad_scaled = brd_rad * msaa_scale; + int w = buf->width * msaa_scale; + int h = buf->height * msaa_scale; + + pixman_image_t *bg; + if (msaa_scale != 1){ + bg = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, w*4); + } else { + bg = buf->pix; + } + + /* Border */ + fill_rounded_rectangle( + PIXMAN_OP_SRC, bg, &urgency->border.color, + 0, 0, w, h, brd_rad_scaled); + + /* Background */ + fill_rounded_rectangle( + PIXMAN_OP_SRC, bg, &urgency->bg, brd_sz_scaled, brd_sz_scaled, + w-(brd_sz_scaled*2), + h-(brd_sz_scaled*2), + brd_rad_scaled - brd_sz_scaled); + + if (msaa_scale != 1){ + pixman_f_transform_t ftrans; + pixman_transform_t trans; + pixman_f_transform_init_scale(&ftrans, msaa_scale, msaa_scale); + pixman_transform_from_pixman_f_transform(&trans, &ftrans); + pixman_image_set_transform(bg, &trans); + pixman_image_set_filter(bg, PIXMAN_FILTER_BILINEAR, NULL, 0); + + pixman_image_composite32( + PIXMAN_OP_SRC, bg, NULL, buf->pix, 0, 0, 0, 0, 0, 0, buf->width, buf->height); + pixman_image_unref(bg); + } + } /* Image */ if (notif->pix != NULL) { @@ -2273,6 +2370,9 @@ async->input_len - async->input_idx); if (count < 0) { + if (errno == EINTR) + return true; + LOG_ERRNO("could not write actions to actions selection helper"); goto done; } @@ -2297,25 +2397,33 @@ { struct action_async *async = data; - const size_t chunk_sz = 128; + const size_t chunk_sz = 1024; char buf[chunk_sz]; ssize_t count = read(async->from_child, buf, chunk_sz); if (count < 0) { + if (errno == EINTR) + return true; + LOG_ERRNO("failed to read from actions selection helper"); - goto check_pollhup; - } - /* Append to previously received response */ - size_t new_len = async->output_len + count; - async->output = realloc(async->output, new_len); - memcpy(&async->output[async->output_len], buf, count); - async->output_len = new_len; + /* FIXME: leaks memory (the async context) */ + return false; + } -check_pollhup: + if (count > 0) { + /* Append to previously received response */ + size_t new_len = async->output_len + count; + async->output = realloc(async->output, new_len); + memcpy(&async->output[async->output_len], buf, count); + async->output_len = new_len; - if (!(events & EPOLLHUP)) + /* There may be more data to read */ return true; + } + + /* No more data to read */ + assert(count == 0); /* Strip trailing spaces/newlines */ while (async->output_len > 0 @@ -2382,7 +2490,7 @@ LOG_WARN("could not map chosen action label to action ID: %.*s", (int)chosen_len, chosen); done: - completion_cb(notif_id, action_id, cb_data); + completion_cb(notif, action_id, cb_data); free(chosen); if (notif->deferred_expiral == EXPIRE_DELAYED) { @@ -2485,6 +2593,28 @@ return tll_length(notif->actions); } +bool +notif_signal_action(const struct notif *notif, const char *action_id) +{ + if (action_id == NULL) + return false; + + tll_foreach(notif->actions, it) { + if (strcmp(it->item.id, action_id) == 0) { + char *activation_token = + wayl_get_activation_token(notif->mgr->wayl, notif->surface); + + if (activation_token != NULL) + dbus_signal_token(notif->mgr->bus, notif->id, activation_token); + free(activation_token); + + return dbus_signal_action(notif->mgr->bus, notif->id, action_id); + } + } + + return false; +} + void notif_select_action( struct notif *notif, notif_select_action_cb completion_cb, void *data) @@ -2625,7 +2755,7 @@ free(copy); free(argv); - completion_cb(notif->id, NULL, data); + completion_cb(notif, NULL, data); notif->deferred_dismissal = DISMISS_IMMEDIATELY; notif->deferred_expiral = EXPIRE_IMMEDIATELY; return; @@ -2635,7 +2765,7 @@ free(input); fdm_del(notif->mgr->fdm, to_child[1]); fdm_del(notif->mgr->fdm, from_child[0]); - completion_cb(notif->id, NULL, data); + completion_cb(notif, NULL, data); notif->deferred_dismissal = DISMISS_IMMEDIATELY; notif->deferred_expiral = EXPIRE_IMMEDIATELY; return; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/notification.h new/fnott-1.7.0/notification.h --- old/fnott-1.6.0/notification.h 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/notification.h 2024-08-02 11:48:03.000000000 +0200 @@ -43,8 +43,11 @@ enum urgency { URGENCY_LOW, URGENCY_NORMAL, URGENCY_CRITICAL }; struct notif; -struct notif *notif_mgr_create_notif(struct notif_mgr *mgr, uint32_t replaces_id); +struct notif *notif_mgr_create_notif( + struct notif_mgr *mgr, uint32_t replaces_id, const char *sync_tag); struct notif *notif_mgr_get_notif(struct notif_mgr *mgr, uint32_t id); +struct notif *notif_mgr_get_notif_for_sync_tag( + struct notif_mgr *mgr, const char *tag); struct notif *notif_mgr_get_notif_for_surface( struct notif_mgr *mgr, const struct wl_surface *surface); bool notif_mgr_del_notif(struct notif_mgr *mgr, uint32_t id); @@ -67,8 +70,9 @@ char *notif_get_summary(const struct notif *notif); typedef void (*notif_select_action_cb)( - uint32_t notif_id, const char *action_id, void *data); + struct notif *notif, const char *action_id, void *data); size_t notif_action_count(const struct notif *notif); +bool notif_signal_action(const struct notif *notif, const char *action_id); void notif_select_action( struct notif *notif, notif_select_action_cb completion_cb, void *data); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/svg.c new/fnott-1.7.0/svg.c --- old/fnott-1.6.0/svg.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/svg.c 2024-08-02 11:48:03.000000000 +0200 @@ -6,8 +6,8 @@ #define LOG_ENABLE_DBG 0 #include "log.h" -#include <nanosvg.h> -#include <nanosvgrast.h> +#include <nanosvg/nanosvg.h> +#include <nanosvg/nanosvgrast.h> pixman_image_t * svg_load(const char *path, int size) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/wayland.c new/fnott-1.7.0/wayland.c --- old/fnott-1.6.0/wayland.c 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/wayland.c 2024-08-02 11:48:03.000000000 +0200 @@ -14,6 +14,7 @@ #include <wayland-client.h> #include <wayland-cursor.h> #include <wlr-layer-shell-unstable-v1.h> +#include <xdg-activation-v1.h> #include <xdg-output-unstable-v1.h> #include <tllist.h> @@ -79,6 +80,7 @@ struct wp_viewporter *viewporter; struct wp_fractional_scale_manager_v1 *fractional_scale_manager; struct wp_cursor_shape_manager_v1 *cursor_shape_manager; + struct xdg_activation_v1 *xdg_activation; bool have_argb8888; @@ -426,6 +428,77 @@ } static void +xdg_activation_token_done( + void *data, struct xdg_activation_token_v1 *xdg_activation_token_v1, + const char *token) +{ + char **out = data; + *out = token != NULL ? strdup(token) : NULL; +} + + +static const struct xdg_activation_token_v1_listener xdg_activation_token_listener = { + .done = &xdg_activation_token_done, +}; + +char * +wayl_get_activation_token(struct wayland *wayl, struct wl_surface *surface) +{ + if (wayl->xdg_activation == NULL) + return NULL; + + struct seat *seat = NULL; + tll_foreach(wayl->seats, it) { + if (it->item.pointer.on_surface == surface) { + seat = &it->item; + break; + } + } + + if (seat == NULL || + seat->pointer.serial == 0 || + seat->pointer.on_surface == NULL) + { + return NULL; + } + + struct xdg_activation_token_v1 *token = + xdg_activation_v1_get_activation_token(wayl->xdg_activation); + + if (token == NULL) + return NULL; + + char *token_str = NULL; + xdg_activation_token_v1_add_listener( + token, &xdg_activation_token_listener, &token_str); + + xdg_activation_token_v1_set_serial(token, seat->pointer.serial, seat->wl_seat); + xdg_activation_token_v1_set_surface(token, seat->pointer.on_surface); + xdg_activation_token_v1_commit(token); + wl_display_flush(wayl->display); + + while (token_str == NULL) { + while (wl_display_prepare_read(wayl->display) != 0) { + if (wl_display_dispatch_pending(wayl->display) < 0) { + LOG_ERRNO("failed to dispatch pending Wayland events"); + goto out; + } + } + + if (wl_display_read_events(wayl->display) < 0) { + LOG_ERRNO("failed to read events from the Wayland socket"); + goto out; + } + } + +out: + xdg_activation_token_v1_destroy(token); + + LOG_DBG("XDG activation token: %s", token_str != NULL ? token_str : "<failed>"); + return token_str; +} + +static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { @@ -434,14 +507,23 @@ struct seat *seat = data; struct wayland *wayl = seat->wayl; + seat->pointer.serial = serial; + switch (state) { case WL_POINTER_BUTTON_STATE_PRESSED: { - if (button == BTN_LEFT) { - struct notif *notif = notif_mgr_get_notif_for_surface( - wayl->notif_mgr, seat->pointer.on_surface); + struct notif *notif = notif_mgr_get_notif_for_surface( + wayl->notif_mgr, seat->pointer.on_surface); + + if (notif != NULL) { + if (button == BTN_LEFT) { + notif_signal_action(notif, "default"); + notif_mgr_dismiss_id(wayl->notif_mgr, notif_id(notif)); + } - if (notif != NULL) + else if (button == BTN_RIGHT) { + /* Dismiss without triggering the default action */ notif_mgr_dismiss_id(wayl->notif_mgr, notif_id(notif)); + } } break; } @@ -848,6 +930,14 @@ wayl->registry, name, &wp_cursor_shape_manager_v1_interface, required); } + else if (strcmp(interface, xdg_activation_v1_interface.name) == 0) { + const uint32_t required = 1; + if (!verify_iface_version(interface, version, required)) + return; + + wayl->xdg_activation = wl_registry_bind( + wayl->registry, name, &xdg_activation_v1_interface, required); + } } static void @@ -1038,6 +1128,8 @@ seat_destroy(&it->item); tll_free(wayl->seats); + if (wayl->xdg_activation != NULL) + xdg_activation_v1_destroy(wayl->xdg_activation); if (wayl->cursor_shape_manager != NULL) wp_cursor_shape_manager_v1_destroy(wayl->cursor_shape_manager); if (wayl->fractional_scale_manager != NULL) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fnott-1.6.0/wayland.h new/fnott-1.7.0/wayland.h --- old/fnott-1.6.0/wayland.h 2024-04-30 08:30:46.000000000 +0200 +++ new/fnott-1.7.0/wayland.h 2024-08-02 11:48:03.000000000 +0200 @@ -82,6 +82,8 @@ struct wp_fractional_scale_manager_v1 *wayl_fractional_scale_manager(const struct wayland *wayl); struct wp_viewporter *wayl_viewporter(const struct wayland *wayl); +char *wayl_get_activation_token(struct wayland *wayl, struct wl_surface *surface); + bool wayl_is_idle_for_urgency(const struct wayland *wayl, const enum urgency urgency); struct buffer *wayl_get_buffer(const struct wayland *wayl, int width, int height);
participants (1)
-
Source-Sync