Merge branch 'master' of ssh://git.code.sf.net/p/tmux/tmux-code

This commit is contained in:
Nicholas Marriott 2013-03-11 21:31:46 +00:00
commit 543420ccd2
25 changed files with 459 additions and 132 deletions

View File

@ -135,6 +135,7 @@ dist_tmux_SOURCES = \
cmd-switch-client.c \ cmd-switch-client.c \
cmd-unbind-key.c \ cmd-unbind-key.c \
cmd-unlink-window.c \ cmd-unlink-window.c \
cmd-wait-for.c \
cmd.c \ cmd.c \
colour.c \ colour.c \
control.c \ control.c \

View File

@ -270,7 +270,7 @@ client_main(int argc, char **argv, int flags)
if (msg == MSG_COMMAND) { if (msg == MSG_COMMAND) {
/* Fill in command line arguments. */ /* Fill in command line arguments. */
cmddata.pid = environ_pid; cmddata.pid = environ_pid;
cmddata.idx = environ_idx; cmddata.session_id = environ_session_id;
/* Prepare command for server. */ /* Prepare command for server. */
cmddata.argc = argc; cmddata.argc = argc;

View File

@ -59,19 +59,21 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq)
struct args *args = self->args; struct args *args = self->args;
struct cmd_if_shell_data *cdata; struct cmd_if_shell_data *cdata;
char *shellcmd; char *shellcmd;
struct session *s; struct session *s = NULL;
struct winlink *wl; struct winlink *wl = NULL;
struct window_pane *wp; struct window_pane *wp = NULL;
struct format_tree *ft; struct format_tree *ft;
wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); if (args_has(args, 't'))
if (wl == NULL) wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp);
return (CMD_RETURN_ERROR);
ft = format_create(); ft = format_create();
format_session(ft, s); if (s != NULL)
format_winlink(ft, s, wl); format_session(ft, s);
format_window_pane(ft, wp); if (s != NULL && wl != NULL)
format_winlink(ft, s, wl);
if (wp != NULL)
format_window_pane(ft, wp);
shellcmd = format_expand(ft, args->argv[0]); shellcmd = format_expand(ft, args->argv[0]);
format_free(ft); format_free(ft);

View File

@ -151,6 +151,22 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...)
free(msg); free(msg);
} }
/* Print a guard line. */
void
cmdq_guard(struct cmd_q *cmdq, const char *guard)
{
struct client *c = cmdq->client;
if (c == NULL || c->session == NULL)
return;
if (!(c->flags & CLIENT_CONTROL))
return;
evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard,
(long) cmdq->time, cmdq->number);
server_push_stdout(c);
}
/* Add command list to queue and begin processing if needed. */ /* Add command list to queue and begin processing if needed. */
void void
cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist) cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist)
@ -179,16 +195,11 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist)
int int
cmdq_continue(struct cmd_q *cmdq) cmdq_continue(struct cmd_q *cmdq)
{ {
struct client *c = cmdq->client;
struct cmd_q_item *next; struct cmd_q_item *next;
enum cmd_retval retval; enum cmd_retval retval;
int guards, empty; int empty;
char s[1024]; char s[1024];
guards = 0;
if (c != NULL && c->session != NULL)
guards = c->flags & CLIENT_CONTROL;
notify_disable(); notify_disable();
empty = TAILQ_EMPTY(&cmdq->queue); empty = TAILQ_EMPTY(&cmdq->queue);
@ -209,15 +220,15 @@ cmdq_continue(struct cmd_q *cmdq)
log_debug("cmdq %p: %s (client %d)", cmdq, s, log_debug("cmdq %p: %s (client %d)", cmdq, s,
cmdq->client != NULL ? cmdq->client->ibuf.fd : -1); cmdq->client != NULL ? cmdq->client->ibuf.fd : -1);
if (guards) cmdq->time = time(NULL);
cmdq_print(cmdq, "%%begin"); cmdq->number++;
cmdq_guard(cmdq, "begin");
retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq);
if (guards) { if (retval == CMD_RETURN_ERROR)
if (retval == CMD_RETURN_ERROR) cmdq_guard(cmdq, "error");
cmdq_print(cmdq, "%%error"); else
else cmdq_guard(cmdq, "end");
cmdq_print(cmdq, "%%end");
}
if (retval == CMD_RETURN_ERROR) if (retval == CMD_RETURN_ERROR)
break; break;

View File

@ -49,16 +49,17 @@ struct cmd_run_shell_data {
char *cmd; char *cmd;
struct cmd_q *cmdq; struct cmd_q *cmdq;
int bflag; int bflag;
u_int wp_id; int wp_id;
}; };
void void
cmd_run_shell_print(struct job *job, const char *msg) cmd_run_shell_print(struct job *job, const char *msg)
{ {
struct cmd_run_shell_data *cdata = job->data; struct cmd_run_shell_data *cdata = job->data;
struct window_pane *wp; struct window_pane *wp = NULL;
wp = window_pane_find_by_id(cdata->wp_id); if (cdata->wp_id != -1)
wp = window_pane_find_by_id(cdata->wp_id);
if (wp == NULL) { if (wp == NULL) {
cmdq_print(cdata->cmdq, "%s", msg); cmdq_print(cdata->cmdq, "%s", msg);
return; return;
@ -76,26 +77,28 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq)
struct args *args = self->args; struct args *args = self->args;
struct cmd_run_shell_data *cdata; struct cmd_run_shell_data *cdata;
char *shellcmd; char *shellcmd;
struct session *s; struct session *s = NULL;
struct winlink *wl; struct winlink *wl = NULL;
struct window_pane *wp; struct window_pane *wp = NULL;
struct format_tree *ft; struct format_tree *ft;
wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); if (args_has(args, 't'))
if (wl == NULL) wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp);
return (CMD_RETURN_ERROR);
ft = format_create(); ft = format_create();
format_session(ft, s); if (s != NULL)
format_winlink(ft, s, wl); format_session(ft, s);
format_window_pane(ft, wp); if (s != NULL && wl != NULL)
format_winlink(ft, s, wl);
if (wp != NULL)
format_window_pane(ft, wp);
shellcmd = format_expand(ft, args->argv[0]); shellcmd = format_expand(ft, args->argv[0]);
format_free(ft); format_free(ft);
cdata = xmalloc(sizeof *cdata); cdata = xmalloc(sizeof *cdata);
cdata->cmd = shellcmd; cdata->cmd = shellcmd;
cdata->bflag = args_has(args, 'b'); cdata->bflag = args_has(args, 'b');
cdata->wp_id = wp->id; cdata->wp_id = wp != NULL ? (int) wp->id : -1;
cdata->cmdq = cmdq; cdata->cmdq = cmdq;
cmdq->references++; cmdq->references++;

View File

@ -80,6 +80,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
server_unzoom_window(wl->window);
window_set_active_pane(wl->window, wl->window->last); window_set_active_pane(wl->window, wl->window->last);
server_status_window(wl->window); server_status_window(wl->window);
server_redraw_window_borders(wl->window); server_redraw_window_borders(wl->window);

View File

@ -102,7 +102,7 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_q *cmdq)
*strchr(tim, '\n') = '\0'; *strchr(tim, '\n') = '\0';
cmdq_print(cmdq, "%2u: %s: %u windows (created %s) [%ux%u] " cmdq_print(cmdq, "%2u: %s: %u windows (created %s) [%ux%u] "
"[flags=0x%x]", s->idx, s->name, "[flags=0x%x]", s->id, s->name,
winlink_count(&s->windows), tim, s->sx, s->sy, s->flags); winlink_count(&s->windows), tim, s->sx, s->sy, s->flags);
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window; w = wl->window;

197
cmd-wait-for.c Normal file
View File

@ -0,0 +1,197 @@
/* $Id$ */
/*
* Copyright (c) 2013 Nicholas Marriott <nicm@users.sourceforge.net>
* Copyright (c) 2013 Thiago de Arruda <tpadilha84@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.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Block or wake a client on a named wait channel.
*/
enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *);
const struct cmd_entry cmd_wait_for_entry = {
"wait-for", "wait",
"LSU", 1, 1,
"[-LSU] channel",
0,
NULL,
NULL,
cmd_wait_for_exec
};
struct wait_channel {
const char *name;
int locked;
TAILQ_HEAD(, cmd_q) waiters;
TAILQ_HEAD(, cmd_q) lockers;
RB_ENTRY(wait_channel) entry;
};
RB_HEAD(wait_channels, wait_channel);
struct wait_channels wait_channels = RB_INITIALIZER(wait_channels);
int wait_channel_cmp(struct wait_channel *, struct wait_channel *);
RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp);
RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp);
int
wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2)
{
return (strcmp(wc1->name, wc2->name));
}
enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *,
struct wait_channel *);
enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *,
struct wait_channel *);
enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *,
struct wait_channel *);
enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *,
struct wait_channel *);
enum cmd_retval
cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
const char *name = args->argv[0];
struct wait_channel *wc, wc0;
wc0.name = name;
wc = RB_FIND(wait_channels, &wait_channels, &wc0);
if (args_has(args, 'S'))
return (cmd_wait_for_signal(cmdq, name, wc));
if (args_has(args, 'L'))
return (cmd_wait_for_lock(cmdq, name, wc));
if (args_has(args, 'U'))
return (cmd_wait_for_unlock(cmdq, name, wc));
return (cmd_wait_for_wait(cmdq, name, wc));
}
enum cmd_retval
cmd_wait_for_signal(struct cmd_q *cmdq, const char *name,
struct wait_channel *wc)
{
struct cmd_q *wq, *wq1;
if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) {
cmdq_error(cmdq, "no waiting clients on %s", name);
return (CMD_RETURN_ERROR);
}
TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) {
TAILQ_REMOVE(&wc->waiters, wq, waitentry);
if (!cmdq_free(wq))
cmdq_continue(wq);
}
if (!wc->locked) {
RB_REMOVE(wait_channels, &wait_channels, wc);
free((void*) wc->name);
free(wc);
}
return (CMD_RETURN_NORMAL);
}
enum cmd_retval
cmd_wait_for_wait(struct cmd_q *cmdq, const char *name,
struct wait_channel *wc)
{
if (cmdq->client == NULL || cmdq->client->session != NULL) {
cmdq_error(cmdq, "not able to wait");
return (CMD_RETURN_ERROR);
}
if (wc == NULL) {
wc = xmalloc(sizeof *wc);
wc->name = xstrdup(name);
wc->locked = 0;
TAILQ_INIT(&wc->waiters);
TAILQ_INIT(&wc->lockers);
RB_INSERT(wait_channels, &wait_channels, wc);
}
TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry);
cmdq->references++;
return (CMD_RETURN_WAIT);
}
enum cmd_retval
cmd_wait_for_lock(struct cmd_q *cmdq, const char *name,
struct wait_channel *wc)
{
if (cmdq->client == NULL || cmdq->client->session != NULL) {
cmdq_error(cmdq, "not able to lock");
return (CMD_RETURN_ERROR);
}
if (wc == NULL) {
wc = xmalloc(sizeof *wc);
wc->name = xstrdup(name);
wc->locked = 0;
TAILQ_INIT(&wc->waiters);
TAILQ_INIT(&wc->lockers);
RB_INSERT(wait_channels, &wait_channels, wc);
}
if (wc->locked) {
TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry);
cmdq->references++;
return (CMD_RETURN_WAIT);
}
wc->locked = 1;
return (CMD_RETURN_NORMAL);
}
enum cmd_retval
cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name,
struct wait_channel *wc)
{
struct cmd_q *wq;
if (wc == NULL || !wc->locked) {
cmdq_error(cmdq, "channel %s not locked", name);
return (CMD_RETURN_ERROR);
}
if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) {
TAILQ_REMOVE(&wc->lockers, wq, waitentry);
if (!cmdq_free(wq))
cmdq_continue(wq);
} else {
wc->locked = 0;
if (TAILQ_EMPTY(&wc->waiters)) {
RB_REMOVE(wait_channels, &wait_channels, wc);
free((void*) wc->name);
free(wc);
}
}
return (CMD_RETURN_NORMAL);
}

25
cmd.c
View File

@ -112,6 +112,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_switch_client_entry, &cmd_switch_client_entry,
&cmd_unbind_key_entry, &cmd_unbind_key_entry,
&cmd_unlink_window_entry, &cmd_unlink_window_entry,
&cmd_wait_for_entry,
NULL NULL
}; };
@ -121,6 +122,7 @@ struct session *cmd_choose_session(int);
struct client *cmd_choose_client(struct clients *); struct client *cmd_choose_client(struct clients *);
struct client *cmd_lookup_client(const char *); struct client *cmd_lookup_client(const char *);
struct session *cmd_lookup_session(const char *, int *); struct session *cmd_lookup_session(const char *, int *);
struct session *cmd_lookup_session_id(const char *);
struct winlink *cmd_lookup_window(struct session *, const char *, int *); struct winlink *cmd_lookup_window(struct session *, const char *, int *);
int cmd_lookup_index(struct session *, const char *, int *); int cmd_lookup_index(struct session *, const char *, int *);
struct window_pane *cmd_lookup_paneid(const char *); struct window_pane *cmd_lookup_paneid(const char *);
@ -356,8 +358,8 @@ cmd_current_session(struct cmd_q *cmdq, int prefer_unattached)
} }
/* Use the session from the TMUX environment variable. */ /* Use the session from the TMUX environment variable. */
if (data != NULL && data->pid == getpid() && data->idx != -1) { if (data != NULL && data->pid == getpid() && data->session_id != -1) {
s = session_find_by_index(data->idx); s = session_find_by_id(data->session_id);
if (s != NULL) if (s != NULL)
return (s); return (s);
} }
@ -549,6 +551,21 @@ cmd_lookup_client(const char *name)
return (NULL); return (NULL);
} }
/* Find the target session or report an error and return NULL. */
struct session *
cmd_lookup_session_id(const char *arg)
{
char *endptr;
long id;
if (arg[0] != '$')
return (NULL);
id = strtol(arg + 1, &endptr, 10);
if (arg[1] != '\0' && *endptr == '\0')
return (session_find_by_id(id));
return (NULL);
}
/* Lookup a session by name. If no session is found, NULL is returned. */ /* Lookup a session by name. If no session is found, NULL is returned. */
struct session * struct session *
cmd_lookup_session(const char *name, int *ambiguous) cmd_lookup_session(const char *name, int *ambiguous)
@ -557,6 +574,10 @@ cmd_lookup_session(const char *name, int *ambiguous)
*ambiguous = 0; *ambiguous = 0;
/* Look for $id first. */
if ((s = cmd_lookup_session_id(name)) != NULL)
return (s);
/* /*
* Look for matches. First look for exact matches - session names must * Look for matches. First look for exact matches - session names must
* be unique so an exact match can't be ambigious and can just be * be unique so an exact match can't be ambigious and can just be

View File

@ -53,8 +53,11 @@ AM_CONDITIONAL(IS_DEBUG, test "x$found_debug" = xyes)
AC_ARG_ENABLE( AC_ARG_ENABLE(
static, static,
AC_HELP_STRING(--enable-static, create a static build), AC_HELP_STRING(--enable-static, create a static build),
[LDFLAGS="$LDFLAGS -static"] found_static=$enable_static
) )
if test "x$found_static" = xyes; then
LDFLAGS="$LDFLAGS -static"
fi
# Is this gcc? # Is this gcc?
AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes)

View File

@ -45,7 +45,7 @@ control_notify_input(struct client *c, struct window_pane *wp,
*/ */
if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) { if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) {
message = evbuffer_new(); message = evbuffer_new();
evbuffer_add_printf(message, "%%output %%%u ", wp->id); evbuffer_add_printf(message, "%%output %u ", wp->id);
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
evbuffer_add_printf(message, "%02hhx", buf[i]); evbuffer_add_printf(message, "%02hhx", buf[i]);
control_write_buffer(c, message); control_write_buffer(c, message);
@ -141,7 +141,7 @@ control_notify_window_renamed(struct window *w)
continue; continue;
s = c->session; 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 +154,7 @@ control_notify_attached_session_changed(struct client *c)
return; return;
s = c->session; s = c->session;
control_write(c, "%%session-changed %d %s", s->idx, s->name); control_write(c, "%%session-changed %u %s", s->id, s->name);
} }
void void

View File

@ -68,8 +68,13 @@ control_callback(struct client *c, int closed, unused void *data)
} }
if (cmd_string_parse(line, &cmdlist, NULL, 0, &cause) != 0) { if (cmd_string_parse(line, &cmdlist, NULL, 0, &cause) != 0) {
control_write(c, "%%error in line \"%s\": %s", line, c->cmdq->time = time(NULL);
cause); c->cmdq->number++;
cmdq_guard(c->cmdq, "begin");
control_write(c, "parse error: %s", cause);
cmdq_guard(c->cmdq, "error");
free(cause); free(cause);
} else { } else {
cmdq_run(c->cmdq, cmdlist); cmdq_run(c->cmdq, cmdlist);

View File

@ -1,57 +0,0 @@
#!/bin/bash
# Copyright (c) 2012 Juan Ignacio Pumarino, jipumarino@gmail.com
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Instructions
# ------------
#
# 1. Install this script and give it execute permission somewhere in your PATH.
# For example:
#
# $ mkdir -p ~/bin
# $ wget https://raw.github.com/jipumarino/tmux-zoom/master/tmux-zoom.sh -O ~/bin/tmux-zoom.sh
# $ chmod +x ~/bin/tmux-zoom.sh
#
# 2. Add a shortcut in your ~/.tmux.conf file:
#
# bind C-k run "tmux-zoom.sh"
#
# 3. When using this shortcut, the current tmux pane will open in a new window by itself.
# Running it again in the zoomed window will return it to its original pane. You can have
# as many zoomed windows as you want.
current=$(tmux display-message -p '#W-#I-#P')
list=$(tmux list-window)
[[ "$current" =~ ^(.*)-([0-9]+)-([0-9]+) ]]
current_window=${BASH_REMATCH[1]}
current_pane=${BASH_REMATCH[2]}-${BASH_REMATCH[3]}
new_zoom_window=ZOOM-$current_pane
if [[ $current_window =~ ZOOM-([0-9]+)-([0-9+]) ]]; then
old_zoom_window=ZOOM-${BASH_REMATCH[1]}-${BASH_REMATCH[2]}
tmux select-window -t ${BASH_REMATCH[1]} \; select-pane -t ${BASH_REMATCH[2]} \; swap-pane -s $old_zoom_window.1 \; kill-window -t $old_zoom_window
elif [[ $list =~ $new_zoom_window ]]; then
tmux select-window -t $new_zoom_window
else
tmux new-window -d -n $new_zoom_window \; swap-pane -s $new_zoom_window.1 \; select-window -t $new_zoom_window
fi

View File

@ -280,6 +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_windows", "%u", winlink_count(&s->windows));
format_add(ft, "session_width", "%u", s->sx); format_add(ft, "session_width", "%u", s->sx);
format_add(ft, "session_height", "%u", s->sy); format_add(ft, "session_height", "%u", s->sy);
format_add(ft, "session_id", "%u", s->id);
sg = session_group_find(s); sg = session_group_find(s);
format_add(ft, "session_grouped", "%d", sg != NULL); format_add(ft, "session_grouped", "%d", sg != NULL);
@ -398,7 +399,8 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp)
struct grid_line *gl; struct grid_line *gl;
unsigned long long size; unsigned long long size;
u_int i, idx; u_int i, idx;
const char *cwd, *cmd; const char *cwd;
char *cmd;
size = 0; size = 0;
for (i = 0; i < gd->hsize; i++) { for (i = 0; i < gd->hsize; i++) {
@ -432,8 +434,10 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_start_path", "%s", wp->cwd); format_add(ft, "pane_start_path", "%s", wp->cwd);
if ((cwd = osdep_get_cwd(wp->fd)) != NULL) if ((cwd = osdep_get_cwd(wp->fd)) != NULL)
format_add(ft, "pane_current_path", "%s", cwd); format_add(ft, "pane_current_path", "%s", cwd);
if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) {
format_add(ft, "pane_current_command", "%s", cmd); format_add(ft, "pane_current_command", "%s", cmd);
free(cmd);
}
format_add(ft, "cursor_x", "%d", wp->base.cx); format_add(ft, "cursor_x", "%d", wp->base.cx);
format_add(ft, "cursor_y", "%d", wp->base.cy); format_add(ft, "cursor_y", "%d", wp->base.cy);

2
grid.c
View File

@ -595,7 +595,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
if (*lastgc == NULL) { if (lastgc != NULL && *lastgc == NULL) {
memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1);
*lastgc = &lastgc1; *lastgc = &lastgc1;
} }

View File

@ -63,7 +63,7 @@ layout_dump(struct window *w)
if (layout_append(w->layout_root, layout, sizeof layout) != 0) if (layout_append(w->layout_root, layout, sizeof layout) != 0)
return (NULL); return (NULL);
xasprintf(&out, "%4x,%s", layout_checksum(layout), layout); xasprintf(&out, "%04x,%s", layout_checksum(layout), layout);
return (out); return (out);
} }
@ -206,11 +206,11 @@ layout_construct(struct layout_cell *lcparent, const char **layout)
{ {
struct layout_cell *lc, *lcchild; struct layout_cell *lc, *lcchild;
u_int sx, sy, xoff, yoff; u_int sx, sy, xoff, yoff;
const char *saved;
if (!isdigit((u_char) **layout)) if (!isdigit((u_char) **layout))
return (NULL); return (NULL);
if (sscanf(*layout, "%ux%u,%u,%u,%*u", &sx, &sy, &xoff, &yoff) != 4 && if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
return (NULL); return (NULL);
while (isdigit((u_char) **layout)) while (isdigit((u_char) **layout))
@ -231,9 +231,12 @@ layout_construct(struct layout_cell *lcparent, const char **layout)
while (isdigit((u_char) **layout)) while (isdigit((u_char) **layout))
(*layout)++; (*layout)++;
if (**layout == ',') { if (**layout == ',') {
saved = *layout;
(*layout)++; (*layout)++;
while (isdigit((u_char) **layout)) while (isdigit((u_char) **layout))
(*layout)++; (*layout)++;
if (**layout == 'x')
*layout = saved;
} }
lc = layout_create_cell(lcparent); lc = layout_create_cell(lcparent);

View File

@ -273,6 +273,9 @@ screen_redraw_pane(struct client *c, struct window_pane *wp)
{ {
u_int i, yoff; u_int i, yoff;
if (!window_pane_visible(wp))
return;
yoff = wp->yoff; yoff = wp->yoff;
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
yoff++; yoff++;

View File

@ -153,7 +153,8 @@ server_client_lost(struct client *c)
evbuffer_free (c->stdin_data); evbuffer_free (c->stdin_data);
evbuffer_free (c->stdout_data); evbuffer_free (c->stdout_data);
evbuffer_free (c->stderr_data); if (c->stderr_data != c->stdout_data)
evbuffer_free (c->stderr_data);
status_free_jobs(&c->status_new); status_free_jobs(&c->status_new);
status_free_jobs(&c->status_old); status_free_jobs(&c->status_old);
@ -955,6 +956,8 @@ server_client_msg_identify(
if (data->flags & IDENTIFY_CONTROL) { if (data->flags & IDENTIFY_CONTROL) {
c->stdin_callback = control_callback; c->stdin_callback = control_callback;
evbuffer_free(c->stderr_data);
c->stderr_data = c->stdout_data;
c->flags |= CLIENT_CONTROL; c->flags |= CLIENT_CONTROL;
if (data->flags & IDENTIFY_TERMIOS) if (data->flags & IDENTIFY_TERMIOS)
evbuffer_add_printf(c->stdout_data, "\033P1000p"); evbuffer_add_printf(c->stdout_data, "\033P1000p");

View File

@ -39,7 +39,7 @@ server_fill_environ(struct session *s, struct environ *env)
term = options_get_string(&s->options, "default-terminal"); term = options_get_string(&s->options, "default-terminal");
environ_set(env, "TERM", term); environ_set(env, "TERM", term);
idx = s->idx; idx = s->id;
} else } else
idx = -1; idx = -1;
pid = getpid(); pid = getpid();
@ -546,6 +546,10 @@ server_push_stderr(struct client *c)
struct msg_stderr_data data; struct msg_stderr_data data;
size_t size; size_t size;
if (c->stderr_data == c->stdout_data) {
server_push_stdout(c);
return;
}
size = EVBUFFER_LENGTH(c->stderr_data); size = EVBUFFER_LENGTH(c->stderr_data);
if (size == 0) if (size == 0)
return; return;

View File

@ -29,7 +29,7 @@
/* Global session list. */ /* Global session list. */
struct sessions sessions; struct sessions sessions;
struct sessions dead_sessions; struct sessions dead_sessions;
u_int next_session; u_int next_session_id;
struct session_groups session_groups; struct session_groups session_groups;
struct winlink *session_next_alert(struct winlink *); struct winlink *session_next_alert(struct winlink *);
@ -69,14 +69,14 @@ session_find(const char *name)
return (RB_FIND(sessions, &sessions, &s)); return (RB_FIND(sessions, &sessions, &s));
} }
/* Find session by index. */ /* Find session by id. */
struct session * struct session *
session_find_by_index(u_int idx) session_find_by_id(u_int id)
{ {
struct session *s; struct session *s;
RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(s, sessions, &sessions) {
if (s->idx == idx) if (s->id == id)
return (s); return (s);
} }
return (NULL); return (NULL);
@ -120,13 +120,13 @@ session_create(const char *name, const char *cmd, const char *cwd,
if (name != NULL) { if (name != NULL) {
s->name = xstrdup(name); s->name = xstrdup(name);
s->idx = next_session++; s->id = next_session_id++;
} else { } else {
s->name = NULL; s->name = NULL;
do { do {
s->idx = next_session++; s->id = next_session_id++;
free (s->name); free (s->name);
xasprintf(&s->name, "%u", s->idx); xasprintf(&s->name, "%u", s->id);
} while (RB_FIND(sessions, &sessions, s) != NULL); } while (RB_FIND(sessions, &sessions, s) != NULL);
} }
RB_INSERT(sessions, &sessions, s); RB_INSERT(sessions, &sessions, s);

117
tmux.1
View File

@ -23,7 +23,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm tmux .Nm tmux
.Bk -words .Bk -words
.Op Fl 28lquvV .Op Fl 28lCquvV
.Op Fl c Ar shell-command .Op Fl c Ar shell-command
.Op Fl f Ar file .Op Fl f Ar file
.Op Fl L Ar socket-name .Op Fl L Ar socket-name
@ -102,6 +102,11 @@ to assume the terminal supports 256 colours.
Like Like
.Fl 2 , .Fl 2 ,
but indicates that the terminal supports 88 colours. 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 .It Fl c Ar shell-command
Execute Execute
.Ar shell-command .Ar shell-command
@ -369,9 +374,9 @@ Clients may be listed with the
command. command.
.Pp .Pp
.Ar target-session .Ar target-session
is either the name of a session (as listed by the is the session id prefixed with a $, the name of a session (as listed by the
.Ic list-sessions .Ic list-sessions
command) or the name of a client with the same syntax as command), or the name of a client with the same syntax as
.Ar target-client , .Ar target-client ,
in which case the session attached to the client is used. in which case the session attached to the client is used.
When looking for the session name, When looking for the session name,
@ -1619,7 +1624,7 @@ is given in lines or cells (the default is 1).
.Pp .Pp
With With
.Fl Z , .Fl Z ,
the active pane is toggled between occupying the whole of the window and it's the active pane is toggled between occupying the whole of the window and its
normal position in the layout. normal position in the layout.
.It Xo Ic respawn-pane .It Xo Ic respawn-pane
.Op Fl k .Op Fl k
@ -3085,6 +3090,7 @@ The following variables are available, where appropriate:
.It Li "session_group" Ta "Number of session group" .It Li "session_group" Ta "Number of session group"
.It Li "session_grouped" Ta "1 if session in a group" .It Li "session_grouped" Ta "1 if session in a group"
.It Li "session_height" Ta "Height of session" .It Li "session_height" Ta "Height of session"
.It Li "session_id" Ta "Unique session ID"
.It Li "session_name" Ta "Name of session" .It Li "session_name" Ta "Name of session"
.It Li "session_width" Ta "Width of session" .It Li "session_width" Ta "Width of session"
.It Li "session_windows" Ta "Number of windows in session" .It Li "session_windows" Ta "Number of windows in session"
@ -3553,6 +3559,23 @@ If the command doesn't return success, the exit status is also displayed.
.It Ic server-info .It Ic server-info
.D1 (alias: Ic info ) .D1 (alias: Ic info )
Show server information and terminal details. Show server information and terminal details.
.It Xo Ic wait-for
.Fl LSU
.Ar channel
.Xc
.D1 (alias: Ic wait )
When used without options, prevents the client from exiting until woken using
.Ic wait-for
.Fl S
with the same channel.
When
.Fl L
is used, the channel is locked and any clients that try to lock the same
channel are made to wait until the channel is unlocked with
.Ic wait-for
.Fl U .
This command only works from outside
.Nm .
.El .El
.Sh TERMINFO EXTENSIONS .Sh TERMINFO EXTENSIONS
.Nm .Nm
@ -3592,6 +3615,92 @@ option above and the
.Xr xterm 1 .Xr xterm 1
man page. man page.
.El .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
contains that output with each byte encoded as two hex digits.
.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 .Sh FILES
.Bl -tag -width "/etc/tmux.confXXX" -compact .Bl -tag -width "/etc/tmux.confXXX" -compact
.It Pa ~/.tmux.conf .It Pa ~/.tmux.conf

8
tmux.c
View File

@ -48,7 +48,7 @@ char socket_path[MAXPATHLEN];
int login_shell; int login_shell;
char *environ_path; char *environ_path;
pid_t environ_pid = -1; pid_t environ_pid = -1;
int environ_idx = -1; int environ_session_id = -1;
__dead void usage(void); __dead void usage(void);
void parseenvironment(void); void parseenvironment(void);
@ -147,16 +147,16 @@ parseenvironment(void)
{ {
char *env, path[256]; char *env, path[256];
long pid; long pid;
int idx; int id;
if ((env = getenv("TMUX")) == NULL) if ((env = getenv("TMUX")) == NULL)
return; return;
if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &idx) != 3) if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &id) != 3)
return; return;
environ_path = xstrdup(path); environ_path = xstrdup(path);
environ_pid = pid; environ_pid = pid;
environ_idx = idx; environ_session_id = id;
} }
char * char *

17
tmux.h
View File

@ -462,8 +462,8 @@ enum msgtype {
* Don't forget to bump PROTOCOL_VERSION if any of these change! * Don't forget to bump PROTOCOL_VERSION if any of these change!
*/ */
struct msg_command_data { struct msg_command_data {
pid_t pid; /* PID from $TMUX or -1 */ pid_t pid; /* from $TMUX or -1 */
int idx; /* index from $TMUX or -1 */ int session_id; /* from $TMUX or -1 */
int argc; int argc;
char argv[COMMAND_LENGTH]; char argv[COMMAND_LENGTH];
@ -1086,7 +1086,7 @@ struct session_group {
TAILQ_HEAD(session_groups, session_group); TAILQ_HEAD(session_groups, session_group);
struct session { struct session {
u_int idx; u_int id;
char *name; char *name;
char *cwd; char *cwd;
@ -1412,10 +1412,15 @@ struct cmd_q {
struct cmd_q_item *item; struct cmd_q_item *item;
struct cmd *cmd; struct cmd *cmd;
time_t time;
u_int number;
void (*emptyfn)(struct cmd_q *); void (*emptyfn)(struct cmd_q *);
void *data; void *data;
struct msg_command_data *msgdata; struct msg_command_data *msgdata;
TAILQ_ENTRY(cmd_q) waitentry;
}; };
/* Command definition. */ /* Command definition. */
@ -1511,7 +1516,7 @@ extern char socket_path[MAXPATHLEN];
extern int login_shell; extern int login_shell;
extern char *environ_path; extern char *environ_path;
extern pid_t environ_pid; extern pid_t environ_pid;
extern int environ_idx; extern int environ_session_id;
void logfile(const char *); void logfile(const char *);
const char *getshell(void); const char *getshell(void);
int checkshell(const char *); int checkshell(const char *);
@ -1835,6 +1840,7 @@ extern const struct cmd_entry cmd_switch_client_entry;
extern const struct cmd_entry cmd_unbind_key_entry; extern const struct cmd_entry cmd_unbind_key_entry;
extern const struct cmd_entry cmd_unlink_window_entry; extern const struct cmd_entry cmd_unlink_window_entry;
extern const struct cmd_entry cmd_up_pane_entry; extern const struct cmd_entry cmd_up_pane_entry;
extern const struct cmd_entry cmd_wait_for_entry;
/* cmd-attach-session.c */ /* cmd-attach-session.c */
enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, int, int); enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, int, int);
@ -1850,6 +1856,7 @@ int cmdq_free(struct cmd_q *);
void printflike2 cmdq_print(struct cmd_q *, const char *, ...); void printflike2 cmdq_print(struct cmd_q *, const char *, ...);
void printflike2 cmdq_info(struct cmd_q *, const char *, ...); void printflike2 cmdq_info(struct cmd_q *, const char *, ...);
void printflike2 cmdq_error(struct cmd_q *, const char *, ...); void printflike2 cmdq_error(struct cmd_q *, const char *, ...);
void cmdq_guard(struct cmd_q *, const char *);
void cmdq_run(struct cmd_q *, struct cmd_list *); void cmdq_run(struct cmd_q *, struct cmd_list *);
void cmdq_append(struct cmd_q *, struct cmd_list *); void cmdq_append(struct cmd_q *, struct cmd_list *);
int cmdq_continue(struct cmd_q *); int cmdq_continue(struct cmd_q *);
@ -2284,7 +2291,7 @@ int session_cmp(struct session *, struct session *);
RB_PROTOTYPE(sessions, session, entry, session_cmp); RB_PROTOTYPE(sessions, session, entry, session_cmp);
int session_alive(struct session *); int session_alive(struct session *);
struct session *session_find(const char *); struct session *session_find(const char *);
struct session *session_find_by_index(u_int); struct session *session_find_by_id(u_int);
struct session *session_create(const char *, const char *, const char *, struct session *session_create(const char *, const char *, const char *,
struct environ *, struct termios *, int, u_int, u_int, struct environ *, struct termios *, int, u_int, u_int,
char **); char **);

View File

@ -82,6 +82,13 @@ const struct tty_default_key_raw tty_default_raw_keys[] = {
{ "\033[C", KEYC_RIGHT }, { "\033[C", KEYC_RIGHT },
{ "\033[D", KEYC_LEFT }, { "\033[D", KEYC_LEFT },
/* Other (xterm) "cursor" keys. */
{ "\033OH", KEYC_HOME },
{ "\033OF", KEYC_END },
{ "\033[H", KEYC_HOME },
{ "\033[F", KEYC_END },
/* rxvt-style arrow + modifier keys. */ /* rxvt-style arrow + modifier keys. */
{ "\033Oa", KEYC_UP|KEYC_CTRL }, { "\033Oa", KEYC_UP|KEYC_CTRL },
{ "\033Ob", KEYC_DOWN|KEYC_CTRL }, { "\033Ob", KEYC_DOWN|KEYC_CTRL },

View File

@ -859,7 +859,7 @@ window_choose_add_session(struct window_pane *wp, struct client *c,
struct window_choose_data *wcd; struct window_choose_data *wcd;
wcd = window_choose_data_create(TREE_SESSION, c, c->session); wcd = window_choose_data_create(TREE_SESSION, c, c->session);
wcd->idx = s->idx; wcd->idx = s->id;
wcd->tree_session = s; wcd->tree_session = s;
wcd->tree_session->references++; wcd->tree_session->references++;