aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--format-draw.c871
-rw-r--r--format.c6
-rw-r--r--mode-tree.c13
-rw-r--r--options-table.c52
-rw-r--r--screen-redraw.c28
-rw-r--r--screen-write.c107
-rw-r--r--server-client.c33
-rw-r--r--status.c425
-rw-r--r--style.c111
-rw-r--r--tmux.172
-rw-r--r--tmux.h85
-rw-r--r--utf8.c60
-rw-r--r--window-client.c26
-rw-r--r--window.c1
15 files changed, 1334 insertions, 557 deletions
diff --git a/Makefile b/Makefile
index 8947cd53..99417ba2 100644
--- a/Makefile
+++ b/Makefile
@@ -75,6 +75,7 @@ SRCS= alerts.c \
control.c \
environ.c \
format.c \
+ format-draw.c \
grid-view.c \
grid.c \
hooks.c \
diff --git a/format-draw.c b/format-draw.c
new file mode 100644
index 00000000..52292c98
--- /dev/null
+++ b/format-draw.c
@@ -0,0 +1,871 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+/* Format range. */
+struct format_range {
+ u_int index;
+ struct screen *s;
+
+ u_int start;
+ u_int end;
+
+ enum style_range_type type;
+ u_int argument;
+
+ TAILQ_ENTRY(format_range) entry;
+};
+TAILQ_HEAD(format_ranges, format_range);
+
+/* Does this range match this style? */
+static int
+format_is_type(struct format_range *fr, struct style *sy)
+{
+ if (fr->type != sy->range_type)
+ return (0);
+ if (fr->type == STYLE_RANGE_WINDOW &&
+ fr->argument != sy->range_argument)
+ return (0);
+ return (1);
+}
+
+/* Free a range. */
+static void
+format_free_range(struct format_ranges *frs, struct format_range *fr)
+{
+ TAILQ_REMOVE(frs, fr, entry);
+ free(fr);
+}
+
+/* Fix range positions. */
+static void
+format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset,
+ u_int start, u_int width)
+{
+ struct format_range *fr, *fr1;
+
+ if (frs == NULL)
+ return;
+
+ TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) {
+ if (fr->s != s)
+ continue;
+
+ if (fr->end <= start || fr->start >= start + width) {
+ format_free_range(frs, fr);
+ continue;
+ }
+
+ if (fr->start < start)
+ fr->start = start;
+ if (fr->end > start + width)
+ fr->end = start + width;
+ if (fr->start == fr->end) {
+ format_free_range(frs, fr);
+ continue;
+ }
+
+ fr->start += offset;
+ fr->end += offset;
+ }
+}
+
+/* Draw a part of the format. */
+static void
+format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy,
+ struct screen *s, struct format_ranges *frs, u_int offset, u_int start,
+ u_int width)
+{
+ /*
+ * The offset is how far from the cursor on the target screen; start
+ * and width how much to copy from the source screen.
+ */
+ screen_write_cursormove(octx, ocx + offset, ocy, 0);
+ screen_write_fast_copy(octx, s, start, 0, width, 1);
+ format_update_ranges(frs, s, offset, start, width);
+}
+
+/* Draw list part of format. */
+static void
+format_draw_put_list(struct screen_write_ctx *octx,
+ u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list,
+ struct screen *list_left, struct screen *list_right, int focus_start,
+ int focus_end, struct format_ranges *frs)
+{
+ u_int start, focus_centre;
+
+ /* If there is enough space for the list, draw it entirely. */
+ if (width >= list->cx) {
+ format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width);
+ return;
+ }
+
+ /* The list needs to be trimmed. Try to keep the focus visible. */
+ focus_centre = focus_start + (focus_end - focus_start) / 2;
+ if (focus_centre < width / 2)
+ start = 0;
+ else
+ start = focus_centre - width / 2;
+ if (start + width > list->cx)
+ start = list->cx - width;
+
+ /* Draw <> markers at either side if needed. */
+ if (start != 0 && width > list_left->cx) {
+ screen_write_cursormove(octx, ocx + offset, ocy, 0);
+ screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1);
+ offset += list_left->cx;
+ start += list_left->cx;
+ width -= list_left->cx;
+ }
+ if (start + width < list->cx && width > list_right->cx) {
+ screen_write_cursormove(octx, ocx + offset + width - 1, ocy, 0);
+ screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
+ 1);
+ width -= list_right->cx;
+ }
+
+ /* Draw the list screen itself. */
+ format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
+}
+
+/* Draw format with no list. */
+static void
+format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
+ u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
+ struct format_ranges *frs)
+{
+ u_int width_left, width_centre, width_right;
+
+ width_left = left->cx;
+ width_centre = centre->cx;
+ width_right = right->cx;
+
+ /*
+ * Try to keep as much of the left and right as possible at the expense
+ * of the centre.
+ */
+ while (width_left + width_centre + width_right > available) {
+ if (width_centre > 0)
+ width_centre--;
+ else if (width_right > 0)
+ width_right--;
+ else
+ width_left--;
+ }
+
+ /* Write left. */
+ format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
+
+ /* Write right at available - width_right. */
+ format_draw_put(octx, ocx, ocy, right, frs,
+ available - width_right,
+ right->cx - width_right,
+ width_right);
+
+ /*
+ * Write centre halfway between
+ * width_left
+ * and
+ * available - width_right.
+ */
+ format_draw_put(octx, ocx, ocy, centre, frs,
+ width_left
+ + ((available - width_right) - width_left) / 2
+ - width_centre / 2,
+ centre->cx / 2 - width_centre / 2,
+ width_centre);
+}
+
+/* Draw format with list on the left. */
+static void
+format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
+ u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
+ struct screen *list, struct screen *list_left, struct screen *list_right,
+ struct screen *after, int focus_start, int focus_end,
+ struct format_ranges *frs)
+{
+ u_int width_left, width_centre, width_right;
+ u_int width_list, width_after;
+ struct screen_write_ctx ctx;
+
+ width_left = left->cx;
+ width_centre = centre->cx;
+ width_right = right->cx;
+ width_list = list->cx;
+ width_after = after->cx;
+
+ /*
+ * Trim first the centre, then the list, then the right, then after the
+ * list, then the left.
+ */
+ while (width_left +
+ width_centre +
+ width_right +
+ width_list +
+ width_after > available) {
+ if (width_centre > 0)
+ width_centre--;
+ else if (width_list > 0)
+ width_list--;
+ else if (width_right > 0)
+ width_right--;
+ else if (width_after > 0)
+ width_after--;
+ else
+ width_left--;
+ }
+
+ /* If there is no list left, pass off to the no list function. */
+ if (width_list == 0) {
+ screen_write_start(&ctx, NULL, left);
+ screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
+ screen_write_stop(&ctx);
+
+ format_draw_none(octx, available, ocx, ocy, left, centre,
+ right, frs);
+ return;
+ }
+
+ /* Write left at 0. */
+ format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
+
+ /* Write right at available - width_right. */
+ format_draw_put(octx, ocx, ocy, right, frs,
+ available - width_right,
+ right->cx - width_right,
+ width_right);
+
+ /* Write after at width_left + width_list. */
+ format_draw_put(octx, ocx, ocy, after, frs,
+ width_left + width_list,
+ 0,
+ width_after);
+
+ /*
+ * Write centre halfway between
+ * width_left + width_list + width_after
+ * and
+ * available - width_right.
+ */
+ format_draw_put(octx, ocx, ocy, centre, frs,
+ (width_left + width_list + width_after)
+ + ((available - width_right)
+ - (width_left + width_list + width_after)) / 2
+ - width_centre / 2,
+ centre->cx / 2 - width_centre / 2,
+ width_centre);
+
+ /*
+ * The list now goes from
+ * width_left
+ * to
+ * width_left + width_list.
+ * If there is no focus given, keep the left in focus.
+ */
+ if (focus_start == -1 || focus_end == -1)
+ focus_start = focus_end = 0;
+ format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
+ list_left, list_right, focus_start, focus_end, frs);
+}
+
+/* Draw format with list in the centre. */
+static void
+format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
+ u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
+ struct screen *list, struct screen *list_left, struct screen *list_right,
+ struct screen *after, int focus_start, int focus_end,
+ struct format_ranges *frs)
+{
+ u_int width_left, width_centre, width_right;
+ u_int width_list, width_after, middle;
+ struct screen_write_ctx ctx;
+
+ width_left = left->cx;
+ width_centre = centre->cx;
+ width_right = right->cx;
+ width_list = list->cx;
+ width_after = after->cx;
+
+ /*
+ * Trim first the list, then after the list, then the centre, then the
+ * right, then the left.
+ */
+ while (width_left +
+ width_centre +
+ width_right +
+ width_list +
+ width_after > available) {
+ if (width_list > 0)
+ width_list--;
+ else if (width_after > 0)
+ width_after--;
+ else if (width_centre > 0)
+ width_centre--;
+ else if (width_right > 0)
+ width_right--;
+ else
+ width_left--;
+ }
+
+ /* If there is no list left, pass off to the no list function. */
+ if (width_list == 0) {
+ screen_write_start(&ctx, NULL, centre);
+ screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
+ screen_write_stop(&ctx);
+
+ format_draw_none(octx, available, ocx, ocy, left, centre,
+ right, frs);
+ return;
+ }
+
+ /* Write left at 0. */
+ format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
+
+ /* Write after at available - width_after. */
+ format_draw_put(octx, ocx, ocy, after, frs,
+ available - width_after,
+ after->cx - width_after,
+ width_after);
+
+ /* Write right at available - width_right. */
+ format_draw_put(octx, ocx, ocy, right, frs,
+ available - width_right,
+ right->cx - width_right,
+ width_right);
+
+ /*
+ * All three centre sections are offset from the middle of the
+ * available space.
+ */
+ middle = (width_left + ((available - width_right) - width_left) / 2);
+
+ /*
+ * Write centre at
+ * middle - width_list / 2 - width_centre.
+ */
+ format_draw_put(octx, ocx, ocy, centre, frs,
+ middle - width_list / 2 - width_centre,
+ 0,
+ width_centre);
+
+ /*
+ * Write after at
+ * middle + width_list / 2 - width_centre.
+ */
+ format_draw_put(octx, ocx, ocy, after, frs,
+ middle + width_list / 2,
+ 0,
+ width_after);
+
+ /*
+ * The list now goes from
+ * middle - width_list / 2
+ * to
+ * middle + width_list / 2
+ * If there is no focus given, keep the centre in focus.
+ */
+ if (focus_start == -1 || focus_end == -1)
+ focus_start = focus_end = list->cx / 2;
+ format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
+ width_list, list, list_left, list_right, focus_start, focus_end,
+ frs);
+}
+
+/* Draw format with list on the right. */
+static void
+format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
+ u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
+ struct screen *list, struct screen *list_left, struct screen *list_right,
+ struct screen *after, int focus_start, int focus_end,
+ struct format_ranges *frs)
+{
+ u_int width_left, width_centre, width_right;
+ u_int width_list, width_after;
+ struct screen_write_ctx ctx;
+
+ width_left = left->cx;
+ width_centre = centre->cx;
+ width_right = right->cx;
+ width_list = list->cx;
+ width_after = after->cx;
+
+ /*
+ * Trim first the centre, then the list, then the right, then
+ * after the list, then the left.
+ */
+ while (width_left +
+ width_centre +
+ width_right +
+ width_list +
+ width_after > available) {
+ if (width_centre > 0)
+ width_centre--;
+ else if (width_list > 0)
+ width_list--;
+ else if (width_right > 0)
+ width_right--;
+ else if (width_after > 0)
+ width_after--;
+ else
+ width_left--;
+ }
+
+ /* If there is no list left, pass off to the no list function. */
+ if (width_list == 0) {
+ screen_write_start(&ctx, NULL, right);
+ screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
+ screen_write_stop(&ctx);
+
+ format_draw_none(octx, available, ocx, ocy, left, centre,
+ right, frs);
+ return;
+ }
+
+ /* Write left at 0. */
+ format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
+
+ /* Write after at available - width_after. */
+ format_draw_put(octx, ocx, ocy, after, frs,
+ available - width_after,
+ after->cx - width_after,
+ width_after);
+
+ /*
+ * Write right at
+ * available - width_right - width_list - width_after.
+ */
+ format_draw_put(octx, ocx, ocy, right, frs,
+ available - width_right - width_list - width_after,
+ 0,
+ width_right);
+
+ /*
+ * Write centre halfway between
+ * width_left
+ * and
+ * available - width_right - width_list - width_after.
+ */
+ format_draw_put(octx, ocx, ocy, centre, frs,
+ width_left
+ + ((available - width_right - width_list - width_after)
+ - width_left) / 2
+ - width_centre / 2,
+ centre->cx / 2 - width_centre / 2,
+ width_centre);
+
+ /*
+ * The list now goes from
+ * available - width_list - width_after
+ * to
+ * available - width_after
+ * If there is no focus given, keep the right in focus.
+ */
+ if (focus_start == -1 || focus_end == -1)
+ focus_start = focus_end = 0;
+ format_draw_put_list(octx, ocx, ocy, available - width_list -
+ width_after, width_list, list, list_left, list_right, focus_start,
+ focus_end, frs);
+}
+
+/* Draw a format to a screen. */
+void
+format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
+ u_int available, const char *expanded, struct style_ranges *srs)
+{
+ enum { LEFT,
+ CENTRE,
+ RIGHT,
+ LIST,
+ LIST_LEFT,
+ LIST_RIGHT,
+ AFTER,
+ TOTAL } current = LEFT, last = LEFT;
+ const char *names[] = { "LEFT",
+ "CENTRE",
+ "RIGHT",
+ "LIST",
+ "LIST_LEFT",
+ "LIST_RIGHT",
+ "AFTER" };
+ size_t size = strlen(expanded);
+ struct screen *os = octx->s, s[TOTAL];
+ struct screen_write_ctx ctx[TOTAL];
+ u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL];
+ u_int map[] = { LEFT, LEFT, CENTRE, RIGHT };
+ int focus_start = -1, focus_end = -1;
+ int list_state = -1;
+ enum style_align list_align = STYLE_ALIGN_DEFAULT;
+ struct style sy;
+ struct utf8_data *ud = &sy.gc.data;
+ const char *cp, *end;
+ enum utf8_state more;
+ char *tmp;
+ struct format_range *fr = NULL, *fr1;
+ struct format_ranges frs;
+ struct style_range *sr;
+
+ style_set(&sy, base);
+ TAILQ_INIT(&frs);
+
+ /*
+ * We build three screens for left, right, centre alignment, one for
+ * the list, one for anything after the list and two for the list left
+ * and right markers.
+ */
+ for (i = 0; i < TOTAL; i++) {
+ screen_init(&s[i], size, 1, 0);
+ screen_write_start(&ctx[i], NULL, &s[i]);
+ screen_write_clearendofline(&ctx[i], base->bg);
+ width[i] = 0;
+ }
+
+ /*
+ * Walk the string and add to the corresponding screens,
+ * parsing styles as we go.
+ */
+ cp = expanded;
+ while (*cp != '\0') {
+ if (cp[0] != '#' || cp[1] != '[') {
+ /* See if this is a UTF-8 character. */
+ if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
+ while (*++cp != '\0' && more == UTF8_MORE)
+ more = utf8_append(ud, *cp);
+ if (more != UTF8_DONE)
+ cp -= ud->have;
+ }
+
+ /* Not a UTF-8 character - ASCII or not valid. */
+ if (more != UTF8_DONE) {
+ if (*cp < 0x20 || *cp > 0x7e) {
+ /* Ignore nonprintable characters. */
+ cp++;
+ continue;
+ }
+ utf8_set(ud, *cp);
+ cp++;
+ }
+
+ /* Draw the cell to th current screen. */
+ screen_write_cell(&ctx[current], &sy.gc);
+ width[current] += ud->width;
+ continue;
+ }
+
+ /* This is a style. Work out where the end is and parse it. */
+ end = format_skip(cp + 2, "]");
+ if (end == NULL)
+ return;
+ tmp = xstrndup(cp + 2, end - (cp + 2));
+ if (style_parse(&sy, base, tmp) != 0) {
+ free(tmp);
+ return;
+ }
+ log_debug("style '%s' -> '%s'", tmp, style_tostring(&sy));
+ free(tmp);
+
+ /* Check the list state. */
+ switch (sy.list) {
+ case STYLE_LIST_ON:
+ /*
+ * Entering the list, exiting a marker, or exiting the
+ * focus.
+ */
+ if (list_state != 0) {
+ if (fr != NULL) { /* abort any region */
+ free(fr);
+ fr = NULL;
+ }
+ list_state = 0;
+ list_align = sy.align;
+ }
+
+ /* End the focus if started. */
+ if (focus_start != -1 && focus_end == -1)
+ focus_end = s[LIST].cx;
+
+ current = LIST;
+ break;
+ case STYLE_LIST_FOCUS:
+ /* Entering the focus. */
+ if (list_state != 0) /* not inside the list */
+ break;
+ if (focus_start == -1) /* focus already started */
+ focus_start = s[LIST].cx;
+ break;
+ case STYLE_LIST_OFF:
+ /* Exiting or outside the list. */
+ if (list_state == 0) {
+ if (fr != NULL) { /* abort any region */
+ free(fr);
+ fr = NULL;
+ }
+ if (focus_start != -1 && focus_end == -1)
+ focus_end = s[LIST].cx;
+
+ map[list_align] = AFTER;
+ if (list_align == STYLE_ALIGN_LEFT)
+ map[STYLE_ALIGN_DEFAULT] = AFTER;
+ list_state = 1;
+ }
+ current = map[sy.align];
+ break;
+ case STYLE_LIST_LEFT_MARKER:
+ /* Entering left marker. */
+ if (list_state != 0) /* not inside the list */
+ break;
+ if (s[LIST_LEFT].cx != 0) /* already have marker */
+ break;
+ if (fr != NULL) { /* abort any region */
+ free(fr);
+ fr = NULL;
+ }
+ if (focus_start != -1 && focus_end == -1)
+ focus_start = focus_end = -1;
+ current = LIST_LEFT;
+ break;
+ case STYLE_LIST_RIGHT_MARKER:
+ /* Entering right marker. */
+ if (list_state != 0) /* not inside the list */
+ break;
+ if (s[LIST_RIGHT].cx != 0) /* already have marker */
+ break;
+ if (fr != NULL) { /* abort any region */
+ free(fr);
+ fr = NULL;
+ }
+ if (focus_start != -1 && focus_end == -1)
+ focus_start = focus_end = -1;
+ current = LIST_RIGHT;
+ break;
+ }
+ if (current != last) {
+ log_debug("%s: change %s -> %s", __func__,
+ names[last], names[current]);
+ last = current;
+ }
+
+ /*
+ * Check if the range style has changed and if so end the
+ * current range and start a new one if needed.
+ */
+ if (srs != NULL) {
+ if (fr != NULL && !format_is_type(fr, &sy)) {
+ if (s[current].cx != fr->start) {
+ fr->end = s[current].cx + 1;
+ TAILQ_INSERT_TAIL(&frs, fr, entry);
+ } else
+ free(fr);
+ fr = NULL;
+ }
+ if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
+ fr = xcalloc(1, sizeof *fr);
+ fr->index = current;
+
+ fr->s = &s[current];
+ fr->start = s[current].cx;
+
+ fr->type = sy.range_type;
+ fr->argument = sy.range_argument;
+ }
+ }
+
+ cp = end + 1;
+ }
+ free(fr);
+
+ for (i = 0; i < TOTAL; i++)
+ log_debug("%s: width %s is %u", __func__, names[i], width[i]);
+ if (focus_start != -1 && focus_end != -1)
+ log_debug("focus is %d-%d", focus_start, focus_end);
+ TAILQ_FOREACH(fr, &frs, entry) {
+ log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
+ fr->argument, names[fr->index], fr->start, fr->end);
+ }
+
+ /*
+ * Draw the screens. How they are arranged depends on where the list
+ * appearsq.
+ */
+ switch (list_align) {
+ case STYLE_ALIGN_DEFAULT:
+ /* No list. */
+ format_draw_none(octx, available, ocx, ocy, &s[LEFT],
+ &s[CENTRE], &s[RIGHT], &frs);
+ break;
+ case STYLE_ALIGN_LEFT:
+ /* List is part of the left. */
+ format_draw_left(octx, available, ocx, ocy, &s[LEFT],
+ &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
+ &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
+ break;
+ case STYLE_ALIGN_CENTRE:
+ /* List is part of the centre. */
+ format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
+ &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
+ &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
+ break;
+ case STYLE_ALIGN_RIGHT:
+ /* List is part of the right. */
+ format_draw_right(octx, available, ocx, ocy, &s[LEFT],
+ &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
+ &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
+ break;
+ }
+
+ /* Create ranges to return. */
+ TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
+ sr = xcalloc(1, sizeof *sr);
+ sr->type = fr->type;
+ sr->argument = fr->argument;
+ sr->start = fr->start;
+ sr->end = fr->end;
+ TAILQ_INSERT_TAIL(srs, sr, entry);
+
+ log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
+ sr->argument, sr->start, sr->end);
+
+ format_free_range(&frs, fr);
+ }
+
+ /* Restore the original cursor position. */
+ screen_write_cursormove(octx, ocx, ocy, 0);
+}
+
+/* Get width, taking #[] into account. */
+u_int
+format_width(const char *expanded)
+{
+ const char *cp, *end;
+ u_int width = 0;
+ struct utf8_data ud;
+ enum utf8_state more;
+
+ cp = expanded;
+ while (*cp != '\0') {
+ if (cp[0] == '#' && cp[1] == '[') {
+ end = format_skip(cp + 2, "]");
+ if (end == NULL)
+ return 0;
+ cp = end + 1;
+ } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
+ while (*++cp != '\0' && more == UTF8_MORE)
+ more = utf8_append(&ud, *cp);
+ if (more == UTF8_DONE)
+ width += ud.width;
+ else
+ cp -= ud.have;
+ } else if (*cp > 0x1f && *cp < 0x7f) {
+ width++;
+ cp++;
+ }
+ }
+ return (width);
+}
+
+/* Trim on the left, taking #[] into account. */
+char *
+format_trim_left(const char *expanded, u_int limit)
+{
+ char *copy, *out;
+ const char *cp = expanded, *end;
+ u_int width = 0;
+ struct utf8_data ud;
+ enum utf8_state more;
+
+ out = copy = xmalloc(strlen(expanded) + 1);
+ while (*cp != '\0') {
+ if (cp[0] == '#' && cp[1] == '[') {
+ end = format_skip(cp + 2, "]");
+ if (end == NULL)
+ break;
+ memcpy(out, cp, end + 1 - cp);
+ out += (end + 1 - cp);
+ cp = end + 1;
+ } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
+ while (*++cp != '\0' && more == UTF8_MORE)
+ more = utf8_append(&ud, *cp);
+ if (more == UTF8_DONE) {
+ if (width + ud.width <= limit) {
+ memcpy(out, ud.data, ud.size);
+ out += ud.size;
+ }
+ width += ud.width;
+ } else
+ cp -= ud.have;
+ } else if (*cp > 0x1f && *cp < 0x7f) {
+ if (width + 1 <= limit)
+ *out++ = *cp;
+ width++;
+ cp++;
+ }
+ }
+ *out = '\0';
+ return (copy);
+}
+
+/* Trim on the right, taking #[] into account. */
+char *
+format_trim_right(const char *expanded, u_int limit)
+{
+ char *copy, *out;
+ const char *cp = expanded, *end;
+ u_int width = 0, total_width, skip;
+ struct utf8_data ud;
+ enum utf8_state more;
+
+ total_width = format_width(expanded);
+ if (total_width <= limit)
+ return (xstrdup(expanded));
+ skip = total_width - limit;
+
+ out = copy = xmalloc(strlen(expanded) + 1);
+ while (*cp != '\0') {
+ if (cp[0] == '#' && cp[1] == '[') {
+ end = format_skip(cp + 2, "]");
+ if (end == NULL)
+ break;
+ memcpy(out, cp, end + 1 - cp);
+ out += (end + 1 - cp);
+ cp = end + 1;
+ } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
+ while (*++cp != '\0' && more == UTF8_MORE)
+ more = utf8_append(&ud, *cp);
+ if (more == UTF8_DONE) {
+ if (width >= skip) {
+ memcpy(out, ud.data, ud.size);
+ out += ud.size;
+ }
+ width += ud.width;
+ } else
+ cp -= ud.have;
+ } else if (*cp > 0x1f && *cp < 0x7f) {
+ if (width >= skip)
+ *out++ = *cp;
+ width++;
+ cp++;
+ }
+ }
+ *out = '\0';
+ return (copy);
+}
diff --git a/format.c b/format.c
index 501b0a34..30391da1 100644
--- a/format.c
+++ b/format.c
@@ -972,7 +972,7 @@ found:
}
/* Skip until end. */
-static const char *
+const char *
format_skip(const char *s, const char *end)
{
int brackets = 0;
@@ -1580,12 +1580,12 @@ done:
/* Truncate the value if needed. */
if (limit > 0) {
- new = utf8_trimcstr(value, limit);
+ new = format_trim_left(value, limit);
format_log(ft, "applied length limit %d: %s", limit, new);
free(value);
value = new;
} else if (limit < 0) {
- new = utf8_rtrimcstr(value, -limit);
+ new = format_trim_right(value, -limit);
format_log(ft, "applied length limit %d: %s", limit, new);
free(value);
value = new;
diff --git a/mode-tree.c b/mode-tree.c
index 112969ea..60e23534 100644
--- a/mode-tree.c
+++ b/mode-tree.c
@@ -497,7 +497,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
struct options *oo = wp->window->options;
struct screen_write_ctx ctx;
struct grid_cell gc0, gc;
- u_int w, h, i, j, sy, box_x, box_y;
+ u_int w, h, i, j, sy, box_x, box_y, width;
char *text, *start, key[7];
const char *tag, *symbol;
size_t size, n;
@@ -572,8 +572,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
tag = "*";
else
tag = "";
- xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
- mti->name, tag, mti->text);
+ xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name,
+ tag);
+ width = utf8_cstrwidth(text);
free(start);
if (mti->tagged) {
@@ -582,11 +583,13 @@ mode_tree_draw(struct mode_tree_data *mtd)
}
if (i != mtd->current) {
- screen_write_cnputs(&ctx, w, &gc0, "%s", text);
screen_write_clearendofline(&ctx, 8);
+ screen_write_puts(&ctx, &gc0, "%s", text);
+ format_draw(&ctx, &gc0, w - width, mti->text, NULL);
} else {
- screen_write_cnputs(&ctx, w, &gc, "%s", text);
screen_write_clearendofline(&ctx, gc.bg);
+ screen_write_puts(&ctx, &gc, "%s", text);
+ format_draw(&ctx, &gc, w - width, mti->text, NULL);
}
free(text);
diff --git a/options-table.c b/options-table.c
index 73d22b0a..a2a3ffe6 100644
--- a/options-table.c
+++ b/options-table.c
@@ -39,6 +39,9 @@ static const char *options_table_mode_keys_list[] = {
static const char *options_table_clock_mode_style_list[] = {
"12", "24", NULL
};
+static const char *options_table_status_list[] = {
+ "off", "on", "2", "3", "4", "5", NULL
+};
static const char *options_table_status_keys_list[] = {
"emacs", "vi", NULL
};
@@ -64,6 +67,46 @@ static const char *options_table_window_size_list[] = {
"largest", "smallest", "manual", NULL
};
+/* Status line format. */
+#define OPTIONS_TABLE_STATUS_FORMAT1 \
+ "#[align=left range=left #{status-left-style}]" \
+ "#{T;=/#{status-left-length}:status-left}#[norange default]" \
+ "#[list=on align=#{status-justify}]" \
+ "#[list=left-marker]<#[list=right-marker]>#[list=on]" \
+ "#{W:" \
+ "#[range=window|#{window_index}" \
+ "#{?window_last_flag, #{window-status-last-style},}" \
+ "#{?window_bell_flag," \
+ " #{window-status-bell-style}," \
+ "#{?window_activity_flag," \
+ " #{window-status-activity-style},}" \
+ "}" \
+ "]" \
+ "#{T:window-status-format}" \
+ "#[norange default]" \
+ "#{?window_end_flag,,#{window-status-separator}}" \
+ "," \
+ "#[range=window|#{window_index} list=focus" \
+ "#{?window_last_flag, #{window-status-last-style},}" \
+ "#{?window_bell_flag," \
+ " #{window-status-bell-style}," \
+ "#{?window_activity_flag," \
+ " #{window-status-activity-style},}" \
+ "}" \
+ "]" \
+ "#{T:window-status-current-format}" \
+ "#[norange list=on default]" \
+ "#{?window_end_flag,,#{window-status-separator}}" \
+ "}" \
+ "#[nolist align=right range=right #{status-right-style}]" \
+ "#{T;=/#{status-right-length}:status-right}#[norange default]"
+#define OPTIONS_TABLE_STATUS_FORMAT2 \
+ "#[align=centre]#{P:#{?pane_active,#[reverse],}" \
+ "#{pane_index}[#{pane_width}x#{pane_height}]#[default] }"
+static const char *options_table_status_format_default[] = {
+ OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL
+};
+
/* Top-level options. */
const struct options_table_entry options_table[] = {
{ .name = "buffer-limit",
@@ -378,8 +421,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "status",
- .type = OPTIONS_TABLE_FLAG,
+ .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
+ .choices = options_table_status_list,
.default_num = 1
},
@@ -404,6 +448,12 @@ const struct options_table_entry options_table[] = {
.style = "status-style"
},
+ { .name = "status-format",
+ .type = OPTIONS_TABLE_ARRAY,
+ .scope = OPTIONS_TABLE_SESSION,
+ .default_arr = options_table_status_format_default,
+ },
+
{ .name = "status-interval",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SESSION,
diff --git a/screen-redraw.c b/screen-redraw.c
index 9880433d..691b2194 100644
--- a/screen-redraw.c
+++ b/screen-redraw.c
@@ -274,8 +274,8 @@ screen_redraw_make_pane_status(struct client *c, struct window *w,
struct grid_cell gc;
const char *fmt;
struct format_tree *ft;
- char *out;
- size_t outlen;
+ char *expanded;
+ u_int width, i;
struct screen_write_ctx ctx;
struct screen old;
@@ -289,27 +289,27 @@ screen_redraw_make_pane_status(struct client *c, struct window *w,
ft = format_create(c, NULL, FORMAT_PANE|wp->id, 0);
format_defaults(ft, c, NULL, NULL, wp);
+ expanded = format_expand_time(ft, fmt);
+ wp->status_size = width = wp->sx - 4;
+
memcpy(&old, &wp->status_screen, sizeof old);
- screen_init(&wp->status_screen, wp->sx, 1, 0);
+ screen_init(&wp->status_screen, width, 1, 0);
wp->status_screen.mode = 0;
- out = format_expand(ft, fmt);
- outlen = screen_write_cstrlen("%s", out);
- if (outlen > wp->sx - 4)
- outlen = wp->sx - 4;
- screen_resize(&wp->status_screen, outlen, 1, 0);
-
screen_write_start(&ctx, NULL, &wp->status_screen);
+
+ gc.attr |= GRID_ATTR_CHARSET;
+ for (i = 0; i < width; i++)
+ screen_write_putc(&ctx, &gc, 'q');
+ gc.attr &= ~GRID_ATTR_CHARSET;
+
screen_write_cursormove(&ctx, 0, 0, 0);
- screen_write_clearline(&ctx, 8);
- screen_write_cnputs(&ctx, outlen, &gc, "%s", out);
+ format_draw(&ctx, &gc, width, expanded, NULL);
screen_write_stop(&ctx);
- free(out);
+ free(expanded);
format_free(ft);
- wp->status_size = outlen;
-
if (grid_compare(wp->status_screen.grid, old.grid) == 0) {
screen_free(&old);
return (0);
diff --git a/screen-write.c b/screen-write.c
index eb12474d..0d57b818 100644
--- a/screen-write.c
+++ b/screen-write.c
@@ -169,41 +169,6 @@ screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
screen_write_cell(ctx, &gc);
}
-/* Calculate string length, with embedded formatting. */
-size_t
-screen_write_cstrlen(const char *fmt, ...)
-{
- va_list ap;
- char *msg, *msg2, *ptr, *ptr2;
- size_t size;
-
- va_start(ap, fmt);
- xvasprintf(&msg, fmt, ap);
- va_end(ap);
- msg2 = xmalloc(strlen(msg) + 1);
-
- ptr = msg;
- ptr2 = msg2;
- while (*ptr != '\0') {
- if (ptr[0] == '#' && ptr[1] == '[') {
- while (*ptr != ']' && *ptr != '\0')
- ptr++;
- if (*ptr == ']')
- ptr++;
- continue;
- }
- *ptr2++ = *ptr++;
- }
- *ptr2 = '\0';
-
- size = screen_write_strlen("%s", msg2);
-
- free(msg);
- free(msg2);
-
- return (size);
-}
-
/* Calculate string length. */
size_t
screen_write_strlen(const char *fmt, ...)
@@ -322,78 +287,6 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
free(msg);
}
-/* Write string, similar to nputs, but with embedded formatting (#[]). */
-void
-screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
- const struct grid_cell *gcp, const char *fmt, ...)
-{
- struct style sy;
- struct utf8_data *ud = &sy.gc.data;
- va_list ap;
- char *msg;
- u_char *ptr, *last;
- size_t left, size = 0;
- enum utf8_state more;
-
- style_set(&sy, gcp);
-
- va_start(ap, fmt);
- xvasprintf(&msg, fmt, ap);
- va_end(ap);
-
- ptr = msg;
- while (*ptr != '\0') {
- if (ptr[0] == '#' && ptr[1] == '[') {
- ptr += 2;
- last = ptr + strcspn(ptr, "]");
- if (*last == '\0') {
- /* No ]. Not much point in doing anything. */
- break;
- }
- *last = '\0';
-
- style_parse(&sy, gcp, ptr);
- ptr = last + 1;
- continue;
- }
- if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
- ptr++;
-
- left = strlen(ptr);
- if (left < (size_t)ud->size - 1)
- break;
- while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
- ptr++;
- ptr++;
-
- if (more != UTF8_DONE)
- continue;
- if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
- while (size < (size_t)maxlen) {
- screen_write_putc(ctx, &sy.gc, ' ');
- size++;
- }
- break;
- }
- size += ud->width;
- screen_write_cell(ctx, &sy.gc);
- } else {
- if (maxlen > 0 && size + 1 > (size_t)maxlen)
- break;
-
- if (*ptr == '\001')
- sy.gc.attr ^= GRID_ATTR_CHARSET;
- else if (*ptr > 0x1f && *ptr < 0x7f) {
- size++;
- screen_write_putc(ctx, &sy.gc, *ptr);
- }
- ptr++;
- }
- }
-
- free(msg);
-}
-
/* Copy from another screen. Assumes target region is big enough. */
void
screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
diff --git a/server-client.c b/server-client.c
index fe79163f..eee5dff5 100644
--- a/server-client.c
+++ b/server-client.c
@@ -411,12 +411,13 @@ server_client_check_mouse(struct client *c)
{
struct session *s = c->session;
struct mouse_event *m = &c->tty.mouse;
- struct window *w;
+ struct winlink *wl;
struct window_pane *wp;
u_int x, y, b, sx, sy, px, py;
int flag;
key_code key;
struct timeval tv;
+ struct style_range *sr;
enum { NOTYPE, MOVE, DOWN, UP, DRAG, WHEEL, DOUBLE, TRIPLE } type;
enum { NOWHERE, PANE, STATUS, STATUS_LEFT, STATUS_RIGHT, BORDER } where;
@@ -503,17 +504,29 @@ have_event:
/* Is this on the status line? */
m->statusat = status_at_line(c);
- if (m->statusat != -1 && y == (u_int)m->statusat) {
- if (x < c->status.left_size)
+ if (m->statusat != -1 &&
+ y >= (u_int)m->statusat &&
+ y < m->statusat + status_line_size(c))
+ sr = status_get_range(c, x, y - m->statusat);
+ else
+ sr = NULL;
+ if (sr != NULL) {
+ switch (sr->type) {
+ case STYLE_RANGE_NONE:
+ break;
+ case STYLE_RANGE_LEFT:
where = STATUS_LEFT;
- else if (x > c->tty.sx - c->status.right_size)
+ break;
+ case STYLE_RANGE_RIGHT:
where = STATUS_RIGHT;
- else {
- w = status_get_window_at(c, x);
- if (w == NULL)
- return (KEYC_UNKNOWN);
- m->w = w->id;
- where = STATUS;
+ break;
+ case STYLE_RANGE_WINDOW:
+ wl = winlink_find_by_index(&s->windows, sr->argument);
+ if (wl != NULL) {
+ m->w = wl->window->id;
+ where = STATUS;
+ }
+ break;
}
}
diff --git a/status.c b/status.c
index de6d2716..467db6ad 100644
--- a/status.c
+++ b/status.c
@@ -29,14 +29,6 @@
#include "tmux.h"
-static char *status_redraw_get_left(struct client *, struct grid_cell *,
- size_t *);
-static char *status_redraw_get_right(struct client *, struct grid_cell *,
- size_t *);
-static char *status_print(struct client *, struct winlink *,
- struct grid_cell *);
-static char *status_replace(struct client *, struct winlink *,
- const char *);
static void status_message_callback(int, short, void *);
static void status_timer_callback(int, short, void *);
@@ -196,7 +188,8 @@ status_timer_start_all(void)
void
status_update_cache(struct session *s)
{
- if (!options_get_number(s->options, "status"))
+ s->statuslines = options_get_number(s->options, "status");
+ if (s->statuslines == 0)
s->statusat = -1;
else if (options_get_number(s->options, "status-position") == 0)
s->statusat = 0;
@@ -225,75 +218,35 @@ status_line_size(struct client *c)
if (c->flags & CLIENT_STATUSOFF)
return (0);
- if (s->statusat == -1)
- return (0);
- return (1);
-}
-
-/* Retrieve options for left string. */
-static char *
-status_redraw_get_left(struct client *c, struct grid_cell *gc, size_t *size)
-{
- struct session *s = c->session;
- const char *template;
- char *left;
- size_t leftlen;
-
- style_apply_update(gc, s->options, "status-left-style");
-
- template = options_get_string(s->options, "status-left");
- left = status_replace(c, NULL, template);
-
- *size = options_get_number(s->options, "status-left-length");
- leftlen = screen_write_cstrlen("%s", left);
- if (leftlen < *size)
- *size = leftlen;
- return (left);
+ return (s->statuslines);
}
-/* Retrieve options for right string. */
-static char *
-status_redraw_get_right(struct client *c, struct grid_cell *gc, size_t *size)
+/* Get window at window list position. */
+struct style_range *
+status_get_range(struct client *c, u_int x, u_int y)
{
- struct session *s = c->session;
- const char *template;
- char *right;
- size_t rightlen;
-
- style_apply_update(gc, s->options, "status-right-style");
-
- template = options_get_string(s->options, "status-right");
- right = status_replace(c, NULL, template);
+ struct status_line *sl = &c->status;
+ struct style_range *sr;
- *size = options_get_number(s->options, "status-right-length");
- rightlen = screen_write_cstrlen("%s", right);
- if (rightlen < *size)
- *size = rightlen;
- return (right);
+ if (y >= nitems(sl->entries))
+ return (NULL);
+ TAILQ_FOREACH(sr, &sl->entries[y].ranges, entry) {
+ if (x >= sr->start && x < sr->end)
+ return (sr);
+ }
+ return (NULL);
}
-/* Get window at window list position. */
-struct window *
-status_get_window_at(struct client *c, u_int x)
+/* Free all ranges. */
+static void
+status_free_ranges(struct style_ranges *srs)
{
- struct session *s = c->session;
- struct winlink *wl;
- struct options *oo;
- const char *sep;
- size_t seplen;
-
- x += c->status.window_list_offset;
- RB_FOREACH(wl, winlinks, &s->windows) {
- oo = wl->window->options;
-
- sep = options_get_string(oo, "window-status-separator");
- seplen = screen_write_cstrlen("%s", sep);
+ struct style_range *sr, *sr1;
- if (x < wl->status_width)
- return (wl->window);
- x -= wl->status_width + seplen;
+ TAILQ_FOREACH_SAFE(sr, srs, entry, sr1) {
+ TAILQ_REMOVE(srs, sr, entry);
+ free(sr);
}
- return (NULL);
}
/* Save old status line. */
@@ -327,6 +280,10 @@ void
status_init(struct client *c)
{
struct status_line *sl = &c->status;
+ u_int i;
+
+ for (i = 0; i < nitems(sl->entries); i++)
+ TAILQ_INIT(&sl->entries[i].ranges);
screen_init(&sl->screen, c->tty.sx, 1, 0);
sl->active = &sl->screen;
@@ -337,6 +294,12 @@ void
status_free(struct client *c)
{
struct status_line *sl = &c->status;
+ u_int i;
+
+ for (i = 0; i < nitems(sl->entries); i++) {
+ status_free_ranges(&sl->entries[i].ranges);
+ free((void *)sl->entries[i].expanded);
+ }
if (event_initialized(&sl->timer))
evtimer_del(&sl->timer);
@@ -352,19 +315,19 @@ status_free(struct client *c)
int
status_redraw(struct client *c)
{
- struct status_line *sl = &c->status;
- struct screen_write_ctx ctx;
- struct session *s = c->session;
- struct winlink *wl;
- struct screen old_screen, window_list;
- struct grid_cell stdgc, lgc, rgc, gc;
- struct options *oo;
- char *left, *right;
- const char *sep;
- u_int offset, needed, lines;
- u_int wlstart, wlwidth, wlavailable, wloffset, wlsize;
- size_t llen, rlen, seplen;
- int larrow, rarrow;
+ struct status_line *sl = &c->status;
+ struct status_line_entry *sle;
+ struct session *s = c->session;
+ struct screen_write_ctx ctx;
+ struct grid_cell gc;
+ u_int lines, i, width = c->tty.sx;
+ int flags, force = 0, changed = 0;
+ struct options_entry *o;
+ struct format_tree *ft;
+ const char *fmt;
+ char *expanded;
+
+ log_debug("%s enter", __func__);
/* Shouldn't get here if not the active screen. */
if (sl->active != &sl->screen)
@@ -374,257 +337,71 @@ status_redraw(struct client *c)
lines = status_line_size(c);
if (c->tty.sy == 0 || lines == 0)
return (1);
- left = right = NULL;
- larrow = rarrow = 0;
/* Set up default colour. */
- style_apply(&stdgc, s->options, "status-style");
-
- /* Create the target screen. */
- memcpy(&old_screen, sl->active, sizeof old_screen);
- screen_init(sl->active, c->tty.sx, lines, 0);
- screen_write_start(&ctx, NULL, sl->active);
- for (offset = 0; offset < lines * c->tty.sx; offset++)
- screen_write_putc(&ctx, &stdgc, ' ');
- screen_write_stop(&ctx);
-
- /* If the height is too small, blank status line. */
- if (c->tty.sy < lines)
- goto out;
-
- /* Work out left and right strings. */
- memcpy(&lgc, &stdgc, sizeof lgc);
- left = status_redraw_get_left(c, &lgc, &llen);
- memcpy(&rgc, &stdgc, sizeof rgc);
- right = status_redraw_get_right(c, &rgc, &rlen);
-
- /*
- * Figure out how much space we have for the window list. If there
- * isn't enough space, just show a blank status line.
- */
- needed = 0;
- if (llen != 0)
- needed += llen;
- if (rlen != 0)
- needed += rlen;
- if (c->tty.sx == 0 || c->tty.sx <= needed)
- goto out;
- wlavailable = c->tty.sx - needed;
-
- /* Calculate the total size needed for the window list. */
- wlstart = wloffset = wlwidth = 0;
- RB_FOREACH(wl, winlinks, &s->windows) {
- free(wl->status_text);
- memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
- wl->status_text = status_print(c, wl, &wl->status_cell);
- wl->status_width = screen_write_cstrlen("%s", wl->status_text);
-
- if (wl == s->curw)
- wloffset = wlwidth;
-
- oo = wl->window->options;
- sep = options_get_string(oo, "window-status-separator");
- seplen = screen_write_cstrlen("%s", sep);
- wlwidth += wl->status_width + seplen;
- }
-
- /* Create a new screen for the window list. */
- screen_init(&window_list, wlwidth, 1, 0);
-
- /* And draw the window list into it. */
- screen_write_start(&ctx, NULL, &window_list);
- RB_FOREACH(wl, winlinks, &s->windows) {
- screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s",
- wl->status_text);
-
- oo = wl->window->options;
- sep = options_get_string(oo, "window-status-separator");
- screen_write_cnputs(&ctx, -1, &stdgc, "%s", sep);
- }
- screen_write_stop(&ctx);
-
- /* If there is enough space for the total width, skip to draw now. */
- if (wlwidth <= wlavailable)
- goto draw;
-
- /* Find size of current window text. */
- wlsize = s->curw->status_width;
-
- /*
- * If the current window is already on screen, good to draw from the
- * start and just leave off the end.
- */
- if (wloffset + wlsize < wlavailable) {
- if (wlavailable > 0) {
- rarrow = 1;
- wlavailable--;
- }
- wlwidth = wlavailable;
- } else {
- /*
- * Work out how many characters we need to omit from the
- * start. There are wlavailable characters to fill, and
- * wloffset + wlsize must be the last. So, the start character
- * is wloffset + wlsize - wlavailable.
- */
- if (wlavailable > 0) {
- larrow = 1;
- wlavailable--;
- }
-
- wlstart = wloffset + wlsize - wlavailable;
- if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
- rarrow = 1;
- wlstart++;
- wlavailable--;
- }
- wlwidth = wlavailable;
+ style_apply(&gc, s->options, "status-style");
+ if (!grid_cells_equal(&gc, &sl->style)) {
+ force = 1;
+ memcpy(&sl->style, &gc, sizeof sl->style);
}
- /* Bail if anything is now too small too. */
- if (wlwidth == 0 || wlavailable == 0) {
- screen_free(&window_list);
- goto out;
+ /* Resize the target screen. */
+ if (screen_size_x(&sl->screen) != width ||
+ screen_size_y(&sl->screen) != lines) {
+ if (screen_size_x(&sl->screen) != width)
+ force = 1;
+ screen_resize(&sl->screen, width, lines, 0);
+ changed = 1;
}
+ screen_write_start(&ctx, NULL, &sl->screen);
- /*
- * Now the start position is known, work out the state of the left and
- * right arrows.
- */
- offset = 0;
- RB_FOREACH(wl, winlinks, &s->windows) {
- if (wl->flags & WINLINK_ALERTFLAGS &&
- larrow == 1 && offset < wlstart)
- larrow = -1;
-
- offset += wl->status_width;
-
- if (wl->flags & WINLINK_ALERTFLAGS &&
- rarrow == 1 && offset > wlstart + wlwidth)
- rarrow = -1;
- }
+ /* Create format tree. */
+ flags = FORMAT_STATUS;
+ if (c->flags & CLIENT_STATUSFORCE)
+ flags |= FORMAT_FORCE;
+ ft = format_create(c, NULL, FORMAT_NONE, flags);
+ format_defaults(ft, c, NULL, NULL, NULL);
-draw:
- /* Begin drawing. */
- screen_write_start(&ctx, NULL, sl->active);
+ /* Write the status lines. */
+ o = options_get(s->options, "status-format");
+ if (o == NULL)
+ screen_write_clearscreen(&ctx, gc.bg);
+ else {
+ for (i = 0; i < lines; i++) {
+ screen_write_cursormove(&ctx, 0, i, 0);
+
+ fmt = options_array_get(o, i);
+ if (fmt == NULL) {
+ screen_write_clearline(&ctx, gc.bg);
+ continue;
+ }
+ sle = &sl->entries[i];
- /* Draw the left string and arrow. */
- screen_write_cursormove(&ctx, 0, 0, 0);
- if (llen != 0)
- screen_write_cnputs(&ctx, llen, &lgc, "%s", left);
- if (larrow != 0) {
- memcpy(&gc, &stdgc, sizeof gc);
- if (larrow == -1)
- gc.attr ^= GRID_ATTR_REVERSE;
- screen_write_putc(&ctx, &gc, '<');
- }
+ expanded = format_expand_time(ft, fmt);
+ if (!force &&
+ sle->expanded != NULL &&
+ strcmp(expanded, sle->expanded) == 0) {
+ free(expanded);
+ continue;
+ }
+ changed = 1;
- /* Draw the right string and arrow. */
- if (rarrow != 0) {
- screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0, 0);
- memcpy(&gc, &stdgc, sizeof gc);
- if (rarrow == -1)
- gc.attr ^= GRID_ATTR_REVERSE;
- screen_write_putc(&ctx, &gc, '>');
- } else
- screen_write_cursormove(&ctx, c->tty.sx - rlen, 0, 0);
- if (rlen != 0)
- screen_write_cnputs(&ctx, rlen, &rgc, "%s", right);
+ screen_write_clearline(&ctx, gc.bg);
+ status_free_ranges(&sle->ranges);
+ format_draw(&ctx, &gc, width, expanded, &sle->ranges);
- /* Figure out the offset for the window list. */
- if (llen != 0)
- wloffset = llen;
- else
- wloffset = 0;
- if (wlwidth < wlavailable) {
- switch (options_get_number(s->options, "status-justify")) {
- case 1: /* centred */
- wloffset += (wlavailable - wlwidth) / 2;
- break;
- case 2: /* right */
- wloffset += (wlavailable - wlwidth);
- break;
+ free(sle->expanded);
+ sle->expanded = expanded;
}
}
- if (larrow != 0)
- wloffset++;
-
- /* Copy the window list. */
- sl->window_list_offset = -wloffset + wlstart;
- screen_write_cursormove(&ctx, wloffset, 0, 0);
- screen_write_fast_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
- screen_free(&window_list);
-
- /* Save left and right size. */
- sl->left_size = llen;
- sl->right_size = rlen;
-
screen_write_stop(&ctx);
-out:
- free(left);
- free(right);
-
- if (grid_compare(sl->active->grid, old_screen.grid) == 0) {
- screen_free(&old_screen);
- return (0);
- }
- screen_free(&old_screen);
- return (1);
-}
-
-/* Replace special sequences in fmt. */
-static char *
-status_replace(struct client *c, struct winlink *wl, const char *fmt)
-{
- struct format_tree *ft;
- char *expanded;
- u_int tag;
-
- if (fmt == NULL)
- return (xstrdup(""));
-
- if (wl != NULL)
- tag = FORMAT_WINDOW|wl->window->id;
- else
- tag = FORMAT_NONE;
- if (c->flags & CLIENT_STATUSFORCE)
- ft = format_create(c, NULL, tag, FORMAT_STATUS|FORMAT_FORCE);
- else
- ft = format_create(c, NULL, tag, FORMAT_STATUS);
- format_defaults(ft, c, NULL, wl, NULL);
-
- expanded = format_expand_time(ft, fmt);
-
+ /* Free the format tree. */
format_free(ft);
- return (expanded);
-}
-/* Return winlink status line entry and adjust gc as necessary. */
-static char *
-status_print(struct client *c, struct winlink *wl, struct grid_cell *gc)
-{
- struct options *oo = wl->window->options;
- struct session *s = c->session;
- const char *fmt;
- char *text;
-
- style_apply_update(gc, oo, "window-status-style");
- fmt = options_get_string(oo, "window-status-format");
- if (wl == s->curw) {
- style_apply_update(gc, oo, "window-status-current-style");
- fmt = options_get_string(oo, "window-status-current-format");
- }
- if (wl == TAILQ_FIRST(&s->lastw))
- style_apply_update(gc, oo, "window-status-last-style");
-
- if (wl->flags & WINLINK_BELL)
- style_apply_update(gc, oo, "window-status-bell-style");
- else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
- style_apply_update(gc, oo, "window-status-activity-style");
-
- text = status_replace(c, wl, fmt);
- return (text);
+ /* Return if the status line has changed. */
+ log_debug("%s exit: force=%d, changed=%d", __func__, force, changed);
+ return (force || changed);
}
/* Set a status line message. */
@@ -713,8 +490,9 @@ status_message_redraw(struct client *c)
style_apply(&gc, s->options, "message-style");
screen_write_start(&ctx, NULL, sl->active);
- screen_write_cursormove(&ctx, 0, 0, 0);
- for (offset = 0; offset < lines * c->tty.sx; offset++)
+ screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
+ screen_write_cursormove(&ctx, 0, lines - 1, 0);
+ for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, 0, lines - 1, 0);
screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
@@ -864,12 +642,13 @@ status_prompt_redraw(struct client *c)
start = c->tty.sx;
screen_write_start(&ctx, NULL, sl->active);
- screen_write_cursormove(&ctx, 0, 0, 0);
- for (offset = 0; offset < lines * c->tty.sx; offset++)
+ screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
+ screen_write_cursormove(&ctx, 0, lines - 1, 0);
+ for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' ');
- screen_write_cursormove(&ctx, 0, 0, 0);
+ screen_write_cursormove(&ctx, 0, lines - 1, 0);
screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string);
- screen_write_cursormove(&ctx, start, 0, 0);
+ screen_write_cursormove(&ctx, start, lines - 1, 0);
left = c->tty.sx - start;
if (left == 0)
diff --git a/style.c b/style.c
index 293c70f1..c3d2960d 100644
--- a/style.c
+++ b/style.c
@@ -19,6 +19,8 @@
#include <sys/types.h>
+#include <ctype.h>
+#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@@ -28,7 +30,12 @@
/* Default style. */
static struct style style_default = {
- { 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } }
+ { 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } },
+
+ STYLE_ALIGN_DEFAULT,
+ STYLE_LIST_OFF,
+
+ STYLE_RANGE_NONE, 0
};
/*
@@ -40,8 +47,8 @@ int
style_parse(struct style *sy, const struct grid_cell *base, const char *in)
{
struct style saved;
- const char delimiters[] = " ,";
- char tmp[32];
+ const char delimiters[] = " ,", *cp;
+ char tmp[256], *found;
int value;
size_t end;
@@ -68,6 +75,60 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->gc.bg = base->bg;
sy->gc.attr = base->attr;
sy->gc.flags = base->flags;
+ } else if (strcasecmp(tmp, "nolist") == 0)
+ sy->list = STYLE_LIST_OFF;
+ else if (strncasecmp(tmp, "list=", 5) == 0) {
+ if (strcasecmp(tmp + 5, "on") == 0)
+ sy->list = STYLE_LIST_ON;
+ else if (strcasecmp(tmp + 5, "focus") == 0)
+ sy->list = STYLE_LIST_FOCUS;
+ else if (strcasecmp(tmp + 5, "left-marker") == 0)
+ sy->list = STYLE_LIST_LEFT_MARKER;
+ else if (strcasecmp(tmp + 5, "right-marker") == 0)
+ sy->list = STYLE_LIST_RIGHT_MARKER;
+ else
+ goto error;
+ } else if (strcasecmp(tmp, "norange") == 0) {
+ sy->range_type = style_default.range_type;
+ sy->range_argument = style_default.range_type;
+ } else if (end > 6 && strncasecmp(tmp, "range=", 6) == 0) {
+ found = strchr(tmp + 6, '|');
+ if (found != NULL) {
+ *found++ = '\0';
+ if (*found == '\0')
+ goto error;
+ for (cp = found; *cp != '\0'; cp++) {
+ if (!isdigit((u_char)*cp))
+ goto error;
+ }
+ }
+ if (strcasecmp(tmp + 6, "left") == 0) {
+ if (found != NULL)
+ goto error;
+ sy->range_type = STYLE_RANGE_LEFT;
+ sy->range_argument = 0;
+ } else if (strcasecmp(tmp + 6, "right") == 0) {
+ if (found != NULL)
+ goto error;
+ sy->range_type = STYLE_RANGE_RIGHT;
+ sy->range_argument = 0;
+ } else if (strcasecmp(tmp + 6, "window") == 0) {
+ if (found == NULL)
+ goto error;
+ sy->range_type = STYLE_RANGE_WINDOW;
+ sy->range_argument = atoi(found);
+ }
+ } else if (strcasecmp(tmp, "noalign") == 0)
+ sy->align = style_default.align;
+ else if (end > 6 && strncasecmp(tmp, "align=", 6) == 0) {
+ if (strcasecmp(tmp + 6, "left") == 0)
+ sy->align = STYLE_ALIGN_LEFT;
+ else if (strcasecmp(tmp + 6, "centre") == 0)
+ sy->align = STYLE_ALIGN_CENTRE;
+ else if (strcasecmp(tmp + 6, "right") == 0)
+ sy->align = STYLE_ALIGN_RIGHT;
+ else
+ goto error;
} else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
if ((value = colour_fromstring(tmp + 3)) == -1)
goto error;
@@ -111,11 +172,49 @@ style_tostring(struct style *sy)
{
struct grid_cell *gc = &sy->gc;
int off = 0;
- const char *comma = "";
+ const char *comma = "", *tmp;
static char s[256];
+ char b[16];
*s = '\0';
+ if (sy->list != STYLE_LIST_OFF) {
+ if (sy->list == STYLE_LIST_ON)
+ tmp = "on";
+ else if (sy->list == STYLE_LIST_FOCUS)
+ tmp = "focus";
+ else if (sy->list == STYLE_LIST_LEFT_MARKER)
+ tmp = "left-marker";
+ else if (sy->list == STYLE_LIST_RIGHT_MARKER)
+ tmp = "right-marker";
+ off += xsnprintf(s + off, sizeof s - off, "%slist=%s", comma,
+ tmp);
+ comma = ",";
+ }
+ if (sy->range_type != STYLE_RANGE_NONE) {
+ if (sy->range_type == STYLE_RANGE_LEFT)
+ tmp = "left";
+ else if (sy->range_type == STYLE_RANGE_RIGHT)
+ tmp = "right";
+ else if (sy->range_type == STYLE_RANGE_WINDOW) {
+ snprintf(b, sizeof b, "window|%u", sy->range_argument);
+ tmp = b;
+ }
+ off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma,
+ tmp);
+ comma = ",";
+ }
+ if (sy->align != STYLE_ALIGN_DEFAULT) {
+ if (sy->align == STYLE_ALIGN_LEFT)
+ tmp = "left";
+ else if (sy->align == STYLE_ALIGN_CENTRE)
+ tmp = "centre";
+ else if (sy->align == STYLE_ALIGN_RIGHT)
+ tmp = "right";
+ off += xsnprintf(s + off, sizeof s - off, "%salign=%s", comma,
+ tmp);
+ comma = ",";
+ }
if (gc->fg != 8) {
off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma,
colour_tostring(gc->fg));
@@ -180,7 +279,7 @@ style_copy(struct style *dst, struct style *src)
memcpy(dst, src, sizeof *dst);
}
-/* Check if two styles are the same. */
+/* Check if two styles are (visibly) the same. */
int
style_equal(struct style *sy1, struct style *sy2)
{
@@ -193,6 +292,8 @@ style_equal(struct style *sy1, struct style *sy2)
return (0);
if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK))
return (0);
+ if (sy1->align != sy2->align)
+ return (0);
return (1);
}
diff --git a/tmux.1 b/tmux.1
index c7b7d812..2399f289 100644
--- a/tmux.1
+++ b/tmux.1
@@ -251,6 +251,7 @@ client.
.It !
Break the current pane out of the window.
.It \&"
+.\" "
Split the current pane into two, top and bottom.
.It #
List all paste buffers.
@@ -2838,9 +2839,22 @@ is on.
The values are the same as those for
.Ic activity-action .
.It Xo Ic status
-.Op Ic on | off
+.Op Ic off | on | 2 | 3 | 4 | 5
.Xc
-Show or hide the status line.
+Show or hide the status line or specify its size.
+Using
+.Ic on
+gives a status line one row in height;
+.Ic 2 ,
+.Ic 3 ,
+.Ic 4
+or
+.Ic 5
+more rows.
+.It Ic status-format[] Ar format
+Specify the format to be used for each line of the status line.
+The default builds the top status line from the various individual status
+options below.
.It Ic status-interval Ar interval
Update the status line every
.Ar interval
@@ -3669,6 +3683,7 @@ For example, to get a list of windows formatted like the status line:
.Bd -literal -offset indent
#{W:#{E:window-status-format} ,#{E:window-status-current-format} }
.Ed
+.Pp
A prefix of the form
.Ql s/foo/bar/:
will substitute
@@ -3872,8 +3887,9 @@ for the terminal default colour; or a hexadecimal RGB string such as
Set the background colour.
.It Ic none
Set no attributes (turn off any active attributes).
-.It Xo Ic bright (or
-.Ic bold )
+.It Xo Ic bright
+(or
+.Ic bold ) ,
.Ic dim ,
.Ic underscore ,
.Ic blink ,
@@ -3890,6 +3906,54 @@ Set an attribute.
Any of the attributes may be prefixed with
.Ql no
to unset.
+.It Xo Ic align=left
+(or
+.Ic noalign ) ,
+.Ic align=centre ,
+.Ic align=right
+.Xc
+Align text to the left, centre or right of the available space if appropriate.
+.It Xo Ic list=on ,
+.Ic list=focus ,
+.Ic list=left-marker ,
+.Ic list=right=marker ,
+.Ic nolist
+.Xc
+Mark the position of the various window list components in the
+.Ic status-format
+option:
+.Ic list=on
+marks the start of the list;
+.Ic list=focus
+is the part of the list that should be kept in focus if the entire list won't fit
+in the available space (typically the current window);
+.Ic list=left-marker
+and
+.Ic list=right-marker
+mark the text to be used to mark that text has been trimmed from the left or
+right of the list if there is not enough space.
+.It Xo Ic range=left ,
+.Ic range=right ,
+.Ic range=window|X ,
+.Ic norange
+.Xc
+Mark a range in the
+. Ic status-format
+option.
+.Ic range=left
+and
+.Ic range=right
+are the text used for the
+.Ql StatusLeft
+and
+.Ql StatusRight
+mouse keys.
+.Ic range=window|X
+is the range for a window passed to the
+.Ql Status
+mouse key, where
+.Ql X
+is a window index.
.El
.Pp
Examples are:
diff --git a/tmux.h b/tmux.h
index 9adef27f..e6ed430b 100644
--- a/tmux.h
+++ b/tmux.h
@@ -635,9 +635,50 @@ struct grid {
struct grid_line *linedata;
};
+/* Style alignment. */
+enum style_align {
+ STYLE_ALIGN_DEFAULT,
+ STYLE_ALIGN_LEFT,
+ STYLE_ALIGN_CENTRE,
+ STYLE_ALIGN_RIGHT
+};
+
+/* Style list. */
+enum style_list {
+ STYLE_LIST_OFF,
+ STYLE_LIST_ON,
+ STYLE_LIST_FOCUS,
+ STYLE_LIST_LEFT_MARKER,
+ STYLE_LIST_RIGHT_MARKER,
+};
+
+/* Style range. */
+enum style_range_type {
+ STYLE_RANGE_NONE,
+ STYLE_RANGE_LEFT,
+ STYLE_RANGE_RIGHT,
+ STYLE_RANGE_WINDOW
+};
+struct style_range {
+ enum style_range_type type;
+ u_int argument;
+
+ u_int start;
+ u_int end; /* not included */
+
+ TAILQ_ENTRY(style_range) entry;
+};
+TAILQ_HEAD(style_ranges, style_range);
+
/* Style option. */
struct style {
- struct grid_cell gc;
+ struct grid_cell gc;
+
+ enum style_align align;
+ enum style_list list;
+
+ enum style_range_type range_type;
+ u_int range_argument;
};
/* Hook data structures. */
@@ -869,10 +910,6 @@ struct winlink {
struct session *session;
struct window *window;
- size_t status_width;
- struct grid_cell status_cell;
- char *status_text;
-
int flags;
#define WINLINK_BELL 0x1
#define WINLINK_ACTIVITY 0x2
@@ -954,6 +991,7 @@ struct session {
struct winlinks windows;
int statusat;
+ u_int statuslines;
struct hooks *hooks;
struct options *options;
@@ -998,7 +1036,9 @@ struct mouse_event {
int valid;
key_code key;
+
int statusat;
+ u_int statuslines;
u_int x;
u_int y;
@@ -1313,17 +1353,20 @@ struct cmd_entry {
};
/* Status line. */
+#define STATUS_LINES_LIMIT 5
+struct status_line_entry {
+ char *expanded;
+ struct style_ranges ranges;
+};
struct status_line {
- struct event timer;
+ struct event timer;
- struct screen screen;
- struct screen *active;
- int references;
-
- int window_list_offset;
+ struct screen screen;
+ struct screen *active;
+ int references;
- u_int left_size;
- u_int right_size;
+ struct grid_cell style;
+ struct status_line_entry entries[STATUS_LINES_LIMIT];
};
/* Client connection. */
@@ -1582,6 +1625,7 @@ char *paste_make_sample(struct paste_buffer *);
#define FORMAT_PANE 0x80000000U
#define FORMAT_WINDOW 0x40000000U
struct format_tree;
+const char *format_skip(const char *s, const char *end);
int format_true(const char *);
struct format_tree *format_create(struct client *, struct cmdq_item *, int,
int);
@@ -1604,6 +1648,14 @@ void format_defaults_paste_buffer(struct format_tree *,
struct paste_buffer *);
void format_lost_client(struct client *);
+/* format-draw.c */
+void format_draw(struct screen_write_ctx *,
+ const struct grid_cell *, u_int, const char *,
+ struct style_ranges *);
+u_int format_width(const char *);
+char *format_trim_left(const char *, u_int);
+char *format_trim_right(const char *, u_int);
+
/* hooks.c */
struct hook;
struct hooks *hooks_get(struct session *);
@@ -1979,7 +2031,7 @@ void status_timer_start_all(void);
void status_update_cache(struct session *);
int status_at_line(struct client *);
u_int status_line_size(struct client *);
-struct window *status_get_window_at(struct client *, u_int);
+struct style_range *status_get_range(struct client *, u_int, u_int);
void status_init(struct client *);
void status_free(struct client *);
int status_redraw(struct client *);
@@ -2079,9 +2131,6 @@ void screen_write_start(struct screen_write_ctx *, struct window_pane *,
struct screen *);
void screen_write_stop(struct screen_write_ctx *);
void screen_write_reset(struct screen_write_ctx *);
-size_t printflike(1, 2) screen_write_cstrlen(const char *, ...);
-void printflike(4, 5) screen_write_cnputs(struct screen_write_ctx *,
- ssize_t, const struct grid_cell *, const char *, ...);
size_t printflike(1, 2) screen_write_strlen(const char *, ...);
void printflike(3, 4) screen_write_puts(struct screen_write_ctx *,
const struct grid_cell *, const char *, ...);
@@ -2418,8 +2467,6 @@ u_int utf8_strwidth(const struct utf8_data *, ssize_t);
struct utf8_data *utf8_fromcstr(const char *);
char *utf8_tocstr(struct utf8_data *);
u_int utf8_cstrwidth(const char *);
-char *utf8_rtrimcstr(const char *, u_int);
-char *utf8_trimcstr(const char *, u_int);
char *utf8_padcstr(const char *, u_int);
/* procname.c */
diff --git a/utf8.c b/utf8.c
index b25ac06b..08990634 100644
--- a/utf8.c
+++ b/utf8.c
@@ -383,66 +383,6 @@ utf8_cstrwidth(const char *s)
return (width);
}
-/* Trim UTF-8 string to width. Caller frees. */
-char *
-utf8_trimcstr(const char *s, u_int width)
-{
- struct utf8_data *tmp, *next;
- char *out;
- u_int at;
-
- tmp = utf8_fromcstr(s);
-
- at = 0;
- for (next = tmp; next->size != 0; next++) {
- if (at + next->width > width) {
- next->size = 0;
- break;
- }
- at += next->width;
- }
-
- out = utf8_tocstr(tmp);
- free(tmp);
- return (out);
-}
-
-/* Trim UTF-8 string to width. Caller frees. */
-char *
-utf8_rtrimcstr(const char *s, u_int width)
-{
- struct utf8_data *tmp, *next, *end;
- char *out;
- u_int at;
-
- tmp = utf8_fromcstr(s);
-
- for (end = tmp; end->size != 0; end++)
- /* nothing */;
- if (end == tmp) {
- free(tmp);
- return (xstrdup(""));
- }
- next = end - 1;
-
- at = 0;
- for (;;) {
- if (at + next->width > width) {
- next++;
- break;
- }
- at += next->width;
-
- if (next == tmp)
- break;
- next--;
- }
-
- out = utf8_tocstr(next);
- free(tmp);
- return (out);
-}
-
/* Pad UTF-8 string to width. Caller frees. */
char *
utf8_padcstr(const char *s, u_int width)
diff --git a/window-client.c b/window-client.c
index ec98984d..e0637a06 100644
--- a/window-client.c
+++ b/window-client.c
@@ -217,20 +217,36 @@ window_client_draw(__unused void *modedata, void *itemdata,
{
struct window_client_itemdata *item = itemdata;
struct client *c = item->c;
+ struct screen *s = ctx->s;
struct window_pane *wp;
- u_int cx = ctx->s->cx, cy = ctx->s->cy;
+ u_int cx = s->cx, cy = s->cy, lines, at;
if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING)))
return;
wp = c->session->curw->window->active;
- screen_write_preview(ctx, &wp->base, sx, sy - 3);
+ lines = status_line_size(c);
+ if (lines >= sy)
+ lines = 0;
+ if (status_at_line(c) == 0)
+ at = lines;
+ else
+ at = 0;
+
+ screen_write_cursormove(ctx, cx, cy + at, 0);
+ screen_write_preview(ctx, &wp->base, sx, sy - 2 - lines);
- screen_write_cursormove(ctx, cx, cy + sy - 2, 0);
+ if (at != 0)
+ screen_write_cursormove(ctx, cx, cy + 2, 0);
+ else
+ screen_write_cursormove(ctx, cx, cy + sy - 1 - lines, 0);
screen_write_hline(ctx, sx, 0, 0);
- screen_write_cursormove(ctx, cx, cy + sy - 1, 0);
- screen_write_fast_copy(ctx, &c->status.screen, 0, 0, sx, 1);
+ if (at != 0)
+ screen_write_cursormove(ctx, cx, cy, 0);
+ else
+ screen_write_cursormove(ctx, cx, cy + sy - lines, 0);
+ screen_write_fast_copy(ctx, &c->status.screen, 0, 0, sx, lines);
}
static struct screen *
diff --git a/window.c b/window.c
index a74d01d7..8539c9ca 100644
--- a/window.c
+++ b/window.c
@@ -217,7 +217,6 @@ winlink_remove(struct winlinks *wwl, struct winlink *wl)
}
RB_REMOVE(winlinks, wwl, wl);
- free(wl->status_text);
free(wl);
}