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.
|
||||
|
||||
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,
|
||||
body, icon, urgency.
|
||||
passed via environment variables. The following variables are available:
|
||||
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
|
||||
one of "LOW", "NORMAL" or "CRITICAL".
|
||||
Another, less recommended way to get notifcations details from a script is via
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
|
||||
char *get_path_from_icon_name(const char *iconname)
|
||||
{
|
||||
if (STR_EMPTY(iconname))
|
||||
return NULL;
|
||||
|
||||
const char *suffixes[] = { ".svg", ".svgz", ".png", ".xpm", NULL };
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
gchar *uri_path = NULL;
|
||||
char *new_name = NULL;
|
||||
|
||||
if (g_str_has_prefix(iconname, "file://")) {
|
||||
uri_path = g_filename_from_uri(iconname, NULL, NULL);
|
||||
@ -212,7 +212,7 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
|
||||
|
||||
/* absolute path? */
|
||||
if (iconname[0] == '/' || iconname[0] == '~') {
|
||||
pixbuf = get_pixbuf_from_file(iconname);
|
||||
new_name = g_strdup(iconname);
|
||||
} else {
|
||||
/* search in 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);
|
||||
|
||||
for (const char **suf = suffixes; *suf; suf++) {
|
||||
maybe_icon_path = g_strconcat(current_folder, "/", iconname, *suf, NULL);
|
||||
if (is_readable_file(maybe_icon_path))
|
||||
pixbuf = get_pixbuf_from_file(maybe_icon_path);
|
||||
gchar *name_with_extension = g_strconcat(iconname, *suf, NULL);
|
||||
maybe_icon_path = g_build_filename(current_folder, name_with_extension, NULL);
|
||||
if (is_readable_file(maybe_icon_path)) {
|
||||
new_name = g_strdup(maybe_icon_path);
|
||||
}
|
||||
g_free(name_with_extension);
|
||||
g_free(maybe_icon_path);
|
||||
|
||||
if (pixbuf)
|
||||
if (new_name)
|
||||
break;
|
||||
}
|
||||
|
||||
g_free(current_folder);
|
||||
if (pixbuf)
|
||||
if (new_name)
|
||||
break;
|
||||
|
||||
start = end + 1;
|
||||
} while (STR_FULL(end));
|
||||
if (!pixbuf)
|
||||
if (!new_name)
|
||||
LOG_W("No icon found in path: '%s'", iconname);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
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);
|
||||
|
||||
/** 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
|
||||
*
|
||||
* @param iconname A string describing a `file://` URL, an arbitary filename
|
||||
|
@ -129,11 +129,32 @@ void notification_run_script(struct notification *n)
|
||||
int status;
|
||||
waitpid(pid1, &status, 0);
|
||||
} else {
|
||||
// second fork to prevent zombie processes
|
||||
int pid2 = fork();
|
||||
if (pid2) {
|
||||
exit(0);
|
||||
} 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,
|
||||
appname,
|
||||
summary,
|
||||
@ -141,10 +162,9 @@ void notification_run_script(struct notification *n)
|
||||
icon,
|
||||
urgency,
|
||||
(char *)NULL);
|
||||
if (ret != 0) {
|
||||
LOG_W("Unable to run script: %s", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LOG_W("Unable to run script %s: %s", n->scripts[i], strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,9 +55,9 @@ struct notification {
|
||||
char *iconname; /**< plain icon information (may be a path or just a name)
|
||||
Use this to compare the icon name with rules.*/
|
||||
|
||||
gint64 start; /**< begin of current display */
|
||||
gint64 timestamp; /**< arrival time */
|
||||
gint64 timeout; /**< time to display */
|
||||
gint64 start; /**< begin of current display (in milliseconds) */
|
||||
gint64 timestamp; /**< arrival time (in milliseconds) */
|
||||
gint64 timeout; /**< time to display (in milliseconds) */
|
||||
int locked; /**< If non-zero the notification is locked **/
|
||||
|
||||
GHashTable *actions;
|
||||
|
12
src/utils.c
12
src/utils.c
@ -247,4 +247,16 @@ const char *user_get_home(void)
|
||||
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: */
|
||||
|
12
src/utils.h
12
src/utils.h
@ -4,6 +4,7 @@
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//! Test if a string is NULL or empty
|
||||
#define STR_EMPTY(s) (!s || (*s == '\0'))
|
||||
@ -137,5 +138,16 @@ gint64 time_monotonic_now(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
|
||||
/* 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;
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("icon1");
|
||||
ASSERT(pixbuf);
|
||||
ASSERTm("SVG pixbuf hasn't precedence", IS_ICON_SVG(pixbuf));
|
||||
// the first icon found is invalid, so the pixbuf is empty
|
||||
ASSERT(!pixbuf);
|
||||
g_clear_pointer(&pixbuf, g_object_unref);
|
||||
|
||||
PASS();
|
||||
@ -170,12 +231,23 @@ TEST test_get_pixbuf_from_icon_both_is_scaled(void)
|
||||
|
||||
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(
|
||||
base, ICONPREFIX "/invalid"
|
||||
":", base, ICONPREFIX "/valid"
|
||||
":", base, ICONPREFIX "/both",
|
||||
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_absolute);
|
||||
RUN_TEST(test_get_pixbuf_from_icon_invalid);
|
||||
|
Loading…
x
Reference in New Issue
Block a user