diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 6a7ebba7..c2074f4f 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -124,17 +124,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (!Eflag) environ_update(s->options, c->environ, s->environ); - c->session = s; + server_client_set_session(c, s); if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); - session_update_activity(s, NULL); - gettimeofday(&s->last_attached_time, NULL); - server_redraw_client(c); - s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = c; } else { if (server_client_open(c, &cause) != 0) { cmdq_error(item, "open terminal failed: %s", cause); @@ -156,25 +148,14 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (!Eflag) environ_update(s->options, c->environ, s->environ); - c->session = s; + server_client_set_session(c, s); server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); - session_update_activity(s, NULL); - gettimeofday(&s->last_attached_time, NULL); - server_redraw_client(c); - s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = c; if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); notify_client("client-attached", c); c->flags |= CLIENT_ATTACHED; } - recalculate_sizes(); - alerts_check_session(s); - server_update_socket(); return (CMD_RETURN_NORMAL); } diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index a955ac32..7e3d23c5 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1FkiI:Np:t:T:", 0, 1 }, - .usage = "[-1FkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE + .args = { "1bFkiI:Np:t:T:", 0, 1 }, + .usage = "[-1bFkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [-T type] [template]", .flags = CMD_CLIENT_TFLAG, @@ -49,17 +49,20 @@ const struct cmd_entry cmd_command_prompt_entry = { }; struct cmd_command_prompt_cdata { - int flags; - enum prompt_type prompt_type; + struct cmdq_item *item; + struct cmd_parse_input pi; - char *inputs; - char *next_input; + int flags; + enum prompt_type prompt_type; - char *prompts; - char *next_prompt; + char *inputs; + char *next_input; - char *template; - int idx; + char *prompts; + char *next_prompt; + + char *template; + int idx; }; static enum cmd_retval @@ -72,21 +75,23 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) struct cmd_command_prompt_cdata *cdata; char *prompt, *ptr, *input = NULL; size_t n; + int wait = !args_has(args, 'b'); if (tc->prompt_string != NULL) return (CMD_RETURN_NORMAL); cdata = xcalloc(1, sizeof *cdata); - - cdata->inputs = NULL; - cdata->next_input = NULL; - - cdata->prompts = NULL; - cdata->next_prompt = NULL; - - cdata->template = NULL; cdata->idx = 1; + cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); + if (wait) + cdata->pi.item = item; + cdata->pi.c = tc; + cmd_find_copy_state(&cdata->pi.fs, target); + + if (wait) + cdata->item = item; + if (args->argc != 0 && args_has(args, 'F')) cdata->template = format_single_from_target(item, args->argv[0]); else if (args->argc != 0) @@ -140,7 +145,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags, cdata->prompt_type); free(prompt); - return (CMD_RETURN_NORMAL); + if (!wait) + return (CMD_RETURN_NORMAL); + return (CMD_RETURN_WAIT); } static int @@ -150,12 +157,13 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, struct cmd_command_prompt_cdata *cdata = data; char *new_template, *prompt, *ptr, *error; char *input = NULL; + struct cmdq_item *item = cdata->item; enum cmd_parse_status status; if (s == NULL) - return (0); + goto out; if (done && (cdata->flags & PROMPT_INCREMENTAL)) - return (0); + goto out; new_template = cmd_template_replace(cdata->template, s, cdata->idx); if (done) { @@ -177,7 +185,13 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, return (1); } - status = cmd_parse_and_append(new_template, NULL, c, NULL, &error); + if (item != NULL) { + status = cmd_parse_and_insert(new_template, &cdata->pi, item, + cmdq_get_state(item), &error); + } else { + status = cmd_parse_and_append(new_template, &cdata->pi, c, NULL, + &error); + } if (status == CMD_PARSE_ERROR) { cmdq_append(c, cmdq_get_error(error)); free(error); @@ -187,6 +201,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s, free(new_template); if (c->prompt_inputcb != cmd_command_prompt_callback) return (1); + +out: + if (item != NULL) + cmdq_continue(item); return (0); } diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 83f9a0f8..6b754370 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -75,7 +75,6 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); - memset(&cdata->pi, 0, sizeof cdata->pi); cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); if (wait) cdata->pi.item = item; @@ -88,8 +87,8 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) status_prompt_set(tc, target, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, PROMPT_SINGLE, PROMPT_TYPE_COMMAND); - free(new_prompt); + if (!wait) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); @@ -104,14 +103,16 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, char *error; struct cmdq_item *item = cdata->item; enum cmd_parse_status status; + int retcode = 1; if (c->flags & CLIENT_DEAD) - return (0); + goto out; if (s == NULL || *s == '\0') goto out; if (tolower((u_char)s[0]) != 'y' || s[1] != '\0') goto out; + retcode = 0; if (item != NULL) { status = cmd_parse_and_insert(cmd, &cdata->pi, item, @@ -124,8 +125,12 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s, } out: - if (item != NULL) - cmdq_continue(item); + if (item != NULL) { + if (cmdq_get_client(item) != NULL && + cmdq_get_client(item)->session == NULL) + cmdq_get_client(item)->retval = retcode; + cmdq_continue(item); + } return (0); } diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 92def23b..9e7bc3a2 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -50,8 +50,8 @@ const struct cmd_entry cmd_display_popup_entry = { .name = "display-popup", .alias = "popup", - .args = { "Cc:d:Eh:t:w:x:y:", 0, -1 }, - .usage = "[-CE] [-c target-client] [-d start-directory] [-h height] " + .args = { "BCc:d:Eh:t:w:x:y:", 0, -1 }, + .usage = "[-BCE] [-c target-client] [-d start-directory] [-h height] " CMD_TARGET_PANE_USAGE " [-w width] " "[-x position] [-y position] [command]", @@ -390,6 +390,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) flags |= POPUP_CLOSEEXITZERO; else if (args_has(args, 'E')) flags |= POPUP_CLOSEEXIT; + if (args_has(args, 'B')) + flags |= POPUP_NOBORDER; if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd, tc, s, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); diff --git a/cmd-display-panes.c b/cmd-display-panes.c index f03b80ed..beadae53 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -186,7 +186,8 @@ out: } static void -cmd_display_panes_draw(struct client *c, struct screen_redraw_ctx *ctx) +cmd_display_panes_draw(struct client *c, __unused void *data, + struct screen_redraw_ctx *ctx) { struct window *w = c->session->curw->window; struct window_pane *wp; @@ -200,9 +201,9 @@ cmd_display_panes_draw(struct client *c, struct screen_redraw_ctx *ctx) } static void -cmd_display_panes_free(struct client *c) +cmd_display_panes_free(__unused struct client *c, void *data) { - struct cmd_display_panes_data *cdata = c->overlay_data; + struct cmd_display_panes_data *cdata = data; if (cdata->item != NULL) cmdq_continue(cdata->item); @@ -211,9 +212,9 @@ cmd_display_panes_free(struct client *c) } static int -cmd_display_panes_key(struct client *c, struct key_event *event) +cmd_display_panes_key(struct client *c, void *data, struct key_event *event) { - struct cmd_display_panes_data *cdata = c->overlay_data; + struct cmd_display_panes_data *cdata = data; char *cmd, *expanded, *error; struct window *w = c->session->curw->window; struct window_pane *wp; diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 65fbf19b..f4c81074 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -104,8 +104,6 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->cmd_if = xstrdup(args->argv[1]); if (args->argc == 3) cdata->cmd_else = xstrdup(args->argv[2]); - else - cdata->cmd_else = NULL; if (!args_has(args, 'b')) cdata->client = cmdq_get_client(item); @@ -116,10 +114,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'b')) cdata->item = item; - else - cdata->item = NULL; - memset(&cdata->input, 0, sizeof cdata->input); cmd_get_source(self, &file, &cdata->input.line); if (file != NULL) cdata->input.file = xstrdup(file); diff --git a/cmd-new-session.c b/cmd-new-session.c index 033c707f..f3a5de26 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -329,18 +329,10 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) proc_send(c->peer, MSG_READY, -1, NULL, 0); } else if (c->session != NULL) c->last_session = c->session; - c->session = s; + server_client_set_session(c, s); if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); - session_update_activity(s, NULL); - gettimeofday(&s->last_attached_time, NULL); - server_redraw_client(c); } - recalculate_sizes(); - server_update_socket(); /* * If there are still configuration file errors to display, put the new diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 56d5f723..7bc1d7cc 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -121,7 +121,6 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) cdata->shell = !args_has(args, 'C'); if (!cdata->shell) { - memset(&cdata->pi, 0, sizeof cdata->pi); cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); if (wait) cdata->pi.item = item; diff --git a/cmd-switch-client.c b/cmd-switch-client.c index b10496e3..bc6baa6a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -134,23 +134,9 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) if (!args_has(args, 'E')) environ_update(s->options, tc->environ, s->environ); - if (tc->session != NULL && tc->session != s) - tc->last_session = tc->session; - tc->session = s; + server_client_set_session(tc, s); if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT) server_client_set_key_table(tc, NULL); - tty_update_client_offset(tc); - status_timer_start(tc); - notify_client("client-session-changed", tc); - session_update_activity(s, NULL); - gettimeofday(&s->last_attached_time, NULL); - - server_check_unattached(); - server_redraw_client(tc); - s->curw->flags &= ~WINLINK_ALERTFLAGS; - s->curw->window->latest = tc; - recalculate_sizes(); - alerts_check_session(s); return (CMD_RETURN_NORMAL); } diff --git a/input.c b/input.c index 49ed68b7..155144b7 100644 --- a/input.c +++ b/input.c @@ -1792,8 +1792,12 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) if (sctx->s->mode & MODE_FOCUSON) break; screen_write_mode_set(sctx, MODE_FOCUSON); - if (wp != NULL) - wp->flags |= PANE_FOCUSPUSH; /* force update */ + if (wp == NULL) + break; + if (wp->flags & PANE_FOCUSED) + bufferevent_write(wp->event, "\033[I", 3); + else + bufferevent_write(wp->event, "\033[O", 3); break; case 1005: screen_write_mode_set(sctx, MODE_MOUSE_UTF8); diff --git a/job.c b/job.c index a1fc0cf5..3624c679 100644 --- a/job.c +++ b/job.c @@ -50,6 +50,7 @@ struct job { char *cmd; pid_t pid; + char tty[TTY_NAME_MAX]; int status; int fd; @@ -79,7 +80,7 @@ job_run(const char *cmd, int argc, char **argv, struct session *s, const char *home; sigset_t set, oldset; struct winsize ws; - char **argvp; + char **argvp, tty[TTY_NAME_MAX]; /* * Do not set TERM during .tmux.conf, it is nice to be able to use @@ -94,7 +95,7 @@ job_run(const char *cmd, int argc, char **argv, struct session *s, memset(&ws, 0, sizeof ws); ws.ws_col = sx; ws.ws_row = sy; - pid = fdforkpty(ptm_fd, &master, NULL, NULL, &ws); + pid = fdforkpty(ptm_fd, &master, tty, NULL, &ws); } else { if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) goto fail; @@ -168,6 +169,7 @@ job_run(const char *cmd, int argc, char **argv, struct session *s, else job->cmd = cmd_stringify_argv(argc, argv); job->pid = pid; + strlcpy(job->tty, tty, sizeof job->tty); job->status = 0; LIST_INSERT_HEAD(&all_jobs, job, entry); @@ -199,6 +201,32 @@ fail: return (NULL); } +/* Take job's file descriptor and free the job. */ +int +job_transfer(struct job *job, pid_t *pid, char *tty, size_t ttylen) +{ + int fd = job->fd; + + log_debug("transfer job %p: %s", job, job->cmd); + + if (pid != NULL) + *pid = job->pid; + if (tty != NULL) + strlcpy(tty, job->tty, ttylen); + + LIST_REMOVE(job, entry); + free(job->cmd); + + if (job->freecb != NULL && job->data != NULL) + job->freecb(job->data); + + if (job->event != NULL) + bufferevent_free(job->event); + + free(job); + return (fd); +} + /* Kill and free an individual job. */ void job_free(struct job *job) diff --git a/menu.c b/menu.c index c8de02a8..7fb1996b 100644 --- a/menu.c +++ b/menu.c @@ -131,18 +131,33 @@ menu_free(struct menu *menu) free(menu); } -static struct screen * -menu_mode_cb(struct client *c, __unused u_int *cx, __unused u_int *cy) +struct screen * +menu_mode_cb(__unused struct client *c, void *data, __unused u_int *cx, + __unused u_int *cy) { - struct menu_data *md = c->overlay_data; + struct menu_data *md = data; return (&md->s); } -static void -menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) +int +menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py) { - struct menu_data *md = c->overlay_data; + struct menu_data *md = data; + struct menu *menu = md->menu; + + if (px < md->px || px > md->px + menu->width + 3) + return (1); + if (py < md->py || py > md->py + menu->count + 1) + return (1); + return (0); +} + +void +menu_draw_cb(struct client *c, void *data, + __unused struct screen_redraw_ctx *rctx) +{ + struct menu_data *md = data; struct tty *tty = &c->tty; struct screen *s = &md->s; struct menu *menu = md->menu; @@ -163,10 +178,10 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) } } -static void -menu_free_cb(struct client *c) +void +menu_free_cb(__unused struct client *c, void *data) { - struct menu_data *md = c->overlay_data; + struct menu_data *md = data; if (md->item != NULL) cmdq_continue(md->item); @@ -179,10 +194,10 @@ menu_free_cb(struct client *c) free(md); } -static int -menu_key_cb(struct client *c, struct key_event *event) +int +menu_key_cb(struct client *c, void *data, struct key_event *event) { - struct menu_data *md = c->overlay_data; + struct menu_data *md = data; struct menu *menu = md->menu; struct mouse_event *m = &event->m; u_int i; @@ -342,8 +357,8 @@ chosen: return (1); } -int -menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, +struct menu_data * +menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px, u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, void *data) { @@ -352,7 +367,7 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, const char *name; if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) - return (-1); + return (NULL); if (px + menu->width + 4 > c->tty.sx) px = c->tty.sx - menu->width - 4; if (py + menu->count + 2 > c->tty.sy) @@ -388,7 +403,19 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, md->cb = cb; md->data = data; + return (md); +} +int +menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, + u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, + void *data) +{ + struct menu_data *md; + + md = menu_prepare(menu, flags, item, px, py, c, fs, cb, data); + if (md == NULL) + return (-1); server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, menu_key_cb, menu_free_cb, NULL, md); return (0); diff --git a/popup.c b/popup.c index eb6ddfa8..9565b012 100644 --- a/popup.c +++ b/popup.c @@ -39,6 +39,10 @@ struct popup_data { popup_close_cb cb; void *arg; + struct menu *menu; + struct menu_data *md; + int close; + /* Current position and size. */ u_int px; u_int py; @@ -66,6 +70,28 @@ struct popup_editor { void *arg; }; +static const struct menu_item popup_menu_items[] = { + { "Close", 'q', NULL }, + { "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL }, + { "", KEYC_NONE, NULL }, + { "Fill Space", 'F', NULL }, + { "Centre", 'C', NULL }, + { "", KEYC_NONE, NULL }, + { "To Horizontal Pane", 'h', NULL }, + { "To Vertical Pane", 'v', NULL }, + + { 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_redraw_cb(const struct tty_ctx *ttyctx) { @@ -90,8 +116,13 @@ popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) ttyctx->wsx = c->tty.sx; ttyctx->wsy = c->tty.sy; - ttyctx->xoff = ttyctx->rxoff = pd->px + 1; - ttyctx->yoff = ttyctx->ryoff = pd->py + 1; + if (pd->flags & POPUP_NOBORDER) { + ttyctx->xoff = ttyctx->rxoff = pd->px; + ttyctx->yoff = ttyctx->ryoff = pd->py; + } else { + ttyctx->xoff = ttyctx->rxoff = pd->px + 1; + ttyctx->yoff = ttyctx->ryoff = pd->py + 1; + } return (1); } @@ -108,20 +139,30 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) } static struct screen * -popup_mode_cb(struct client *c, u_int *cx, u_int *cy) +popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; - *cx = pd->px + 1 + pd->s.cx; - *cy = pd->py + 1 + pd->s.cy; + if (pd->md != NULL) + return (menu_mode_cb(c, pd->md, cx, cy)); + + if (pd->flags & POPUP_NOBORDER) { + *cx = pd->px + pd->s.cx; + *cy = pd->py + pd->s.cy; + } else { + *cx = pd->px + 1 + pd->s.cx; + *cy = pd->py + 1 + pd->s.cy; + } return (&pd->s); } static int -popup_check_cb(struct client *c, u_int px, u_int py) +popup_check_cb(struct client *c, void *data, u_int px, u_int py) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; + if (pd->md != NULL && menu_check_cb(c, pd->md, px, py) == 0) + return (0); if (px < pd->px || px > pd->px + pd->sx - 1) return (1); if (py < pd->py || py > pd->py + pd->sy - 1) @@ -130,9 +171,9 @@ popup_check_cb(struct client *c, u_int px, u_int py) } static void -popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) +popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; struct tty *tty = &c->tty; struct screen s; struct screen_write_ctx ctx; @@ -144,8 +185,10 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) screen_write_start(&ctx, &s); screen_write_clearscreen(&ctx, 8); - /* Skip drawing popup if the terminal is too small. */ - if (pd->sx > 2 && pd->sy > 2) { + if (pd->flags & POPUP_NOBORDER) { + screen_write_cursormove(&ctx, 0, 0, 0); + screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy); + } else if (pd->sx > 2 && pd->sy > 2) { screen_write_box(&ctx, pd->sx, pd->sy); screen_write_cursormove(&ctx, 1, 1, 0); screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, @@ -157,18 +200,33 @@ popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) gc.fg = pd->palette.fg; gc.bg = pd->palette.bg; - c->overlay_check = NULL; + if (pd->md != NULL) { + c->overlay_check = menu_check_cb; + c->overlay_data = pd->md; + } else { + c->overlay_check = NULL; + c->overlay_data = NULL; + } for (i = 0; i < pd->sy; i++) tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &gc, palette); + if (pd->md != NULL) { + c->overlay_check = NULL; + c->overlay_data = NULL; + menu_draw_cb(c, pd->md, rctx); + } c->overlay_check = popup_check_cb; + c->overlay_data = pd; } static void -popup_free_cb(struct client *c) +popup_free_cb(struct client *c, void *data) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; struct cmdq_item *item = pd->item; + if (pd->md != NULL) + menu_free_cb(c, pd->md); + if (pd->cb != NULL) pd->cb(pd->status, pd->arg); @@ -186,17 +244,20 @@ popup_free_cb(struct client *c) screen_free(&pd->s); colour_palette_free(&pd->palette); + free(pd); } static void -popup_resize_cb(struct client *c) +popup_resize_cb(__unused struct client *c, void *data) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; struct tty *tty = &c->tty; if (pd == NULL) return; + if (pd->md != NULL) + menu_free_cb(c, pd->md); /* Adjust position and size. */ if (pd->psy > tty->sy) @@ -217,13 +278,103 @@ popup_resize_cb(struct client *c) pd->px = pd->ppx; /* Avoid zero size screens. */ - if (pd->sx > 2 && pd->sy > 2) { + if (pd->flags & POPUP_NOBORDER) { + screen_resize(&pd->s, pd->sx, pd->sy, 0); + if (pd->job != NULL) + job_resize(pd->job, pd->sx, pd->sy ); + } else if (pd->sx > 2 && pd->sy > 2) { screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); if (pd->job != NULL) job_resize(pd->job, pd->sx - 2, pd->sy - 2); } } +static void +popup_make_pane(struct popup_data *pd, enum layout_type type) +{ + struct client *c = pd->c; + struct session *s = c->session; + struct window *w = s->curw->window; + struct layout_cell *lc; + struct window_pane *wp = w->active, *new_wp; + u_int hlimit; + const char *shell; + + window_unzoom(w); + + lc = layout_split_pane(wp, type, -1, 0); + hlimit = options_get_number(s->options, "history-limit"); + new_wp = window_add_pane(wp->window, NULL, hlimit, 0); + layout_assign_pane(lc, new_wp, 0); + + new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty, + sizeof new_wp->tty); + pd->job = NULL; + + screen_set_title(&pd->s, new_wp->base.title); + screen_free(&new_wp->base); + memcpy(&new_wp->base, &pd->s, sizeof wp->base); + screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1); + screen_init(&pd->s, 1, 1, 0); + + shell = options_get_string(s->options, "default-shell"); + if (!checkshell(shell)) + shell = _PATH_BSHELL; + new_wp->shell = xstrdup(shell); + + window_pane_set_event(new_wp); + window_set_active_pane(w, new_wp, 1); + new_wp->flags |= PANE_CHANGED; + + pd->close = 1; +} + +static void +popup_menu_done(__unused struct menu *menu, __unused u_int choice, + key_code key, void *data) +{ + struct popup_data *pd = data; + struct client *c = pd->c; + struct paste_buffer *pb; + const char *buf; + size_t len; + + pd->md = NULL; + pd->menu = NULL; + server_redraw_client(pd->c); + + switch (key) { + case 'p': + pb = paste_get_top(NULL); + if (pb != NULL) { + buf = paste_buffer_data(pb, &len); + bufferevent_write(job_get_event(pd->job), buf, len); + } + break; + case 'F': + pd->sx = c->tty.sx; + pd->sy = c->tty.sy; + pd->px = 0; + pd->py = 0; + server_redraw_client(c); + break; + case 'C': + pd->px = c->tty.sx / 2 - pd->sx / 2; + pd->py = c->tty.sy / 2 - pd->sy / 2; + server_redraw_client(c); + break; + case 'h': + popup_make_pane(pd, LAYOUT_LEFTRIGHT); + break; + case 'v': + popup_make_pane(pd, LAYOUT_TOPBOTTOM); + break; + case 'q': + pd->close = 1; + break; + } +} + static void popup_handle_drag(struct client *c, struct popup_data *pd, struct mouse_event *m) @@ -253,29 +404,55 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->ppy = py; server_redraw_client(c); } else if (pd->dragging == SIZE) { - if (m->x < pd->px + 3) - return; - if (m->y < pd->py + 3) - return; + if (pd->flags & POPUP_NOBORDER) { + if (m->x < pd->px + 1) + return; + if (m->y < pd->py + 1) + return; + } else { + if (m->x < pd->px + 3) + return; + if (m->y < pd->py + 3) + return; + } pd->sx = m->x - pd->px; pd->sy = m->y - pd->py; pd->psx = pd->sx; pd->psy = pd->sy; - screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); - if (pd->job != NULL) - job_resize(pd->job, pd->sx - 2, pd->sy - 2); + if (pd->flags & POPUP_NOBORDER) { + screen_resize(&pd->s, pd->sx, pd->sy, 0); + if (pd->job != NULL) + job_resize(pd->job, pd->sx, pd->sy); + } else { + screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); + if (pd->job != NULL) + job_resize(pd->job, pd->sx - 2, pd->sy - 2); + } server_redraw_client(c); } } static int -popup_key_cb(struct client *c, struct key_event *event) +popup_key_cb(struct client *c, void *data, struct key_event *event) { - struct popup_data *pd = c->overlay_data; + struct popup_data *pd = data; struct mouse_event *m = &event->m; const char *buf; size_t len; + u_int px, py, x; + + if (pd->md != NULL) { + if (menu_key_cb(c, pd->md, event) == 1) { + pd->md = NULL; + pd->menu = NULL; + if (pd->close) + server_client_clear_overlay(c); + else + server_redraw_client(c); + } + return (0); + } if (KEYC_IS_MOUSE(event->key)) { if (pd->dragging != OFF) { @@ -286,15 +463,24 @@ popup_key_cb(struct client *c, struct key_event *event) m->x > pd->px + pd->sx - 1 || m->y < pd->py || m->y > pd->py + pd->sy - 1) { - if (MOUSE_BUTTONS (m->b) == 1) - return (1); + if (MOUSE_BUTTONS(m->b) == 2) + goto menu; return (0); } - if ((m->b & MOUSE_MASK_META) || - m->x == pd->px || + if ((~pd->flags & POPUP_NOBORDER) && + (~m->b & MOUSE_MASK_META) && + MOUSE_BUTTONS(m->b) == 2 && + (m->x == pd->px || m->x == pd->px + pd->sx - 1 || m->y == pd->py || - m->y == pd->py + pd->sy - 1) { + m->y == pd->py + pd->sy - 1)) + goto menu; + if ((m->b & MOUSE_MASK_META) || + ((~pd->flags & POPUP_NOBORDER) && + (m->x == pd->px || + m->x == pd->px + pd->sx - 1 || + m->y == pd->py || + m->y == pd->py + pd->sy - 1))) { if (!MOUSE_DRAG(m->b)) goto out; if (MOUSE_BUTTONS(m->lb) == 0) @@ -306,7 +492,6 @@ popup_key_cb(struct client *c, struct key_event *event) goto out; } } - if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) || pd->job == NULL) && (event->key == '\033' || event->key == '\003')) @@ -314,8 +499,14 @@ popup_key_cb(struct client *c, struct key_event *event) if (pd->job != NULL) { if (KEYC_IS_MOUSE(event->key)) { /* Must be inside, checked already. */ - if (!input_key_get_mouse(&pd->s, m, m->x - pd->px - 1, - m->y - pd->py - 1, &buf, &len)) + if (pd->flags & POPUP_NOBORDER) { + px = m->x - pd->px; + py = m->y - pd->py; + } else { + px = m->x - pd->px - 1; + py = m->y - pd->py - 1; + } + if (!input_key_get_mouse(&pd->s, m, px, py, &buf, &len)) return (0); bufferevent_write(job_get_event(pd->job), buf, len); return (0); @@ -324,6 +515,21 @@ popup_key_cb(struct client *c, struct key_event *event) } return (0); +menu: + pd->menu = menu_create(""); + if (pd->flags & POPUP_INTERNAL) { + menu_add_items(pd->menu, popup_internal_menu_items, NULL, NULL, + NULL); + } else + menu_add_items(pd->menu, popup_menu_items, NULL, NULL, NULL); + if (m->x >= (pd->menu->width + 4) / 2) + x = m->x - (pd->menu->width + 4) / 2; + else + x = 0; + pd->md = menu_prepare(pd->menu, 0, NULL, x, m->y, c, NULL, + popup_menu_done, pd); + c->flags |= CLIENT_REDRAWOVERLAY; + out: pd->lx = m->x; pd->ly = m->y; @@ -344,9 +550,16 @@ popup_job_update_cb(struct job *job) if (size == 0) return; - c->overlay_check = NULL; + if (pd->md != NULL) { + c->overlay_check = menu_check_cb; + c->overlay_data = pd->md; + } else { + c->overlay_check = NULL; + c->overlay_data = NULL; + } input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size); c->overlay_check = popup_check_cb; + c->overlay_data = pd; evbuffer_drain(evb, size); } @@ -377,9 +590,19 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, struct client *c, struct session *s, popup_close_cb cb, void *arg) { struct popup_data *pd; + u_int jx, jy; - if (sx < 3 || sy < 3) - return (-1); + if (flags & POPUP_NOBORDER) { + if (sx < 1 || sy < 1) + return (-1); + jx = sx; + jy = sy; + } else { + if (sx < 3 || sy < 3) + return (-1); + jx = sx - 2; + jy = sy - 2; + } if (c->tty.sx < sx || c->tty.sy < sy) return (-1); @@ -410,7 +633,7 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, pd->job = job_run(shellcmd, argc, argv, s, cwd, popup_job_update_cb, popup_job_complete_cb, NULL, pd, - JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, pd->sx - 2, pd->sy - 2); + JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, 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, @@ -497,8 +720,8 @@ popup_editor(struct client *c, const char *buf, size_t len, py = (c->tty.sy / 2) - (sy / 2); xasprintf(&cmd, "%s %s", editor, path); - if (popup_display(POPUP_CLOSEEXIT, NULL, px, py, sx, sy, cmd, 0, NULL, - _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) { + if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, + cmd, 0, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) { popup_editor_free(pe); free(cmd); return (-1); diff --git a/screen-redraw.c b/screen-redraw.c index 3df57383..82e390cd 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -624,7 +624,7 @@ screen_redraw_screen(struct client *c) } if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) { log_debug("%s: redrawing overlay", c->name); - c->overlay_draw(c, &ctx); + c->overlay_draw(c, c->overlay_data, &ctx); } tty_reset(&c->tty); @@ -690,7 +690,8 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) struct grid_cell gc; const struct grid_cell *tmp; - if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) + if (c->overlay_check != NULL && + !c->overlay_check(c, c->overlay_data, x, y)) return; cell_type = screen_redraw_check_cell(c, x, y, pane_status, &wp); diff --git a/server-client.c b/server-client.c index 56818bd7..e96f4f60 100644 --- a/server-client.c +++ b/server-client.c @@ -30,7 +30,6 @@ #include "tmux.h" static void server_client_free(int, short, void *); -static void server_client_check_pane_focus(struct window_pane *); static void server_client_check_pane_resize(struct window_pane *); static void server_client_check_pane_buffer(struct window_pane *); static void server_client_check_window_resize(struct window *); @@ -132,7 +131,7 @@ server_client_clear_overlay(struct client *c) evtimer_del(&c->overlay_timer); if (c->overlay_free != NULL) - c->overlay_free(c); + c->overlay_free(c, c->overlay_data); c->overlay_check = NULL; c->overlay_mode = NULL; @@ -298,9 +297,8 @@ server_client_attached_lost(struct client *c) s = loop->session; if (loop == c || s == NULL || s->curw->window != w) continue; - if (found == NULL || - timercmp(&loop->activity_time, &found->activity_time, - >)) + if (found == NULL || timercmp(&loop->activity_time, + &found->activity_time, >)) found = loop; } if (found != NULL) @@ -308,6 +306,40 @@ server_client_attached_lost(struct client *c) } } + +/* Set client session. */ +void +server_client_set_session(struct client *c, struct session *s) +{ + struct session *old = c->session; + + if (s != NULL && c->session != NULL && c->session != s) + c->last_session = c->session; + else if (s == NULL) + c->last_session = NULL; + c->session = s; + c->flags |= CLIENT_FOCUSED; + recalculate_sizes(); + + if (old != NULL && old->curw != NULL) + window_update_focus(old->curw->window); + if (s != NULL) { + window_update_focus(s->curw->window); + session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); + s->curw->flags &= ~WINLINK_ALERTFLAGS; + s->curw->window->latest = c; + alerts_check_session(s); + tty_update_client_offset(c); + status_timer_start(c); + notify_client("client-session-changed", c); + server_redraw_client(c); + } + + server_check_unattached(); + server_update_socket(); +} + /* Lost a client. */ void server_client_lost(struct client *c) @@ -1355,7 +1387,7 @@ server_client_handle_key(struct client *c, struct key_event *event) status_message_clear(c); } if (c->overlay_key != NULL) { - switch (c->overlay_key(c, event)) { + switch (c->overlay_key(c, c->overlay_data, event)) { case 0: return (0); case 1: @@ -1386,7 +1418,6 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; - int focus; /* Check for window resize. This is done before redrawing. */ RB_FOREACH(w, windows, &windows) @@ -1404,14 +1435,11 @@ server_client_loop(void) /* * Any windows will have been redrawn as part of clients, so clear - * their flags now. Also check pane focus and resize. + * their flags now. */ - focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { - if (focus) - server_client_check_pane_focus(wp); server_client_check_pane_resize(wp); server_client_check_pane_buffer(wp); } @@ -1612,54 +1640,6 @@ out: bufferevent_enable(wp->event, EV_READ); } -/* Check whether pane should be focused. */ -static void -server_client_check_pane_focus(struct window_pane *wp) -{ - struct client *c; - int push; - - /* Do we need to push the focus state? */ - push = wp->flags & PANE_FOCUSPUSH; - wp->flags &= ~PANE_FOCUSPUSH; - - /* If we're not the active pane in our window, we're not focused. */ - if (wp->window->active != wp) - goto not_focused; - - /* - * If our window is the current window in any focused clients with an - * attached session, we're focused. - */ - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || !(c->flags & CLIENT_FOCUSED)) - continue; - if (c->session->attached == 0) - continue; - - if (c->session->curw->window == wp->window) - goto focused; - } - -not_focused: - if (push || (wp->flags & PANE_FOCUSED)) { - if (wp->base.mode & MODE_FOCUSON) - bufferevent_write(wp->event, "\033[O", 3); - notify_pane("pane-focus-out", wp); - } - wp->flags &= ~PANE_FOCUSED; - return; - -focused: - if (push || !(wp->flags & PANE_FOCUSED)) { - if (wp->base.mode & MODE_FOCUSON) - bufferevent_write(wp->event, "\033[I", 3); - notify_pane("pane-focus-in", wp); - session_update_activity(c->session, NULL); - } - wp->flags |= PANE_FOCUSED; -} - /* * Update cursor position and mode settings. The scroll region and attributes * are cleared when idle (waiting for an event) as this is the most likely time @@ -1690,7 +1670,7 @@ server_client_reset_state(struct client *c) /* Get mode from overlay if any, else from screen. */ if (c->overlay_draw != NULL) { if (c->overlay_mode != NULL) - s = c->overlay_mode(c, &cx, &cy); + s = c->overlay_mode(c, c->overlay_data, &cx, &cy); } else s = wp->screen; if (s != NULL) @@ -2067,7 +2047,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) if (c->overlay_resize == NULL) server_client_clear_overlay(c); else - c->overlay_resize(c); + c->overlay_resize(c, c->overlay_data); server_redraw_client(c); if (c->session != NULL) notify_client("client-resized", c); @@ -2075,7 +2055,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) case MSG_EXITING: if (datalen != 0) fatalx("bad MSG_EXITING size"); - c->session = NULL; + server_client_set_session(c, NULL); tty_close(&c->tty); proc_send(c->peer, MSG_EXITED, -1, NULL, 0); break; diff --git a/server-fn.c b/server-fn.c index 656de08e..2521200a 100644 --- a/server-fn.c +++ b/server-fn.c @@ -446,21 +446,9 @@ server_destroy_session(struct session *s) TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; - if (s_new == NULL) { - c->session = NULL; + server_client_set_session(c, NULL); + if (s_new == NULL) c->flags |= CLIENT_EXIT; - } else { - c->last_session = NULL; - c->session = s_new; - server_client_set_key_table(c, NULL); - tty_update_client_offset(c); - status_timer_start(c); - notify_client("client-session-changed", c); - session_update_activity(s_new, NULL); - gettimeofday(&s_new->last_attached_time, NULL); - server_redraw_client(c); - alerts_check_session(s_new); - } } recalculate_sizes(); } diff --git a/session.c b/session.c index 76ad6ddf..8665cccc 100644 --- a/session.c +++ b/session.c @@ -487,6 +487,8 @@ session_last(struct session *s) int session_set_current(struct session *s, struct winlink *wl) { + struct winlink *old = s->curw; + if (wl == NULL) return (-1); if (wl == s->curw) @@ -495,6 +497,10 @@ session_set_current(struct session *s, struct winlink *wl) winlink_stack_remove(&s->lastw, wl); winlink_stack_push(&s->lastw, s->curw); s->curw = wl; + if (options_get_number(global_options, "focus-events")) { + window_update_focus(old->window); + window_update_focus(wl->window); + } winlink_clear_flags(wl); window_update_activity(wl->window); tty_update_window_offset(wl->window); diff --git a/tmux-protocol.h b/tmux-protocol.h new file mode 100644 index 00000000..08422291 --- /dev/null +++ b/tmux-protocol.h @@ -0,0 +1,114 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 Nicholas Marriott + * + * 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. + */ + +#ifndef TMUX_PROTOCOL_H +#define TMUX_PROTOCOL_H + +/* Protocol version. */ +#define PROTOCOL_VERSION 8 + +/* Message types. */ +enum msgtype { + MSG_VERSION = 12, + + MSG_IDENTIFY_FLAGS = 100, + MSG_IDENTIFY_TERM, + MSG_IDENTIFY_TTYNAME, + MSG_IDENTIFY_OLDCWD, /* unused */ + MSG_IDENTIFY_STDIN, + MSG_IDENTIFY_ENVIRON, + MSG_IDENTIFY_DONE, + MSG_IDENTIFY_CLIENTPID, + MSG_IDENTIFY_CWD, + MSG_IDENTIFY_FEATURES, + MSG_IDENTIFY_STDOUT, + MSG_IDENTIFY_LONGFLAGS, + MSG_IDENTIFY_TERMINFO, + + MSG_COMMAND = 200, + MSG_DETACH, + MSG_DETACHKILL, + MSG_EXIT, + MSG_EXITED, + MSG_EXITING, + MSG_LOCK, + MSG_READY, + MSG_RESIZE, + MSG_SHELL, + MSG_SHUTDOWN, + MSG_OLDSTDERR, /* unused */ + MSG_OLDSTDIN, /* unused */ + MSG_OLDSTDOUT, /* unused */ + MSG_SUSPEND, + MSG_UNLOCK, + MSG_WAKEUP, + MSG_EXEC, + MSG_FLAGS, + + MSG_READ_OPEN = 300, + MSG_READ, + MSG_READ_DONE, + MSG_WRITE_OPEN, + MSG_WRITE, + MSG_WRITE_READY, + MSG_WRITE_CLOSE +}; + +/* + * Message data. + * + * Don't forget to bump PROTOCOL_VERSION if any of these change! + */ +struct msg_command { + int argc; +}; /* followed by packed argv */ + +struct msg_read_open { + int stream; + int fd; +}; /* followed by path */ + +struct msg_read_data { + int stream; +}; + +struct msg_read_done { + int stream; + int error; +}; + +struct msg_write_open { + int stream; + int fd; + int flags; +}; /* followed by path */ + +struct msg_write_data { + int stream; +}; /* followed by data */ + +struct msg_write_ready { + int stream; + int error; +}; + +struct msg_write_close { + int stream; +}; + +#endif /* TMUX_PROTOCOL_H */ diff --git a/tmux.1 b/tmux.1 index 743584f8..2f5ac243 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5414,7 +5414,7 @@ See for possible values for .Ar prompt-type . .It Xo Ic command-prompt -.Op Fl 1FikN +.Op Fl 1bFikN .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -5517,6 +5517,11 @@ option: .It Li "Move cursor to start" Ta "0" Ta "C-a" .It Li "Transpose characters" Ta "" Ta "C-t" .El +.Pp +With +.Fl b , +the prompt is shown in the background and the invoking client does not exit +until it is dismissed. .It Xo Ic confirm-before .Op Fl b .Op Fl p Ar prompt @@ -5537,7 +5542,8 @@ It may contain the special character sequences supported by the option. With .Fl b , -the prompt is shown in the background and the client. +the prompt is shown in the background and the invoking client does not exit +until it is dismissed. .It Xo Ic display-menu .Op Fl O .Op Fl c Ar target-client @@ -5671,7 +5677,7 @@ lists the format variables and their values. forwards any input read from stdin to the empty pane given by .Ar target-pane . .It Xo Ic display-popup -.Op Fl CE +.Op Fl BCE .Op Fl c Ar target-client .Op Fl d Ar start-directory .Op Fl h Ar height @@ -5711,6 +5717,8 @@ and give the width and height - both may be a percentage (followed by .Ql % ) . If omitted, half of the terminal size is used. +.Fl B +does not surround the popup by a border. .Pp The .Fl C diff --git a/tmux.h b/tmux.h index 930bfa8b..3c88107d 100644 --- a/tmux.h +++ b/tmux.h @@ -32,6 +32,7 @@ #endif #include "compat.h" +#include "tmux-protocol.h" #include "xmalloc.h" extern char **environ; @@ -51,6 +52,7 @@ struct format_job_tree; struct format_tree; struct input_ctx; struct job; +struct menu_data; struct mode_tree_data; struct mouse_event; struct options; @@ -61,13 +63,12 @@ struct screen_write_cline; struct screen_write_ctx; struct session; struct tty_ctx; +struct tty_code; +struct tty_key; struct tmuxpeer; struct tmuxproc; struct winlink; -/* Client-server protocol version. */ -#define PROTOCOL_VERSION 8 - /* Default configuration files and socket paths. */ #ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf:~/.tmux.conf" @@ -505,95 +506,6 @@ enum tty_code_code { TTYC_XT }; -/* Message codes. */ -enum msgtype { - MSG_VERSION = 12, - - MSG_IDENTIFY_FLAGS = 100, - MSG_IDENTIFY_TERM, - MSG_IDENTIFY_TTYNAME, - MSG_IDENTIFY_OLDCWD, /* unused */ - MSG_IDENTIFY_STDIN, - MSG_IDENTIFY_ENVIRON, - MSG_IDENTIFY_DONE, - MSG_IDENTIFY_CLIENTPID, - MSG_IDENTIFY_CWD, - MSG_IDENTIFY_FEATURES, - MSG_IDENTIFY_STDOUT, - MSG_IDENTIFY_LONGFLAGS, - MSG_IDENTIFY_TERMINFO, - - MSG_COMMAND = 200, - MSG_DETACH, - MSG_DETACHKILL, - MSG_EXIT, - MSG_EXITED, - MSG_EXITING, - MSG_LOCK, - MSG_READY, - MSG_RESIZE, - MSG_SHELL, - MSG_SHUTDOWN, - MSG_OLDSTDERR, /* unused */ - MSG_OLDSTDIN, /* unused */ - MSG_OLDSTDOUT, /* unused */ - MSG_SUSPEND, - MSG_UNLOCK, - MSG_WAKEUP, - MSG_EXEC, - MSG_FLAGS, - - MSG_READ_OPEN = 300, - MSG_READ, - MSG_READ_DONE, - MSG_WRITE_OPEN, - MSG_WRITE, - MSG_WRITE_READY, - MSG_WRITE_CLOSE -}; - -/* - * Message data. - * - * Don't forget to bump PROTOCOL_VERSION if any of these change! - */ -struct msg_command { - int argc; -}; /* followed by packed argv */ - -struct msg_read_open { - int stream; - int fd; -}; /* followed by path */ - -struct msg_read_data { - int stream; -}; - -struct msg_read_done { - int stream; - int error; -}; - -struct msg_write_open { - int stream; - int fd; - int flags; -}; /* followed by path */ - -struct msg_write_data { - int stream; -}; /* followed by data */ - -struct msg_write_ready { - int stream; - int error; -}; - -struct msg_write_close { - int stream; -}; - /* Character classes. */ #define WHITESPACE " " @@ -1005,7 +917,7 @@ struct window_pane { #define PANE_FOCUSED 0x4 /* 0x8 unused */ /* 0x10 unused */ -#define PANE_FOCUSPUSH 0x20 +/* 0x20 unused */ #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 #define PANE_EXITED 0x100 @@ -1293,18 +1205,7 @@ struct key_event { struct mouse_event m; }; -/* TTY information. */ -struct tty_key { - char ch; - key_code key; - - struct tty_key *left; - struct tty_key *right; - - struct tty_key *next; -}; - -struct tty_code; +/* Terminal definition. */ struct tty_term { char *name; struct tty *tty; @@ -1326,6 +1227,7 @@ struct tty_term { }; LIST_HEAD(tty_terms, tty_term); +/* Client terminal. */ struct tty { struct client *client; struct event start_timer; @@ -1394,7 +1296,7 @@ struct tty { struct tty_key *key_tree; }; -/* TTY command context. */ +/* Terminal command context. */ typedef void (*tty_ctx_redraw_cb)(const struct tty_ctx *); typedef int (*tty_ctx_set_client_cb)(struct tty_ctx *, struct client *); struct tty_ctx { @@ -1635,12 +1537,14 @@ RB_HEAD(client_windows, client_window); /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); -typedef int (*overlay_check_cb)(struct client *, u_int, u_int); -typedef struct screen *(*overlay_mode_cb)(struct client *, u_int *, u_int *); -typedef void (*overlay_draw_cb)(struct client *, struct screen_redraw_ctx *); -typedef int (*overlay_key_cb)(struct client *, struct key_event *); -typedef void (*overlay_free_cb)(struct client *); -typedef void (*overlay_resize_cb)(struct client *); +typedef int (*overlay_check_cb)(struct client *, void *, u_int, u_int); +typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *, + u_int *); +typedef void (*overlay_draw_cb)(struct client *, void *, + struct screen_redraw_ctx *); +typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *); +typedef void (*overlay_free_cb)(struct client *, void *); +typedef void (*overlay_resize_cb)(struct client *, void *); struct client { const char *name; struct tmuxpeer *peer; @@ -2139,6 +2043,7 @@ struct job *job_run(const char *, int, char **, struct session *, const char *, job_update_cb, job_complete_cb, job_free_cb, void *, int, int, int); void job_free(struct job *); +int job_transfer(struct job *, pid_t *, char *, size_t); void job_resize(struct job *, u_int, u_int); void job_check_died(pid_t, int); int job_get_status(struct job *); @@ -2507,6 +2412,7 @@ int server_client_handle_key(struct client *, struct key_event *); struct client *server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); +void server_client_set_session(struct client *, struct session *); void server_client_lost(struct client *); void server_client_suspend(struct client *); void server_client_detach(struct client *, enum msgtype); @@ -2827,6 +2733,8 @@ struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *, int); +void window_update_focus(struct window *); +void window_pane_update_focus(struct window_pane *); void window_redraw_active_switch(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, struct window_pane *, @@ -3116,13 +3024,24 @@ void menu_add_item(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); void menu_free(struct menu *); +struct menu_data *menu_prepare(struct menu *, int, struct cmdq_item *, u_int, + u_int, struct client *, struct cmd_find_state *, + menu_choice_cb, void *); int menu_display(struct menu *, int, struct cmdq_item *, u_int, u_int, struct client *, struct cmd_find_state *, menu_choice_cb, void *); +struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *); +int menu_check_cb(struct client *, void *, u_int, u_int); +void menu_draw_cb(struct client *, void *, + struct screen_redraw_ctx *); +void menu_free_cb(struct client *, void *); +int menu_key_cb(struct client *, void *, struct key_event *); /* popup.c */ #define POPUP_CLOSEEXIT 0x1 #define POPUP_CLOSEEXITZERO 0x2 +#define POPUP_NOBORDER 0x4 +#define POPUP_INTERNAL 0x8 typedef void (*popup_close_cb)(int, void *); typedef void (*popup_finish_edit_cb)(char *, size_t, void *); int popup_display(int, struct cmdq_item *, u_int, u_int, u_int, diff --git a/tty-keys.c b/tty-keys.c index 02fba0e5..6dfa70f3 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -58,6 +58,17 @@ static int tty_keys_device_attributes(struct tty *, const char *, size_t, static int tty_keys_extended_device_attributes(struct tty *, const char *, size_t, size_t *); +/* A key tree entry. */ +struct tty_key { + char ch; + key_code key; + + struct tty_key *left; + struct tty_key *right; + + struct tty_key *next; +}; + /* Default raw keys. */ struct tty_default_key_raw { const char *string; @@ -821,11 +832,13 @@ complete_key: /* Check for focus events. */ if (key == KEYC_FOCUS_OUT) { - tty->client->flags &= ~CLIENT_FOCUSED; + c->flags &= ~CLIENT_FOCUSED; + window_update_focus(c->session->curw->window); notify_client("client-focus-out", c); } else if (key == KEYC_FOCUS_IN) { - tty->client->flags |= CLIENT_FOCUSED; + c->flags |= CLIENT_FOCUSED; notify_client("client-focus-in", c); + window_update_focus(c->session->curw->window); } /* Fire the key. */ diff --git a/tty.c b/tty.c index ecad9249..2a9f8b01 100644 --- a/tty.c +++ b/tty.c @@ -1318,7 +1318,7 @@ tty_check_overlay(struct tty *tty, u_int px, u_int py) if (c->overlay_check == NULL) return (1); - return (c->overlay_check(c, px, py)); + return (c->overlay_check(c, c->overlay_data, px, py)); } void diff --git a/window.c b/window.c index ce02df9e..5fcb547d 100644 --- a/window.c +++ b/window.c @@ -465,6 +465,52 @@ window_has_pane(struct window *w, struct window_pane *wp) return (0); } +void +window_update_focus(struct window *w) +{ + if (w != NULL) { + log_debug("%s: @%u", __func__, w->id); + window_pane_update_focus(w->active); + } +} + +void +window_pane_update_focus(struct window_pane *wp) +{ + struct client *c; + int focused = 0; + + if (wp != NULL) { + if (wp != wp->window->active) + focused = 0; + else { + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL && + c->session->attached != 0 && + (c->flags & CLIENT_FOCUSED) && + c->session->curw->window == wp->window) { + focused = 1; + break; + } + } + } + if (!focused && (wp->flags & PANE_FOCUSED)) { + log_debug("%s: %%%u focus out", __func__, wp->id); + if (wp->base.mode & MODE_FOCUSON) + bufferevent_write(wp->event, "\033[O", 3); + notify_pane("pane-focus-out", wp); + wp->flags &= ~PANE_FOCUSED; + } else if (focused && (~wp->flags & PANE_FOCUSED)) { + log_debug("%s: %%%u focus in", __func__, wp->id); + if (wp->base.mode & MODE_FOCUSON) + bufferevent_write(wp->event, "\033[I", 3); + notify_pane("pane-focus-in", wp); + wp->flags |= PANE_FOCUSED; + } else + log_debug("%s: %%%u focus unchanged", __func__, wp->id); + } +} + int window_set_active_pane(struct window *w, struct window_pane *wp, int notify) { @@ -478,6 +524,11 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) w->active->active_point = next_active_point++; w->active->flags |= PANE_CHANGED; + if (options_get_number(global_options, "focus-events")) { + window_pane_update_focus(w->last); + window_pane_update_focus(w->active); + } + tty_update_window_offset(w); if (notify)