aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd-attach-session.c23
-rw-r--r--cmd-command-prompt.c60
-rw-r--r--cmd-confirm-before.c15
-rw-r--r--cmd-display-menu.c6
-rw-r--r--cmd-display-panes.c11
-rw-r--r--cmd-if-shell.c5
-rw-r--r--cmd-new-session.c10
-rw-r--r--cmd-run-shell.c1
-rw-r--r--cmd-switch-client.c16
-rw-r--r--input.c8
-rw-r--r--job.c32
-rw-r--r--menu.c57
-rw-r--r--popup.c305
-rw-r--r--screen-redraw.c5
-rw-r--r--server-client.c104
-rw-r--r--server-fn.c16
-rw-r--r--session.c6
-rw-r--r--tmux-protocol.h114
-rw-r--r--tmux.114
-rw-r--r--tmux.h143
-rw-r--r--tty-keys.c17
-rw-r--r--tty.c2
-rw-r--r--window.c51
23 files changed, 681 insertions, 340 deletions
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,20 +75,22 @@ 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->idx = 1;
- cdata->inputs = NULL;
- cdata->next_input = NULL;
-
- cdata->prompts = NULL;
- cdata->next_prompt = NULL;
+ 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);
- cdata->template = NULL;
- cdata->idx = 1;
+ if (wait)
+ cdata->item = item;
if (args->argc != 0 && args_has(args, 'F'))
cdata->template = format_single_from_target(item, args->argv[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 = 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 = c->overlay_data;
+ 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,7 +278,11 @@ 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);
@@ -225,6 +290,92 @@ popup_resize_cb(struct client *c)
}
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 ((~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))
+ goto menu;
if ((m->b & MOUSE_MASK_META) ||
- m->x == pd->px ||
+ ((~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) {
+ 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;
-
- if (sx < 3 || sy < 3)
- return (-1);
+ u_int jx, jy;
+
+ 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 <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#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)