diff --git a/dunst.c b/dunst.c index abd63e4..27d2bd2 100644 --- a/dunst.c +++ b/dunst.c @@ -72,6 +72,8 @@ static bool print_notifications = false; static dimension_t window_dim; static bool pause_display = false; +static r_line_cache line_cache; + bool dunst_grab_errored = false; int next_notification_id = 1; @@ -103,6 +105,10 @@ void hide_win(void); void move_all_to_history(void); void print_version(void); +void r_line_cache_init(r_line_cache *c); +void r_line_cache_append(r_line_cache *c, const char *s, ColorSet *col); +void r_line_cache_reset(r_line_cache *c); + /* show warning notification */ void warn(const char *text, int urg); @@ -383,6 +389,40 @@ void update_lists() } } +void r_line_cache_init(r_line_cache *c) +{ + c->count = 0; + c->size = 0; + c->lines = NULL; +} + +void r_line_cache_append(r_line_cache *c, const char *s, ColorSet *col) +{ + if (!c || !s) + return; + + /* resize cache if it's too small */ + if (c->count >= c->size) { + c->size++; + c->lines = realloc(c->lines, c->size * sizeof(r_line)); + } + + c->count++; + c->lines[c->count-1].colors = col; + c->lines[c->count-1].str = strdup(s); +} + +void r_line_cache_reset(r_line_cache *c) +{ + for (int i = 0; i < c->count; i++) { + if (c->lines[i].str) + free(c->lines[i].str); + c->lines[i].str = NULL; + c->lines[i].colors = NULL; + } + c->count = 0; +} + /* TODO get draw_txt_buf as argument */ int do_word_wrap(char *source, int max_width) { @@ -430,16 +470,13 @@ int do_word_wrap(char *source, int max_width) } } -void update_draw_txt_buf(notification * n, int max_width) +void add_notification_to_line_cache(notification *n, int max_width) { rstrip(n->msg); char *msg = n->msg; while (isspace(*msg)) msg++; - if (n->draw_txt_buf.txt) - free(n->draw_txt_buf.txt); - char *buf; /* print dup_count */ @@ -481,22 +518,18 @@ void update_draw_txt_buf(notification * n, int max_width) buf = new_buf; } - n->draw_txt_buf.line_count = do_word_wrap(buf, max_width); - n->draw_txt_buf.txt = buf; -} -char *draw_txt_get_line(draw_txt * dt, int line) -{ - if (line > dt->line_count) { - return NULL; + int linecnt = do_word_wrap(buf, max_width); + n->line_count = linecnt; + char *cur = buf; + for (int i = 0; i < linecnt; i++) { + r_line_cache_append(&line_cache, cur, n->colors); + + while (*cur != '\0') + cur++; + cur++; } - - char *begin = dt->txt; - for (int i = 1; i < line; i++) { - begin += strlen(begin) + 1; - } - - return begin; + free(buf); } int calculate_x_offset(int line_width, int text_width) @@ -570,148 +603,114 @@ unsigned long calculate_foreground_color(unsigned long source_color) return color.pixel; } -void draw_win(void) +int calculate_width(void) { - int width, x, y, height; - - line_height = MAX(line_height, font_h); - - update_screen_info(); - - /* calculate width */ if (geometry.mask & WidthValue && geometry.w == 0) { /* dynamic width */ - width = 0; + return 0; } else if (geometry.mask & WidthValue) { /* fixed width */ if (geometry.negative_width) { - width = scr.dim.w - geometry.w; + return scr.dim.w - geometry.w; } else { - width = geometry.w; + return geometry.w; } } else { /* across the screen */ - width = scr.dim.w; + return scr.dim.w; } +} - /* update draw_txt_bufs and line_cnt */ - int line_cnt = 0; + +void draw_win(void) +{ + + r_line_cache_reset(&line_cache); + update_screen_info(); + int width = calculate_width(); + + + line_height = MAX(line_height, font_h); + + + /* create cache with all lines */ for (l_node * iter = displayed_notifications->head; iter; iter = iter->next) { notification *n = (notification *) iter->data; - update_draw_txt_buf(n, width); - line_cnt += n->draw_txt_buf.line_count; + add_notification_to_line_cache(n, width); } - /* calculate height */ - if (geometry.h == 0) { - height = line_cnt * line_height; - } else { - height = MAX(geometry.h, (line_cnt * line_height)); - } - - height += (l_length(displayed_notifications) - 1) * separator_height; - - /* add "(x more)" */ - draw_txt x_more; - x_more.txt = NULL; - - char *print_to; - int more = l_length(notification_queue); - if (indicate_hidden && more > 0) { - - int x_more_len = strlen(" ( more) ") + digit_count(more); + assert(line_cache.count > 0); + /* add (x more) */ + int queue_cnt = l_length(notification_queue); + if (indicate_hidden && queue_cnt > 0) { if (geometry.h != 1) { - /* add additional line */ - x_more.txt = calloc(x_more_len, sizeof(char)); - height += line_height; - line_cnt++; - - print_to = x_more.txt; - + char *tmp; + asprintf(&tmp, "(%d more)", queue_cnt); + ColorSet *last_colors = + line_cache.lines[line_cache.count-1].colors; + r_line_cache_append(&line_cache, strdup(tmp), last_colors); + free(tmp); } else { - /* append "(x more)" message to notification text */ - notification *n = - (notification *) displayed_notifications->head-> - data; - print_to = - draw_txt_get_line(&n->draw_txt_buf, - n->draw_txt_buf.line_count); - for (; *print_to != '\0'; print_to++) ; + char *old = line_cache.lines[0].str; + char *new; + asprintf(&new, "%s (%d more)", old, queue_cnt); + free(old); + line_cache.lines[0].str = new; } - snprintf(print_to, x_more_len, "(%d more)", more); } + /* if we have a dynamic width, calculate the actual width */ if (width == 0) { - for (l_node * iter = displayed_notifications->head; iter; - iter = iter->next) { - notification *n = (notification *) iter->data; - for (int i = 0; i < n->draw_txt_buf.line_count; i++) { - char *line = - draw_txt_get_line(&n->draw_txt_buf, i + 1); - assert(line != NULL); - width = MAX(width, textw(dc, line)); - } + for (int i = 0; i < line_cache.count; i++) { + char *line = line_cache.lines[i].str; + width = MAX(width, textw(dc, line)); } } - assert(line_height > 0); - assert(font_h > 0); - assert(width > 0); - assert(height > 0); - assert(line_cnt > 0); + /* resize dc to correct width */ + + int height = (line_cache.count * line_height) + + ((line_cache.count - 1) * separator_height); + resizedc(dc, width, height); - /* draw buffers */ dc->y = 0; - ColorSet *last_color; - assert(displayed_notifications->head != NULL); - for (l_node * iter = displayed_notifications->head; iter; - iter = iter->next) { - notification *n = (notification *) iter->data; - last_color = n->colors; + for (int i = 0; i < line_cache.count; i++) { + dc->x = 0; - for (int i = 0; i < n->draw_txt_buf.line_count; i++) { + r_line line = line_cache.lines[i]; - char *line = draw_txt_get_line(&n->draw_txt_buf, i + 1); - dc->x = 0; - drawrect(dc, 0, 0, width, line_height, true, - n->colors->BG); - dc->x = calculate_x_offset(width, textw(dc, line)); - dc->y += (line_height - font_h) / 2; - drawtextn(dc, line, strlen(line), n->colors); - dc->y += line_height - ((line_height - font_h) / 2); - } + /* draw background */ + drawrect(dc, 0, 0, width, line_height, true, line.colors->BG); + + /* draw text */ + dc->x = calculate_x_offset(width, textw(dc, line.str)); + dc->y += (line_height - font_h) / 2; + drawtextn(dc, line.str, strlen(line.str), line.colors); + dc->y += line_height - ((line_height - font_h) / 2); /* draw separator */ - if (separator_height > 0) { + if (separator_height > 0 && i < line_cache.count - 1) { dc->x = 0; double color; if (sep_color == AUTO) - color = - calculate_foreground_color(n->colors->BG); + color = calculate_foreground_color(line.colors->BG); else - color = n->colors->FG; - - drawrect(dc, 0, 0, width, separator_height, true, - color); + color = line.colors->FG; + drawrect(dc, 0, 0, width, separator_height, true, color); dc->y += separator_height; } + } - /* draw x_more */ - if (x_more.txt) { - dc->x = 0; - drawrect(dc, 0, 0, width, line_height, true, last_color->BG); - dc->x = calculate_x_offset(width, textw(dc, x_more.txt)); - drawtext(dc, x_more.txt, last_color); - } + int x,y; /* calculate window position */ if (geometry.mask & XNegative) { x = (scr.dim.x + (scr.dim.w - width)) + geometry.x; @@ -739,8 +738,6 @@ void draw_win(void) } mapdc(dc, win, width, height); - - free(x_more.txt); } char @@ -808,7 +805,7 @@ void handle_mouse_click(XEvent ev) assert(iter); for (; iter; iter = iter->next) { n = (notification *) iter->data; - int height = font_h * n->draw_txt_buf.line_count; + int height = font_h * n->line_count; if (ev.xbutton.y > y && ev.xbutton.y < y + height) break; else @@ -958,7 +955,6 @@ int init_notification(notification * n, int id) n->msg = fix_markup(n->msg); n->dup_count = 0; - n->draw_txt_buf.txt = NULL; /* check if n is a duplicate */ for (l_node * iter = notification_queue->head; iter; iter = iter->next) { @@ -1702,6 +1698,7 @@ int main(int argc, char *argv[]) notification_history = l_init(); displayed_notifications = l_init(); rules = l_init(); + r_line_cache_init(&line_cache); for (int i = 0; i < LENGTH(default_rules); i++) { l_push(rules, &default_rules[i]); diff --git a/dunst.h b/dunst.h index e56e8d4..471987c 100644 --- a/dunst.h +++ b/dunst.h @@ -32,18 +32,12 @@ typedef struct _screen_info { dimension_t dim; } screen_info; -typedef struct _draw_txt { - char *txt; - int line_count; -} draw_txt; - typedef struct _notification { char *appname; char *summary; char *body; char *icon; char *msg; - draw_txt draw_txt_buf; const char *format; char *dbus_client; time_t start; @@ -56,6 +50,7 @@ typedef struct _notification { ColorSet *colors; char *color_strings[2]; int progress; /* percentage + 1, 0 to hide */ + int line_count; } notification; typedef struct _notification_buffer { @@ -88,6 +83,19 @@ typedef struct _keyboard_shortcut { bool is_valid; } keyboard_shortcut; +typedef struct _r_line { + ColorSet *colors; + char *str; +} r_line; + +typedef struct r_line_cache { + int count; + int size; + r_line *lines; +} r_line_cache; + + + extern int verbosity; /* return id of notification */