/* * Functionality for a bar listing the windows currently managed. * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "sdorfehs.h" /* Possible values for bar_is_raised status. */ #define BAR_IS_HIDDEN 0 #define BAR_IS_WINDOW_LIST 1 #define BAR_IS_VSCREEN_LIST 2 #define BAR_IS_MESSAGE 3 #define BAR_IS_STICKY 4 #define BAR_IS_RAISED(s) (s->bar_is_raised != BAR_IS_HIDDEN && \ s->bar_is_raised != BAR_IS_STICKY) /* A copy of the last message displayed in the message bar. */ static char *last_msg = NULL; static int last_mark_start = 0; static int last_mark_end = 0; /* Buffers for sticky bar */ static struct sbuf *bar_buf = NULL; static struct sbuf *bar_line = NULL; struct list_head bar_chunks; static char *last_bar_line; static char *last_bar_window_fmt; static char bar_tmp_line[256]; static Pixmap bar_pm; struct bar_chunk { char *text; int length; char *color; char *font; char *clickcmd; int text_x; int text_width; struct list_head node; }; static void draw_partial_string(rp_screen *s, char *msg, int len, int x_offset, int y_offset, int style, char *color); static void marked_message_internal(char *msg, int mark_start, int mark_end, int bar_type); /* Reset the alarm to auto-hide the bar in BAR_TIMEOUT seconds. */ void bar_reset_alarm(void) { struct timeval timeout; struct itimerval alarmtimer; memset(&timeout, 0, sizeof(timeout)); memset(&alarmtimer, 0, sizeof(alarmtimer)); timeout.tv_sec = (time_t)(defaults.bar_timeout); alarmtimer.it_value = timeout; setitimer(ITIMER_REAL, &alarmtimer, NULL); alarm_signalled = 0; } static int bar_time_left(void) { struct itimerval left; getitimer(ITIMER_REAL, &left); return left.it_value.tv_sec > 0; } int bar_mkfifo(void) { char *config_dir; config_dir = get_config_dir(); rp_glob_screen.bar_fifo_path = xsprintf("%s/bar", config_dir); free(config_dir); unlink(rp_glob_screen.bar_fifo_path); if (mkfifo(rp_glob_screen.bar_fifo_path, S_IRUSR|S_IWUSR) == -1) { warn("failed creating bar FIFO at %s", rp_glob_screen.bar_fifo_path); return 0; } return bar_open_fifo(); } void init_bar(void) { rp_screen *primary = screen_primary(); bar_buf = sbuf_new(sizeof(bar_tmp_line)); bar_line = sbuf_new(sizeof(bar_tmp_line)); bar_pm = XCreatePixmap(dpy, primary->bar_window, primary->width, FONT_HEIGHT(rp_current_screen) * 2, DefaultDepth(dpy, DefaultScreen(dpy))); INIT_LIST_HEAD(&bar_chunks); } /* Hide the bar from sight. */ void hide_bar(rp_screen *s, int force) { if (!s->full_screen_win && defaults.bar_sticky && !force) { redraw_sticky_bar_text(0); if (s == screen_primary()) return; /* otherwise, we need to hide this secondary screen's bar */ } s->bar_is_raised = BAR_IS_HIDDEN; XUnmapWindow(dpy, s->bar_window); /* Possibly restore colormap. */ if (current_window()) { XUninstallColormap(dpy, s->def_cmap); XInstallColormap(dpy, current_window()->colormap); } } /* Show window listing in bar. */ void show_bar(rp_screen *s, char *fmt) { s->bar_is_raised = BAR_IS_WINDOW_LIST; XMapRaised(dpy, s->bar_window); update_window_names(s, fmt); /* Switch to the default colormap */ if (current_window()) XUninstallColormap(dpy, current_window()->colormap); XInstallColormap(dpy, s->def_cmap); raise_utility_windows(); bar_reset_alarm(); } /* Show vscreen listing in bar. */ void show_vscreen_bar(rp_screen *s) { s->bar_is_raised = BAR_IS_VSCREEN_LIST; XMapRaised(dpy, s->bar_window); update_vscreen_names(s); /* Switch to the default colormap */ if (current_window()) XUninstallColormap(dpy, current_window()->colormap); XInstallColormap(dpy, s->def_cmap); raise_utility_windows(); bar_reset_alarm(); } int bar_x(rp_screen *s, int width) { int x = 0; switch (defaults.bar_location) { case NorthWestGravity: case WestGravity: case SouthWestGravity: x = s->left + (defaults.bar_in_padding ? 0 : defaults.padding_left); break; case NorthGravity: case CenterGravity: case SouthGravity: x = s->left + (s->width - width - defaults.bar_border_width * 2) / 2 - (defaults.bar_in_padding ? 0 : defaults.padding_left); break; case NorthEastGravity: case EastGravity: case SouthEastGravity: x = s->left + s->width - width - (defaults.bar_border_width * 2) - (defaults.bar_in_padding ? 0 : defaults.padding_right); break; } return x; } int bar_y(rp_screen *s, int height) { int y = 0; switch (defaults.bar_location) { case NorthEastGravity: case NorthGravity: case NorthWestGravity: y = s->top + (defaults.bar_in_padding ? 0 : defaults.padding_top); break; case EastGravity: case CenterGravity: case WestGravity: y = s->top + (s->height - height - defaults.bar_border_width * 2) / 2 - (defaults.bar_in_padding ? 0 : defaults.padding_top); break; case SouthEastGravity: case SouthGravity: case SouthWestGravity: y = s->top + (s->height - height - defaults.bar_border_width * 2) - (defaults.bar_in_padding ? 0 : defaults.padding_top); break; } return y; } int sticky_bar_height(rp_screen *s) { if (defaults.bar_sticky) return FONT_HEIGHT(s) + (defaults.bar_border_width * 2) + (defaults.bar_y_padding * 2); return 0; } void update_bar(rp_screen *s) { switch (s->bar_is_raised) { case BAR_IS_WINDOW_LIST: update_window_names(s, defaults.window_fmt); break; case BAR_IS_VSCREEN_LIST: update_vscreen_names(s); break; case BAR_IS_STICKY: hide_bar(s, 0); break; } } /* * To avoid having to parse the bar line text and redraw text over and over * again, text is drawn on a pixmap in two lines. When the window format text * changes (such as when the current window changes) or the bar line text * changes, each line that is different is redrawn. Then the two lines are * copied into the actual bar window side by side. */ void redraw_sticky_bar_text(int force) { rp_screen *s = screen_primary(); struct list_head *iter, *tmp; struct bar_chunk *chunk; struct sbuf *tbuf, *curcmd, *curtxt; char *tline, *font, *color, *clickcmd; int diff = 0, len, cmd = 0, skip = 0, xftx = 0, x; int width, height; if (!defaults.bar_sticky || (!force && (s->full_screen_win || bar_time_left()))) return; /* * If we were showing a message or window list before, make sure we * clear it all. */ if (s->bar_is_raised != BAR_IS_STICKY) force = 1; width = s->width - (defaults.bar_border_width * 2); if (!defaults.bar_in_padding) width -= defaults.padding_right + defaults.padding_left; height = FONT_HEIGHT(s) + (defaults.bar_y_padding * 2); XMoveResizeWindow(dpy, s->bar_window, bar_x(s, width), bar_y(s, height), width, height); if (force) { XFreePixmap(dpy, bar_pm); XClearWindow(dpy, s->bar_window); bar_pm = XCreatePixmap(dpy, s->bar_window, width, FONT_HEIGHT(s) * 2, DefaultDepth(dpy, DefaultScreen(dpy))); } s->bar_is_raised = BAR_IS_STICKY; XMapRaised(dpy, s->bar_window); /* check window title for changes */ tbuf = sbuf_new(0); get_current_window_in_fmt(defaults.sticky_fmt, tbuf); diff = (last_bar_window_fmt == NULL || strcmp(last_bar_window_fmt, sbuf_get(tbuf)) != 0); if (diff || force) { PRINT_DEBUG(("redrawing window format in bar\n")); if (last_bar_window_fmt != NULL) free(last_bar_window_fmt); last_bar_window_fmt = xstrdup(sbuf_get(tbuf)); XFillRectangle(dpy, bar_pm, s->inverse_gc, 0, 0, s->width, FONT_HEIGHT(s)); rp_draw_string(s, bar_pm, STYLE_NORMAL, 0, FONT_ASCENT(s), last_bar_window_fmt, strlen(last_bar_window_fmt), NULL, NULL); } sbuf_free(tbuf); XCopyArea(dpy, bar_pm, s->bar_window, s->inverse_gc, 0, 0, (s->width / 2) - (defaults.bar_x_padding * 2), FONT_HEIGHT(s), defaults.bar_x_padding, defaults.bar_y_padding); /* Repeat for bar text line */ diff = (last_bar_line == NULL || strcmp(last_bar_line, sbuf_get(bar_line)) != 0); if (!diff && !force) goto redraw_bar_text; PRINT_DEBUG(("recalculating bar chunks\n")); if (last_bar_line) free(last_bar_line); last_bar_line = xstrdup(sbuf_get(bar_line)); len = strlen(last_bar_line); curcmd = sbuf_new(0); curtxt = sbuf_new(0); list_for_each_safe_entry(chunk, iter, tmp, &bar_chunks, node) { free(chunk->text); if (chunk->color) free(chunk->color); if (chunk->font) free(chunk->font); if (chunk->clickcmd) free(chunk->clickcmd); list_del(&chunk->node); free(chunk); } color = font = clickcmd = NULL; for (x = 0; x < len; x++) { if (last_bar_line[x] == '\\' && !skip) { skip = 1; continue; } if (cmd) { if (last_bar_line[x] == ')' && !skip) { tline = sbuf_get(curcmd); if (strncmp(tline, "fg(", 3) == 0) { /* ^fg(green)text^fg() */ if (color) free(color); color = NULL; if (strlen(tline) > 3) color = xstrdup(tline + 3); } else if (strncmp(tline, "fn(", 3) == 0) { /* ^fn(courier)text^fn() */ if (font) free(font); font = NULL; if (strlen(tline) > 3) font = xstrdup(tline + 3); } else if (strncmp(tline, "ca(", 3) == 0) { /* ^ca(1,some command)*^ca() */ if (clickcmd) free(clickcmd); clickcmd = NULL; if (strlen(tline) > 4) clickcmd = xstrdup(tline + 3); } else { PRINT_DEBUG(("unsupported bar command " "\"%s\", ignoring\n", tline)); } sbuf_clear(curcmd); cmd = 0; } else sbuf_nconcat(curcmd, last_bar_line + x, 1); } else if (last_bar_line[x] == '^' && !skip) { cmd = 1; chunk = xmalloc(sizeof(struct bar_chunk)); memset(chunk, 0, sizeof(struct bar_chunk)); chunk->text = xstrdup(sbuf_get(curtxt)); chunk->length = strlen(chunk->text); chunk->color = color ? xstrdup(color) : NULL; chunk->font = font ? xstrdup(font) : NULL; chunk->clickcmd = clickcmd ? xstrdup(clickcmd) : NULL; list_add_tail(&chunk->node, &bar_chunks); sbuf_clear(curtxt); } else { sbuf_nconcat(curtxt, last_bar_line + x, 1); } skip = 0; } tline = sbuf_get(curtxt); if (strlen(tline)) { chunk = xmalloc(sizeof(struct bar_chunk)); memset(chunk, 0, sizeof(struct bar_chunk)); chunk->text = xstrdup(sbuf_get(curtxt)); chunk->length = strlen(chunk->text); chunk->color = color ? xstrdup(color) : NULL; chunk->font = font ? xstrdup(font) : NULL; chunk->clickcmd = clickcmd ? xstrdup(clickcmd) : NULL; list_add_tail(&chunk->node, &bar_chunks); } sbuf_free(curcmd); sbuf_free(curtxt); if (color) free(color); if (font) free(font); if (clickcmd) free(clickcmd); redraw_bar_text: XFillRectangle(dpy, bar_pm, s->inverse_gc, 0, FONT_HEIGHT(s), s->width, FONT_HEIGHT(s)); xftx = 0; list_for_each_entry(chunk, &bar_chunks, node) { rp_draw_string(s, bar_pm, STYLE_NORMAL, (width / 2) + xftx, FONT_HEIGHT(s) + FONT_ASCENT(s), chunk->text, chunk->length, chunk->font ? chunk->font : NULL, chunk->color ? chunk->color : NULL); xftx += (chunk->text_width = rp_text_width(s, chunk->text, chunk->length, chunk->font)); } /* update each chunk's text_x relative to its final location */ x = 0; list_for_each_entry(chunk, &bar_chunks, node) { chunk->text_x = width - xftx - defaults.bar_x_padding + x; x += chunk->text_width; } /* we can only copy up to half of the bar width */ if (xftx > (width / 2) - (defaults.bar_x_padding * 2)) xftx = (width / 2) - (defaults.bar_x_padding * 2); XCopyArea(dpy, bar_pm, s->bar_window, s->inverse_gc, xftx + (defaults.bar_x_padding * 2), FONT_HEIGHT(s), (width / 2) - (defaults.bar_x_padding * 2), FONT_HEIGHT(s), (width / 2) + defaults.bar_x_padding, defaults.bar_y_padding); /* Our XMapRaise may have covered one */ raise_utility_windows(); } void bar_handle_click(rp_screen *s, XButtonEvent *e) { struct bar_chunk *chunk; char *cmd, *actcmd; int btn, len; PRINT_DEBUG(("bar click at %d,%d button %d\n", e->x, e->y, e->button)); list_for_each_entry(chunk, &bar_chunks, node) { PRINT_DEBUG(("chunk: text_x:%d text_width:%d text:%s\n", chunk->text_x, chunk->text_width, chunk->text)); if (!chunk->clickcmd) continue; if (e->x < chunk->text_x || e->x > (chunk->text_x + chunk->text_width)) continue; /* 1,somecmd,2,someothercmd */ cmd = chunk->clickcmd; PRINT_DEBUG(("chunk: parsing btns/cmds:%s\n", cmd)); while (cmd != NULL && cmd[0] != '\0') { len = strlen(cmd); actcmd = xmalloc(len); memset(actcmd, 0, len); if (sscanf(cmd, "%d,%[^,]%n", &btn, actcmd, &len) != 2) { PRINT_DEBUG(("chunk: invalid format\n")); free(actcmd); break; } PRINT_DEBUG(("chunk: btn:%d cmd:%s\n", btn, actcmd)); if (e->button == btn) { PRINT_DEBUG(("executing bar click action %s\n", actcmd)); spawn(actcmd, current_frame(rp_current_vscreen)); free(actcmd); break; } cmd += len; free(actcmd); if (cmd[0] != ',') break; cmd++; } } } /* * Note that we use marked_message_internal to avoid resetting the alarm. */ void update_window_names(rp_screen *s, char *fmt) { struct sbuf *bar_buffer; int mark_start = 0; int mark_end = 0; char *delimiter; bar_buffer = sbuf_new(0); if (s->bar_is_raised == BAR_IS_STICKY) { redraw_sticky_bar_text(0); } else if (s->bar_is_raised == BAR_IS_WINDOW_LIST) { delimiter = (defaults.window_list_style == STYLE_ROW) ? " " : "\n"; get_window_list(fmt, delimiter, bar_buffer, &mark_start, &mark_end); marked_message(sbuf_get(bar_buffer), mark_start, mark_end, BAR_IS_WINDOW_LIST); } sbuf_free(bar_buffer); } /* * Note that we use marked_message_internal to avoid resetting the alarm. */ void update_vscreen_names(rp_screen *s) { struct sbuf *bar_buffer; int mark_start = 0; int mark_end = 0; char *delimiter; if (s->bar_is_raised != BAR_IS_VSCREEN_LIST) return; delimiter = (defaults.window_list_style == STYLE_ROW) ? " " : "\n"; bar_buffer = sbuf_new(0); get_vscreen_list(s, delimiter, bar_buffer, &mark_start, &mark_end); marked_message_internal(sbuf_get(bar_buffer), mark_start, mark_end, BAR_IS_VSCREEN_LIST); sbuf_free(bar_buffer); } void message(char *s) { marked_message(s, 0, 0, BAR_IS_MESSAGE); } void marked_message_printf(int mark_start, int mark_end, char *fmt,...) { char *buffer; va_list ap; va_start(ap, fmt); buffer = xvsprintf(fmt, ap); va_end(ap); marked_message(buffer, mark_start, mark_end, BAR_IS_MESSAGE); free(buffer); } static int count_lines(char *msg, int len) { int ret = 1; int i; if (len < 1) return 1; for (i = 0; i < len; i++) { if (msg[i] == '\n') ret++; } return ret; } static int max_line_length(char *msg) { rp_screen *s = screen_primary(); size_t i; size_t start; int ret = 0; /* Count each line and keep the length of the longest one. */ for (start = 0, i = 0; i <= strlen(msg); i++) { if (msg[i] == '\n' || msg[i] == '\0') { int current_width; /* Check if this line is the longest so far. */ current_width = rp_text_width(s, msg + start, i - start, NULL); if (current_width > ret) { ret = current_width; } /* Update the start of the new line. */ start = i + 1; } } return ret; } static int pos_in_line(char *msg, int pos) { int ret; int i; if (pos <= 0) return 0; /* * Go backwards until we hit the beginning of the string or a new line. */ ret = 0; for (i = pos - 1; i >= 0; ret++, i--) { if (msg[i] == '\n') break; } return ret; } static int line_beginning(char *msg, int pos) { int ret = 0; int i; if (pos <= 0) return 0; /* * Go backwards until we hit a new line or the beginning of the string. */ for (i = pos - 1; i >= 0; --i) { if (msg[i] == '\n') { ret = i + 1; break; } } return ret; } static void draw_partial_string(rp_screen *s, char *msg, int len, int x_offset, int y_offset, int style, char *color) { rp_draw_string(s, s->bar_window, style, defaults.bar_x_padding + x_offset, defaults.bar_y_padding + FONT_ASCENT(s) + y_offset * FONT_HEIGHT(s), msg, len + 1, NULL, color); } #define REASON_NONE 0x00 #define REASON_STYLE 0x01 #define REASON_NEWLINE 0x02 static void draw_string(rp_screen *s, char *msg, int mark_start, int mark_end) { int i, start; int x_offset, y_offset; /* Base coordinates where to print. */ int print_reason = REASON_NONE; /* Should we print something? */ int style = STYLE_NORMAL, next_style = STYLE_NORMAL; int msg_len, part_len; start = 0; x_offset = y_offset = 0; msg_len = strlen(msg); /* Walk through the string, print each part. */ for (i = 0; i < msg_len; ++i) { /* Should we ignore style hints? */ if (mark_start != mark_end) { if (i == mark_start) { next_style = STYLE_INVERSE; if (i > start) print_reason |= REASON_STYLE; } else if (i == mark_end) { next_style = STYLE_NORMAL; if (i > start) print_reason |= REASON_STYLE; } } if (msg[i] == '\n') print_reason |= REASON_NEWLINE; if (print_reason != REASON_NONE) { /* Strip the trailing newline if necessary. */ part_len = i - start - ((print_reason & REASON_NEWLINE) ? 1 : 0); draw_partial_string(s, msg + start, part_len, x_offset, y_offset, style, NULL); /* Adjust coordinates. */ if (print_reason & REASON_NEWLINE) { x_offset = 0; y_offset++; /* Skip newline. */ start = i + 1; } else { x_offset += rp_text_width(s, msg + start, part_len, NULL); start = i; } print_reason = REASON_NONE; } style = next_style; } part_len = i - start - 1; /* Print the last line. */ draw_partial_string(s, msg + start, part_len, x_offset, y_offset, style, NULL); XSync(dpy, False); } #undef REASON_NONE #undef REASON_STYLE #undef REASON_NEWLINE /* * Move the marks if they are outside the string or if the start is after the * end. */ static void correct_mark(int msg_len, int *mark_start, int *mark_end) { /* Make sure the marks are inside the string. */ if (*mark_start < 0) *mark_start = 0; if (*mark_end < 0) *mark_end = 0; if (*mark_start > msg_len) *mark_start = msg_len; if (*mark_end > msg_len) *mark_end = msg_len; /* Make sure the marks aren't reversed. */ if (*mark_start > *mark_end) { int tmp; tmp = *mark_start; *mark_start = *mark_end; *mark_end = tmp; } } /* Raise the bar and put it in the right spot */ static void prepare_bar(rp_screen *s, int width, int height, int bar_type) { if (defaults.bar_sticky) width = s->width - (defaults.bar_border_width * 2); else width = width < s->width ? width : s->width; if (!defaults.bar_in_padding) width -= defaults.padding_right + defaults.padding_left; height = height < s->height ? height : s->height; XMoveResizeWindow(dpy, s->bar_window, bar_x(s, width), bar_y(s, height), width, height); /* Map the bar if needed */ if (!BAR_IS_RAISED(s)) { s->bar_is_raised = bar_type; XMapRaised(dpy, s->bar_window); /* Switch to the default colormap */ if (current_window()) XUninstallColormap(dpy, current_window()->colormap); XInstallColormap(dpy, s->def_cmap); } XRaiseWindow(dpy, s->bar_window); XClearWindow(dpy, s->bar_window); raise_utility_windows(); XSync(dpy, False); } static void get_mark_box(char *msg, size_t mark_start, size_t mark_end, int *x, int *y, int *width, int *height) { rp_screen *s = rp_current_screen; int start, end; int mark_end_is_new_line = 0; int start_line; int end_line; int start_pos_in_line; int end_pos_in_line; int start_line_beginning; int end_line_beginning; /* * If the mark_end is on a new line or the end of the string, then back * it up one character. */ if (msg[mark_end - 1] == '\n' || mark_end == strlen(msg)) { mark_end--; mark_end_is_new_line = 1; } start_line = count_lines(msg, mark_start); end_line = count_lines(msg, mark_end); start_pos_in_line = pos_in_line(msg, mark_start); end_pos_in_line = pos_in_line(msg, mark_end); start_line_beginning = line_beginning(msg, mark_start); end_line_beginning = line_beginning(msg, mark_end); PRINT_DEBUG(("start_line = %d, end_line = %d\n", start_line, end_line)); PRINT_DEBUG(("start_line_beginning = %d, end_line_beginning = %d\n", start_line_beginning, end_line_beginning)); if (mark_start == 0 || start_pos_in_line == 0) start = 0; else start = rp_text_width(s, &msg[start_line_beginning], start_pos_in_line, NULL) + defaults.bar_x_padding; end = rp_text_width(s, &msg[end_line_beginning], end_pos_in_line, NULL) + defaults.bar_x_padding * 2; if (mark_end != strlen(msg)) end -= defaults.bar_x_padding; /* * A little hack to highlight to the end of the line, if the mark_end * is at the end of a line. */ if (mark_end_is_new_line) { *width = max_line_length(msg) + defaults.bar_x_padding * 2; } else { *width = end - start; } *x = start; *y = (start_line - 1) * FONT_HEIGHT(s) + defaults.bar_y_padding; *height = (end_line - start_line + 1) * FONT_HEIGHT(s); } static void draw_box(rp_screen *s, int x, int y, int width, int height) { XGCValues lgv; GC lgc; unsigned long mask; lgv.foreground = rp_glob_screen.fgcolor; mask = GCForeground; lgc = XCreateGC(dpy, s->root, mask, &lgv); XFillRectangle(dpy, s->bar_window, lgc, x, y, width, height); XFreeGC(dpy, lgc); } static void draw_mark(rp_screen *s, char *msg, int mark_start, int mark_end) { int x, y, width, height; /* when this happens, there is no mark. */ if (mark_end == 0 || mark_start == mark_end) return; get_mark_box(msg, mark_start, mark_end, &x, &y, &width, &height); draw_box(s, x, y, width, height); } static void update_last_message(char *msg, int mark_start, int mark_end) { free(last_msg); last_msg = xstrdup(msg); last_mark_start = mark_start; last_mark_end = mark_end; } void marked_message(char *msg, int mark_start, int mark_end, int bar_type) { /* Schedule the bar to be hidden after some amount of time. */ bar_reset_alarm(); marked_message_internal(msg, mark_start, mark_end, bar_type); } static void marked_message_internal(char *msg, int mark_start, int mark_end, int bar_type) { rp_screen *s; int num_lines; int width; int height; if (bar_type == BAR_IS_STICKY) s = screen_primary(); else s = rp_current_screen; PRINT_DEBUG(("msg = %s\n", msg ? msg : "NULL")); PRINT_DEBUG(("mark_start = %d, mark_end = %d\n", mark_start, mark_end)); /* Calculate the width and height of the window. */ num_lines = count_lines(msg, strlen(msg)); width = defaults.bar_x_padding * 2 + max_line_length(msg); if (!defaults.bar_in_padding) width -= defaults.padding_right + defaults.padding_left; height = FONT_HEIGHT(s) * num_lines + defaults.bar_y_padding * 2; prepare_bar(s, width, height, bar_type); /* Draw the mark over the designated part of the string. */ correct_mark(strlen(msg), &mark_start, &mark_end); draw_mark(s, msg, mark_start, mark_end); draw_string(s, msg, mark_start, mark_end); /* Keep a record of the message. */ update_last_message(msg, mark_start, mark_end); if (bar_type != BAR_IS_STICKY && bar_time_left()) bar_reset_alarm(); } /* * Use this just to update the bar. show_last_message will draw it and leave it * up for a period of time. */ void redraw_last_message(void) { char *msg; if (last_msg == NULL) return; /* * A little kludge to avoid last_msg in marked_message from being * strdup'd right after freeing the pointer. Note: in this case * marked_message's msg arg would have been the same as last_msg. */ msg = xstrdup(last_msg); marked_message_internal(msg, last_mark_start, last_mark_end, BAR_IS_MESSAGE); free(msg); } void show_last_message(void) { redraw_last_message(); bar_reset_alarm(); } /* Free any memory associated with the bar. */ void free_bar(void) { free(last_msg); last_msg = NULL; } int bar_open_fifo(void) { rp_glob_screen.bar_fifo_fd = open(rp_glob_screen.bar_fifo_path, O_RDONLY|O_NONBLOCK|O_CLOEXEC); if (rp_glob_screen.bar_fifo_fd == -1) { warn("failed opening newly-created bar FIFO at %s", rp_glob_screen.bar_fifo_path); rp_glob_screen.bar_fifo_fd = -1; return -1; } return 0; } void bar_read_fifo(void) { ssize_t ret; int x, start; PRINT_DEBUG(("bar FIFO data to read\n")); for (;;) { memset(bar_tmp_line, 0, sizeof(bar_tmp_line)); ret = read(rp_glob_screen.bar_fifo_fd, &bar_tmp_line, sizeof(bar_tmp_line)); if (ret < 1) { if (ret == 0) PRINT_DEBUG(("FIFO %d closed, re-opening\n", rp_glob_screen.bar_fifo_fd)); else if (ret == -1 && errno != EAGAIN) PRINT_DEBUG(("error reading bar FIFO: %s\n", strerror(errno))); close(rp_glob_screen.bar_fifo_fd); bar_open_fifo(); break; } for (x = 0, start = 0; x < ret; x++) { if (bar_tmp_line[x] == '\0') { sbuf_nconcat(bar_buf, bar_tmp_line + start, x - start); start = x + 1; break; } else if (bar_tmp_line[x] == '\n') { sbuf_nconcat(bar_buf, bar_tmp_line + start, x - start); sbuf_copy(bar_line, sbuf_get(bar_buf)); redraw_sticky_bar_text(0); sbuf_clear(bar_buf); start = x + 1; } } if (x == ret) sbuf_nconcat(bar_buf, bar_tmp_line + start, x - start); } }