aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client.c1
-rw-r--r--cmd-display-message.c14
-rw-r--r--cmd-queue.c21
-rw-r--r--control-notify.c26
-rw-r--r--format.c2
-rw-r--r--screen-redraw.c57
-rw-r--r--tmux.198
-rw-r--r--tmux.h2
-rw-r--r--window.c4
9 files changed, 195 insertions, 30 deletions
diff --git a/client.c b/client.c
index 56caa338..91f47650 100644
--- a/client.c
+++ b/client.c
@@ -524,6 +524,7 @@ client_dispatch_wait(void *data)
event_del(&client_stdin);
client_attached = 1;
+ client_write_server(MSG_RESIZE, NULL, 0);
break;
case MSG_STDIN:
if (datalen != 0)
diff --git a/cmd-display-message.c b/cmd-display-message.c
index 319280ba..485ccf08 100644
--- a/cmd-display-message.c
+++ b/cmd-display-message.c
@@ -70,6 +70,18 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq)
return (CMD_RETURN_ERROR);
}
+ if (args_has(args, 'c')) {
+ c = cmd_find_client(cmdq, args_get(args, 'c'), 0);
+ if (c == NULL)
+ return (CMD_RETURN_ERROR);
+ } else {
+ c = cmd_current_client(cmdq);
+ if (c == NULL && !args_has(self->args, 'p')) {
+ cmdq_error(cmdq, "no client available");
+ return (CMD_RETURN_ERROR);
+ }
+ }
+
template = args_get(args, 'F');
if (args->argc != 0)
template = args->argv[0];
@@ -77,7 +89,7 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq)
template = DISPLAY_MESSAGE_TEMPLATE;
ft = format_create();
- if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 1)) != NULL)
+ if (c != NULL)
format_client(ft, c);
format_session(ft, s);
format_winlink(ft, s, wl);
diff --git a/cmd-queue.c b/cmd-queue.c
index 17992a37..6ea6f468 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -152,19 +152,20 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...)
}
/* Print a guard line. */
-void
+int
cmdq_guard(struct cmd_q *cmdq, const char *guard)
{
struct client *c = cmdq->client;
if (c == NULL || c->session == NULL)
- return;
+ return 0;
if (!(c->flags & CLIENT_CONTROL))
- return;
+ return 0;
evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard,
(long) cmdq->time, cmdq->number);
server_push_stdout(c);
+ return 1;
}
/* Add command list to queue and begin processing if needed. */
@@ -197,7 +198,7 @@ cmdq_continue(struct cmd_q *cmdq)
{
struct cmd_q_item *next;
enum cmd_retval retval;
- int empty;
+ int empty, guard;
char s[1024];
notify_disable();
@@ -223,12 +224,14 @@ cmdq_continue(struct cmd_q *cmdq)
cmdq->time = time(NULL);
cmdq->number++;
- cmdq_guard(cmdq, "begin");
+ guard = cmdq_guard(cmdq, "begin");
retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq);
- if (retval == CMD_RETURN_ERROR)
- cmdq_guard(cmdq, "error");
- else
- cmdq_guard(cmdq, "end");
+ if (guard) {
+ if (retval == CMD_RETURN_ERROR)
+ cmdq_guard(cmdq, "error");
+ else
+ cmdq_guard(cmdq, "end");
+ }
if (retval == CMD_RETURN_ERROR)
break;
diff --git a/control-notify.c b/control-notify.c
index 6bc98b6f..0931c23a 100644
--- a/control-notify.c
+++ b/control-notify.c
@@ -46,8 +46,12 @@ control_notify_input(struct client *c, struct window_pane *wp,
if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) {
message = evbuffer_new();
evbuffer_add_printf(message, "%%output %%%u ", wp->id);
- for (i = 0; i < len; i++)
- evbuffer_add_printf(message, "%02hhx", buf[i]);
+ for (i = 0; i < len; i++) {
+ if (buf[i] < ' ' || buf[i] == '\\')
+ evbuffer_add_printf(message, "\\%03o", buf[i]);
+ else
+ evbuffer_add_printf(message, "%c", buf[i]);
+ }
control_write_buffer(c, message);
evbuffer_free(message);
}
@@ -104,7 +108,7 @@ control_notify_window_unlinked(unused struct session *s, struct window *w)
continue;
cs = c->session;
- control_write(c, "%%window-close %u", w->id);
+ control_write(c, "%%window-close @%u", w->id);
}
}
@@ -122,9 +126,9 @@ control_notify_window_linked(unused struct session *s, struct window *w)
cs = c->session;
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
- control_write(c, "%%window-add %u", w->id);
+ control_write(c, "%%window-add @%u", w->id);
else
- control_write(c, "%%unlinked-window-add %u", w->id);
+ control_write(c, "%%unlinked-window-add @%u", w->id);
}
}
@@ -141,7 +145,7 @@ control_notify_window_renamed(struct window *w)
continue;
s = c->session;
- control_write(c, "%%window-renamed %u %s", w->id, w->name);
+ control_write(c, "%%window-renamed @%u %s", w->id, w->name);
}
}
@@ -154,7 +158,7 @@ control_notify_attached_session_changed(struct client *c)
return;
s = c->session;
- control_write(c, "%%session-changed %d %s", s->id, s->name);
+ control_write(c, "%%session-changed $%u %s", s->id, s->name);
}
void
@@ -165,10 +169,10 @@ control_notify_session_renamed(struct session *s)
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i);
- if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session != s)
+ if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
- control_write(c, "%%session-renamed %s", s->name);
+ control_write(c, "%%session-renamed $%u %s", s->id, s->name);
}
}
@@ -180,7 +184,7 @@ control_notify_session_created(unused struct session *s)
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i);
- if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
+ if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%sessions-changed");
@@ -195,7 +199,7 @@ control_notify_session_close(unused struct session *s)
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i);
- if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
+ if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%sessions-changed");
diff --git a/format.c b/format.c
index 4d70d59b..fa2dd0b2 100644
--- a/format.c
+++ b/format.c
@@ -280,7 +280,7 @@ format_session(struct format_tree *ft, struct session *s)
format_add(ft, "session_windows", "%u", winlink_count(&s->windows));
format_add(ft, "session_width", "%u", s->sx);
format_add(ft, "session_height", "%u", s->sy);
- format_add(ft, "session_id", "%u", s->id);
+ format_add(ft, "session_id", "$%u", s->id);
sg = session_group_find(s);
format_add(ft, "session_grouped", "%d", sg != NULL);
diff --git a/screen-redraw.c b/screen-redraw.c
index 14b73164..a7bf81ff 100644
--- a/screen-redraw.c
+++ b/screen-redraw.c
@@ -24,7 +24,11 @@
int screen_redraw_cell_border1(struct window_pane *, u_int, u_int);
int screen_redraw_cell_border(struct client *, u_int, u_int);
-int screen_redraw_check_cell(struct client *, u_int, u_int);
+int screen_redraw_check_cell(struct client *, u_int, u_int,
+ struct window_pane **);
+int screen_redraw_check_active(u_int, u_int, int, struct window *,
+ struct window_pane *);
+
void screen_redraw_draw_number(struct client *, struct window_pane *);
#define CELL_INSIDE 0
@@ -93,7 +97,8 @@ screen_redraw_cell_border(struct client *c, u_int px, u_int py)
/* Check if cell inside a pane. */
int
-screen_redraw_check_cell(struct client *c, u_int px, u_int py)
+screen_redraw_check_cell(struct client *c, u_int px, u_int py,
+ struct window_pane **wpp)
{
struct window *w = c->session->curw->window;
struct window_pane *wp;
@@ -105,6 +110,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py)
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp))
continue;
+ *wpp = wp;
/* If outside the pane and its border, skip it. */
if ((wp->xoff != 0 && px < wp->xoff - 1) ||
@@ -162,9 +168,52 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py)
}
}
+ *wpp = NULL;
return (CELL_OUTSIDE);
}
+/* Check active pane indicator. */
+int
+screen_redraw_check_active(u_int px, u_int py, int type, struct window *w,
+ struct window_pane *wp)
+{
+ /* Is this off the active pane border? */
+ if (screen_redraw_cell_border1(w->active, px, py) != 1)
+ return (0);
+
+ /* If there are more than two panes, that's enough. */
+ if (window_count_panes(w) != 2)
+ return (1);
+
+ /* Else if the cell is not a border cell, forget it. */
+ if (wp == NULL || (type == CELL_OUTSIDE || type == CELL_INSIDE))
+ return (1);
+
+ /* Check if the pane covers the whole width. */
+ if (wp->xoff == 0 && wp->sx == w->sx) {
+ /* This can either be the top pane or the bottom pane. */
+ if (wp->yoff == 0) { /* top pane */
+ if (wp == w->active)
+ return (px <= wp->sx / 2);
+ return (px > wp->sx / 2);
+ }
+ return (0);
+ }
+
+ /* Check if the pane covers the whole height. */
+ if (wp->yoff == 0 && wp->sy == w->sy) {
+ /* This can either be the left pane or the right pane. */
+ if (wp->xoff == 0) { /* left pane */
+ if (wp == w->active)
+ return (py <= wp->sy / 2);
+ return (py > wp->sy / 2);
+ }
+ return (0);
+ }
+
+ return (type);
+}
+
/* Redraw entire screen. */
void
screen_redraw_screen(struct client *c, int status_only, int borders_only)
@@ -223,10 +272,10 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only)
break;
}
for (i = 0; i < tty->sx; i++) {
- type = screen_redraw_check_cell(c, i, j);
+ type = screen_redraw_check_cell(c, i, j, &wp);
if (type == CELL_INSIDE)
continue;
- if (screen_redraw_cell_border1(w->active, i, j) == 1)
+ if (screen_redraw_check_active(i, j, type, w, wp))
tty_attributes(tty, &active_gc);
else
tty_attributes(tty, &other_gc);
diff --git a/tmux.1 b/tmux.1
index 39ae62dc..cd9637b9 100644
--- a/tmux.1
+++ b/tmux.1
@@ -23,7 +23,7 @@
.Sh SYNOPSIS
.Nm tmux
.Bk -words
-.Op Fl 28lquvV
+.Op Fl 28lCquvV
.Op Fl c Ar shell-command
.Op Fl f Ar file
.Op Fl L Ar socket-name
@@ -102,6 +102,11 @@ to assume the terminal supports 256 colours.
Like
.Fl 2 ,
but indicates that the terminal supports 88 colours.
+.It Fl C
+Start in control mode.
+Given twice
+.Xo ( Fl CC ) Xc
+disables echo.
.It Fl c Ar shell-command
Execute
.Ar shell-command
@@ -1622,8 +1627,8 @@ is given in lines or cells (the default is 1).
.Pp
With
.Fl Z ,
-the active pane is toggled between occupying the whole of the window and its
-normal position in the layout.
+the active pane is toggled between zoomed (occupying the whole of the window)
+and unzoomed (its normal position in the layout).
.It Xo Ic respawn-pane
.Op Fl k
.Op Fl t Ar target-pane
@@ -3255,6 +3260,7 @@ The flag is one of the following symbols appended to the window name:
.It Li "!" Ta "A bell has occurred in the window."
.It Li "+" Ta "Window is monitored for content and it has appeared."
.It Li "~" Ta "The window has been silent for the monitor-silence interval."
+.It Li "Z" Ta "The window's active pane is zoomed."
.El
.Pp
The # symbol relates to the
@@ -3613,6 +3619,92 @@ option above and the
.Xr xterm 1
man page.
.El
+.Sh CONTROL MODE
+.Nm
+offers a textual interface called
+.Em control mode .
+This allows applications to communicate with
+.Nm
+using a simple text-only protocol.
+.Pp
+In control mode, a client sends
+.Nm
+commands or command sequences terminated by newlines on standard input.
+Each command will produce one block of output on standard output.
+An output block consists of a
+.Em %begin
+line followed by the output (which may be empty).
+The output block ends with a
+.Em %end
+or
+.Em %error .
+.Em %begin
+and matching
+.Em %end
+or
+.Em %error
+have two arguments: an integer time (as seconds from epoch) and command number.
+For example:
+.Bd -literal -offset indent
+%begin 1363006971 2
+0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active)
+%end 1363006971 2
+.Ed
+.Pp
+In control mode,
+.Nm
+outputs notifications.
+A notification will never occur inside an output block.
+.Pp
+The following notifications are defined:
+.Pp
+.Bl -tag -width Ds
+.It Ic %exit Op Ar reason
+The
+.Nm
+client is exiting immediately, either because it is not attached to any session
+or an error occurred.
+If present,
+.Ar reason
+describes why the client exited.
+.It Ic %layout-change Ar window-id Ar window-layout
+The layout of a window with ID
+.Ar window-id
+changed.
+The new layout is
+.Ar window-layout .
+.It Ic %output Ar pane-id Ar value
+A window pane produced output.
+.Ar value
+escapes non-printable characters and backslash as octal \\xxx.
+.It Ic %session-changed Ar session-id Ar name
+The client is now attached to the session with ID
+.Ar session-id ,
+which is named
+.Ar name .
+.It Ic %session-renamed Ar name
+The current session was renamed to
+.Ar name .
+.It Ic %sessions-changed
+A session was created or destroyed.
+.It Ic %unlinked-window-add Ar window-id
+The window with ID
+.Ar window-id
+was created but is not linked to the current session.
+.It Ic %window-add Ar window-id
+The window with ID
+.Ar window-id
+was linked to the current session.
+.It Ic %window-close Ar window-id
+The window with ID
+.Ar window-id
+closed.
+.It Ic %window-renamed Ar window-id Ar name
+The window with ID
+.Ar window-id
+was renamed to
+.Ar name .
+.El
.Sh FILES
.Bl -tag -width "/etc/tmux.confXXX" -compact
.It Pa ~/.tmux.conf
diff --git a/tmux.h b/tmux.h
index 89860cb8..7c4c55be 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1856,7 +1856,7 @@ int cmdq_free(struct cmd_q *);
void printflike2 cmdq_print(struct cmd_q *, const char *, ...);
void printflike2 cmdq_info(struct cmd_q *, const char *, ...);
void printflike2 cmdq_error(struct cmd_q *, const char *, ...);
-void cmdq_guard(struct cmd_q *, const char *);
+int cmdq_guard(struct cmd_q *, const char *);
void cmdq_run(struct cmd_q *, struct cmd_list *);
void cmdq_append(struct cmd_q *, struct cmd_list *);
int cmdq_continue(struct cmd_q *);
diff --git a/window.c b/window.c
index fc06e871..2be51e93 100644
--- a/window.c
+++ b/window.c
@@ -480,6 +480,10 @@ window_zoom(struct window_pane *wp)
if (!window_pane_visible(wp))
return (-1);
+
+ if (window_count_panes(w) == 1)
+ return (-1);
+
if (w->active != wp)
window_set_active_pane(w, wp);