mirror of
https://github.com/tmux/tmux.git
synced 2025-03-23 05:38:56 +00:00
Merge branch 'obsd-master'
This commit is contained in:
commit
962f255ee8
@ -132,6 +132,7 @@ dist_tmux_SOURCES = \
|
||||
control.c \
|
||||
environ.c \
|
||||
format.c \
|
||||
format-draw.c \
|
||||
grid-view.c \
|
||||
grid.c \
|
||||
hooks.c \
|
||||
|
871
format-draw.c
Normal file
871
format-draw.c
Normal file
@ -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);
|
||||
}
|
6
format.c
6
format.c
@ -988,7 +988,7 @@ found:
|
||||
}
|
||||
|
||||
/* Skip until end. */
|
||||
static const char *
|
||||
const char *
|
||||
format_skip(const char *s, const char *end)
|
||||
{
|
||||
int brackets = 0;
|
||||
@ -1596,12 +1596,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;
|
||||
|
13
mode-tree.c
13
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);
|
||||
|
||||
|
@ -38,6 +38,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
|
||||
};
|
||||
@ -63,6 +66,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",
|
||||
@ -377,8 +420,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
|
||||
},
|
||||
|
||||
@ -403,6 +447,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,
|
||||
|
@ -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);
|
||||
|
107
screen-write.c
107
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,
|
||||
|
@ -409,12 +409,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;
|
||||
|
||||
@ -501,17 +502,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
433
status.c
433
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,77 +218,37 @@ 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);
|
||||
}
|
||||
|
||||
/* Retrieve options for right string. */
|
||||
static char *
|
||||
status_redraw_get_right(struct client *c, struct grid_cell *gc, size_t *size)
|
||||
{
|
||||
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);
|
||||
|
||||
*size = options_get_number(s->options, "status-right-length");
|
||||
rightlen = screen_write_cstrlen("%s", right);
|
||||
if (rightlen < *size)
|
||||
*size = rightlen;
|
||||
return (right);
|
||||
return (s->statuslines);
|
||||
}
|
||||
|
||||
/* Get window at window list position. */
|
||||
struct window *
|
||||
status_get_window_at(struct client *c, u_int x)
|
||||
struct style_range *
|
||||
status_get_range(struct client *c, u_int x, u_int y)
|
||||
{
|
||||
struct session *s = c->session;
|
||||
struct winlink *wl;
|
||||
struct options *oo;
|
||||
const char *sep;
|
||||
size_t seplen;
|
||||
struct status_line *sl = &c->status;
|
||||
struct style_range *sr;
|
||||
|
||||
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);
|
||||
|
||||
if (x < wl->status_width)
|
||||
return (wl->window);
|
||||
x -= wl->status_width + seplen;
|
||||
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);
|
||||
}
|
||||
|
||||
/* Free all ranges. */
|
||||
static void
|
||||
status_free_ranges(struct style_ranges *srs)
|
||||
{
|
||||
struct style_range *sr, *sr1;
|
||||
|
||||
TAILQ_FOREACH_SAFE(sr, srs, entry, sr1) {
|
||||
TAILQ_REMOVE(srs, sr, entry);
|
||||
free(sr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Save old status line. */
|
||||
static void
|
||||
status_push_screen(struct client *c)
|
||||
@ -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;
|
||||
style_apply(&gc, s->options, "status-style");
|
||||
if (!grid_cells_equal(&gc, &sl->style)) {
|
||||
force = 1;
|
||||
memcpy(&sl->style, &gc, sizeof sl->style);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
/* 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_stop(&ctx);
|
||||
screen_write_start(&ctx, NULL, &sl->screen);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Bail if anything is now too small too. */
|
||||
if (wlwidth == 0 || wlavailable == 0) {
|
||||
screen_free(&window_list);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
draw:
|
||||
/* Begin drawing. */
|
||||
screen_write_start(&ctx, NULL, sl->active);
|
||||
|
||||
/* 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, '<');
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
/* Create format tree. */
|
||||
flags = FORMAT_STATUS;
|
||||
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);
|
||||
flags |= FORMAT_FORCE;
|
||||
ft = format_create(c, NULL, FORMAT_NONE, flags);
|
||||
format_defaults(ft, c, NULL, NULL, NULL);
|
||||
|
||||
expanded = format_expand_time(ft, fmt);
|
||||
/* 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);
|
||||
|
||||
format_free(ft);
|
||||
return (expanded);
|
||||
}
|
||||
fmt = options_array_get(o, i);
|
||||
if (fmt == NULL) {
|
||||
screen_write_clearline(&ctx, gc.bg);
|
||||
continue;
|
||||
}
|
||||
sle = &sl->entries[i];
|
||||
|
||||
/* 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;
|
||||
expanded = format_expand_time(ft, fmt);
|
||||
if (!force &&
|
||||
sle->expanded != NULL &&
|
||||
strcmp(expanded, sle->expanded) == 0) {
|
||||
free(expanded);
|
||||
continue;
|
||||
}
|
||||
changed = 1;
|
||||
|
||||
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");
|
||||
screen_write_clearline(&ctx, gc.bg);
|
||||
status_free_ranges(&sle->ranges);
|
||||
format_draw(&ctx, &gc, width, expanded, &sle->ranges);
|
||||
|
||||
free(sle->expanded);
|
||||
sle->expanded = expanded;
|
||||
}
|
||||
}
|
||||
if (wl == TAILQ_FIRST(&s->lastw))
|
||||
style_apply_update(gc, oo, "window-status-last-style");
|
||||
screen_write_stop(&ctx);
|
||||
|
||||
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");
|
||||
/* Free the format tree. */
|
||||
format_free(ft);
|
||||
|
||||
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)
|
||||
|
111
style.c
111
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);
|
||||
}
|
||||
|
||||
|
72
tmux.1
72
tmux.1
@ -255,6 +255,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.
|
||||
@ -2925,9 +2926,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
|
||||
@ -3771,6 +3785,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
|
||||
@ -3977,8 +3992,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 ,
|
||||
@ -3995,6 +4011,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:
|
||||
|
85
tmux.h
85
tmux.h
@ -637,9 +637,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. */
|
||||
@ -871,10 +912,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
|
||||
@ -956,6 +993,7 @@ struct session {
|
||||
struct winlinks windows;
|
||||
|
||||
int statusat;
|
||||
u_int statuslines;
|
||||
|
||||
struct hooks *hooks;
|
||||
struct options *options;
|
||||
@ -1000,7 +1038,9 @@ struct mouse_event {
|
||||
int valid;
|
||||
|
||||
key_code key;
|
||||
|
||||
int statusat;
|
||||
u_int statuslines;
|
||||
|
||||
u_int x;
|
||||
u_int y;
|
||||
@ -1315,17 +1355,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;
|
||||
struct screen screen;
|
||||
struct screen *active;
|
||||
int references;
|
||||
|
||||
int window_list_offset;
|
||||
|
||||
u_int left_size;
|
||||
u_int right_size;
|
||||
struct grid_cell style;
|
||||
struct status_line_entry entries[STATUS_LINES_LIMIT];
|
||||
};
|
||||
|
||||
/* Client connection. */
|
||||
@ -1584,6 +1627,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);
|
||||
@ -1606,6 +1650,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 *);
|
||||
@ -1981,7 +2033,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 *);
|
||||
@ -2081,9 +2133,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 *, ...);
|
||||
@ -2420,8 +2469,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);
|
||||
|
||||
/* osdep-*.c */
|
||||
|
60
utf8.c
60
utf8.c
@ -408,66 +408,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)
|
||||
|
@ -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 + sy - 2, 0);
|
||||
screen_write_cursormove(ctx, cx, cy + at, 0);
|
||||
screen_write_preview(ctx, &wp->base, sx, sy - 2 - lines);
|
||||
|
||||
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 *
|
||||
|
Loading…
Reference in New Issue
Block a user