diff --git a/src/dbus.c b/src/dbus.c index f6ff62b..9ec39d8 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -128,10 +128,7 @@ static void on_get_capabilities(GDBusConnection *connection, g_dbus_connection_flush(connection, NULL, NULL, NULL); } -static void on_notify(GDBusConnection *connection, - const gchar *sender, - GVariant *parameters, - GDBusMethodInvocation *invocation) +static notification *dbus_message_to_notification(const gchar *sender, GVariant *parameters) { gchar *appname = NULL; @@ -268,14 +265,15 @@ static void on_notify(GDBusConnection *connection, fflush(stdout); notification *n = notification_create(); + + n->id = replaces_id; n->appname = appname; n->summary = summary; n->body = body; n->icon = icon; n->raw_icon = raw_icon; n->timeout = timeout < 0 ? -1 : timeout * 1000; - n->markup = settings.markup; - n->progress = (progress < 0 || progress > 100) ? -1 : progress; + n->progress = progress; n->urgency = urgency; n->category = category; n->dbus_client = g_strdup(sender); @@ -287,14 +285,20 @@ static void on_notify(GDBusConnection *connection, } n->actions = actions; - for (int i = 0; i < ColLast; i++) { - n->color_strings[i] = NULL; - } - n->color_strings[ColFG] = fgcolor; - n->color_strings[ColBG] = bgcolor; + n->colors[ColFG] = fgcolor; + n->colors[ColBG] = bgcolor; notification_init(n); - int id = queues_notification_insert(n, replaces_id); + return n; +} + +static void on_notify(GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + notification *n = dbus_message_to_notification(sender, parameters); + int id = queues_notification_insert(n); GVariant *reply = g_variant_new("(u)", id); g_dbus_method_invocation_return_value(invocation, reply); diff --git a/src/dunst.c b/src/dunst.c index 933f7f8..6cf2fca 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -149,6 +149,7 @@ int dunst_main(int argc, char *argv[]) if (settings.startup_notification) { notification *n = notification_create(); + n->id = 0; n->appname = g_strdup("dunst"); n->summary = g_strdup("startup"); n->body = g_strdup("dunst is up and running"); @@ -157,7 +158,7 @@ int dunst_main(int argc, char *argv[]) n->markup = MARKUP_NO; n->urgency = URG_LOW; notification_init(n); - queues_notification_insert(n, 0); + queues_notification_insert(n); // we do not call wakeup now, wake_up does not work here yet } diff --git a/src/dunst.h b/src/dunst.h index 99aeaef..d950b8f 100644 --- a/src/dunst.h +++ b/src/dunst.h @@ -17,7 +17,7 @@ #define ColBG 0 extern GSList *rules; -extern const char *color_strings[3][3]; +extern const char *colors[3][3]; gboolean run(void *data); void wake_up(void); diff --git a/src/markup.c b/src/markup.c index cd91ff8..ad4484f 100644 --- a/src/markup.c +++ b/src/markup.c @@ -4,6 +4,8 @@ #include #include +#include +#include #include "settings.h" #include "utils.h" @@ -44,6 +46,178 @@ static char *markup_br2nl(char *str) return str; } +/* + * Remove HTML hyperlinks of a string. + * + * @str: The string to replace a tags + * @urls: (nullable): If any href-attributes found, an '\n' concatenated + * string of the URLs in format '[] ' + */ +void markup_strip_a(char **str, char **urls) +{ + char *tag1 = NULL; + + if (urls) + *urls = NULL; + + while ((tag1 = strstr(*str, ""); + char *tag2 = strstr(tag1, ""); + + // the tag is broken, ignore it + if (!tag1_end) { + fprintf(stderr, + "WARNING: Given link is broken: '%s'\n", + tag1); + string_replace_at(*str, tag1-*str, strlen(tag1), ""); + break; + } + if (tag2 && tag2 < tag1_end) { + int repl_len = (tag2 - tag1) + strlen(""); + fprintf(stderr, + "WARNING: Given link is broken: '%.*s.'\n", + repl_len, tag1); + string_replace_at(*str, tag1-*str, repl_len, ""); + break; + } + + // search contents of href attribute + char *plain_url = NULL; + if (href && href < tag1_end) { + + // shift href to the actual begin of the value + href = href+6; + + const char *quote = strstr(href, "\""); + + if (quote && quote < tag1_end) { + plain_url = g_strndup(href, quote-href); + } + } + + // text between a tags + int text_len; + if (tag2) + text_len = tag2 - (tag1_end+1); + else + text_len = strlen(tag1_end+1); + + char *text = g_strndup(tag1_end+1, text_len); + + int repl_len = text_len + (tag1_end-tag1) + 1; + repl_len += tag2 ? strlen("") : 0; + + *str = string_replace_at(*str, tag1-*str, repl_len, text); + + // if there had been a href attribute, + // add it to the URLs + if (plain_url && urls) { + text = string_replace_all("]", "", text); + text = string_replace_all("[", "", text); + + char *url = g_strdup_printf("[%s] %s", text, plain_url); + + *urls = string_append(*urls, url, "\n"); + g_free(url); + } + + g_free(plain_url); + g_free(text); + } +} + +/* + * Remove img-tags of a string. If alt attribute given, use this as replacement. + * + * @str: The string to replace img tags + * @urls: (nullable): If any src-attributes found, an '\n' concatenated string of + * the URLs in format '[] ' + */ +void markup_strip_img(char **str, char **urls) +{ + const char *start = *str; + + if (urls) + *urls = NULL; + + while ((start = strstr(*str, ""); + + // the tag is broken, ignore it + if (!end) { + fprintf(stderr, "WARNING: Given image is broken: '%s'\n", start); + string_replace_at(*str, start-*str, strlen(start), ""); + break; + } + + // use attribute=" as stated in the notification spec + const char *alt_s = strstr(start, "alt=\""); + const char *src_s = strstr(start, "src=\""); + + char *text_alt = NULL; + char *text_src = NULL; + + const char *src_e = NULL, *alt_e = NULL; + if (alt_s) + alt_e = strstr(alt_s + strlen("alt=\""), "\""); + if (src_s) + src_e = strstr(src_s + strlen("src=\""), "\""); + + // Move pointer to the actual start + alt_s = alt_s ? alt_s + strlen("alt=\"") : NULL; + src_s = src_s ? src_s + strlen("src=\"") : NULL; + + /* check if alt and src attribute are given + * If both given, check the alignment of all pointers */ + if ( alt_s && alt_e + && src_s && src_e + && ( (alt_s < src_s && alt_e < src_s-strlen("src=\"") && src_e < end) + ||(src_s < alt_s && src_e < alt_s-strlen("alt=\"") && alt_e < end)) ) { + + text_alt = g_strndup(alt_s, alt_e-alt_s); + text_src = g_strndup(src_s, src_e-src_s); + + /* check if single valid alt attribute is available */ + } else if (alt_s && alt_e && alt_e < end && (!src_s || src_s < alt_s || alt_e < src_s - strlen("src=\""))) { + text_alt = g_strndup(alt_s, alt_e-alt_s); + + /* check if single valid src attribute is available */ + } else if (src_s && src_e && src_e < end && (!alt_s || alt_s < src_s || src_e < alt_s - strlen("alt=\""))) { + text_src = g_strndup(src_s, src_e-src_s); + + } else { + fprintf(stderr, + "WARNING: Given image argument is broken: '%.*s'\n", + (int)(end-start), start); + } + + // replacement text for alt + int repl_len = end - start + 1; + + if (!text_alt) + text_alt = g_strdup("[image]"); + + *str = string_replace_at(*str, start-*str, repl_len, text_alt); + + // if there had been a href attribute, + // add it to the URLs + if (text_src && urls) { + text_alt = string_replace_all("]", "", text_alt); + text_alt = string_replace_all("[", "", text_alt); + + char *url = g_strdup_printf("[%s] %s", text_alt, text_src); + + *urls = string_append(*urls, url, "\n"); + g_free(url); + } + + g_free(text_src); + g_free(text_alt); + } +} + /* * Strip any markup from text; turn it in to plain text. * @@ -96,6 +270,8 @@ char *markup_transform(char *str, enum markup_mode markup_mode) break; case MARKUP_FULL: str = markup_br2nl(str); + markup_strip_a(&str, NULL); + markup_strip_img(&str, NULL); break; } diff --git a/src/markup.h b/src/markup.h index 8304e2d..9d5cda7 100644 --- a/src/markup.h +++ b/src/markup.h @@ -5,6 +5,10 @@ #include "settings.h" char *markup_strip(char *str); + +void markup_strip_a(char **str, char **urls); +void markup_strip_img(char **str, char **urls); + char *markup_transform(char *str, enum markup_mode markup_mode); #endif diff --git a/src/menu.c b/src/menu.c index 4a21f70..9699b4b 100644 --- a/src/menu.c +++ b/src/menu.c @@ -95,10 +95,14 @@ char *extract_urls(const char *to_match) */ void open_browser(const char *in) { - // remove prefix and test url - char *url = extract_urls(in); - if (!url) - return; + char *url = NULL; + + // If any, remove leading [ linktext ] from URL + const char *end = strstr(in, "] "); + if (*in == '[' && end) + url = g_strdup(end + 2); + else + url = g_strdup(in); int browser_pid1 = fork(); diff --git a/src/notification.c b/src/notification.c index 810e72f..349668e 100644 --- a/src/notification.c +++ b/src/notification.c @@ -23,6 +23,10 @@ #include "utils.h" #include "x11/x.h" +static void notification_extract_urls(notification *n); +static void notification_format_message(notification *n); +static void notification_dmenu_string(notification *n); + /* * print a human readable representation * of the given notification to stdout. @@ -40,15 +44,17 @@ void notification_print(notification *n) printf("\turgency: %s\n", notification_urgency_to_string(n->urgency)); printf("\ttransient: %d\n", n->transient); printf("\tformatted: '%s'\n", n->msg); - printf("\tfg: %s\n", n->color_strings[ColFG]); - printf("\tbg: %s\n", n->color_strings[ColBG]); - printf("\tframe: %s\n", n->color_strings[ColFrame]); + printf("\tfg: %s\n", n->colors[ColFG]); + printf("\tbg: %s\n", n->colors[ColBG]); + printf("\tframe: %s\n", n->colors[ColFrame]); printf("\tid: %d\n", n->id); if (n->urls) { + char *urls = string_replace_all("\n", "\t\t\n", g_strdup(n->urls)); printf("\turls:\n"); printf("\t{\n"); - printf("\t\t%s\n", n->urls); + printf("\t\t%s\n", urls); printf("\t}\n"); + g_free(urls); } if (n->actions) { @@ -210,6 +216,9 @@ void notification_free(notification *n) g_free(n->category); g_free(n->text_to_render); g_free(n->urls); + g_free(n->colors[ColFG]); + g_free(n->colors[ColBG]); + g_free(n->colors[ColFrame]); actions_free(n->actions); rawimage_free(n->raw_icon); @@ -248,95 +257,88 @@ void notification_replace_single_field(char **haystack, g_free(input); } -char *notification_extract_markup_urls(char **str_ptr) -{ - char *start, *end, *replace_buf, *str, *urls = NULL, *url, *index_buf; - int linkno = 1; - - str = *str_ptr; - while ((start = strstr(str, ""); - if (end != NULL) { - replace_buf = g_strndup(start, end - start + 1); - url = extract_urls(replace_buf); - if (url != NULL) { - str = string_replace(replace_buf, "[", str); - - index_buf = g_strdup_printf("[#%d]", linkno++); - if (urls == NULL) { - urls = g_strconcat(index_buf, " ", url, NULL); - } else { - char *tmp = urls; - urls = g_strconcat(tmp, "\n", index_buf, " ", url, NULL); - g_free(tmp); - } - - index_buf[0] = ' '; - str = string_replace("", index_buf, str); - g_free(index_buf); - g_free(url); - } else { - str = string_replace(replace_buf, "", str); - str = string_replace("", "", str); - } - g_free(replace_buf); - } else { - break; - } - } - *str_ptr = str; - return urls; -} - /* - * Create notification struct and initialise everything to NULL, - * this function is guaranteed to return a valid pointer. + * Create notification struct and initialise all fields with either + * - the default (if it's not needed to be freed later) + * - its undefined representation (NULL, -1) + * + * This function is guaranteed to return a valid pointer. + * @Returns: The generated notification */ notification *notification_create(void) { - return g_malloc0(sizeof(notification)); -} + notification *n = g_malloc0(sizeof(notification)); -void notification_init_defaults(notification *n) -{ - assert(n != NULL); - if(n->appname == NULL) n->appname = g_strdup("unknown"); - if(n->summary == NULL) n->summary = g_strdup(""); - if(n->body == NULL) n->body = g_strdup(""); - if(n->category == NULL) n->category = g_strdup(""); + /* Unparameterized default values */ + n->first_render = true; + n->markup = settings.markup; + n->format = settings.format; + + n->timestamp = g_get_monotonic_time(); + + n->urgency = URG_NORM; + n->timeout = -1; + + n->transient = false; + n->progress = -1; + + return n; } /* - * Initialize the given notification + * Sanitize values of notification, apply all matching rules + * and generate derived fields. * - * n should be a pointer to a notification allocated with - * notification_create, it is undefined behaviour to pass a notification - * allocated some other way. + * @n: the notification to sanitize */ void notification_init(notification *n) { - assert(n != NULL); + /* default to empty string to avoid further NULL faults */ + n->appname = n->appname ? n->appname : g_strdup("unknown"); + n->summary = n->summary ? n->summary : g_strdup(""); + n->body = n->body ? n->body : g_strdup(""); + n->category = n->category ? n->category : g_strdup(""); - //Prevent undefined behaviour by initialising required fields - notification_init_defaults(n); + /* sanitize urgency */ + if (n->urgency < URG_MIN) + n->urgency = URG_LOW; + if (n->urgency > URG_MAX) + n->urgency = URG_CRIT; - n->script = NULL; - n->text_to_render = NULL; + /* Timeout processing */ + if (n->timeout < 0) + n->timeout = settings.timeouts[n->urgency]; - n->format = settings.format; + /* Icon handling */ + if (n->icon && strlen(n->icon) <= 0) + g_clear_pointer(&n->icon, g_free); + if (!n->raw_icon && !n->icon) + n->icon = g_strdup(settings.icons[n->urgency]); + /* Color hints */ + if (!n->colors[ColFG]) + n->colors[ColFG] = g_strdup(xctx.colors[ColFG][n->urgency]); + if (!n->colors[ColBG]) + n->colors[ColBG] = g_strdup(xctx.colors[ColBG][n->urgency]); + if (!n->colors[ColFrame]) + n->colors[ColFrame] = g_strdup(xctx.colors[ColFrame][n->urgency]); + + /* Sanitize misc hints */ + if (n->progress < 0 || n->progress > 100) + n->progress = -1; + + /* Process rules */ rule_apply_all(n); - if (n->icon != NULL && strlen(n->icon) <= 0) { - g_free(n->icon); - n->icon = NULL; - } + /* UPDATE derived fields */ + notification_extract_urls(n); + notification_dmenu_string(n); + notification_format_message(n); +} - if (n->raw_icon == NULL && n->icon == NULL) { - n->icon = g_strdup(settings.icons[n->urgency]); - } - - n->urls = notification_extract_markup_urls(&(n->body)); +static void notification_format_message(notification *n) +{ + g_clear_pointer(&n->msg, g_free); n->msg = string_replace_all("\\n", "\n", g_strdup(n->format)); @@ -438,45 +440,36 @@ void notification_init(notification *n) g_free(n->msg); n->msg = buffer; } +} - n->dup_count = 0; +static void notification_extract_urls(notification *n) +{ + g_clear_pointer(&n->urls, g_free); - /* urgency > URG_CRIT -> array out of range */ - if (n->urgency < URG_MIN) - n->urgency = URG_LOW; - if (n->urgency > URG_MAX) - n->urgency = URG_CRIT; + char *urls_in = string_append(g_strdup(n->summary), n->body, " "); - if (!n->color_strings[ColFG]) { - n->color_strings[ColFG] = xctx.color_strings[ColFG][n->urgency]; - } + char *urls_a = NULL; + char *urls_img = NULL; + markup_strip_a(&urls_in, &urls_a); + markup_strip_img(&urls_in, &urls_img); + // remove links and images first to not confuse + // plain urls extraction + char *urls_text = extract_urls(urls_in); - if (!n->color_strings[ColBG]) { - n->color_strings[ColBG] = xctx.color_strings[ColBG][n->urgency]; - } + n->urls = string_append(n->urls, urls_a, "\n"); + n->urls = string_append(n->urls, urls_img, "\n"); + n->urls = string_append(n->urls, urls_text, "\n"); - if (!n->color_strings[ColFrame]) { - n->color_strings[ColFrame] = xctx.color_strings[ColFrame][n->urgency]; - } - - n->timeout = - n->timeout < 0 ? settings.timeouts[n->urgency] : n->timeout; - n->start = 0; - - n->timestamp = g_get_monotonic_time(); - - n->redisplayed = false; - - n->first_render = true; - - char *tmp = g_strconcat(n->summary, " ", n->body, NULL); - - char *tmp_urls = extract_urls(tmp); - n->urls = string_append(n->urls, tmp_urls, "\n"); - g_free(tmp_urls); + g_free(urls_in); + g_free(urls_a); + g_free(urls_img); + g_free(urls_text); +} +static void notification_dmenu_string(notification *n) +{ if (n->actions) { - n->actions->dmenu_str = NULL; + g_clear_pointer(&n->actions->dmenu_str, g_free); for (int i = 0; i < n->actions->count; i += 2) { char *human_readable = n->actions->actions[i + 1]; string_replace_char('[', '(', human_readable); // kill square brackets @@ -489,14 +482,11 @@ void notification_init(notification *n) } } } - - g_free(tmp); } void notification_update_text_to_render(notification *n) { - g_free(n->text_to_render); - n->text_to_render = NULL; + g_clear_pointer(&n->text_to_render, g_free); char *buf = NULL; diff --git a/src/notification.h b/src/notification.h index c7da691..5b05e8a 100644 --- a/src/notification.h +++ b/src/notification.h @@ -35,34 +35,44 @@ typedef struct _actions { } Actions; typedef struct _notification { + int id; + char *dbus_client; + char *appname; char *summary; char *body; - char *icon; - RawImage *raw_icon; - char *msg; /* formatted message */ char *category; - char *text_to_render; - const char *format; - char *dbus_client; - gint64 start; - gint64 timestamp; - gint64 timeout; enum urgency urgency; - enum markup_mode markup; - bool redisplayed; /* has been displayed before? */ - int id; - int dup_count; - int displayed_height; - const char *color_strings[3]; - bool first_render; - bool transient; - int progress; /* percentage (-1: undefined) */ - int history_ignore; - const char *script; - char *urls; + char *icon; /* plain icon information (may be a path or just a name) */ + RawImage *raw_icon; /* passed icon data of notification, takes precedence over icon */ + + gint64 start; /* begin of current display */ + gint64 timestamp; /* arrival time */ + gint64 timeout; /* time to display */ + Actions *actions; + + enum markup_mode markup; + const char *format; + const char *script; + char *colors[3]; + + /* Hints */ + bool transient; /* timeout albeit user is idle */ + int progress; /* percentage (-1: undefined) */ + int history_ignore; /* push to history or free directly */ + + /* internal */ + bool redisplayed; /* has been displayed before? */ + bool first_render; /* markup has been rendered before? */ + int dup_count; /* amount of duplicate notifications stacked onto this */ + int displayed_height; + + /* derived fields */ + char *msg; /* formatted message */ + char *text_to_render; /* formatted message (with age and action indicators) */ + char *urls; /* urllist delimited by '\n' */ } notification; notification *notification_create(void); diff --git a/src/queues.c b/src/queues.c index ed5d0f1..67953b5 100644 --- a/src/queues.c +++ b/src/queues.c @@ -51,7 +51,7 @@ unsigned int queues_length_history() return history->length; } -int queues_notification_insert(notification *n, int replaces_id) +int queues_notification_insert(notification *n) { /* do not display the message, if the message is empty */ @@ -72,12 +72,11 @@ int queues_notification_insert(notification *n, int replaces_id) return 0; } - if (replaces_id == 0) { + if (n->id == 0) { n->id = ++next_notification_id; if (!settings.stack_duplicates || !queues_stack_duplicate(n)) g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); } else { - n->id = replaces_id; if (!queues_notification_replace_id(n)) g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); } diff --git a/src/queues.h b/src/queues.h index f280cd5..dab452b 100644 --- a/src/queues.h +++ b/src/queues.h @@ -34,13 +34,13 @@ unsigned int queues_length_history(); * Insert a fully initialized notification into queues * Respects stack_duplicates, and notification replacement * - * If replaces_id != 0, n replaces notification with id replaces_id - * If replaces_id == 0, n gets occupies a new position + * If n->id != 0, n replaces notification with id n->id + * If n->id == 0, n gets a new id assigned * * Returns the assigned notification id * If returned id == 0, the message was dismissed */ -int queues_notification_insert(notification *n, int replaces_id); +int queues_notification_insert(notification *n); /* * Replace the notification which matches the id field of diff --git a/src/rules.c b/src/rules.c index 086c627..7be1abb 100644 --- a/src/rules.c +++ b/src/rules.c @@ -28,10 +28,14 @@ void rule_apply(rule_t *r, notification *n) rawimage_free(n->raw_icon); n->raw_icon = NULL; } - if (r->fg) - n->color_strings[ColFG] = r->fg; - if (r->bg) - n->color_strings[ColBG] = r->bg; + if (r->fg) { + g_free(n->colors[ColFG]); + n->colors[ColFG] = g_strdup(r->fg); + } + if (r->bg) { + g_free(n->colors[ColBG]); + n->colors[ColBG] = g_strdup(r->bg); + } if (r->format) n->format = r->format; if (r->script) diff --git a/src/x11/x.c b/src/x11/x.c index 795be15..dcfef35 100644 --- a/src/x11/x.c +++ b/src/x11/x.c @@ -468,9 +468,9 @@ static colored_layout *r_init_shared(cairo_t *c, notification *n) cl->icon = NULL; } - cl->fg = x_string_to_color_t(n->color_strings[ColFG]); - cl->bg = x_string_to_color_t(n->color_strings[ColBG]); - cl->frame = x_string_to_color_t(n->color_strings[ColFrame]); + cl->fg = x_string_to_color_t(n->colors[ColFG]); + cl->bg = x_string_to_color_t(n->colors[ColBG]); + cl->frame = x_string_to_color_t(n->colors[ColFrame]); cl->n = n; @@ -987,26 +987,26 @@ void x_setup(void) x_shortcut_grab(&settings.context_ks); x_shortcut_ungrab(&settings.context_ks); - xctx.color_strings[ColFG][URG_LOW] = settings.lowfgcolor; - xctx.color_strings[ColFG][URG_NORM] = settings.normfgcolor; - xctx.color_strings[ColFG][URG_CRIT] = settings.critfgcolor; + xctx.colors[ColFG][URG_LOW] = settings.lowfgcolor; + xctx.colors[ColFG][URG_NORM] = settings.normfgcolor; + xctx.colors[ColFG][URG_CRIT] = settings.critfgcolor; - xctx.color_strings[ColBG][URG_LOW] = settings.lowbgcolor; - xctx.color_strings[ColBG][URG_NORM] = settings.normbgcolor; - xctx.color_strings[ColBG][URG_CRIT] = settings.critbgcolor; + xctx.colors[ColBG][URG_LOW] = settings.lowbgcolor; + xctx.colors[ColBG][URG_NORM] = settings.normbgcolor; + xctx.colors[ColBG][URG_CRIT] = settings.critbgcolor; if (settings.lowframecolor) - xctx.color_strings[ColFrame][URG_LOW] = settings.lowframecolor; + xctx.colors[ColFrame][URG_LOW] = settings.lowframecolor; else - xctx.color_strings[ColFrame][URG_LOW] = settings.frame_color; + xctx.colors[ColFrame][URG_LOW] = settings.frame_color; if (settings.normframecolor) - xctx.color_strings[ColFrame][URG_NORM] = settings.normframecolor; + xctx.colors[ColFrame][URG_NORM] = settings.normframecolor; else - xctx.color_strings[ColFrame][URG_NORM] = settings.frame_color; + xctx.colors[ColFrame][URG_NORM] = settings.frame_color; if (settings.critframecolor) - xctx.color_strings[ColFrame][URG_CRIT] = settings.critframecolor; + xctx.colors[ColFrame][URG_CRIT] = settings.critframecolor; else - xctx.color_strings[ColFrame][URG_CRIT] = settings.frame_color; + xctx.colors[ColFrame][URG_CRIT] = settings.frame_color; /* parse and set xctx.geometry and monitor position */ if (settings.geom[0] == '-') { diff --git a/src/x11/x.h b/src/x11/x.h index 1ccd9a3..3b08ca4 100644 --- a/src/x11/x.h +++ b/src/x11/x.h @@ -28,7 +28,7 @@ typedef struct _xctx { Window win; bool visible; dimension_t geometry; - const char *color_strings[3][3]; + const char *colors[3][3]; XScreenSaverInfo *screensaver_info; dimension_t window_dim; unsigned long sep_custom_col; diff --git a/test/markup.c b/test/markup.c index cbd8bad..76f2b91 100644 --- a/test/markup.c +++ b/test/markup.c @@ -45,12 +45,108 @@ TEST test_markup_transform(void) ASSERT_STR_EQ("foo bar baz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_FULL))); g_free(ptr); + // Test replacement of img and a tags, not renderable by pango + ASSERT_STR_EQ("foo bar bar baz", (ptr=markup_transform(g_strdup("\"foo
bar\nbaz"), MARKUP_FULL))); + g_free(ptr); + ASSERT_STR_EQ("test ", (ptr=markup_transform(g_strdup("test \"foo image"), MARKUP_FULL))); + g_free(ptr); + ASSERT_STR_EQ("bar baz", (ptr=markup_transform(g_strdup("bar baz"), MARKUP_FULL))); + g_free(ptr); + + PASS(); +} + +TEST helper_markup_strip_a (const char *in, const char *exp, const char *urls) +{ + // out_urls is a return parameter and the content should be ignored + char *out_urls = (char *)0x04; //Chosen by a fair dice roll + char *out = g_strdup(in); + char *msg = g_strconcat("url: ", in, NULL); + + markup_strip_a(&out, &out_urls); + + ASSERT_STR_EQm(msg, exp, out); + + if (urls) { + ASSERT_STR_EQm(msg, urls, out_urls); + } else { + ASSERT_EQm(msg, urls, out_urls); + } + + g_free(out_urls); + g_free(out); + g_free(msg); + + PASS(); +} + +TEST test_markup_strip_a(void) +{ + RUN_TESTp(helper_markup_strip_a, "valid link", "valid link", "[valid] https://url.com"); + RUN_TESTp(helper_markup_strip_a, "valid link", "valid link", "[valid] "); + RUN_TESTp(helper_markup_strip_a, "valid link", "valid link", NULL); + RUN_TESTp(helper_markup_strip_a, "valid link", "valid link", "[valid link] https://url.com"); + + RUN_TESTp(helper_markup_strip_a, " link", " link", NULL); + RUN_TESTp(helper_markup_strip_a, " link", " link", NULL); + + PASS(); +} + +TEST helper_markup_strip_img (const char *in, const char *exp, const char *urls) +{ + // out_urls is a return parameter and the content should be ignored + char *out_urls = (char *)0x04; //Chosen by a fair dice roll + char *out = g_strdup(in); + char *msg = g_strconcat("url: ", in, NULL); + + markup_strip_img(&out, &out_urls); + + ASSERT_STR_EQm(msg, exp, out); + + if (urls) { + ASSERT_STR_EQm(msg, urls, out_urls); + } else { + ASSERT_EQm(msg, urls, out_urls); + } + + g_free(out_urls); + g_free(out); + g_free(msg); + + PASS(); +} + +TEST test_markup_strip_img(void) +{ + RUN_TESTp(helper_markup_strip_img, "v img", "v [image] img", NULL); + RUN_TESTp(helper_markup_strip_img, "v \"valid\" img", "v valid img", NULL); + RUN_TESTp(helper_markup_strip_img, "v img", "v [image] img", "[image] url.com"); + + RUN_TESTp(helper_markup_strip_img, "v \"valid\" img", "v valid img", "[valid] url.com"); + RUN_TESTp(helper_markup_strip_img, "v \"valid\" img", "v valid img", "[valid] url.com"); + RUN_TESTp(helper_markup_strip_img, "v \"valid\" img", "v valid img", "[valid] url.com"); + + RUN_TESTp(helper_markup_strip_img, "i \"invalid img", "i [image] img", "[image] https://url.com"); + RUN_TESTp(helper_markup_strip_img, "i \"broken\" img", "i broken img", NULL); + RUN_TESTp(helper_markup_strip_img, "i \"invalid img", "i [image] img", NULL); + + RUN_TESTp(helper_markup_strip_img, "i \"broken\" img", "i broken img", NULL); + RUN_TESTp(helper_markup_strip_img, "i \"invalid img", "i [image] img", "[image] url.com"); + RUN_TESTp(helper_markup_strip_img, "i \"invalid img", "i [image] img", NULL); + + RUN_TESTp(helper_markup_strip_img, "i