diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 5e742ce1..37fbbaed 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -432,7 +432,15 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) w = tty->sx; if (h > tty->sy) h = tty->sy; - if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h)) + if (tc->flags & CLIENT_CONTROL) { + /* Control clients may not have a window size, so provide a reasonable default so popups can still work. */ + if (w == 0) + w = 80; + if (h == 0) + h = 25; + px = 0; + py = 0; + } else if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h)) return (CMD_RETURN_NORMAL); value = args_get(args, 'b'); @@ -485,7 +493,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc, - argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0) { + argv, cwd, title, tc, s, style, border_style, NULL, NULL, target->wp) != 0) { cmd_free_argv(argc, argv); if (env != NULL) environ_free(env); @@ -498,5 +506,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) free(cwd); free(title); cmd_free_argv(argc, argv); + if (tc->flags & CLIENT_CONTROL) + return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/control-notify.c b/control-notify.c index 30f94194..ab3ad3cc 100644 --- a/control-notify.c +++ b/control-notify.c @@ -260,3 +260,19 @@ control_notify_paste_buffer_deleted(const char *name) control_write(c, "%%paste-buffer-deleted %s", name); } } + +void +control_notify_popup(struct client *c, int status, char *buf, size_t len, int wp) +{ + struct evbuffer *message = evbuffer_new(); + + if (message == NULL) + fatalx("out of memory"); + evbuffer_add_printf(message, "%%popup %d", status); + if (wp != -1) + evbuffer_add_printf(message, " %u", wp); + evbuffer_add_printf(message, " : "); + control_escape(message, buf, len); + control_write_buffer(c, message); + evbuffer_free(message); +} diff --git a/control.c b/control.c index 578d04cb..d5a1a5a4 100644 --- a/control.c +++ b/control.c @@ -43,7 +43,9 @@ */ struct control_block { size_t size; + /* exactly one of `line` and `buffer` will be nonnull */ char *line; + struct evbuffer *buffer; uint64_t t; TAILQ_ENTRY(control_block) entry; @@ -225,7 +227,10 @@ control_free_sub(struct control_state *cs, struct control_sub *csub) static void control_free_block(struct control_state *cs, struct control_block *cb) { - free(cb->line); + if (cb->line != NULL) + free(cb->line); + if (cb->buffer != NULL) + evbuffer_free(cb->buffer); TAILQ_REMOVE(&cs->all_blocks, cb, all_entry); free(cb); } @@ -401,13 +406,63 @@ control_vwrite(struct client *c, const char *fmt, va_list ap) free(s); } +static void +control_vwrite_buffer(struct client *c, struct evbuffer *buffer) +{ + struct control_state *cs = c->control_state; + + log_debug("%s: %s: writing buffer", __func__, c->name); + + bufferevent_write_buffer(cs->write_event, buffer); + bufferevent_write(cs->write_event, "\n", 1); + + bufferevent_enable(cs->write_event, EV_WRITE); +} + +/* Frees line and buffer after using them asynchronously. */ +static void +control_enqueue(struct client *c, struct control_state *cs, char *line, struct evbuffer *buffer) +{ + struct control_block *cb = xcalloc(1, sizeof *cb); + + if (line != NULL) { + log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line); + cb->line = line; + } else { + log_debug("%s: %s: storing buffer", __func__, c->name); + cb->buffer = buffer; + } + + TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); + cb->t = get_timer(); + + bufferevent_enable(cs->write_event, EV_WRITE); +} + +void +control_write_buffer(struct client *c, struct evbuffer *buffer) +{ + struct control_state *cs = c->control_state; + struct control_block *cb; + va_list ap; + + if (TAILQ_EMPTY(&cs->all_blocks)) { + control_vwrite_buffer(c, buffer); + return; + } + + control_enqueue(c, cs, NULL, buffer); + + va_end(ap); +} + /* Write a line. */ void control_write(struct client *c, const char *fmt, ...) { struct control_state *cs = c->control_state; - struct control_block *cb; va_list ap; + char *line; va_start(ap, fmt); @@ -417,13 +472,8 @@ control_write(struct client *c, const char *fmt, ...) return; } - cb = xcalloc(1, sizeof *cb); - xvasprintf(&cb->line, fmt, ap); - TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry); - cb->t = get_timer(); - - log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line); - bufferevent_enable(cs->write_event, EV_WRITE); + xvasprintf(&line, fmt, ap); + control_enqueue(c, cs, line, NULL); va_end(ap); } @@ -604,6 +654,17 @@ control_flush_all_blocks(struct client *c) } } +void +control_escape(struct evbuffer *message, char *s, size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (s[i] < ' ' || s[i] == '\\') + evbuffer_add_printf(message, "\\%03o", s[i]); + else + evbuffer_add_printf(message, "%c", s[i]); + } +} + /* Append data to buffer. */ static struct evbuffer * control_append_data(struct client *c, struct control_pane *cp, uint64_t age, @@ -611,7 +672,6 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age, { u_char *new_data; size_t new_size; - u_int i; if (message == NULL) { message = evbuffer_new(); @@ -628,12 +688,7 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age, new_data = window_pane_get_new_data(wp, &cp->offset, &new_size); if (new_size < size) fatalx("not enough data: %zu < %zu", new_size, size); - for (i = 0; i < size; i++) { - if (new_data[i] < ' ' || new_data[i] == '\\') - evbuffer_add_printf(message, "\\%03o", new_data[i]); - else - evbuffer_add_printf(message, "%c", new_data[i]); - } + control_escape(message, new_data, size); window_pane_update_used_data(wp, &cp->offset, size); return (message); } diff --git a/popup.c b/popup.c index 4cd147e1..ceb935b5 100644 --- a/popup.c +++ b/popup.c @@ -28,6 +28,7 @@ struct popup_data { struct client *c; + int wp; struct cmdq_item *item; int flags; char *title; @@ -614,6 +615,47 @@ popup_job_update_cb(struct job *job) evbuffer_drain(evb, size); } +// NOTE TO REVIEWER: This is a copy of cmd_capture_pane_append. I think we'd want a shared implementation but I don't know where it should go. +static char * +popup_append(char *buf, size_t *len, char *line, size_t linelen) +{ + buf = xrealloc(buf, *len + linelen + 1); + memcpy(buf + *len, line, linelen); + *len += linelen; + return (buf); +} + +static void +popup_notify_control(struct client *c, int status, struct screen *s, int wp) +{ + char *buf = NULL; + struct grid_cell *gc = NULL; + int sx = screen_size_x(s); + int sy = screen_size_y(s); + char *line; + size_t linelen; + size_t len = 0; + int i; + struct grid *gd = s->grid; + const struct grid_line *gl; + + for (i = 0; i < sy; i++) { + line = grid_string_cells(gd, 0, i, sx, &gc, GRID_STRING_WITH_SEQUENCES, s); + linelen = strlen(line); + + buf = popup_append(buf, &len, line, linelen); + + gl = grid_peek_line(gd, i); + if (!(gl->flags & GRID_LINE_WRAPPED)) + buf[len++] = '\n'; + + free(line); + } + + control_notify_popup(c, status, buf, len, wp); + free(buf); +} + static void popup_job_complete_cb(struct job *job) { @@ -629,8 +671,10 @@ popup_job_complete_cb(struct job *job) pd->status = 0; pd->job = NULL; - if ((pd->flags & POPUP_CLOSEEXIT) || - ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0)) + if (pd->c->flags & CLIENT_CONTROL) + popup_notify_control(pd->c, pd->status, &pd->s, pd->wp); + else if ((pd->flags & POPUP_CLOSEEXIT) || + ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0)) server_client_clear_overlay(pd->c); } @@ -639,7 +683,7 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd, int argc, char **argv, const char *cwd, const char *title, struct client *c, struct session *s, const char *style, const char *border_style, - popup_close_cb cb, void *arg) + popup_close_cb cb, void *arg, struct window_pane *wp) { struct popup_data *pd; u_int jx, jy; @@ -664,10 +708,14 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, jx = sx - 2; jy = sy - 2; } - if (c->tty.sx < sx || c->tty.sy < sy) + if (!(c->flags & CLIENT_CONTROL) && (c->tty.sx < sx || c->tty.sy < sy)) return (-1); pd = xcalloc(1, sizeof *pd); + if (wp != NULL) + pd->wp = wp->id; + else + pd->wp = -1; pd->item = item; pd->flags = flags; if (title != NULL) @@ -723,8 +771,10 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy); pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette); - 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); + if (!(c->flags & CLIENT_CONTROL)) { + 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); + } return (0); } @@ -811,7 +861,7 @@ popup_editor(struct client *c, const char *buf, size_t len, xasprintf(&cmd, "%s %s", editor, path); if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, BOX_LINES_DEFAULT, NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, NULL, c, NULL, - NULL, NULL, popup_editor_close_cb, pe) != 0) { + NULL, NULL, popup_editor_close_cb, pe, NULL) != 0) { popup_editor_free(pe); free(cmd); return (-1); diff --git a/tmux.h b/tmux.h index d448faa3..9447f03a 100644 --- a/tmux.h +++ b/tmux.h @@ -3379,11 +3379,13 @@ struct window_pane_offset *control_pane_offset(struct client *, struct window_pane *, int *); void control_reset_offsets(struct client *); void printflike(2, 3) control_write(struct client *, const char *, ...); +void control_write_buffer(struct client *c, struct evbuffer *buffer); void control_write_output(struct client *, struct window_pane *); int control_all_done(struct client *); void control_add_sub(struct client *, const char *, enum control_sub_type, int, const char *); void control_remove_sub(struct client *, const char *); +void control_escape(struct evbuffer *, char *, size_t); /* control-notify.c */ void control_notify_pane_mode_changed(int); @@ -3400,6 +3402,7 @@ void control_notify_session_closed(struct session *); void control_notify_session_window_changed(struct session *); void control_notify_paste_buffer_changed(const char *); void control_notify_paste_buffer_deleted(const char *); +void control_notify_popup(struct client *c, int status, char *buf, size_t len, int wp); /* session.c */ extern struct sessions sessions; @@ -3531,7 +3534,7 @@ int popup_display(int, enum box_lines, struct cmdq_item *, u_int, u_int, u_int, u_int, struct environ *, const char *, int, char **, const char *, const char *, struct client *, struct session *, const char *, const char *, - popup_close_cb, void *); + popup_close_cb, void *, struct window_pane *); int popup_editor(struct client *, const char *, size_t, popup_finish_edit_cb, void *);