summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/org.mate.NotificationDaemon.gschema.xml.in5
-rw-r--r--src/capplet/mate-notification-properties.c31
-rw-r--r--src/capplet/mate-notification-properties.ui16
-rw-r--r--src/common/constants.h1
-rw-r--r--src/daemon/daemon.c117
-rw-r--r--src/daemon/mnd-daemon.c13
-rw-r--r--src/themes/coco/coco-theme.c11
-rw-r--r--src/themes/nodoka/nodoka-theme.c11
-rw-r--r--src/themes/slider/theme.c9
-rw-r--r--src/themes/standard/theme.c9
10 files changed, 179 insertions, 44 deletions
diff --git a/data/org.mate.NotificationDaemon.gschema.xml.in b/data/org.mate.NotificationDaemon.gschema.xml.in
index 80beac4..a28edc3 100644
--- a/data/org.mate.NotificationDaemon.gschema.xml.in
+++ b/data/org.mate.NotificationDaemon.gschema.xml.in
@@ -51,5 +51,10 @@
<summary>Enable notification history</summary>
<description>When enabled, notifications are stored in history for later viewing. When disabled, no notification history is kept for privacy. Note: This only controls MATE's notification daemon storage; other applications may still have access to notifications through system logs or other means.</description>
</key>
+ <key name="force-display" type="b">
+ <default>false</default>
+ <summary>Force display all notifications</summary>
+ <description>When enabled, all notifications are shown regardless of urgency, fullscreen, or screensaver state. Do Not Disturb still takes precedence.</description>
+ </key>
</schema>
</schemalist>
diff --git a/src/capplet/mate-notification-properties.c b/src/capplet/mate-notification-properties.c
index f81dd2c..a27eb91 100644
--- a/src/capplet/mate-notification-properties.c
+++ b/src/capplet/mate-notification-properties.c
@@ -49,6 +49,7 @@ typedef struct {
GtkWidget* timeout_spin;
GtkWidget* persistence_checkbox;
GtkWidget* countdown_checkbox;
+ GtkWidget* force_display_checkbox;
NotifyNotification* preview1;
NotifyNotification* preview2;
@@ -502,6 +503,7 @@ static gboolean notification_properties_dialog_init(NotificationAppletDialog* di
dialog->timeout_spin = GTK_WIDGET(gtk_builder_get_object(builder, "timeout_spin"));
dialog->persistence_checkbox = GTK_WIDGET(gtk_builder_get_object(builder, "enable_persistence_check"));
dialog->countdown_checkbox = GTK_WIDGET(gtk_builder_get_object(builder, "show_countdown_check"));
+ dialog->force_display_checkbox = GTK_WIDGET(gtk_builder_get_object(builder, "force_display_check"));
g_object_unref (builder);
@@ -518,6 +520,7 @@ static gboolean notification_properties_dialog_init(NotificationAppletDialog* di
g_settings_bind (dialog->gsettings, GSETTINGS_KEY_DEFAULT_TIMEOUT, timeout_adjustment, "value", G_SETTINGS_BIND_DEFAULT);
g_settings_bind (dialog->gsettings, GSETTINGS_KEY_ENABLE_PERSISTENCE, dialog->persistence_checkbox, "active", G_SETTINGS_BIND_DEFAULT);
g_settings_bind (dialog->gsettings, GSETTINGS_KEY_SHOW_COUNTDOWN, dialog->countdown_checkbox, "active", G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (dialog->gsettings, GSETTINGS_KEY_FORCE_DISPLAY, dialog->force_display_checkbox, "active", G_SETTINGS_BIND_DEFAULT);
notification_properties_dialog_setup_themes (dialog);
notification_properties_dialog_setup_positions (dialog);
@@ -562,6 +565,14 @@ static void notification_properties_dialog_finalize(NotificationAppletDialog* di
int main(int argc, char** argv)
{
NotificationAppletDialog *dialog;
+ static gboolean show_version = FALSE;
+ GOptionContext *option_context;
+ GError *error = NULL;
+
+ static GOptionEntry option_entries[] = {
+ { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
+ { NULL }
+ };
#ifdef ENABLE_NLS
bindtextdomain(GETTEXT_PACKAGE, NOTIFICATION_LOCALEDIR);
@@ -569,7 +580,25 @@ int main(int argc, char** argv)
textdomain(GETTEXT_PACKAGE);
#endif /* ENABLE_NLS */
- gtk_init(&argc, &argv);
+ option_context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (option_context, option_entries, GETTEXT_PACKAGE);
+ g_option_context_add_group (option_context, gtk_get_option_group (TRUE));
+
+ if (!g_option_context_parse (option_context, &argc, &argv, &error))
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ g_option_context_free (option_context);
+ return 1;
+ }
+
+ g_option_context_free (option_context);
+
+ if (show_version)
+ {
+ g_print ("%s %s\n", PACKAGE, VERSION);
+ return 0;
+ }
notify_init("mate-notification-properties");
diff --git a/src/capplet/mate-notification-properties.ui b/src/capplet/mate-notification-properties.ui
index 9b461bf..0c802e5 100644
--- a/src/capplet/mate-notification-properties.ui
+++ b/src/capplet/mate-notification-properties.ui
@@ -347,6 +347,22 @@
<property name="position">2</property>
</packing>
</child>
+ <child>
+ <object class="GtkCheckButton" id="force_display_check">
+ <property name="label" translatable="yes">Force Display All Notifications</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="draw_indicator">True</property>
+ <property name="tooltip_text" translatable="yes">Show all notifications regardless of urgency, fullscreen, or screensaver state. Do Not Disturb still takes precedence.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
</object>
</child>
</object>
diff --git a/src/common/constants.h b/src/common/constants.h
index 1a81977..972cd7f 100644
--- a/src/common/constants.h
+++ b/src/common/constants.h
@@ -32,6 +32,7 @@
#define GSETTINGS_KEY_ENABLE_PERSISTENCE "enable-persistence"
#define GSETTINGS_KEY_SHOW_COUNTDOWN "show-countdown"
#define GSETTINGS_KEY_HISTORY_ENABLED "history-enabled"
+#define GSETTINGS_KEY_FORCE_DISPLAY "force-display"
#define NOTIFICATION_BUS_NAME "org.freedesktop.Notifications"
#define NOTIFICATION_BUS_PATH "/org/freedesktop/Notifications"
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c
index e5033de..473a598 100644
--- a/src/daemon/daemon.c
+++ b/src/daemon/daemon.c
@@ -32,6 +32,8 @@
#include <glib/gi18n.h>
#include <glib.h>
#include <glib-object.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
#include <gtk/gtk.h>
#ifdef HAVE_X11
@@ -756,8 +758,14 @@ static void _notification_destroyed_cb(GtkWindow* nw, NotifyDaemon* daemon)
/*
* This usually won't happen, but can if notification-daemon dies before
* all notifications are closed. Mark them as expired.
+ *
+ * But if instead the notification's close button was clicked, the
+ * "_user_closed" flag will be set on the window, so we send the
+ * correct reason.
*/
- _close_notification(daemon, NW_GET_NOTIFY_ID(nw), FALSE, NOTIFYD_CLOSED_EXPIRED);
+ gboolean user_closed = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(nw), "_user_closed"));
+ _close_notification(daemon, NW_GET_NOTIFY_ID(nw), FALSE,
+ user_closed ? NOTIFYD_CLOSED_USER : NOTIFYD_CLOSED_EXPIRED);
}
#ifdef HAVE_X11
@@ -960,7 +968,7 @@ static gboolean _check_expiration(NotifyDaemon* daemon)
return has_more_timeouts;
}
-static void _calculate_timeout(NotifyDaemon* daemon, NotifyTimeout* nt, int timeout, gboolean resident)
+static void _calculate_timeout(NotifyDaemon* daemon, NotifyTimeout* nt, int timeout, gboolean resident, guint urgency)
{
GSettings *gsettings = g_settings_new ("org.mate.NotificationDaemon");
gboolean persistence_enabled = g_settings_get_boolean (gsettings, "enable-persistence");
@@ -975,7 +983,9 @@ static void _calculate_timeout(NotifyDaemon* daemon, NotifyTimeout* nt, int time
g_object_unref (gsettings);
}
- if (timeout == 0 || nt->resident)
+ /* Per the spec, critical notifications should not automatically
+ * expire. They should only be closed by user action. */
+ if (timeout == 0 || nt->resident || urgency == URGENCY_CRITICAL)
{
nt->has_timeout = FALSE;
}
@@ -1046,7 +1056,7 @@ static NotifyTimeout* _store_notification(NotifyDaemon* daemon, GtkWindow* nw,
nt->timestamp = g_get_real_time();
nt->close_reason = NOTIFYD_CLOSED_EXPIRED; /* Default to expired, since we don't care about active notifications */
- _calculate_timeout(daemon, nt, timeout, resident);
+ _calculate_timeout(daemon, nt, timeout, resident, urgency);
#if GLIB_CHECK_VERSION (2, 68, 0)
g_hash_table_insert(daemon->notification_hash, g_memdup2(&id, sizeof(guint)), nt);
@@ -1636,36 +1646,47 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
* 3. icon (which falls back to the desktop entry)
* 4. icon_data (for backward compatibility)
*/
- if (g_variant_lookup(hints, "image-data", "@(iiibiiay)", &data))
+ if (pixbuf == NULL && g_variant_lookup(hints, "image-data", "@(iiibiiay)", &data))
{
pixbuf = _notify_daemon_pixbuf_from_data_hint (data);
g_variant_unref(data);
}
- else if (g_variant_lookup(hints, "image_data", "@(iiibiiay)", &data))
+
+ if (pixbuf == NULL && g_variant_lookup(hints, "image_data", "@(iiibiiay)", &data))
{
pixbuf = _notify_daemon_pixbuf_from_data_hint (data);
g_variant_unref(data);
}
- else if (g_variant_lookup(hints, "image-path", "@s", &data))
+
+ if (pixbuf == NULL && g_variant_lookup(hints, "image-path", "@s", &data))
{
const char *path = g_variant_get_string (data, NULL);
pixbuf = _notify_daemon_pixbuf_from_path (path);
- resolved_icon = g_strdup(path);
+ if (pixbuf != NULL) {
+ resolved_icon = g_strdup(path);
+ }
g_variant_unref(data);
}
- else if (g_variant_lookup(hints, "image_path", "@s", &data))
+
+ if (pixbuf == NULL && g_variant_lookup(hints, "image_path", "@s", &data))
{
const char *path = g_variant_get_string (data, NULL);
pixbuf = _notify_daemon_pixbuf_from_path (path);
- resolved_icon = g_strdup(path);
+ if (pixbuf != NULL) {
+ resolved_icon = g_strdup(path);
+ }
g_variant_unref(data);
}
- else if (*icon != '\0')
+
+ if (pixbuf == NULL && icon != NULL && *icon != '\0')
{
pixbuf = _notify_daemon_pixbuf_from_path (icon);
- resolved_icon = g_strdup(icon);
+ if (pixbuf != NULL) {
+ resolved_icon = g_strdup(icon);
+ }
}
- else if (desktop_entry != NULL && *desktop_entry != '\0')
+
+ if (pixbuf == NULL && desktop_entry != NULL && *desktop_entry != '\0')
{
/* Use desktop-entry to resolve application icon as fallback */
gchar *desktop_file = g_strdup_printf("%s.desktop", desktop_entry);
@@ -1691,7 +1712,7 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
{
pixbuf = _notify_daemon_pixbuf_from_path (icon_name);
- if (!resolved_icon && (*icon == '\0' || icon == NULL)) {
+ if (pixbuf != NULL && !resolved_icon) {
resolved_icon = g_strdup(icon_name);
}
@@ -1701,7 +1722,43 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
g_key_file_free(key_file);
g_free(desktop_file);
}
- else if (g_variant_lookup(hints, "icon_data", "@(iiibiiay)", &data))
+
+ if (pixbuf == NULL && app_name != NULL && *app_name != '\0')
+ {
+ /* Fallback: Try to derive desktop entry from app_name */
+ gchar *desktop_key = g_ascii_strdown(app_name, -1);
+ for (gchar *p = desktop_key; *p != '\0'; p++) {
+ if (*p == ' ') *p = '-';
+ }
+ gchar *desktop_id = g_strdup_printf("%s.desktop", desktop_key);
+ GDesktopAppInfo *app_info = g_desktop_app_info_new(desktop_id);
+
+ if (app_info != NULL)
+ {
+ GIcon *gicon = g_app_info_get_icon(G_APP_INFO(app_info));
+ if (gicon != NULL)
+ {
+ GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon(gtk_icon_theme_get_default(),
+ gicon,
+ IMAGE_SIZE,
+ GTK_ICON_LOOKUP_USE_BUILTIN);
+ if (icon_info != NULL)
+ {
+ pixbuf = gtk_icon_info_load_icon(icon_info, NULL);
+ if (pixbuf != NULL && !resolved_icon) {
+ resolved_icon = g_strdup(g_icon_to_string(gicon));
+ }
+ g_object_unref(icon_info);
+ }
+ }
+ g_object_unref(app_info);
+ }
+
+ g_free(desktop_id);
+ g_free(desktop_key);
+ }
+
+ if (pixbuf == NULL && g_variant_lookup(hints, "icon_data", "@(iiibiiay)", &data))
{
g_warning("\"icon_data\" hint is deprecated, please use \"image_data\" instead");
pixbuf = _notify_daemon_pixbuf_from_data_hint (data);
@@ -1832,22 +1889,10 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
#endif /* HAVE_X11 */
/* fullscreen_window is assumed to be false on Wayland, as there is no trivial way to check */
- /* If there is no timeout, show the notification also if screensaver
- * is active or there are fullscreen windows
- */
- if (!nt->has_timeout || (!screensaver_active (GTK_WIDGET (nw)) && !fullscreen_window))
- {
- if (!do_not_disturb)
- {
- theme_show_notification (nw);
+ gboolean force_display = g_settings_get_boolean (daemon->gsettings, GSETTINGS_KEY_FORCE_DISPLAY);
- if (sound_file != NULL)
- {
- sound_play_file (GTK_WIDGET (nw), sound_file);
- }
- }
- }
- else
+ /* If on DnD or screensaver, suppress and close notification */
+ if (do_not_disturb || (!force_display && screensaver_active (GTK_WIDGET (nw)) && urgency != URGENCY_CRITICAL))
{
_NotifyPendingClose *notification_data;
@@ -1858,6 +1903,16 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
notification_data->daemon = g_object_ref (daemon);
g_idle_add ((GSourceFunc) _close_notification_not_shown, notification_data);
}
+ else
+ {
+ /* Show if critical, force-display, or no fullscreen window */
+ if (force_display || urgency == URGENCY_CRITICAL || !fullscreen_window)
+ theme_show_notification (nw);
+
+ /* Play sound unless low urgency during fullscreen (when not force-display) */
+ if (sound_file != NULL && (force_display || !(fullscreen_window && urgency == URGENCY_LOW)))
+ sound_play_file (GTK_WIDGET (nw), sound_file);
+ }
g_free (sound_file);
@@ -1865,7 +1920,7 @@ static gboolean notify_daemon_notify_handler(NotifyDaemonNotifications *object,
if (nt)
{
- _calculate_timeout (daemon, nt, timeout, resident && !transient);
+ _calculate_timeout (daemon, nt, timeout, resident && !transient, urgency);
}
if (resolved_icon) {
diff --git a/src/daemon/mnd-daemon.c b/src/daemon/mnd-daemon.c
index 59921c7..cfaedfd 100644
--- a/src/daemon/mnd-daemon.c
+++ b/src/daemon/mnd-daemon.c
@@ -32,6 +32,7 @@
static gboolean debug = FALSE;
static gboolean replace = FALSE;
static gboolean idle_exit = FALSE;
+static gboolean show_version = FALSE;
static GOptionEntry entries[] =
{
@@ -54,6 +55,12 @@ static GOptionEntry entries[] =
NULL
},
{
+ "version", 'v', G_OPTION_FLAG_NONE,
+ G_OPTION_ARG_NONE, &show_version,
+ "Version of this application",
+ NULL
+ },
+ {
NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL
}
};
@@ -102,6 +109,12 @@ int main (int argc, char *argv[])
if (!parse_arguments (&argc, &argv))
return EXIT_FAILURE;
+ if (show_version)
+ {
+ g_print ("%s %s\n", PACKAGE, VERSION);
+ return EXIT_SUCCESS;
+ }
+
daemon = notify_daemon_new (replace, idle_exit);
gtk_main();
diff --git a/src/themes/coco/coco-theme.c b/src/themes/coco/coco-theme.c
index b131283..08cfa44 100644
--- a/src/themes/coco/coco-theme.c
+++ b/src/themes/coco/coco-theme.c
@@ -90,19 +90,13 @@ void set_notification_hints(GtkWindow *nw, GVariant *hints);
void notification_tick(GtkWindow *nw, glong remaining);
static void create_pie_countdown(WindowData* windata);
-#define STRIPE_WIDTH 32
#define WIDTH 300
#define IMAGE_SIZE 32
-#define IMAGE_PADDING 10
-#define SPACER_LEFT 30
+#define IMAGE_PADDING 24
#define PIE_RADIUS 12
#define PIE_WIDTH (2 * PIE_RADIUS)
#define PIE_HEIGHT (2 * PIE_RADIUS)
-#define BODY_X_OFFSET (IMAGE_SIZE + 8)
-#define DEFAULT_ARROW_OFFSET (SPACER_LEFT + 12)
-#define DEFAULT_ARROW_HEIGHT 14
-#define DEFAULT_ARROW_WIDTH 22
-#define DEFAULT_ARROW_SKEW -6
+#define BODY_X_OFFSET (IMAGE_SIZE + IMAGE_PADDING)
#define BACKGROUND_OPACITY 0.9
#define GRADIENT_CENTER 0.7
@@ -464,6 +458,7 @@ create_notification(UrlClickedCb url_clicked)
gtk_widget_set_halign (windata->main_hbox, GTK_ALIGN_START);
gtk_widget_set_valign (windata->main_hbox, GTK_ALIGN_START);
gtk_widget_set_margin_top (windata->main_hbox, 8);
+ gtk_widget_set_margin_bottom (windata->main_hbox, 8);
gtk_widget_set_margin_end (windata->main_hbox, 8);
gtk_widget_show (windata->main_hbox);
gtk_box_pack_start (GTK_BOX(main_vbox), windata->main_hbox, FALSE, FALSE, 0);
diff --git a/src/themes/nodoka/nodoka-theme.c b/src/themes/nodoka/nodoka-theme.c
index 8600d60..4de4d5d 100644
--- a/src/themes/nodoka/nodoka-theme.c
+++ b/src/themes/nodoka/nodoka-theme.c
@@ -748,6 +748,13 @@ get_theme_info(char **theme_name,
*homepage = g_strdup("https://nodoka.fedorahosted.org/");
}
+static void
+close_button_clicked_cb(GtkButton* button, GtkWidget* win)
+{
+ g_object_set_data(G_OBJECT(win), "_user_closed", GINT_TO_POINTER(1));
+ gtk_widget_destroy(win);
+}
+
/* Create new notification */
GtkWindow *
create_notification(UrlClickedCb url_clicked)
@@ -860,8 +867,8 @@ create_notification(UrlClickedCb url_clicked)
gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
gtk_container_set_border_width(GTK_CONTAINER(close_button), 0);
gtk_widget_set_size_request(close_button, 24, 24);
- g_signal_connect_swapped(G_OBJECT(close_button), "clicked",
- G_CALLBACK(gtk_widget_destroy), win);
+ g_signal_connect(G_OBJECT(close_button), "clicked",
+ G_CALLBACK(close_button_clicked_cb), win);
atkobj = gtk_widget_get_accessible(close_button);
atk_action_set_description(ATK_ACTION(atkobj), 0,
diff --git a/src/themes/slider/theme.c b/src/themes/slider/theme.c
index 7672a8d..c75967a 100644
--- a/src/themes/slider/theme.c
+++ b/src/themes/slider/theme.c
@@ -306,6 +306,13 @@ static void on_composited_changed(GtkWidget* window, WindowData* windata)
gtk_widget_queue_draw (windata->win);
}
+static void
+close_button_clicked_cb(GtkButton* button, GtkWidget* win)
+{
+ g_object_set_data(G_OBJECT(win), "_user_closed", GINT_TO_POINTER(1));
+ gtk_widget_destroy(win);
+}
+
GtkWindow* create_notification(UrlClickedCb url_clicked)
{
GtkWidget* win;
@@ -400,7 +407,7 @@ GtkWindow* create_notification(UrlClickedCb url_clicked)
gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
gtk_container_set_border_width(GTK_CONTAINER(close_button), 0);
- g_signal_connect_swapped(G_OBJECT(close_button), "clicked", G_CALLBACK(gtk_widget_destroy), win);
+ g_signal_connect(G_OBJECT(close_button), "clicked", G_CALLBACK(close_button_clicked_cb), win);
atkobj = gtk_widget_get_accessible(close_button);
atk_action_set_description(ATK_ACTION(atkobj), 0,
diff --git a/src/themes/standard/theme.c b/src/themes/standard/theme.c
index 90171c6..b670f5b 100644
--- a/src/themes/standard/theme.c
+++ b/src/themes/standard/theme.c
@@ -642,6 +642,13 @@ static gboolean activate_link(GtkLabel* label, const char* url, WindowData* wind
return TRUE;
}
+static void
+close_button_clicked_cb(GtkButton* button, GtkWidget* win)
+{
+ g_object_set_data(G_OBJECT(win), "_user_closed", GINT_TO_POINTER(1));
+ gtk_widget_destroy(win);
+}
+
GtkWindow* create_notification(UrlClickedCb url_clicked)
{
GtkWidget* spacer;
@@ -754,7 +761,7 @@ GtkWindow* create_notification(UrlClickedCb url_clicked)
gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
gtk_container_set_border_width(GTK_CONTAINER(close_button), 0);
//gtk_widget_set_size_request(close_button, 20, 20);
- g_signal_connect_swapped(G_OBJECT(close_button), "clicked", G_CALLBACK(gtk_widget_destroy), win);
+ g_signal_connect(G_OBJECT(close_button), "clicked", G_CALLBACK(close_button_clicked_cb), win);
atkobj = gtk_widget_get_accessible(close_button);
atk_action_set_description(ATK_ACTION(atkobj), 0,