Merge pull request #802 from fwSmit/environment-variables
Set environment variables for scripts
This commit is contained in:
commit
814e620247
@ -869,11 +869,22 @@ Within rules you can specify a script to be run every time the rule is matched
|
|||||||
by assigning the 'script' option to the name of the script to be run.
|
by assigning the 'script' option to the name of the script to be run.
|
||||||
|
|
||||||
When the script is called details of the notification that triggered it will be
|
When the script is called details of the notification that triggered it will be
|
||||||
passed via command line parameters in the following order: appname, summary,
|
passed via environment variables. The following variables are available:
|
||||||
body, icon, urgency.
|
B<DUNST_APP_NAME>, B<DUNST_SUMMARY>, B<DUNST_BODY>, B<DUNST_ICON_PATH>,
|
||||||
|
B<DUNST_URGENCY>, B<DUNST_ID>, B<DUNST_PROGRESS>, B<DUNST_CATEGORY>,
|
||||||
|
B<DUNST_STACK_TAG>, B<DUNST_URLS>, B<DUNST_TIMEOUT>, B<DUNST_TIMESTAMP>
|
||||||
|
and B<DUNST_STACK_TAG>.
|
||||||
|
|
||||||
Where icon is the absolute path to the icon file if there is one and urgency is
|
Another, less recommended way to get notifcations details from a script is via
|
||||||
one of "LOW", "NORMAL" or "CRITICAL".
|
command line parameters. These are passed to the script in the following order:
|
||||||
|
B<appname>, B<summary>, B<body>, B<icon_path>, B<urgency>.
|
||||||
|
|
||||||
|
Where B<DUNST_ICON_PATH> or B<icon_path> is the absolute path to the icon file
|
||||||
|
if there is one. B<DUNST_URGENCY> or B<urgency> is one of "LOW", "NORMAL" or
|
||||||
|
"CRITICAL". B<DUNST_URLS> is a newline-separated list of urls associated with
|
||||||
|
the notification.
|
||||||
|
|
||||||
|
Note that some variables may be empty.
|
||||||
|
|
||||||
If the notification is suppressed, the script will not be run unless
|
If the notification is suppressed, the script will not be run unless
|
||||||
B<always_run_scripts> is set to true.
|
B<always_run_scripts> is set to true.
|
||||||
|
39
src/icon.c
39
src/icon.c
@ -195,14 +195,14 @@ GdkPixbuf *get_pixbuf_from_file(const char *filename)
|
|||||||
return pixbuf;
|
return pixbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
|
char *get_path_from_icon_name(const char *iconname)
|
||||||
{
|
{
|
||||||
if (STR_EMPTY(iconname))
|
if (STR_EMPTY(iconname))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
const char *suffixes[] = { ".svg", ".svgz", ".png", ".xpm", NULL };
|
const char *suffixes[] = { ".svg", ".svgz", ".png", ".xpm", NULL };
|
||||||
GdkPixbuf *pixbuf = NULL;
|
|
||||||
gchar *uri_path = NULL;
|
gchar *uri_path = NULL;
|
||||||
|
char *new_name = NULL;
|
||||||
|
|
||||||
if (g_str_has_prefix(iconname, "file://")) {
|
if (g_str_has_prefix(iconname, "file://")) {
|
||||||
uri_path = g_filename_from_uri(iconname, NULL, NULL);
|
uri_path = g_filename_from_uri(iconname, NULL, NULL);
|
||||||
@ -212,7 +212,7 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
|
|||||||
|
|
||||||
/* absolute path? */
|
/* absolute path? */
|
||||||
if (iconname[0] == '/' || iconname[0] == '~') {
|
if (iconname[0] == '/' || iconname[0] == '~') {
|
||||||
pixbuf = get_pixbuf_from_file(iconname);
|
new_name = g_strdup(iconname);
|
||||||
} else {
|
} else {
|
||||||
/* search in icon_path */
|
/* search in icon_path */
|
||||||
char *start = settings.icon_path,
|
char *start = settings.icon_path,
|
||||||
@ -224,26 +224,47 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
|
|||||||
current_folder = g_strndup(start, end - start);
|
current_folder = g_strndup(start, end - start);
|
||||||
|
|
||||||
for (const char **suf = suffixes; *suf; suf++) {
|
for (const char **suf = suffixes; *suf; suf++) {
|
||||||
maybe_icon_path = g_strconcat(current_folder, "/", iconname, *suf, NULL);
|
gchar *name_with_extension = g_strconcat(iconname, *suf, NULL);
|
||||||
if (is_readable_file(maybe_icon_path))
|
maybe_icon_path = g_build_filename(current_folder, name_with_extension, NULL);
|
||||||
pixbuf = get_pixbuf_from_file(maybe_icon_path);
|
if (is_readable_file(maybe_icon_path)) {
|
||||||
|
new_name = g_strdup(maybe_icon_path);
|
||||||
|
}
|
||||||
|
g_free(name_with_extension);
|
||||||
g_free(maybe_icon_path);
|
g_free(maybe_icon_path);
|
||||||
|
|
||||||
if (pixbuf)
|
if (new_name)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(current_folder);
|
g_free(current_folder);
|
||||||
if (pixbuf)
|
if (new_name)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
start = end + 1;
|
start = end + 1;
|
||||||
} while (STR_FULL(end));
|
} while (STR_FULL(end));
|
||||||
if (!pixbuf)
|
if (!new_name)
|
||||||
LOG_W("No icon found in path: '%s'", iconname);
|
LOG_W("No icon found in path: '%s'", iconname);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(uri_path);
|
g_free(uri_path);
|
||||||
|
return new_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
|
||||||
|
{
|
||||||
|
char *path = get_path_from_icon_name(iconname);
|
||||||
|
if (!path) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GdkPixbuf *pixbuf = NULL;
|
||||||
|
|
||||||
|
pixbuf = get_pixbuf_from_file(path);
|
||||||
|
g_free(path);
|
||||||
|
|
||||||
|
if (!pixbuf)
|
||||||
|
LOG_W("No icon found in path: '%s'", iconname);
|
||||||
|
|
||||||
return pixbuf;
|
return pixbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
src/icon.h
11
src/icon.h
@ -17,6 +17,17 @@ cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf);
|
|||||||
*/
|
*/
|
||||||
GdkPixbuf *get_pixbuf_from_file(const char *filename);
|
GdkPixbuf *get_pixbuf_from_file(const char *filename);
|
||||||
|
|
||||||
|
/** Retrieve a path from an icon name.
|
||||||
|
*
|
||||||
|
* @param iconname A string describing a `file://` URL, an arbitary filename
|
||||||
|
* or an icon name, which then gets searched for in the
|
||||||
|
* settings.icon_path
|
||||||
|
*
|
||||||
|
* @return a newly allocated string with the icon path
|
||||||
|
* @retval NULL: file does not exist, not readable, etc..
|
||||||
|
*/
|
||||||
|
char *get_path_from_icon_name(const char *iconname);
|
||||||
|
|
||||||
/** Retrieve an icon by its name sent via the notification bus, scaled according to settings
|
/** Retrieve an icon by its name sent via the notification bus, scaled according to settings
|
||||||
*
|
*
|
||||||
* @param iconname A string describing a `file://` URL, an arbitary filename
|
* @param iconname A string describing a `file://` URL, an arbitary filename
|
||||||
|
@ -129,11 +129,32 @@ void notification_run_script(struct notification *n)
|
|||||||
int status;
|
int status;
|
||||||
waitpid(pid1, &status, 0);
|
waitpid(pid1, &status, 0);
|
||||||
} else {
|
} else {
|
||||||
|
// second fork to prevent zombie processes
|
||||||
int pid2 = fork();
|
int pid2 = fork();
|
||||||
if (pid2) {
|
if (pid2) {
|
||||||
exit(0);
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
int ret = execlp(script,
|
// Set environment variables
|
||||||
|
gchar *n_id_str = g_strdup_printf("%i", n->id);
|
||||||
|
gchar *n_progress_str = g_strdup_printf("%i", n->progress);
|
||||||
|
gchar *n_timeout_str = g_strdup_printf("%li", n->timeout/1000);
|
||||||
|
gchar *n_timestamp_str = g_strdup_printf("%li", n->timestamp / 1000);
|
||||||
|
char* icon_path = get_path_from_icon_name(icon);
|
||||||
|
safe_setenv("DUNST_APP_NAME", appname);
|
||||||
|
safe_setenv("DUNST_SUMMARY", summary);
|
||||||
|
safe_setenv("DUNST_BODY", body);
|
||||||
|
safe_setenv("DUNST_ICON_PATH", icon_path);
|
||||||
|
safe_setenv("DUNST_URGENCY", urgency);
|
||||||
|
safe_setenv("DUNST_ID", n_id_str);
|
||||||
|
safe_setenv("DUNST_PROGRESS", n_progress_str);
|
||||||
|
safe_setenv("DUNST_CATEGORY", n->category);
|
||||||
|
safe_setenv("DUNST_STACK_TAG", n->stack_tag);
|
||||||
|
safe_setenv("DUNST_URLS", n->urls);
|
||||||
|
safe_setenv("DUNST_TIMEOUT", n_timeout_str);
|
||||||
|
safe_setenv("DUNST_TIMESTAMP", n_timestamp_str);
|
||||||
|
safe_setenv("DUNST_STACK_TAG", n->stack_tag);
|
||||||
|
|
||||||
|
execlp(script,
|
||||||
script,
|
script,
|
||||||
appname,
|
appname,
|
||||||
summary,
|
summary,
|
||||||
@ -141,14 +162,13 @@ void notification_run_script(struct notification *n)
|
|||||||
icon,
|
icon,
|
||||||
urgency,
|
urgency,
|
||||||
(char *)NULL);
|
(char *)NULL);
|
||||||
if (ret != 0) {
|
|
||||||
LOG_W("Unable to run script: %s", strerror(errno));
|
LOG_W("Unable to run script %s: %s", n->scripts[i], strerror(errno));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper function to convert an urgency to a string
|
* Helper function to convert an urgency to a string
|
||||||
|
@ -55,9 +55,9 @@ struct notification {
|
|||||||
char *iconname; /**< plain icon information (may be a path or just a name)
|
char *iconname; /**< plain icon information (may be a path or just a name)
|
||||||
Use this to compare the icon name with rules.*/
|
Use this to compare the icon name with rules.*/
|
||||||
|
|
||||||
gint64 start; /**< begin of current display */
|
gint64 start; /**< begin of current display (in milliseconds) */
|
||||||
gint64 timestamp; /**< arrival time */
|
gint64 timestamp; /**< arrival time (in milliseconds) */
|
||||||
gint64 timeout; /**< time to display */
|
gint64 timeout; /**< time to display (in milliseconds) */
|
||||||
int locked; /**< If non-zero the notification is locked **/
|
int locked; /**< If non-zero the notification is locked **/
|
||||||
|
|
||||||
GHashTable *actions;
|
GHashTable *actions;
|
||||||
|
12
src/utils.c
12
src/utils.c
@ -247,4 +247,16 @@ const char *user_get_home(void)
|
|||||||
return home_directory;
|
return home_directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool safe_setenv(const char* key, const char* value){
|
||||||
|
if (!key)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
setenv(key, "", 1);
|
||||||
|
else
|
||||||
|
setenv(key, value, 1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
12
src/utils.h
12
src/utils.h
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
//! Test if a string is NULL or empty
|
//! Test if a string is NULL or empty
|
||||||
#define STR_EMPTY(s) (!s || (*s == '\0'))
|
#define STR_EMPTY(s) (!s || (*s == '\0'))
|
||||||
@ -137,5 +138,16 @@ gint64 time_monotonic_now(void);
|
|||||||
*/
|
*/
|
||||||
const char *user_get_home(void);
|
const char *user_get_home(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to set an environment variable safely. If an environment variable with
|
||||||
|
* name `key` exists, it will be overwritten.
|
||||||
|
* If `value` is null, `key` will be set to an empty string.
|
||||||
|
*
|
||||||
|
* @param key (nullable) The environment variable to change
|
||||||
|
* @param value (nullable) The value to change it to.
|
||||||
|
* @returns: A bool that is true when it succeeds
|
||||||
|
*/
|
||||||
|
bool safe_setenv(const char* key, const char* value);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
76
test/icon.c
76
test/icon.c
@ -12,6 +12,67 @@
|
|||||||
|
|
||||||
extern const char *base;
|
extern const char *base;
|
||||||
|
|
||||||
|
TEST test_get_path_from_icon_null(void){
|
||||||
|
char *result = get_path_from_icon_name(NULL);
|
||||||
|
ASSERT_EQ(result, NULL);
|
||||||
|
PASS();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST test_get_path_from_icon_sorting(void)
|
||||||
|
{
|
||||||
|
const char *iconpath = ICONPREFIX;
|
||||||
|
|
||||||
|
const char* icon_names[] = { "onlypng", "onlysvg", "icon1" };
|
||||||
|
const char* icon_paths[] = { "valid/onlypng.png", "valid/onlysvg.svg", "invalid/icon1.svg" };
|
||||||
|
ASSERT_EQm("Test is incorrect", G_N_ELEMENTS(icon_names), G_N_ELEMENTS(icon_paths));
|
||||||
|
|
||||||
|
for (int i = 0; i < G_N_ELEMENTS(icon_names); i++){
|
||||||
|
gchar *path = g_build_filename(base, iconpath, icon_paths[i], NULL);
|
||||||
|
char *result = get_path_from_icon_name(icon_names[i]);
|
||||||
|
ASSERT(result);
|
||||||
|
ASSERT_EQ(strcmp(result, path), 0);
|
||||||
|
g_free(path);
|
||||||
|
g_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
PASS();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST test_get_path_from_icon_name(void)
|
||||||
|
{
|
||||||
|
const char *iconpath = ICONPREFIX;
|
||||||
|
|
||||||
|
const char* icon_name = "onlypng";
|
||||||
|
const char* expected_suffix = ".png";
|
||||||
|
char* full_name = g_strconcat(icon_name, expected_suffix, NULL);
|
||||||
|
|
||||||
|
gchar *path = g_build_filename(base, iconpath, "valid", full_name, NULL);
|
||||||
|
char *result = get_path_from_icon_name(icon_name);
|
||||||
|
|
||||||
|
ASSERT(result);
|
||||||
|
ASSERT_EQ(strcmp(result, path), 0);
|
||||||
|
|
||||||
|
g_free(full_name);
|
||||||
|
g_free(path);
|
||||||
|
g_free(result);
|
||||||
|
PASS();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST test_get_path_from_icon_name_full(void)
|
||||||
|
{
|
||||||
|
const char *iconpath = ICONPREFIX;
|
||||||
|
|
||||||
|
gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL);
|
||||||
|
|
||||||
|
char *result = get_path_from_icon_name(path);
|
||||||
|
ASSERT(result);
|
||||||
|
ASSERT_EQ(strcmp(result, path), 0);
|
||||||
|
|
||||||
|
g_free(path);
|
||||||
|
g_free(result);
|
||||||
|
PASS();
|
||||||
|
}
|
||||||
|
|
||||||
TEST test_get_pixbuf_from_file_tilde(void)
|
TEST test_get_pixbuf_from_file_tilde(void)
|
||||||
{
|
{
|
||||||
const char *home = g_get_home_dir();
|
const char *home = g_get_home_dir();
|
||||||
@ -62,8 +123,8 @@ TEST test_get_pixbuf_from_icon_invalid(void)
|
|||||||
TEST test_get_pixbuf_from_icon_both(void)
|
TEST test_get_pixbuf_from_icon_both(void)
|
||||||
{
|
{
|
||||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("icon1");
|
GdkPixbuf *pixbuf = get_pixbuf_from_icon("icon1");
|
||||||
ASSERT(pixbuf);
|
// the first icon found is invalid, so the pixbuf is empty
|
||||||
ASSERTm("SVG pixbuf hasn't precedence", IS_ICON_SVG(pixbuf));
|
ASSERT(!pixbuf);
|
||||||
g_clear_pointer(&pixbuf, g_object_unref);
|
g_clear_pointer(&pixbuf, g_object_unref);
|
||||||
|
|
||||||
PASS();
|
PASS();
|
||||||
@ -170,12 +231,23 @@ TEST test_get_pixbuf_from_icon_both_is_scaled(void)
|
|||||||
|
|
||||||
SUITE(suite_icon)
|
SUITE(suite_icon)
|
||||||
{
|
{
|
||||||
|
// set only valid icons in the path
|
||||||
|
settings.icon_path = g_strconcat(
|
||||||
|
base, ICONPREFIX "/valid"
|
||||||
|
":", base, ICONPREFIX "/both",
|
||||||
|
NULL);
|
||||||
|
RUN_TEST(test_get_path_from_icon_name);
|
||||||
|
|
||||||
|
g_clear_pointer(&settings.icon_path, g_free);
|
||||||
settings.icon_path = g_strconcat(
|
settings.icon_path = g_strconcat(
|
||||||
base, ICONPREFIX "/invalid"
|
base, ICONPREFIX "/invalid"
|
||||||
":", base, ICONPREFIX "/valid"
|
":", base, ICONPREFIX "/valid"
|
||||||
":", base, ICONPREFIX "/both",
|
":", base, ICONPREFIX "/both",
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
RUN_TEST(test_get_path_from_icon_null);
|
||||||
|
RUN_TEST(test_get_path_from_icon_sorting);
|
||||||
|
RUN_TEST(test_get_path_from_icon_name_full);
|
||||||
RUN_TEST(test_get_pixbuf_from_file_tilde);
|
RUN_TEST(test_get_pixbuf_from_file_tilde);
|
||||||
RUN_TEST(test_get_pixbuf_from_file_absolute);
|
RUN_TEST(test_get_pixbuf_from_file_absolute);
|
||||||
RUN_TEST(test_get_pixbuf_from_icon_invalid);
|
RUN_TEST(test_get_pixbuf_from_icon_invalid);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user