Hi Did you forget to add control.c itself? :-)
Few more questions, sorry: Did you try tcflush()/TCSAFLUSH instead of read_stray_input? Sleeping for two seconds is pretty nasty. I'm just going to move identify message to the last thing right now since it is harmless for !control. Not sure I'm wild about strcmp() on client_exitmsg. Might be better to turn client_exitmsg into an enum and just move the strings there. Maybe it's ok for now since that'd make the change bigger. Would it be possible to use evbuffer from libevent instead of adding dstring? Looks like you need: ds_init -> evbuffer_new ds_free -> evbuffer_free ds_appendf -> evbuffer_add_printf ds_append -> evbuffer_add ds_appendl -> evbuffer_add ds_truncate -> evbuffer_drain This would cut out a bit of new code in favour of something we have to have anyway. Can you use the options infrastructure which is already a key-value pair with strings? You can just create your own private options tree with a NULL parent. Thanks On Sun, Mar 18, 2012 at 07:01:29PM -0700, George Nachman wrote: > The attached patch adds control mode and is up to date with the latest > changes. > Index: tmux.h > =================================================================== > --- tmux.h (revision 2749) > +++ tmux.h (working copy) > @@ -402,6 +402,7 @@ > #define IDENTIFY_UTF8 0x1 > #define IDENTIFY_256COLOURS 0x2 > #define IDENTIFY_88COLOURS 0x4 > +#define IDENTIFY_CONTROL 0x8 > int flags; > }; > > @@ -743,6 +744,16 @@ > #define screen_hsize(s) ((s)->grid->hsize) > #define screen_hlimit(s) ((s)->grid->hlimit) > > +/* dstring is a dynamic string. It is null terminated. It allocates memory as > + * needed and has an amortized O(n) cost of appending. */ > +struct dstring { > + char *buffer; /* always points at the current buffer. */ > + int used; /* this does not include the trailing nul. */ > + int available; /* amount of allocated space in buffer. */ > +#define DSTRING_STATIC_BUFFER_SIZE 1024 > + char staticbuffer[DSTRING_STATIC_BUFFER_SIZE]; > +}; > + > /* Input parser context. */ > struct input_ctx { > struct window_pane *wp; > @@ -773,6 +784,11 @@ > #define INPUT_DISCARD 0x1 > > const struct input_state *state; > + > + /* All input received since we were last in the ground state. Sent to > + * control clients on connection so their vt100 state can be the same as > + * ours. */ > + struct dstring input_since_ground; > }; > > /* > @@ -948,6 +964,7 @@ > > struct session { > u_int idx; > + u_int id; > > char *name; > char *cwd; > @@ -964,7 +981,8 @@ > > struct options options; > > -#define SESSION_UNATTACHED 0x1 /* not attached to any clients */ > +#define SESSION_UNATTACHED 0x1 /* not attached to any clients */ > +#define SESSION_RENAMED 0x2 /* notify control clients */ > int flags; > > struct termios *tio; > @@ -1167,7 +1185,11 @@ > #define CLIENT_BORDERS 0x400 > #define CLIENT_READONLY 0x800 > #define CLIENT_REDRAWWINDOW 0x1000 > - int flags; > +#define CLIENT_CONTROL 0x2000 /* is a control client */ > +#define CLIENT_CONTROL_READY 0x4000 /* control client ready for messages */ > +#define CLIENT_SESSION_CHANGED 0x8000 /* needs session-changed notification > */ > +#define CLIENT_SESSION_HANDSHAKE 0x10000 /* has sent handshake */ > + int flags; > > struct event identify_timer; > > @@ -1591,6 +1613,7 @@ > extern const struct cmd_entry cmd_display_message_entry; > extern const struct cmd_entry cmd_display_panes_entry; > extern const struct cmd_entry cmd_down_pane_entry; > +extern const struct cmd_entry cmd_control_entry; > extern const struct cmd_entry cmd_find_window_entry; > extern const struct cmd_entry cmd_has_session_entry; > extern const struct cmd_entry cmd_if_shell_entry; > @@ -1639,6 +1662,7 @@ > extern const struct cmd_entry cmd_send_prefix_entry; > extern const struct cmd_entry cmd_server_info_entry; > extern const struct cmd_entry cmd_set_buffer_entry; > +extern const struct cmd_entry cmd_set_control_client_attr_entry; > extern const struct cmd_entry cmd_set_environment_entry; > extern const struct cmd_entry cmd_set_option_entry; > extern const struct cmd_entry cmd_set_window_option_entry; > @@ -1670,6 +1694,46 @@ > /* client.c */ > int client_main(int, char **, int); > > +/* cmd-join-pane.c */ > +int join_pane( > + struct cmd *self, struct cmd_ctx *ctx, int require_diff_windows); > + > +/* control.c */ > +void control_init(void); > +void control_start(struct client *); > +void control_write(struct client *, const char *, int); > +void control_write_str(struct client*, const char*); > +void control_write_printf(struct client*, const char*, ...); > +void control_write_window(struct client *, struct window *); > +void control_write_window_pane(struct client *, struct window_pane *); > +void control_write_input(struct client *, struct window_pane *, > + const u_char *, int); > +void control_broadcast_input(struct window_pane *, const u_char *, > + size_t); > +void control_set_spontaneous_messages_allowed(int); > +void control_notify_layout_change(struct window *); > +void control_notify_window_added(struct window *w); > +void control_notify_window_removed(struct window *); > +void control_broadcast_queue(void); > +void control_handshake(struct client *); > +void control_print_session_layouts(struct session *session, > + struct cmd_ctx *); > +void control_set_kvp(const char *, const char *); > +char *control_get_kvp_value(const char *); > +void control_notify_attached_session_changed(struct client *); > +void control_notify_session_closed(struct session *); > +void control_notify_session_created(struct session *); > +void control_notify_session_renamed(struct session *); > +void control_notify_window_renamed(struct window *w); > + > +/* dstring.c */ > +void ds_init(struct dstring *ds); > +void ds_free(struct dstring *ds); > +void ds_appendf(struct dstring *ds, const char *fmt, ...); > +void ds_append(struct dstring *ds, const char *str); > +void ds_appendl(struct dstring *ds, const char *str, int len); > +void ds_truncate(struct dstring *ds, int new_length); > + > /* key-bindings.c */ > extern struct key_bindings key_bindings; > int key_bindings_cmp(struct key_binding *, struct key_binding *); > Index: cmd-new-session.c > =================================================================== > --- cmd-new-session.c (revision 2749) > +++ cmd-new-session.c (working copy) > @@ -64,7 +64,7 @@ > struct passwd *pw; > const char *newname, *target, *update, *cwd, *errstr; > char *overrides, *cmd, *cause; > - int detached, idx; > + int detached, idx, hastty; > u_int sx, sy, i; > > newname = args_get(args, 's'); > @@ -110,6 +110,14 @@ > if (ctx->cmdclient == NULL && ctx->curclient == NULL) > detached = 1; > > + /* Control clients don't have a tty, so avoid doing tty-ish things in > + * that case. */ > + if ((ctx->cmdclient && (ctx->cmdclient->flags & CLIENT_CONTROL)) || > + (ctx->curclient && (ctx->curclient->flags & CLIENT_CONTROL))) > + hastty = 0; > + else > + hastty = 1; > + > /* > * Save the termios settings, part of which is used for new windows in > * this session. > @@ -127,7 +135,7 @@ > tiop = NULL; > > /* Open the terminal if necessary. */ > - if (!detached && ctx->cmdclient != NULL) { > + if (hastty && !detached && ctx->cmdclient != NULL) { > if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { > ctx->error(ctx, "not a terminal"); > return (-1); > Index: cmd-list.c > =================================================================== > --- cmd-list.c (revision 2749) > +++ cmd-list.c (working copy) > @@ -83,11 +83,29 @@ > { > struct cmd *cmd; > int n, retval; > + struct client *c; > + int print_guards; > > + c = ctx->curclient; > + /* print %begin...%end guards around command output only if the client > + * is a control client that has an attached session. The requirement > + * for an attached session exists because the local client may issue an > + * attach-session or new-session command on startup that the remote > + * client is unaware of. Only after attaching to a session does the > + * remote client take charge.*/ > + print_guards = c && (c->flags & CLIENT_CONTROL) && c->session; > retval = 0; > + control_set_spontaneous_messages_allowed(0); > TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { > - if ((n = cmd_exec(cmd, ctx)) == -1) > + if (print_guards) > + ctx->print(ctx, "%%begin"); > + if ((n = cmd_exec(cmd, ctx)) == -1) { > + if (print_guards) > + ctx->print(ctx, "%%end"); > return (-1); > + } > + if (print_guards) > + ctx->print(ctx, "%%end"); > > /* > * A 1 return value means the command client is being attached > @@ -111,6 +129,7 @@ > } > } > } > + control_set_spontaneous_messages_allowed(1); > return (retval); > } > > Index: session.c > =================================================================== > --- session.c (revision 2749) > +++ session.c (working copy) > @@ -34,6 +34,7 @@ > > struct winlink *session_next_alert(struct winlink *); > struct winlink *session_previous_alert(struct winlink *); > +u_int next_session_id = 0; > > RB_GENERATE(sessions, session, entry, session_cmp); > > @@ -91,6 +92,7 @@ > struct session *s; > > s = xmalloc(sizeof *s); > + s->id = next_session_id++; > s->references = 0; > s->flags = 0; > > Index: Makefile.am > =================================================================== > --- Makefile.am (revision 2749) > +++ Makefile.am (working copy) > @@ -60,6 +60,7 @@ > cfg.c \ > client.c \ > clock.c \ > + control.c \ > cmd-attach-session.c \ > cmd-bind-key.c \ > cmd-break-pane.c \ > @@ -72,6 +73,7 @@ > cmd-clock-mode.c \ > cmd-command-prompt.c \ > cmd-confirm-before.c \ > + cmd-control.c \ > cmd-copy-mode.c \ > cmd-delete-buffer.c \ > cmd-detach-client.c \ > @@ -135,6 +137,7 @@ > cmd-unlink-window.c \ > cmd.c \ > colour.c \ > + dstring.c \ > environ.c \ > format.c \ > grid-utf8.c \ > Index: server-client.c > =================================================================== > --- server-client.c (revision 2749) > +++ server-client.c (working copy) > @@ -508,6 +508,9 @@ > if (c->flags & CLIENT_SUSPENDED) > return; > > + if (c->flags & CLIENT_CONTROL) > + return; > + > tty_region(&c->tty, 0, c->tty.sy - 1); > > status = options_get_number(oo, "status"); > @@ -602,6 +605,9 @@ > struct window_pane *wp; > int flags, redraw; > > + if (c->flags & CLIENT_CONTROL) > + return; > + > flags = c->tty.flags & TTY_FREEZE; > c->tty.flags &= ~TTY_FREEZE; > > @@ -797,7 +803,8 @@ > if (datalen != 0) > fatalx("bad MSG_RESIZE size"); > > - if (tty_resize(&c->tty)) { > + if (!(c->flags & CLIENT_CONTROL) && > + tty_resize(&c->tty)) { > recalculate_sizes(); > server_redraw_client(c); > } > @@ -948,13 +955,24 @@ > server_client_msg_identify( > struct client *c, struct msg_identify_data *data, int fd) > { > - int tty_fd; > + int tty_fd; > + struct termios dummy_termios; > > c->cwd = NULL; > data->cwd[(sizeof data->cwd) - 1] = '\0'; > if (*data->cwd != '\0') > c->cwd = xstrdup(data->cwd); > > + /* > + * If this is a control client, mark the client, and initiate the > + * control events. > + */ > + if (data->flags & IDENTIFY_CONTROL) { > + c->flags |= CLIENT_CONTROL; > + tty_init_termios(fd, &dummy_termios, NULL); > + control_start(c); > + } > + > if (!isatty(fd)) > return; > if ((tty_fd = dup(fd)) == -1) > @@ -972,7 +990,8 @@ > > tty_resize(&c->tty); > > - c->flags |= CLIENT_TERMINAL; > + if (!(data->flags & IDENTIFY_CONTROL)) > + c->flags |= CLIENT_TERMINAL; > } > > /* Handle shell message. */ > Index: resize.c > =================================================================== > --- resize.c (revision 2749) > +++ resize.c (working copy) > @@ -49,10 +49,13 @@ > struct client *c; > struct window *w; > struct window_pane *wp; > - u_int i, j, ssx, ssy, has, limit; > - int flag; > + u_int i, j, ssx, ssy, has, limit; > + int flag; > + int session_has_status; > + u_int ssy_ex_status; > > RB_FOREACH(s, sessions, &sessions) { > + session_has_status = options_get_number(&s->options, "status"); > ssx = ssy = UINT_MAX; > for (j = 0; j < ARRAY_LENGTH(&clients); j++) { > c = ARRAY_ITEM(&clients, j); > @@ -61,8 +64,16 @@ > if (c->session == s) { > if (c->tty.sx < ssx) > ssx = c->tty.sx; > - if (c->tty.sy < ssy) > - ssy = c->tty.sy; > + /* Reserve one line for status if the session > + * wants it, the client supports it, and there > + * is room. */ > + ssy_ex_status = c->tty.sy; > + if (session_has_status && > + !(c->flags & CLIENT_CONTROL) && > + ssy_ex_status > 1) > + --ssy_ex_status; > + if (ssy_ex_status < ssy) > + ssy = ssy_ex_status; > } > } > if (ssx == UINT_MAX || ssy == UINT_MAX) { > @@ -71,12 +82,9 @@ > } > s->flags &= ~SESSION_UNATTACHED; > > - if (options_get_number(&s->options, "status")) { > - if (ssy == 0) > - ssy = 1; > - else > - ssy--; > - } > + if (session_has_status && ssy == 0) > + ssy = 1; > + > if (s->sx == ssx && s->sy == ssy) > continue; > > Index: server-fn.c > =================================================================== > --- server-fn.c (revision 2749) > +++ server-fn.c (working copy) > @@ -226,6 +226,11 @@ > size_t cmdlen; > struct msg_lock_data lockdata; > > + /* Control clients aren't able to lock, but just in case the command > + * gets sent, do nothing because it doesn't have a tty. */ > + if (!(c->flags & CLIENT_CONTROL)) > + return; > + > if (c->flags & CLIENT_SUSPENDED) > return; > > Index: tmux.1 > =================================================================== > --- tmux.1 (revision 2749) > +++ tmux.1 (working copy) > @@ -23,7 +23,7 @@ > .Sh SYNOPSIS > .Nm tmux > .Bk -words > -.Op Fl 28lquvV > +.Op Fl 28ClquvV > .Op Fl c Ar shell-command > .Op Fl f Ar file > .Op Fl L Ar socket-name > @@ -116,6 +116,10 @@ > when > .Nm > is used as a login shell. > +.It Fl C > +Operate in > +.Ic control mode , > +which allows compatible terminal emulators to offer a native user interface > for tmux. > .It Fl f Ar file > Specify an alternative configuration file. > By default, > Index: server-window.c > =================================================================== > --- server-window.c (revision 2749) > +++ server-window.c (working copy) > @@ -85,7 +85,7 @@ > visual = options_get_number(&s->options, "visual-bell"); > for (i = 0; i < ARRAY_LENGTH(&clients); i++) { > c = ARRAY_ITEM(&clients, i); > - if (c == NULL || c->session != s) > + if (c == NULL || c->session != s || (c->flags & > CLIENT_CONTROL)) > continue; > if (!visual) { > tty_bell(&c->tty); > @@ -105,7 +105,7 @@ > visual = options_get_number(&s->options, "visual-bell"); > for (i = 0; i < ARRAY_LENGTH(&clients); i++) { > c = ARRAY_ITEM(&clients, i); > - if (c == NULL || c->session != s) > + if (c == NULL || c->session != s || (c->flags & > CLIENT_CONTROL)) > continue; > if (c->session->curw->window != w) > continue; > @@ -255,7 +255,7 @@ > > for (i = 0; i < ARRAY_LENGTH(&clients); i++) { > c = ARRAY_ITEM(&clients, i); > - if (c != NULL && c->session == s) > + if (c != NULL && c->session == s && !(c->flags & > CLIENT_CONTROL)) > tty_bell(&c->tty); > } > } > Index: client.c > =================================================================== > --- client.c (revision 2749) > +++ client.c (working copy) > @@ -29,6 +29,7 @@ > #include <stdlib.h> > #include <string.h> > #include <unistd.h> > +#include <termios.h> > > #include "tmux.h" > > @@ -38,6 +39,7 @@ > int client_exitval; > enum msgtype client_exittype; > int client_attached; > +static int is_control_client; > > int client_get_lock(char *); > int client_connect(char *, int); > @@ -120,10 +122,34 @@ > return (-1); > } > > +/* Sleep and then read any pending input. This tries to handle a problem > + * in control mode where the client is unexpectedly detached while commands > it > + * has sent are on the wire. We'd like to read those commands and throw them > + * away rather than have them sent to the shell after tmux quits. > + */ > +static void > +read_stray_input(int fd) > +{ > + int saved_flags; > + int n; > + char buffer[100]; > + > + sleep(2); > + saved_flags = fcntl(fd, F_GETFL); > + fcntl(fd, F_SETFL, saved_flags | O_NONBLOCK); > + do { > + n = read(fd, buffer, sizeof(buffer)); > + if (n == -1 && errno != EINTR) { > + break; > + } > + } while (n > 0); > +} > + > /* Client main loop. */ > int > client_main(int argc, char **argv, int flags) > { > + struct termios saved_termios; > struct cmd *cmd; > struct cmd_list *cmdlist; > struct msg_command_data cmddata; > @@ -181,6 +207,17 @@ > log_warn("failed to connect to server"); > return (1); > } > + if (flags & IDENTIFY_CONTROL) { > + if (!isatty(fileno(stdout))) { > + /* This test must be performed earlier for control > clients because > + * the CLIENT_TERMINAL flag is used for more than > detecting the presence > + * of a terminal and control mode is completely useless > without a tty > + * because echo can't be disabled. */ > + log_fatalx("not a terminal"); > + return (1); > + } > + is_control_client = 1; > + } > > /* Set process title, log and signals now this is the client. */ > #ifdef HAVE_SETPROCTITLE > @@ -200,6 +237,14 @@ > client_send_environ(); > client_send_identify(flags); > > + if (is_control_client) { > + /* Save termios and restore it on exit. Can't count on the > + * server to do that because it might crash. */ > + struct termios tio; > + tcgetattr(fileno(stdout), &tio); > + saved_termios = tio; > + } > + > /* Send first command. */ > if (msg == MSG_COMMAND) { > /* Fill in command line arguments. */ > @@ -224,13 +269,25 @@ > > /* Print the exit message, if any, and exit. */ > if (client_attached) { > - if (client_exitmsg != NULL && !login_shell) > - printf("[%s]\n", client_exitmsg); > + if (client_exitmsg != NULL && !login_shell) { > + if (flags & IDENTIFY_CONTROL) { > + printf("%%exit %s\n", client_exitmsg); > + /* Exit messages other than "detached" are > + * not client-initiated. */ > + if (strcmp("detached", client_exitmsg)) > + read_stray_input(fileno(stdin)); > + } else > + printf("[%s]\n", client_exitmsg); > + } > > ppid = getppid(); > if (client_exittype == MSG_DETACHKILL && ppid > 1) > kill(ppid, SIGHUP); > } > + if (is_control_client) > + /* Turn off echo in control mode (we only get here if stdout is > + * a tty so it's ok to do). */ > + tcsetattr(fileno(stdout), TCSANOW, &saved_termios); > return (client_exitval); > } > > @@ -252,11 +309,6 @@ > strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) > *data.term = '\0'; > > - if ((fd = dup(STDIN_FILENO)) == -1) > - fatal("dup failed"); > - imsg_compose(&client_ibuf, > - MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); > - > if ((fd = dup(STDOUT_FILENO)) == -1) > fatal("dup failed"); > imsg_compose(&client_ibuf, > @@ -266,6 +318,12 @@ > fatal("dup failed"); > imsg_compose(&client_ibuf, > MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0); > + > + if ((fd = dup(STDIN_FILENO)) == -1) > + fatal("dup failed"); > + /* For control clients, this has to be the last message. */ > + imsg_compose(&client_ibuf, > + MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); > } > > /* Forward entire environment to server. */ > Index: input.c > =================================================================== > --- input.c (revision 2749) > +++ input.c (working copy) > @@ -692,12 +692,17 @@ > > ictx->state = &input_state_ground; > ictx->flags = 0; > + > + ds_init(&ictx->input_since_ground); > } > > /* Destroy input parser. */ > void > input_free(unused struct window_pane *wp) > { > + if (wp) { > + ds_free(&wp->ictx.input_since_ground); > + } > } > > /* Parse input. */ > @@ -728,6 +733,8 @@ > > buf = EVBUFFER_DATA(evb); > len = EVBUFFER_LENGTH(evb); > + > + control_broadcast_input(wp, buf, len); > off = 0; > > /* Parse the input. */ > @@ -758,10 +765,19 @@ > if (itr->state != NULL) { > if (ictx->state->exit != NULL) > ictx->state->exit(ictx); > + if (ictx->state != &input_state_ground && > + itr->state == &input_state_ground) { > + /* Entering ground state. */ > + ds_truncate(&ictx->input_since_ground, 0); > + } > ictx->state = itr->state; > if (ictx->state->enter != NULL) > ictx->state->enter(ictx); > } > + if (ictx->state != &input_state_ground) { > + /* Not in ground state, so save input. */ > + ds_appendl(&ictx->input_since_ground, (const char *) > &ictx->ch, 1); > + } > } > > /* Close the screen. */ > Index: cmd.c > =================================================================== > --- cmd.c (revision 2749) > +++ cmd.c (working copy) > @@ -39,6 +39,7 @@ > &cmd_clear_history_entry, > &cmd_clock_mode_entry, > &cmd_command_prompt_entry, > + &cmd_control_entry, > &cmd_confirm_before_entry, > &cmd_copy_mode_entry, > &cmd_delete_buffer_entry, > Index: server.c > =================================================================== > --- server.c (revision 2749) > +++ server.c (working copy) > @@ -152,6 +152,7 @@ > mode_key_init_trees(); > key_bindings_init(); > utf8_build(); > + control_init(); > > start_time = time(NULL); > log_debug("socket path %s", socket_path); > Index: notify.c > =================================================================== > --- notify.c (revision 2749) > +++ notify.c (working copy) > @@ -21,39 +21,50 @@ > void > notify_window_layout_changed(unused struct window *w) > { > + control_notify_layout_change(w); > } > > void > notify_window_unlinked(unused struct session *s, unused struct window *w) > { > + control_notify_window_removed(w); > } > > void > notify_window_linked(unused struct session *s, unused struct window *w) > { > + control_notify_window_added(w); > } > > void > notify_window_renamed(unused struct window *w) > { > + control_notify_window_renamed(w); > } > > void > notify_attached_session_changed(unused struct client *c) > { > + if (c->flags & CLIENT_CONTROL) { > + control_handshake(c); > + control_notify_attached_session_changed(c); > + } > } > > void > notify_session_renamed(unused struct session *s) > { > + control_notify_session_renamed(s); > } > > void > notify_session_created(unused struct session *s) > { > + control_notify_session_created(s); > } > > void > notify_session_closed(unused struct session *s) > { > + control_notify_session_closed(s); > } > Index: cmd-attach-session.c > =================================================================== > --- cmd-attach-session.c (revision 2749) > +++ cmd-attach-session.c (working copy) > @@ -79,17 +79,20 @@ > server_redraw_client(ctx->curclient); > s->curw->flags &= ~WINLINK_ALERTFLAGS; > } else { > - if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { > + if (!(ctx->cmdclient->flags & CLIENT_CONTROL) && > + !(ctx->cmdclient->flags & CLIENT_TERMINAL)) { > ctx->error(ctx, "not a terminal"); > return (-1); > } > > - overrides = > - options_get_string(&s->options, "terminal-overrides"); > - if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) { > - ctx->error(ctx, "terminal open failed: %s", cause); > - xfree(cause); > - return (-1); > + if (!(ctx->cmdclient->flags & CLIENT_CONTROL)) { > + overrides = > + options_get_string(&s->options, > "terminal-overrides"); > + if (tty_open(&ctx->cmdclient->tty, overrides, &cause) > != 0) { > + ctx->error(ctx, "terminal open failed: %s", > cause); > + xfree(cause); > + return (-1); > + } > } > > if (args_has(self->args, 'r')) > Index: tmux.c > =================================================================== > --- tmux.c (revision 2749) > +++ tmux.c (working copy) > @@ -62,7 +62,7 @@ > usage(void) > { > fprintf(stderr, > - "usage: %s [-28lquvV] [-c shell-command] [-f file] [-L > socket-name]\n" > + "usage: %s [-28ClquvV] [-c shell-command] [-f file] [-L > socket-name]\n" > " [-S socket-path] [command [flags]]\n", > __progname); > exit(1); > @@ -245,7 +245,7 @@ > quiet = flags = 0; > label = path = NULL; > login_shell = (**argv == '-'); > - while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUvV")) != -1) { > + while ((opt = getopt(argc, argv, "28Cc:df:lL:qS:uUvV")) != -1) { > switch (opt) { > case '2': > flags |= IDENTIFY_256COLOURS; > @@ -255,6 +255,9 @@ > flags |= IDENTIFY_88COLOURS; > flags &= ~IDENTIFY_256COLOURS; > break; > + case 'C': > + flags |= IDENTIFY_CONTROL; > + break; > case 'c': > if (shell_cmd != NULL) > xfree(shell_cmd); > ------------------------------------------------------------------------------ > This SF email is sponsosred by: > Try Windows Azure free for 90 days Click Here > http://p.sf.net/sfu/sfd2d-msazure > _______________________________________________ > tmux-users mailing list > tmux-users@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/tmux-users ------------------------------------------------------------------------------ This SF email is sponsosred by: Try Windows Azure free for 90 days Click Here http://p.sf.net/sfu/sfd2d-msazure _______________________________________________ tmux-users mailing list tmux-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tmux-users