diff --git a/popup.c b/popup.c index 733aa3d7..c11f4483 100644 --- a/popup.c +++ b/popup.c @@ -75,12 +75,6 @@ struct popup_data { u_int lb; }; -struct popup_editor { - char *path; - popup_finish_edit_cb cb; - void *arg; -}; - static const struct menu_item popup_menu_items[] = { { "Close", 'q', NULL }, { "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL }, @@ -94,15 +88,6 @@ static const struct menu_item popup_menu_items[] = { { NULL, KEYC_NONE, NULL } }; -static const struct menu_item popup_internal_menu_items[] = { - { "Close", 'q', NULL }, - { "", KEYC_NONE, NULL }, - { "Fill Space", 'F', NULL }, - { "Centre", 'C', NULL }, - - { NULL, KEYC_NONE, NULL } -}; - static void popup_free(struct popup_data *pd) { @@ -650,11 +635,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event) menu: pd->menu = menu_create(""); - if (pd->flags & POPUP_INTERNAL) { - menu_add_items(pd->menu, popup_internal_menu_items, NULL, c, - NULL); - } else - menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL); + menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL); if (m->x >= (pd->menu->width + 4) / 2) x = m->x - (pd->menu->width + 4) / 2; else @@ -861,127 +842,16 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, pd->psx = sx; pd->psy = sy; - if (flags & POPUP_NOJOB) - pd->ictx = input_init(NULL, NULL, &pd->palette, NULL); - else { - pd->job = job_run(shellcmd, argc, argv, env, s, cwd, - popup_job_update_cb, popup_job_complete_cb, NULL, pd, - JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy); - if (pd->job == NULL) { - popup_free(pd); - return (-1); - } - pd->ictx = input_init(NULL, job_get_event(pd->job), - &pd->palette, c); + pd->job = job_run(shellcmd, argc, argv, env, s, cwd, + popup_job_update_cb, popup_job_complete_cb, NULL, pd, + JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy); + if (pd->job == NULL) { + popup_free(pd); + return (-1); } + 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, popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); return (0); } - -void -popup_write(struct client *c, const char *data, size_t size) -{ - struct popup_data *pd = c->overlay_data; - - if (!popup_present(c)) - return; - c->overlay_check = NULL; - c->overlay_data = NULL; - input_parse_screen(pd->ictx, &pd->s, popup_init_ctx_cb, pd, data, size); - c->overlay_check = popup_check_cb; - c->overlay_data = pd; -} - -static void -popup_editor_free(struct popup_editor *pe) -{ - unlink(pe->path); - free(pe->path); - free(pe); -} - -static void -popup_editor_close_cb(int status, void *arg) -{ - struct popup_editor *pe = arg; - FILE *f; - char *buf = NULL; - off_t len = 0; - - if (status != 0) { - pe->cb(NULL, 0, pe->arg); - popup_editor_free(pe); - return; - } - - f = fopen(pe->path, "r"); - if (f != NULL) { - fseeko(f, 0, SEEK_END); - len = ftello(f); - fseeko(f, 0, SEEK_SET); - - if (len == 0 || - (uintmax_t)len > (uintmax_t)SIZE_MAX || - (buf = malloc(len)) == NULL || - fread(buf, len, 1, f) != 1) { - free(buf); - buf = NULL; - len = 0; - } - fclose(f); - } - pe->cb(buf, len, pe->arg); /* callback now owns buffer */ - popup_editor_free(pe); -} - -int -popup_editor(struct client *c, const char *buf, size_t len, - popup_finish_edit_cb cb, void *arg) -{ - struct popup_editor *pe; - int fd; - FILE *f; - char *cmd; - char path[] = _PATH_TMP "tmux.XXXXXXXX"; - const char *editor; - u_int px, py, sx, sy; - - editor = options_get_string(global_options, "editor"); - if (*editor == '\0') - return (-1); - - fd = mkstemp(path); - if (fd == -1) - return (-1); - f = fdopen(fd, "w"); - if (f == NULL) - return (-1); - if (fwrite(buf, len, 1, f) != 1) { - fclose(f); - return (-1); - } - fclose(f); - - pe = xcalloc(1, sizeof *pe); - pe->path = xstrdup(path); - pe->cb = cb; - pe->arg = arg; - - sx = c->tty.sx * 9 / 10; - sy = c->tty.sy * 9 / 10; - px = (c->tty.sx / 2) - (sx / 2); - py = (c->tty.sy / 2) - (sy / 2); - - 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) { - popup_editor_free(pe); - free(cmd); - return (-1); - } - free(cmd); - return (0); -} diff --git a/server.c b/server.c index bf556fd5..f00fbdc3 100644 --- a/server.c +++ b/server.c @@ -498,6 +498,7 @@ server_child_exited(pid_t pid, int status) wp->flags |= PANE_EXITED; window_pane_wait_finish(wp); + spawn_editor_finish(wp); if (window_pane_destroy_ready(wp)) server_destroy_pane(wp, 1); diff --git a/spawn.c b/spawn.c index ddeca0d3..975fd830 100644 --- a/spawn.c +++ b/spawn.c @@ -17,9 +17,11 @@ */ #include +#include #include #include +#include #include #include #include @@ -54,11 +56,10 @@ spawn_log(const char *from, struct spawn_context *sc) struct session *s = sc->s; struct winlink *wl = sc->wl; struct window_pane *wp0 = sc->wp0; - const char *name = cmdq_get_name(sc->item); + const char *name = (sc->name == NULL ? "none" : sc->name); char tmp[128]; - log_debug("%s: %s, flags=%#x", from, name, sc->flags); - + log_debug("%s: name=%s, flags=%#x", from, name, sc->flags); if (wl != NULL && wp0 != NULL) xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id); else if (wl != NULL) @@ -68,7 +69,6 @@ spawn_log(const char *from, struct spawn_context *sc) else xsnprintf(tmp, sizeof tmp, "wl=none wp0=none"); log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); - log_debug("%s: name=%s", from, sc->name == NULL ? "none" : sc->name); } struct winlink * @@ -201,9 +201,9 @@ struct window_pane * spawn_pane(struct spawn_context *sc, char **cause) { struct cmdq_item *item = sc->item; - struct cmd_find_state *target = cmdq_get_target(item); - struct client *c = cmdq_get_client(item); + struct client *c; struct session *s = sc->s; + struct session *ts; struct window *w = sc->wl->window; struct window_pane *new_wp; struct environ *child; @@ -220,6 +220,14 @@ spawn_pane(struct spawn_context *sc, char **cause) sigset_t set, oldset; key_code key; + if (item != NULL) { + ts = cmdq_get_target(item)->s; + c = cmdq_get_client(item); + } else { + ts = s; + c = sc->tc; + } + spawn_log(__func__, sc); /* @@ -227,16 +235,19 @@ spawn_pane(struct spawn_context *sc, char **cause) * the pane's stored one unless specified. */ if (sc->cwd != NULL) { - cwd = format_single(item, sc->cwd, c, target->s, NULL, NULL); + if (item != NULL) + cwd = format_single(item, sc->cwd, c, ts, NULL, NULL); + else + cwd = xstrdup(sc->cwd); if (*cwd != '/') { xasprintf(&new_cwd, "%s%s%s", - server_client_get_cwd(c, target->s), + server_client_get_cwd(c, ts), *cwd != '\0' ? "/" : "", cwd); free(cwd); cwd = new_cwd; } } else if (~sc->flags & SPAWN_RESPAWN) - cwd = xstrdup(server_client_get_cwd(c, target->s)); + cwd = xstrdup(server_client_get_cwd(c, ts)); else cwd = NULL; @@ -516,3 +527,169 @@ complete: notify_window("window-layout-changed", w); return (new_wp); } + +struct spawn_editor_state { + char *path; + pid_t pid; + spawn_finish_edit_cb cb; + void *arg; +}; + +static void +spawn_editor_free(struct spawn_editor_state *es) +{ + unlink(es->path); + free(es->path); + free(es); +} + +void +spawn_cancel_editor(struct spawn_editor_state *es) +{ + if (es == NULL) + return; + es->cb = NULL; + es->arg = NULL; +} + +pid_t +spawn_get_editor_pid(struct spawn_editor_state *es) +{ + if (es == NULL) + return (-1); + return (es->pid); +} + +void +spawn_editor_finish(struct window_pane *wp) +{ + struct spawn_editor_state *es = wp->editor; + FILE *f; + char *buf = NULL; + off_t len = 0; + int status = 128 + SIGHUP; + + if (es == NULL) + return; + wp->editor = NULL; + + if (wp->flags & PANE_STATUSREADY) { + if (WIFEXITED(wp->status)) + status = WEXITSTATUS(wp->status); + else if (WIFSIGNALED(wp->status)) + status = WTERMSIG(wp->status) + 128; + } + + if (es->cb == NULL) { + spawn_editor_free(es); + return; + } + if (status != 0) { + es->cb(NULL, 0, es->arg); + spawn_editor_free(es); + return; + } + + f = fopen(es->path, "r"); + if (f != NULL) { + if (fseeko(f, 0, SEEK_END) == 0) { + len = ftello(f); + if (len > 0 && (uintmax_t)len <= (uintmax_t)SIZE_MAX) { + if (fseeko(f, 0, SEEK_SET) == 0) { + buf = malloc(len); + if (buf != NULL && + fread(buf, len, 1, f) != 1) { + free(buf); + buf = NULL; + len = 0; + } + } + } else + len = 0; + } + fclose(f); + } + + es->cb(buf, len, es->arg); + spawn_editor_free(es); +} + +struct spawn_editor_state * +spawn_editor(struct client *c, const char *buf, size_t len, + spawn_finish_edit_cb cb, void *arg) +{ + struct spawn_editor_state *es; + struct spawn_context sc = { 0 }; + struct session *s = c->session; + struct winlink *wl = s->curw; + struct window *w = wl->window; + struct window_pane *wp; + struct layout_cell *lc; + struct environ *env; + FILE *f; + char *cmd, *cause = NULL; + char path[] = _PATH_TMP "tmux.XXXXXXXX"; + const char *editor; + int fd; + u_int px, py, sx, sy; + + editor = options_get_string(global_options, "editor"); + fd = mkstemp(path); + if (fd == -1) + return (NULL); + f = fdopen(fd, "w"); + if (f == NULL) { + close(fd); + unlink(path); + return (NULL); + } + if (fwrite(buf, len, 1, f) != 1) { + fclose(f); + unlink(path); + return (NULL); + } + fclose(f); + + es = xcalloc(1, sizeof *es); + es->path = xstrdup(path); + es->cb = cb; + es->arg = arg; + + sx = w->sx * 9 / 10; + sy = w->sy * 9 / 10; + px = w->sx / 2 - sx / 2; + py = w->sy / 2 - sy / 2; + window_push_zoom(w, 1, 0); + lc = layout_floating_pane(w, sx, sy, px, py); + if (lc == NULL) { + spawn_editor_free(es); + return (NULL); + } + + xasprintf(&cmd, "%s %s", editor, path); + env = environ_create(); + sc.s = s; + sc.wl = wl; + sc.tc = c; + sc.wp0 = w->active; + sc.lc = lc; + sc.argc = 1; + sc.argv = &cmd; + sc.environ = env; + sc.idx = -1; + sc.cwd = _PATH_TMP; + sc.flags = SPAWN_FLOATING; + + wp = spawn_pane(&sc, &cause); + free(cmd); + environ_free(env); + if (wp == NULL) { + free(cause); + spawn_editor_free(es); + return (NULL); + } + options_set_number(wp->options, "remain-on-exit", 0); + es->pid = wp->pid; + wp->editor = es; + return (es); +} diff --git a/tmux.h b/tmux.h index 5a3e7176..b271896c 100644 --- a/tmux.h +++ b/tmux.h @@ -1309,6 +1309,7 @@ struct window_pane { int status; struct timeval dead_time; struct cmdq_item *wait_item; /* new-pane -W: waiting for pane exit */ + struct spawn_editor_state *editor; int fd; struct bufferevent *event; @@ -3840,19 +3841,13 @@ int menu_key_cb(struct client *, void *, struct key_event *); /* popup.c */ #define POPUP_CLOSEEXIT 0x1 #define POPUP_CLOSEEXITZERO 0x2 -#define POPUP_INTERNAL 0x4 -#define POPUP_CLOSEANYKEY 0x8 -#define POPUP_NOJOB 0x10 +#define POPUP_CLOSEANYKEY 0x4 typedef void (*popup_close_cb)(int, void *); -typedef void (*popup_finish_edit_cb)(char *, size_t, void *); 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 *); -void popup_write(struct client *, const char *, size_t); -int popup_editor(struct client *, const char *, size_t, - popup_finish_edit_cb, void *); int popup_present(struct client *); int popup_modify(struct client *, const char *, const char *, const char *, enum box_lines, int); @@ -3876,6 +3871,12 @@ struct style_range *style_ranges_get_range(struct style_ranges *, u_int); /* spawn.c */ struct winlink *spawn_window(struct spawn_context *, char **); struct window_pane *spawn_pane(struct spawn_context *, char **); +typedef void (*spawn_finish_edit_cb)(char *, size_t, void *); +struct spawn_editor_state *spawn_editor(struct client *, const char *, size_t, + spawn_finish_edit_cb, void *); +void spawn_cancel_editor(struct spawn_editor_state *); +pid_t spawn_get_editor_pid(struct spawn_editor_state *); +void spawn_editor_finish(struct window_pane *); /* regsub.c */ char *regsub(const char *, const char *, const char *, int); diff --git a/window-buffer.c b/window-buffer.c index 5287d2cd..436435de 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -87,6 +87,8 @@ struct window_buffer_modedata { struct cmd_find_state fs; struct mode_tree_data *data; + struct spawn_editor_state *editor; + struct window_buffer_editdata *edit; char *command; char *format; char *key_format; @@ -99,8 +101,12 @@ struct window_buffer_editdata { u_int wp_id; char *name; struct paste_buffer *pb; + struct spawn_editor_state *editor; }; +static void window_buffer_finish_edit(struct window_buffer_editdata *); +static void window_buffer_draw_waiting(struct window_buffer_modedata *); + static enum sort_order window_buffer_order_seq[] = { SORT_CREATION, SORT_NAME, @@ -394,6 +400,11 @@ window_buffer_free(struct window_mode_entry *wme) if (data == NULL) return; + if (data->editor != NULL) { + spawn_cancel_editor(data->editor); + window_buffer_finish_edit(data->edit); + } + mode_tree_free(data->data); for (i = 0; i < data->item_size; i++) @@ -422,6 +433,7 @@ window_buffer_update(struct window_mode_entry *wme) mode_tree_build(data->data); mode_tree_draw(data->data); + window_buffer_draw_waiting(data); data->wp->flags |= PANE_REDRAW; } @@ -466,6 +478,49 @@ window_buffer_finish_edit(struct window_buffer_editdata *ed) free(ed); } +static void +window_buffer_draw_waiting(struct window_buffer_modedata *data) +{ + struct screen_write_ctx ctx; + struct screen *s = data->wp->screen; + struct grid_cell gc; + char text[128]; + u_int sx, sy, box_w, box_h, x, y, text_x; + size_t textlen; + pid_t pid; + + if (data->editor == NULL) + return; + sx = screen_size_x(s); + sy = screen_size_y(s); + if (sx == 0 || sy == 0) + return; + + pid = spawn_get_editor_pid(data->editor); + if (pid == -1) + xsnprintf(text, sizeof text, "WAITING FOR EDITOR"); + else + xsnprintf(text, sizeof text, "WAITING FOR EDITOR (PID %ld)", + (long)pid); + + textlen = strlen(text); + box_w = textlen + 4; + box_h = 3; + if (sx < box_w || sy < box_h) + return; + x = (sx - box_w) / 2; + y = (sy - box_h) / 2; + text_x = x + (box_w - textlen) / 2; + + memcpy(&gc, &grid_default_cell, sizeof gc); + screen_write_start(&ctx, s); + screen_write_cursormove(&ctx, x, y, 0); + screen_write_box(&ctx, box_w, box_h, BOX_LINES_DEFAULT, &gc, NULL); + screen_write_cursormove(&ctx, text_x, y + 1, 0); + screen_write_nputs(&ctx, box_w - 2, &gc, "%s", text); + screen_write_stop(&ctx); +} + static void window_buffer_edit_close_cb(char *buf, size_t len, void *arg) { @@ -477,6 +532,18 @@ window_buffer_edit_close_cb(char *buf, size_t len, void *arg) struct window_buffer_modedata *data; struct window_mode_entry *wme; + wp = window_pane_find_by_id(ed->wp_id); + if (wp != NULL) { + wme = TAILQ_FIRST(&wp->modes); + if (wme != NULL && wme->mode == &window_buffer_mode) { + data = wme->data; + if (data->editor == ed->editor) { + data->editor = NULL; + data->edit = NULL; + } + } + } + if (buf == NULL || len == 0) { window_buffer_finish_edit(ed); return; @@ -499,10 +566,11 @@ window_buffer_edit_close_cb(char *buf, size_t len, void *arg) wp = window_pane_find_by_id(ed->wp_id); if (wp != NULL) { wme = TAILQ_FIRST(&wp->modes); - if (wme->mode == &window_buffer_mode) { + if (wme != NULL && wme->mode == &window_buffer_mode) { data = wme->data; mode_tree_build(data->data); mode_tree_draw(data->data); + window_buffer_draw_waiting(data); } wp->flags |= PANE_REDRAW; } @@ -518,6 +586,8 @@ window_buffer_start_edit(struct window_buffer_modedata *data, size_t len; struct window_buffer_editdata *ed; + if (data->editor != NULL) + return; if ((pb = paste_get_name(item->name)) == NULL) return; buf = paste_buffer_data(pb, &len); @@ -527,8 +597,13 @@ window_buffer_start_edit(struct window_buffer_modedata *data, ed->name = xstrdup(paste_buffer_name(pb)); ed->pb = pb; - if (popup_editor(c, buf, len, window_buffer_edit_close_cb, ed) != 0) + ed->editor = spawn_editor(c, buf, len, window_buffer_edit_close_cb, ed); + if (ed->editor == NULL) window_buffer_finish_edit(ed); + else { + data->editor = ed->editor; + data->edit = ed; + } } static void @@ -546,6 +621,13 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c, finished = 1; goto out; } + if (data->editor != NULL) { + if (key == 'q' || key == '\033' || key == '\003') + finished = 1; + else + finished = 0; + goto out; + } finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); switch (key) { @@ -579,6 +661,7 @@ out: window_pane_reset_mode(wp); else { mode_tree_draw(mtd); + window_buffer_draw_waiting(data); wp->flags |= PANE_REDRAW; } } diff --git a/window-visible.c b/window-visible.c index 7feb496d..f5b03447 100644 --- a/window-visible.c +++ b/window-visible.c @@ -131,6 +131,8 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width, for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; + if (ri->nx == 0) + continue; if (no_border) { lb = wp->xoff; rb = wp->xoff + (int)wp->sx - 1; @@ -159,6 +161,8 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width, rb = w->sx - 1; else if (!no_border && rb > (int)w->sx) rb = w->sx - 1; + if (lb > rb) + continue; sx = ri->px; ex = sx + ri->nx - 1; diff --git a/window.c b/window.c index 11410b52..d5b24a0a 100644 --- a/window.c +++ b/window.c @@ -388,6 +388,8 @@ window_pane_destroy_ready(struct window_pane *wp) */ if (wp->wait_item != NULL && (~wp->flags & PANE_STATUSREADY)) return (0); + if (wp->editor != NULL && (~wp->flags & PANE_STATUSREADY)) + return (0); return (1); } @@ -1131,6 +1133,7 @@ static void window_pane_destroy(struct window_pane *wp) { window_pane_wait_finish(wp); + spawn_editor_finish(wp); window_pane_reset_mode_all(wp); free(wp->searchstr);