Merge branch 'master' into floating_panes

This commit is contained in:
Michael Grant
2026-02-18 09:28:38 +00:00
19 changed files with 356 additions and 195 deletions

View File

@@ -57,30 +57,30 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct paste_buffer *pb; struct paste_buffer *pb = NULL;
char *bufdata, *cause; char *bufname = NULL, *bufdata = NULL, *cause = NULL;
const char *bufname, *olddata; const char *olddata;
size_t bufsize, newsize; size_t bufsize = 0, newsize;
bufname = args_get(args, 'b'); if (args_get(args, 'b') != NULL) {
if (bufname == NULL) bufname = xstrdup(args_get(args, 'b'));
pb = NULL;
else
pb = paste_get_name(bufname); pb = paste_get_name(bufname);
}
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) { if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
if (pb == NULL) { if (pb == NULL) {
if (bufname != NULL) { if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname); cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR); goto fail;
} }
pb = paste_get_top(&bufname); pb = paste_get_top(&bufname);
} }
if (pb == NULL) { if (pb == NULL) {
cmdq_error(item, "no buffer"); cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR); goto fail;
} }
paste_free(pb); paste_free(pb);
free(bufname);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -88,32 +88,28 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (pb == NULL) { if (pb == NULL) {
if (bufname != NULL) { if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname); cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR); goto fail;
} }
pb = paste_get_top(&bufname); pb = paste_get_top(&bufname);
} }
if (pb == NULL) { if (pb == NULL) {
cmdq_error(item, "no buffer"); cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR); goto fail;
} }
if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); goto fail;
return (CMD_RETURN_ERROR);
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_count(args) != 1) { if (args_count(args) != 1) {
cmdq_error(item, "no data specified"); cmdq_error(item, "no data specified");
return (CMD_RETURN_ERROR); goto fail;
} }
if ((newsize = strlen(args_string(args, 0))) == 0) if ((newsize = strlen(args_string(args, 0))) == 0)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
bufsize = 0;
bufdata = NULL;
if (args_has(args, 'a') && pb != NULL) { if (args_has(args, 'a') && pb != NULL) {
olddata = paste_buffer_data(pb, &bufsize); olddata = paste_buffer_data(pb, &bufsize);
bufdata = xmalloc(bufsize); bufdata = xmalloc(bufsize);
@@ -126,12 +122,16 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (paste_set(bufdata, bufsize, bufname, &cause) != 0) { if (paste_set(bufdata, bufsize, bufname, &cause) != 0) {
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(bufdata); goto fail;
free(cause);
return (CMD_RETURN_ERROR);
} }
if (args_has(args, 'w') && tc != NULL) if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, "", bufdata, bufsize); tty_set_selection(&tc->tty, "", bufdata, bufsize);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
fail:
free(bufdata);
free(bufname);
free(cause);
return (CMD_RETURN_ERROR);
} }

View File

@@ -51,29 +51,24 @@ control_notify_window_layout_changed(struct window *w)
template = "%layout-change #{window_id} #{window_layout} " template = "%layout-change #{window_id} #{window_layout} "
"#{window_visible_layout} #{window_raw_flags}"; "#{window_visible_layout} #{window_raw_flags}";
/*
* When the last pane in a window is closed it won't have a layout root
* and we don't need to inform the client about the layout change
* because the whole window will go away soon.
*/
wl = TAILQ_FIRST(&w->winlinks);
if (wl == NULL || w->layout_root == NULL)
return;
cp = format_single(NULL, template, NULL, NULL, wl, NULL);
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue; continue;
s = c->session; s = c->session;
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
if (winlink_find_by_window_id(&s->windows, w->id) == NULL)
continue;
/*
* When the last pane in a window is closed it won't have a
* layout root and we don't need to inform the client about the
* layout change because the whole window will go away soon.
*/
if (w->layout_root == NULL)
continue;
wl = winlink_find_by_window(&s->windows, w);
if (wl != NULL) {
cp = format_single(NULL, template, c, NULL, wl, NULL);
control_write(c, "%s", cp); control_write(c, "%s", cp);
}
free(cp); free(cp);
}
}
} }
void void

134
control.c
View File

@@ -610,7 +610,7 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
struct evbuffer *message, struct window_pane *wp, size_t size) struct evbuffer *message, struct window_pane *wp, size_t size)
{ {
u_char *new_data; u_char *new_data;
size_t new_size; size_t new_size, start;
u_int i; u_int i;
if (message == NULL) { if (message == NULL) {
@@ -629,10 +629,16 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
if (new_size < size) if (new_size < size)
fatalx("not enough data: %zu < %zu", new_size, size); fatalx("not enough data: %zu < %zu", new_size, size);
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (new_data[i] < ' ' || new_data[i] == '\\') if (new_data[i] < ' ' || new_data[i] == '\\') {
evbuffer_add_printf(message, "\\%03o", new_data[i]); evbuffer_add_printf(message, "\\%03o", new_data[i]);
else } else {
evbuffer_add_printf(message, "%c", new_data[i]); start = i;
while (i + 1 < size &&
new_data[i + 1] >= ' ' &&
new_data[i + 1] != '\\')
i++;
evbuffer_add(message, new_data + start, i - start + 1);
}
} }
window_pane_update_used_data(wp, &cp->offset, size); window_pane_update_used_data(wp, &cp->offset, size);
return (message); return (message);
@@ -840,15 +846,13 @@ control_stop(struct client *c)
/* Check session subscription. */ /* Check session subscription. */
static void static void
control_check_subs_session(struct client *c, struct control_sub *csub) control_check_subs_session(struct client *c, struct control_sub *csub,
struct format_tree *ft)
{ {
struct session *s = c->session; struct session *s = c->session;
struct format_tree *ft;
char *value; char *value;
ft = format_create_defaults(NULL, c, s, NULL, NULL);
value = format_expand(ft, csub->format); value = format_expand(ft, csub->format);
format_free(ft);
if (csub->last != NULL && strcmp(value, csub->last) == 0) { if (csub->last != NULL && strcmp(value, csub->last) == 0) {
free(value); free(value);
@@ -909,24 +913,17 @@ control_check_subs_pane(struct client *c, struct control_sub *csub)
} }
} }
/* Check all panes subscription. */ /* Check all-panes subscription for a pane. */
static void static void
control_check_subs_all_panes(struct client *c, struct control_sub *csub) control_check_subs_all_panes_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl, struct window_pane *wp)
{ {
struct session *s = c->session; struct session *s = c->session;
struct window_pane *wp; struct window *w = wl->window;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value; char *value;
struct control_sub_pane *csp, find; struct control_sub_pane *csp, find;
RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window;
TAILQ_FOREACH(wp, &w->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
value = format_expand(ft, csub->format); value = format_expand(ft, csub->format);
format_free(ft);
find.pane = wp->id; find.pane = wp->id;
find.idx = wl->idx; find.idx = wl->idx;
@@ -939,18 +936,15 @@ control_check_subs_all_panes(struct client *c, struct control_sub *csub)
RB_INSERT(control_sub_panes, &csub->panes, csp); RB_INSERT(control_sub_panes, &csub->panes, csp);
} }
if (csp->last != NULL && if (csp->last != NULL && strcmp(value, csp->last) == 0) {
strcmp(value, csp->last) == 0) {
free(value); free(value);
continue; return;
} }
control_write(c, control_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s", "%%subscription-changed %s $%u @%u %u %%%u : %s",
csub->name, s->id, w->id, wl->idx, wp->id, value); csub->name, s->id, w->id, wl->idx, wp->id, value);
free(csp->last); free(csp->last);
csp->last = value; csp->last = value;
}
}
} }
/* Check window subscription. */ /* Check window subscription. */
@@ -999,23 +993,17 @@ control_check_subs_window(struct client *c, struct control_sub *csub)
} }
} }
/* Check all windows subscription. */ /* Check all-windows subscription for a window. */
static void static void
control_check_subs_all_windows(struct client *c, struct control_sub *csub) control_check_subs_all_windows_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl)
{ {
struct session *s = c->session; struct session *s = c->session;
struct window *w; struct window *w = wl->window;
struct winlink *wl;
struct format_tree *ft;
char *value; char *value;
struct control_sub_window *csw, find; struct control_sub_window *csw, find;
RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window;
ft = format_create_defaults(NULL, c, s, wl, NULL);
value = format_expand(ft, csub->format); value = format_expand(ft, csub->format);
format_free(ft);
find.window = w->id; find.window = w->id;
find.idx = wl->idx; find.idx = wl->idx;
@@ -1030,14 +1018,13 @@ control_check_subs_all_windows(struct client *c, struct control_sub *csub)
if (csw->last != NULL && strcmp(value, csw->last) == 0) { if (csw->last != NULL && strcmp(value, csw->last) == 0) {
free(value); free(value);
continue; return;
} }
control_write(c, control_write(c,
"%%subscription-changed %s $%u @%u %u - : %s", "%%subscription-changed %s $%u @%u %u - : %s",
csub->name, s->id, w->id, wl->idx, value); csub->name, s->id, w->id, wl->idx, value);
free(csw->last); free(csw->last);
csw->last = value; csw->last = value;
}
} }
/* Check subscriptions timer. */ /* Check subscriptions timer. */
@@ -1047,30 +1034,91 @@ control_check_subs_timer(__unused int fd, __unused short events, void *data)
struct client *c = data; struct client *c = data;
struct control_state *cs = c->control_state; struct control_state *cs = c->control_state;
struct control_sub *csub, *csub1; struct control_sub *csub, *csub1;
struct session *s = c->session;
struct format_tree *ft;
struct winlink *wl;
struct window_pane *wp;
struct timeval tv = { .tv_sec = 1 }; struct timeval tv = { .tv_sec = 1 };
int have_session = 0, have_all_panes = 0;
int have_all_windows = 0;
log_debug("%s: timer fired", __func__); log_debug("%s: timer fired", __func__);
evtimer_add(&cs->subs_timer, &tv); evtimer_add(&cs->subs_timer, &tv);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) { /* Find which subscription types are present. */
RB_FOREACH(csub, control_subs, &cs->subs) {
switch (csub->type) { switch (csub->type) {
case CONTROL_SUB_SESSION: case CONTROL_SUB_SESSION:
control_check_subs_session(c, csub); have_session = 1;
break;
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
break; break;
case CONTROL_SUB_ALL_PANES: case CONTROL_SUB_ALL_PANES:
control_check_subs_all_panes(c, csub); have_all_panes = 1;
break;
case CONTROL_SUB_ALL_WINDOWS:
have_all_windows = 1;
break;
default:
break;
}
}
/* Check session subscriptions. */
if (have_session) {
ft = format_create_defaults(NULL, c, s, NULL, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
if (csub->type == CONTROL_SUB_SESSION)
control_check_subs_session(c, csub, ft);
}
format_free(ft);
}
/* Check pane and window subscriptions. */
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
switch (csub->type) {
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
break; break;
case CONTROL_SUB_WINDOW: case CONTROL_SUB_WINDOW:
control_check_subs_window(c, csub); control_check_subs_window(c, csub);
break; break;
case CONTROL_SUB_SESSION:
case CONTROL_SUB_ALL_PANES:
case CONTROL_SUB_ALL_WINDOWS: case CONTROL_SUB_ALL_WINDOWS:
control_check_subs_all_windows(c, csub);
break; break;
} }
} }
/* Check all-panes subscriptions. */
if (have_all_panes) {
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_PANES)
continue;
control_check_subs_all_panes_one(c,
csub, ft, wl, wp);
}
format_free(ft);
}
}
}
/* Check all-windows subscriptions. */
if (have_all_windows) {
RB_FOREACH(wl, winlinks, &s->windows) {
ft = format_create_defaults(NULL, c, s, wl, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_WINDOWS)
continue;
control_check_subs_all_windows_one(c, csub, ft,
wl);
}
format_free(ft);
}
}
} }
/* Add a subscription. */ /* Add a subscription. */

View File

@@ -2012,8 +2012,10 @@ format_cb_pane_bottom(struct format_tree *ft)
static void * static void *
format_cb_pane_dead(struct format_tree *ft) format_cb_pane_dead(struct format_tree *ft)
{ {
if (ft->wp != NULL) { struct window_pane *wp = ft->wp;
if (ft->wp->fd == -1)
if (wp != NULL) {
if (wp->fd == -1 && (wp->flags & PANE_STATUSREADY))
return (xstrdup("1")); return (xstrdup("1"));
return (xstrdup("0")); return (xstrdup("0"));
} }

12
grid.c
View File

@@ -205,10 +205,16 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
struct grid_line *gl = &gd->linedata[py]; struct grid_line *gl = &gd->linedata[py];
struct grid_cell_entry *gce = &gl->celldata[px]; struct grid_cell_entry *gce = &gl->celldata[px];
struct grid_extd_entry *gee; struct grid_extd_entry *gee;
u_int old_offset = gce->offset;
int had_extd = (gce->flags & GRID_FLAG_EXTENDED);
memcpy(gce, &grid_cleared_entry, sizeof *gce); memcpy(gce, &grid_cleared_entry, sizeof *gce);
if (bg != 8) { if (bg != 8) {
if (bg & COLOUR_FLAG_RGB) { if (bg & COLOUR_FLAG_RGB) {
if (had_extd && old_offset < gl->extdsize) {
gce->flags |= GRID_FLAG_EXTENDED;
gce->offset = old_offset;
} else
grid_get_extended_cell(gl, gce, gce->flags); grid_get_extended_cell(gl, gce, gce->flags);
gee = grid_extended_cell(gl, gce, &grid_cleared_cell); gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
gee->bg = bg; gee->bg = bg;
@@ -1089,12 +1095,16 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off = 0; off = 0;
gl = grid_peek_line(gd, py); gl = grid_peek_line(gd, py);
if (gl == NULL) {
buf[0] = '\0';
return (buf);
}
if (flags & GRID_STRING_EMPTY_CELLS) if (flags & GRID_STRING_EMPTY_CELLS)
end = gl->cellsize; end = gl->cellsize;
else else
end = gl->cellused; end = gl->cellused;
for (xx = px; xx < px + nx; xx++) { for (xx = px; xx < px + nx; xx++) {
if (gl == NULL || xx >= end) if (xx >= end)
break; break;
grid_get_cell(gd, xx, py, &gc); grid_get_cell(gd, xx, py, &gc);
if (gc.flags & GRID_FLAG_PADDING) if (gc.flags & GRID_FLAG_PADDING)

75
input.c
View File

@@ -101,6 +101,7 @@ struct input_ctx {
struct bufferevent *event; struct bufferevent *event;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
struct colour_palette *palette; struct colour_palette *palette;
struct client *c;
struct input_cell cell; struct input_cell cell;
struct input_cell old_cell; struct input_cell old_cell;
@@ -854,7 +855,7 @@ input_restore_state(struct input_ctx *ictx)
/* Initialise input parser. */ /* Initialise input parser. */
struct input_ctx * struct input_ctx *
input_init(struct window_pane *wp, struct bufferevent *bev, input_init(struct window_pane *wp, struct bufferevent *bev,
struct colour_palette *palette) struct colour_palette *palette, struct client *c)
{ {
struct input_ctx *ictx; struct input_ctx *ictx;
@@ -862,6 +863,7 @@ input_init(struct window_pane *wp, struct bufferevent *bev,
ictx->wp = wp; ictx->wp = wp;
ictx->event = bev; ictx->event = bev;
ictx->palette = palette; ictx->palette = palette;
ictx->c = c;
ictx->input_space = INPUT_BUF_START; ictx->input_space = INPUT_BUF_START;
ictx->input_buf = xmalloc(INPUT_BUF_START); ictx->input_buf = xmalloc(INPUT_BUF_START);
@@ -3110,31 +3112,28 @@ input_osc_52_reply(struct input_ctx *ictx)
input_add_request(ictx, INPUT_REQUEST_CLIPBOARD, ictx->input_end); input_add_request(ictx, INPUT_REQUEST_CLIPBOARD, ictx->input_end);
} }
/* Handle the OSC 52 sequence for setting the clipboard. */ /*
static void * Parse and decode OSC 52 clipboard data. Returns 0 on failure or if handled
input_osc_52(struct input_ctx *ictx, const char *p) * as a query. On success, returns 1 and sets *out, *outlen, and *flags (caller
* must free *out).
*/
static int
input_osc_52_parse(struct input_ctx *ictx, const char *p, u_char **out,
int *outlen, char *flags)
{ {
struct window_pane *wp = ictx->wp;
size_t len;
char *end; char *end;
u_char *out; size_t len;
int outlen, state; const char *allow = "cpqs01234567";
struct screen_write_ctx ctx;
const char* allow = "cpqs01234567";
char flags[sizeof "cpqs01234567"] = "";
u_int i, j = 0; u_int i, j = 0;
if (wp == NULL) if (options_get_number(global_options, "set-clipboard") != 2)
return; return (0);
state = options_get_number(global_options, "set-clipboard");
if (state != 2)
return;
if ((end = strchr(p, ';')) == NULL) if ((end = strchr(p, ';')) == NULL)
return; return (0);
end++; end++;
if (*end == '\0') if (*end == '\0')
return; return (0);
log_debug("%s: %s", __func__, end); log_debug("%s: %s", __func__, end);
for (i = 0; p + i != end; i++) { for (i = 0; p + i != end; i++) {
@@ -3145,25 +3144,53 @@ input_osc_52(struct input_ctx *ictx, const char *p)
if (strcmp(end, "?") == 0) { if (strcmp(end, "?") == 0) {
input_osc_52_reply(ictx); input_osc_52_reply(ictx);
return; return (0);
} }
len = (strlen(end) / 4) * 3; len = (strlen(end) / 4) * 3;
if (len == 0) if (len == 0)
return (0);
*out = xmalloc(len);
if ((*outlen = b64_pton(end, *out, len)) == -1) {
free(*out);
*out = NULL;
return (0);
}
return (1);
}
/* Handle the OSC 52 sequence for setting the clipboard. */
static void
input_osc_52(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
struct screen_write_ctx ctx;
u_char *out;
int outlen;
char flags[sizeof "cpqs01234567"] = "";
if (!input_osc_52_parse(ictx, p, &out, &outlen, flags))
return; return;
out = xmalloc(len); if (wp == NULL) {
if ((outlen = b64_pton(end, out, len)) == -1) { /* Popup window. */
if (ictx->c == NULL) {
free(out); free(out);
return; return;
} }
tty_set_selection(&ictx->c->tty, flags, out, outlen);
paste_add(NULL, out, outlen);
} else {
/* Normal window. */
screen_write_start_pane(&ctx, wp, NULL); screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, flags, out, outlen); screen_write_setselection(&ctx, flags, out, outlen);
screen_write_stop(&ctx); screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp); notify_pane("pane-set-clipboard", wp);
paste_add(NULL, out, outlen); paste_add(NULL, out, outlen);
}
free(out);
} }
/* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */ /* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */
@@ -3434,7 +3461,7 @@ input_cancel_requests(struct client *c)
{ {
struct input_request *ir, *ir1; struct input_request *ir, *ir1;
TAILQ_FOREACH_SAFE(ir, &c->input_requests, entry, ir1) TAILQ_FOREACH_SAFE(ir, &c->input_requests, centry, ir1)
input_free_request(ir); input_free_request(ir);
} }

View File

@@ -297,12 +297,12 @@ key_bindings_remove_table(const char *name)
table = key_bindings_get_table(name, 0); table = key_bindings_get_table(name, 0);
if (table != NULL) { if (table != NULL) {
RB_REMOVE(key_tables, &key_tables, table); RB_REMOVE(key_tables, &key_tables, table);
key_bindings_unref_table(table);
}
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (c->keytable == table) if (c->keytable == table)
server_client_set_key_table(c, NULL); server_client_set_key_table(c, NULL);
} }
key_bindings_unref_table(table);
}
} }
void void

View File

@@ -106,7 +106,7 @@ paste_is_empty(void)
/* Get the most recent automatic buffer. */ /* Get the most recent automatic buffer. */
struct paste_buffer * struct paste_buffer *
paste_get_top(const char **name) paste_get_top(char **name)
{ {
struct paste_buffer *pb; struct paste_buffer *pb;
@@ -116,7 +116,7 @@ paste_get_top(const char **name)
if (pb == NULL) if (pb == NULL)
return (NULL); return (NULL);
if (name != NULL) if (name != NULL)
*name = pb->name; *name = xstrdup(pb->name);
return (pb); return (pb);
} }

View File

@@ -850,7 +850,7 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
pd->job = job_run(shellcmd, argc, argv, env, s, cwd, pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
popup_job_update_cb, popup_job_complete_cb, NULL, pd, popup_job_update_cb, popup_job_complete_cb, NULL, pd,
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy); JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette); pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette, c);
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);

View File

@@ -250,7 +250,7 @@ skip:
/* Return whether a suitable size was found. */ /* Return whether a suitable size was found. */
if (type == WINDOW_SIZE_MANUAL) { if (type == WINDOW_SIZE_MANUAL) {
log_debug("%s: type is manual", __func__); log_debug("%s: type is manual", __func__);
return (1); return (w != NULL);
} }
if (type == WINDOW_SIZE_LARGEST) { if (type == WINDOW_SIZE_LARGEST) {
log_debug("%s: type is largest", __func__); log_debug("%s: type is largest", __func__);

View File

@@ -2746,15 +2746,28 @@ server_client_loop(void)
struct client *c; struct client *c;
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
struct window_mode_entry *wme;
/* Check for window resize. This is done before redrawing. */ /* Check for window resize. This is done before redrawing. */
RB_FOREACH(w, windows, &windows) RB_FOREACH(w, windows, &windows)
server_client_check_window_resize(w); server_client_check_window_resize(w);
/* Notify modes that pane styles may have changed. */
RB_FOREACH(w, windows, &windows) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->flags & PANE_STYLECHANGED) {
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL &&
wme->mode->style_changed != NULL)
wme->mode->style_changed(wme);
}
}
}
/* Check clients. */ /* Check clients. */
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
server_client_check_exit(c); server_client_check_exit(c);
if (c->session != NULL) { if (c->session != NULL && c->session->curw != NULL) {
server_client_check_modes(c); server_client_check_modes(c);
server_client_check_redraw(c); server_client_check_redraw(c);
server_client_reset_state(c); server_client_reset_state(c);

40
sort.c
View File

@@ -197,23 +197,25 @@ sort_pane_cmp(const void *a0, const void *b0)
case SORT_CREATION: case SORT_CREATION:
result = a->id - b->id; result = a->id - b->id;
break; break;
case SORT_INDEX:
case SORT_NAME:
case SORT_ORDER:
case SORT_SIZE: case SORT_SIZE:
case SORT_END: result = a->sx * a->sy - b->sx * b->sy;
break; break;
} case SORT_INDEX:
if (result == 0) {
/*
* Panes don't have names, so use number order for any other
* sort field.
*/
window_pane_index(a, &ai); window_pane_index(a, &ai);
window_pane_index(b, &bi); window_pane_index(b, &bi);
result = ai - bi; result = ai - bi;
break;
case SORT_NAME:
result = strcmp(a->screen->title, b->screen->title);
break;
case SORT_ORDER:
case SORT_END:
break;
} }
if (result == 0)
result = strcmp(a->screen->title, b->screen->title);
if (sort_crit->reversed) if (sort_crit->reversed)
result = -result; result = -result;
return (result); return (result);
@@ -235,6 +237,16 @@ sort_winlink_cmp(const void *a0, const void *b0)
case SORT_INDEX: case SORT_INDEX:
result = wla->idx - wlb->idx; result = wla->idx - wlb->idx;
break; break;
case SORT_CREATION:
if (timercmp(&wa->creation_time, &wb->creation_time, >)) {
result = -1;
break;
}
if (timercmp(&wa->creation_time, &wb->creation_time, <)) {
result = 1;
break;
}
break;
case SORT_ACTIVITY: case SORT_ACTIVITY:
if (timercmp(&wa->activity_time, &wb->activity_time, >)) { if (timercmp(&wa->activity_time, &wb->activity_time, >)) {
result = -1; result = -1;
@@ -248,9 +260,10 @@ sort_winlink_cmp(const void *a0, const void *b0)
case SORT_NAME: case SORT_NAME:
result = strcmp(wa->name, wb->name); result = strcmp(wa->name, wb->name);
break; break;
case SORT_CREATION:
case SORT_ORDER:
case SORT_SIZE: case SORT_SIZE:
result = wa->sx * wa->sy - wb->sx * wb->sy;
break;
case SORT_ORDER:
case SORT_END: case SORT_END:
break; break;
} }
@@ -295,7 +308,8 @@ sort_order_from_string(const char* order)
return (SORT_CREATION); return (SORT_CREATION);
if (strcasecmp(order, "index") == 0) if (strcasecmp(order, "index") == 0)
return (SORT_INDEX); return (SORT_INDEX);
if (strcasecmp(order, "name") == 0) if (strcasecmp(order, "name") == 0 ||
strcasecmp(order, "title") == 0)
return (SORT_NAME); return (SORT_NAME);
if (strcasecmp(order, "order") == 0) if (strcasecmp(order, "order") == 0)
return (SORT_ORDER); return (SORT_ORDER);

10
tmux.1
View File

@@ -3092,7 +3092,11 @@ See the
section. section.
.Fl O .Fl O
specifies the sort order: one of specifies the sort order: one of
.Ql name , .Ql name
(title),
.Ql index ,
.Ql size
(area),
.Ql creation .Ql creation
(time), or (time), or
.Ql activity .Ql activity
@@ -3125,6 +3129,10 @@ section.
specifies the sort order: one of specifies the sort order: one of
.Ql index , .Ql index ,
.Ql name , .Ql name ,
.Ql size
(area),
.Ql creation
(time),
or or
.Ql activity .Ql activity
(time). (time).

6
tmux.h
View File

@@ -1101,6 +1101,7 @@ struct window_mode {
void (*free)(struct window_mode_entry *); void (*free)(struct window_mode_entry *);
void (*resize)(struct window_mode_entry *, u_int, u_int); void (*resize)(struct window_mode_entry *, u_int, u_int);
void (*update)(struct window_mode_entry *); void (*update)(struct window_mode_entry *);
void (*style_changed)(struct window_mode_entry *);
void (*key)(struct window_mode_entry *, struct client *, void (*key)(struct window_mode_entry *, struct client *,
struct session *, struct winlink *, key_code, struct session *, struct winlink *, key_code,
struct mouse_event *); struct mouse_event *);
@@ -1305,6 +1306,7 @@ struct window {
struct event offset_timer; struct event offset_timer;
struct timeval activity_time; struct timeval activity_time;
struct timeval creation_time;
struct window_pane *active; struct window_pane *active;
struct window_panes last_panes; struct window_panes last_panes;
@@ -2363,7 +2365,7 @@ time_t paste_buffer_created(struct paste_buffer *);
const char *paste_buffer_data(struct paste_buffer *, size_t *); const char *paste_buffer_data(struct paste_buffer *, size_t *);
struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_walk(struct paste_buffer *);
int paste_is_empty(void); int paste_is_empty(void);
struct paste_buffer *paste_get_top(const char **); struct paste_buffer *paste_get_top(char **);
struct paste_buffer *paste_get_name(const char *); struct paste_buffer *paste_get_name(const char *);
void paste_free(struct paste_buffer *); void paste_free(struct paste_buffer *);
void paste_add(const char *, char *, size_t); void paste_add(const char *, char *, size_t);
@@ -3065,7 +3067,7 @@ void recalculate_sizes_now(int);
/* input.c */ /* input.c */
#define INPUT_BUF_DEFAULT_SIZE 1048576 #define INPUT_BUF_DEFAULT_SIZE 1048576
struct input_ctx *input_init(struct window_pane *, struct bufferevent *, struct input_ctx *input_init(struct window_pane *, struct bufferevent *,
struct colour_palette *); struct colour_palette *, struct client *);
void input_free(struct input_ctx *); void input_free(struct input_ctx *);
void input_reset(struct input_ctx *, int); void input_reset(struct input_ctx *, int);
struct evbuffer *input_pending(struct input_ctx *); struct evbuffer *input_pending(struct input_ctx *);

View File

@@ -262,10 +262,12 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
else else
next_state = TTY_DRAW_LINE_NEW1; next_state = TTY_DRAW_LINE_NEW1;
} }
log_debug("%s: cell %u empty %u, bg %u; state: current %s, " if (log_get_level() != 0) {
"next %s", __func__, px + i, empty, gcp->bg, log_debug("%s: cell %u empty %u, bg %u; state: "
tty_draw_line_states[current_state], "current %s, next %s", __func__, px + i, empty,
gcp->bg, tty_draw_line_states[current_state],
tty_draw_line_states[next_state]); tty_draw_line_states[next_state]);
}
/* If the state has changed, flush any collected data. */ /* If the state has changed, flush any collected data. */
if (next_state != current_state) { if (next_state != current_state) {

View File

@@ -1377,6 +1377,10 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
/* Convert from base64. */ /* Convert from base64. */
needed = (end / 4) * 3; needed = (end / 4) * 3;
if (needed == 0) {
free(copy);
return (0);
}
out = xmalloc(needed); out = xmalloc(needed);
if ((outlen = b64_pton(copy, out, len)) == -1) { if ((outlen = b64_pton(copy, out, len)) == -1) {
free(out); free(out);
@@ -1612,8 +1616,10 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
} }
if (i == (sizeof tmp) - 1) if (i == (sizeof tmp) - 1)
return (-1); return (-1);
tmp[i - 1] = '\0';
*size = 5 + i; *size = 5 + i;
if (i == 0)
return (0);
tmp[i - 1] = '\0';
/* Add terminal features. */ /* Add terminal features. */
if (strncmp(tmp, "iTerm2 ", 7) == 0) if (strncmp(tmp, "iTerm2 ", 7) == 0)
@@ -1686,11 +1692,13 @@ tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size,
} }
if (i == (sizeof tmp) - 1) if (i == (sizeof tmp) - 1)
return (-1); return (-1);
*size = 6 + i;
if (i == 0)
return (0);
if (tmp[i - 1] == '\033') if (tmp[i - 1] == '\033')
tmp[i - 1] = '\0'; tmp[i - 1] = '\0';
else else
tmp[i] = '\0'; tmp[i] = '\0';
*size = 6 + i;
/* Work out the colour. */ /* Work out the colour. */
n = colour_parseX11(tmp); n = colour_parseX11(tmp);
@@ -1755,11 +1763,13 @@ tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size)
} }
if (i == (sizeof tmp) - 1) if (i == (sizeof tmp) - 1)
return (-1); return (-1);
*size = 5 + i;
if (i == 0)
return (0);
if (tmp[i - 1] == '\033') if (tmp[i - 1] == '\033')
tmp[i - 1] = '\0'; tmp[i - 1] = '\0';
else else
tmp[i] = '\0'; tmp[i] = '\0';
*size = 5 + i;
/* Parse index. */ /* Parse index. */
idx = strtol(tmp, &endptr, 10); idx = strtol(tmp, &endptr, 10);

4
tty.c
View File

@@ -633,7 +633,8 @@ tty_add(struct tty *tty, const char *buf, size_t len)
if (tty_log_fd != -1) if (tty_log_fd != -1)
write(tty_log_fd, buf, len); write(tty_log_fd, buf, len);
if (tty->flags & TTY_STARTED) if ((tty->flags & TTY_STARTED) &&
!event_pending(&tty->event_out, EV_WRITE, NULL))
event_add(&tty->event_out, NULL); event_add(&tty->event_out, NULL);
} }
@@ -2289,7 +2290,6 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
/* If it is a single character, write with putc to handle ACS. */ /* If it is a single character, write with putc to handle ACS. */
if (gcp->data.size == 1) { if (gcp->data.size == 1) {
tty_attributes(tty, gcp, defaults, palette, hl);
if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f) if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
return; return;
tty_putc(tty, *gcp->data.data); tty_putc(tty, *gcp->data.data);

View File

@@ -51,6 +51,7 @@ static void window_copy_redraw_selection(struct window_mode_entry *, u_int);
static void window_copy_redraw_lines(struct window_mode_entry *, u_int, static void window_copy_redraw_lines(struct window_mode_entry *, u_int,
u_int); u_int);
static void window_copy_redraw_screen(struct window_mode_entry *); static void window_copy_redraw_screen(struct window_mode_entry *);
static void window_copy_style_changed(struct window_mode_entry *);
static void window_copy_write_line(struct window_mode_entry *, static void window_copy_write_line(struct window_mode_entry *,
struct screen_write_ctx *, u_int); struct screen_write_ctx *, u_int);
static void window_copy_write_lines(struct window_mode_entry *, static void window_copy_write_lines(struct window_mode_entry *,
@@ -158,6 +159,7 @@ const struct window_mode window_copy_mode = {
.init = window_copy_init, .init = window_copy_init,
.free = window_copy_free, .free = window_copy_free,
.resize = window_copy_resize, .resize = window_copy_resize,
.style_changed = window_copy_style_changed,
.key_table = window_copy_key_table, .key_table = window_copy_key_table,
.command = window_copy_command, .command = window_copy_command,
.formats = window_copy_formats, .formats = window_copy_formats,
@@ -170,6 +172,7 @@ const struct window_mode window_view_mode = {
.init = window_copy_view_init, .init = window_copy_view_init,
.free = window_copy_free, .free = window_copy_free,
.resize = window_copy_resize, .resize = window_copy_resize,
.style_changed = window_copy_style_changed,
.key_table = window_copy_key_table, .key_table = window_copy_key_table,
.command = window_copy_command, .command = window_copy_command,
.formats = window_copy_formats, .formats = window_copy_formats,
@@ -352,7 +355,7 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx,
if (trim) { if (trim) {
while (sy > screen_hsize(src)) { while (sy > screen_hsize(src)) {
gl = grid_peek_line(src->grid, sy - 1); gl = grid_peek_line(src->grid, sy - 1);
if (gl->cellused != 0) if (gl == NULL || gl->cellused != 0)
break; break;
sy--; sy--;
} }
@@ -491,7 +494,7 @@ window_copy_view_init(struct window_mode_entry *wme,
data->backing = xmalloc(sizeof *data->backing); data->backing = xmalloc(sizeof *data->backing);
screen_init(data->backing, sx, screen_size_y(base), UINT_MAX); screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
data->ictx = input_init(NULL, NULL, NULL); data->ictx = input_init(NULL, NULL, NULL, NULL);
data->mx = data->cx; data->mx = data->cx;
data->my = screen_hsize(data->backing) + data->cy - data->oy; data->my = screen_hsize(data->backing) + data->cy - data->oy;
data->showmark = 0; data->showmark = 0;
@@ -3622,6 +3625,10 @@ window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
buf = xrealloc(buf, bufsize); buf = xrealloc(buf, bufsize);
gl = grid_peek_line(gd, py); gl = grid_peek_line(gd, py);
if (gl == NULL) {
buf[*size - 1] = '\0';
return (buf);
}
bx = *size - 1; bx = *size - 1;
for (ax = first; ax < last; ax++) { for (ax = first; ax < last; ax++) {
d = window_copy_cellstring(gl, ax, &dlen, &allocated); d = window_copy_cellstring(gl, ax, &dlen, &allocated);
@@ -3667,6 +3674,10 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
px = *ppx; px = *ppx;
pywrap = *ppy; pywrap = *ppy;
gl = grid_peek_line(gd, pywrap); gl = grid_peek_line(gd, pywrap);
if (gl == NULL) {
free(cells);
return;
}
while (cell < ncells) { while (cell < ncells) {
cells[cell].d = window_copy_cellstring(gl, px, cells[cell].d = window_copy_cellstring(gl, px,
&cells[cell].dlen, &cells[cell].allocated); &cells[cell].dlen, &cells[cell].allocated);
@@ -3676,6 +3687,8 @@ window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
px = 0; px = 0;
pywrap++; pywrap++;
gl = grid_peek_line(gd, pywrap); gl = grid_peek_line(gd, pywrap);
if (gl == NULL)
break;
} }
} }
@@ -4077,7 +4090,7 @@ window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
for (*start = gd->hsize - data->oy; *start > 0; (*start)--) { for (*start = gd->hsize - data->oy; *start > 0; (*start)--) {
gl = grid_peek_line(gd, (*start) - 1); gl = grid_peek_line(gd, (*start) - 1);
if (~gl->flags & GRID_LINE_WRAPPED) if (gl == NULL || ~gl->flags & GRID_LINE_WRAPPED)
break; break;
} }
*end = gd->hsize - data->oy + gd->sy; *end = gd->hsize - data->oy + gd->sy;
@@ -4268,6 +4281,9 @@ window_copy_clear_marks(struct window_mode_entry *wme)
{ {
struct window_copy_mode_data *data = wme->data; struct window_copy_mode_data *data = wme->data;
data->searchcount = -1;
data->searchmore = 0;
free(data->searchmark); free(data->searchmark);
data->searchmark = NULL; data->searchmark = NULL;
} }
@@ -4595,6 +4611,16 @@ window_copy_redraw_screen(struct window_mode_entry *wme)
window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen)); window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
} }
static void
window_copy_style_changed(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
if (data->screen.sel != NULL)
window_copy_set_selection(wme, 0, 1);
window_copy_redraw_screen(wme);
}
static void static void
window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
int no_reset) int no_reset)
@@ -5036,9 +5062,9 @@ static void
window_copy_append_selection(struct window_mode_entry *wme) window_copy_append_selection(struct window_mode_entry *wme)
{ {
struct window_pane *wp = wme->wp; struct window_pane *wp = wme->wp;
char *buf; char *buf, *bufname = NULL;
struct paste_buffer *pb; struct paste_buffer *pb;
const char *bufdata, *bufname = NULL; const char *bufdata;
size_t len, bufsize; size_t len, bufsize;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
@@ -5063,6 +5089,7 @@ window_copy_append_selection(struct window_mode_entry *wme)
} }
if (paste_set(buf, len, bufname, NULL) != 0) if (paste_set(buf, len, bufname, NULL) != 0)
free(buf); free(buf);
free(bufname);
} }
static void static void

View File

@@ -329,6 +329,9 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
RB_INSERT(windows, &windows, w); RB_INSERT(windows, &windows, w);
window_set_fill_character(w); window_set_fill_character(w);
if (gettimeofday(&w->creation_time, NULL) != 0)
fatal("gettimeofday failed");
window_update_activity(w); window_update_activity(w);
log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy, log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy,
@@ -1171,7 +1174,7 @@ window_pane_set_event(struct window_pane *wp)
NULL, window_pane_error_callback, wp); NULL, window_pane_error_callback, wp);
if (wp->event == NULL) if (wp->event == NULL)
fatalx("out of memory"); fatalx("out of memory");
wp->ictx = input_init(wp, wp->event, &wp->palette); wp->ictx = input_init(wp, wp->event, &wp->palette, NULL);
bufferevent_enable(wp->event, EV_READ|EV_WRITE); bufferevent_enable(wp->event, EV_READ|EV_WRITE);
} }