diff --git a/CHANGES b/CHANGES index b2702008..d5c2beaa 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,7 @@ 17 January 2009 +* suspend-client command to suspend a client. Don't try to background it + though... * tmux 0.6 released. 15 January 2009 @@ -932,7 +934,7 @@ (including mutt, emacs). No status bar yet and no key remapping or other customisation. -$Id: CHANGES,v 1.214 2009-01-17 19:08:12 nicm Exp $ +$Id: CHANGES,v 1.215 2009-01-18 12:09:42 nicm Exp $ LocalWords: showw utf UTF fulvio ciriaco joshe OSC APC gettime abc DEF OA clr LocalWords: rivo nurges lscm Erdely eol smysession mysession ek dstname RB ms diff --git a/GNUmakefile b/GNUmakefile index a4ff89be..fbfb61ab 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,4 +1,4 @@ -# $Id: GNUmakefile,v 1.58 2009-01-18 00:08:43 nicm Exp $ +# $Id: GNUmakefile,v 1.59 2009-01-18 12:09:42 nicm Exp $ .PHONY: clean @@ -36,6 +36,7 @@ SRCS= tmux.c server.c server-msg.c server-fn.c buffer.c buffer-poll.c status.c \ cmd-save-buffer.c cmd-select-pane.c cmd-split-window.c \ cmd-resize-pane-up.c cmd-resize-pane-down.c cmd-kill-pane.c \ cmd-up-pane.c cmd-down-pane.c cmd-choose-window.c cmd-choose-session.c \ + cmd-suspend-client.c \ window-clock.c window-scroll.c window-more.c window-copy.c \ window-choose.c \ options.c options-cmd.c paste.c colour.c utf8.c clock.c \ diff --git a/Makefile b/Makefile index 02765128..67672dd2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.97 2009-01-18 00:08:43 nicm Exp $ +# $Id: Makefile,v 1.98 2009-01-18 12:09:42 nicm Exp $ .SUFFIXES: .c .o .y .h .PHONY: clean update-index.html upload-index.html @@ -40,6 +40,7 @@ SRCS= tmux.c server.c server-msg.c server-fn.c buffer.c buffer-poll.c status.c \ cmd-save-buffer.c cmd-select-pane.c cmd-split-window.c \ cmd-resize-pane-up.c cmd-resize-pane-down.c cmd-kill-pane.c \ cmd-up-pane.c cmd-down-pane.c cmd-choose-window.c cmd-choose-session.c \ + cmd-suspend-client.c \ window-clock.c window-scroll.c window-more.c window-copy.c \ window-choose.c \ options.c options-cmd.c paste.c colour.c utf8.c clock.c \ diff --git a/TODO b/TODO index 7c657ab4..f57ccafc 100644 --- a/TODO +++ b/TODO @@ -72,7 +72,7 @@ (copy from other session) - function groups, bind-key ^W { select-window 0; send-key ^W } etc *** - neww should support -k -- suspend-client command bound to ^Z +- document suspend-client - flag to scroll-mode/copy-mode to automatically scroll up a page - would be nice if tmux could be the shell - key to switch to copy mode from scroll mode diff --git a/client-msg.c b/client-msg.c index 3510e3ac..7f2f3dca 100644 --- a/client-msg.c +++ b/client-msg.c @@ -1,4 +1,4 @@ -/* $Id: client-msg.c,v 1.16 2009-01-07 22:57:03 nicm Exp $ */ +/* $Id: client-msg.c,v 1.17 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -29,8 +29,7 @@ int client_msg_fn_detach(struct hdr *, struct client_ctx *, char **); int client_msg_fn_error(struct hdr *, struct client_ctx *, char **); int client_msg_fn_exit(struct hdr *, struct client_ctx *, char **); int client_msg_fn_exited(struct hdr *, struct client_ctx *, char **); -int client_msg_fn_okay(struct hdr *, struct client_ctx *, char **); -int client_msg_fn_pause(struct hdr *, struct client_ctx *, char **); +int client_msg_fn_suspend(struct hdr *, struct client_ctx *, char **); struct client_msg { enum hdrtype type; @@ -40,7 +39,8 @@ struct client_msg client_msg_table[] = { { MSG_DETACH, client_msg_fn_detach }, { MSG_ERROR, client_msg_fn_error }, { MSG_EXIT, client_msg_fn_exit }, - { MSG_EXITED, client_msg_fn_exited } + { MSG_EXITED, client_msg_fn_exited }, + { MSG_SUSPEND, client_msg_fn_suspend }, }; int @@ -116,3 +116,29 @@ client_msg_fn_exited( return (-1); } + +int +client_msg_fn_suspend( + struct hdr *hdr, unused struct client_ctx *cctx, unused char **error) +{ + struct sigaction act; + + if (hdr->size != 0) + fatalx("bad MSG_SUSPEND size"); + + memset(&act, 0, sizeof act); + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + + act.sa_handler = SIG_DFL; + if (sigaction(SIGTSTP, &act, NULL) != 0) + fatal("sigaction failed"); + + act.sa_handler = sighandler; + if (sigaction(SIGCONT, &act, NULL) != 0) + fatal("sigaction failed"); + + kill(getpid(), SIGTSTP); + + return (0); +} diff --git a/client.c b/client.c index 3ad5bff1..e30e1841 100644 --- a/client.c +++ b/client.c @@ -1,4 +1,4 @@ -/* $Id: client.c,v 1.39 2009-01-15 00:21:58 nicm Exp $ */ +/* $Id: client.c,v 1.40 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -146,6 +146,11 @@ client_main(struct client_ctx *cctx) while (!sigterm) { if (sigwinch) client_handle_winch(cctx); + if (sigcont) { + siginit(); + client_write_server(cctx, MSG_WAKEUP, NULL, 0); + sigcont = 0; + } switch (client_msg_dispatch(cctx, &error)) { case -1: diff --git a/cmd-suspend-client.c b/cmd-suspend-client.c new file mode 100644 index 00000000..540b2f68 --- /dev/null +++ b/cmd-suspend-client.c @@ -0,0 +1,65 @@ +/* $Id: cmd-suspend-client.c,v 1.1 2009-01-18 12:09:42 nicm Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Suspend client with SIGTSTP. + */ + +void cmd_suspend_client_exec(struct cmd *, struct cmd_ctx *); + +struct cmd_suspend_client_data { + char *name; + char *target; +}; + +const struct cmd_entry cmd_suspend_client_entry = { + "suspend-client", "suspendc", + "[-c target-client]", + 0, + cmd_target_init, + cmd_target_parse, + cmd_suspend_client_exec, + cmd_target_send, + cmd_target_recv, + cmd_target_free, + cmd_target_print +}; + +void +cmd_suspend_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_target_data *data = self->data; + struct client *c; + + if ((c = cmd_find_client(ctx, data->target)) == NULL) + return; + + tty_stop_tty(&c->tty); + c->flags |= CLIENT_SUSPENDED; + server_write_client(c, MSG_SUSPEND, NULL, 0); + + if (ctx->cmdclient != NULL) + server_write_client(ctx->cmdclient, MSG_EXIT, NULL, 0); +} diff --git a/cmd.c b/cmd.c index 0b110bef..8e7a0e52 100644 --- a/cmd.c +++ b/cmd.c @@ -1,4 +1,4 @@ -/* $Id: cmd.c,v 1.79 2009-01-15 19:27:31 nicm Exp $ */ +/* $Id: cmd.c,v 1.80 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -80,6 +80,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_source_file_entry, &cmd_split_window_entry, &cmd_start_server_entry, + &cmd_suspend_client_entry, &cmd_swap_window_entry, &cmd_switch_client_entry, &cmd_unbind_key_entry, diff --git a/key-bindings.c b/key-bindings.c index 89f39dab..8b20000c 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -1,4 +1,4 @@ -/* $Id: key-bindings.c,v 1.53 2009-01-17 18:34:12 nicm Exp $ */ +/* $Id: key-bindings.c,v 1.54 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -108,7 +108,8 @@ key_bindings_init(void) { 's', &cmd_choose_session_entry }, { 't', &cmd_clock_mode_entry }, { 'w', &cmd_choose_window_entry }, - { 'x', &cmd_kill_pane_entry, }, + { 'x', &cmd_kill_pane_entry, }, + { '\032', &cmd_suspend_client_entry }, { KEYC_UP, &cmd_up_pane_entry }, { KEYC_DOWN, &cmd_down_pane_entry }, { KEYC_ADDESC(KEYC_UP), &cmd_resize_pane_up_entry }, diff --git a/server-msg.c b/server-msg.c index 43293a13..a91c8397 100644 --- a/server-msg.c +++ b/server-msg.c @@ -1,4 +1,4 @@ -/* $Id: server-msg.c,v 1.57 2009-01-11 23:31:46 nicm Exp $ */ +/* $Id: server-msg.c,v 1.58 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -31,6 +31,7 @@ int server_msg_fn_identify(struct hdr *, struct client *); int server_msg_fn_resize(struct hdr *, struct client *); int server_msg_fn_exiting(struct hdr *, struct client *); int server_msg_fn_unlock(struct hdr *, struct client *); +int server_msg_fn_wakeup(struct hdr *, struct client *); void printflike2 server_msg_fn_command_error( struct cmd_ctx *, const char *, ...); @@ -48,7 +49,8 @@ const struct server_msg server_msg_table[] = { { MSG_COMMAND, server_msg_fn_command }, { MSG_RESIZE, server_msg_fn_resize }, { MSG_EXITING, server_msg_fn_exiting }, - { MSG_UNLOCK, server_msg_fn_unlock } + { MSG_UNLOCK, server_msg_fn_unlock }, + { MSG_WAKEUP, server_msg_fn_wakeup }, }; int @@ -242,7 +244,7 @@ server_msg_fn_exiting(struct hdr *hdr, struct client *c) c->session = NULL; - tty_close(&c->tty); + tty_close(&c->tty, c->flags & CLIENT_SUSPENDED); server_write_client(c, MSG_EXITED, NULL, 0); @@ -271,3 +273,18 @@ server_msg_fn_unlock(struct hdr *hdr, struct client *c) return (0); } + +int +server_msg_fn_wakeup(struct hdr *hdr, struct client *c) +{ + if (hdr->size != 0) + fatalx("bad MSG_WAKEUP size"); + + log_debug("wakeup msg from client"); + + c->flags &= ~CLIENT_SUSPENDED; + tty_start_tty(&c->tty); + server_redraw_client(c); + + return (0); +} diff --git a/server.c b/server.c index 98490068..c5c482c5 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $Id: server.c,v 1.106 2009-01-17 17:42:10 nicm Exp $ */ +/* $Id: server.c,v 1.107 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -484,7 +484,8 @@ server_fill_clients(struct pollfd **pfd) } (*pfd)++; - if (c == NULL || c->tty.fd == -1 || c->session == NULL) + if (c == NULL || c->flags & CLIENT_SUSPENDED || + c->tty.fd == -1 || c->session == NULL) (*pfd)->fd = -1; else { (*pfd)->fd = c->tty.fd; @@ -516,7 +517,8 @@ server_handle_clients(struct pollfd **pfd) } (*pfd)++; - if (c != NULL && c->tty.fd != -1 && c->session != NULL) { + if (c != NULL && !(c->flags & CLIENT_SUSPENDED) && + c->tty.fd != -1 && c->session != NULL) { if (buffer_poll(*pfd, c->tty.in, c->tty.out) != 0) server_lost_client(c); else @@ -602,7 +604,7 @@ server_handle_client(struct client *c) if (c->session == NULL) return; - wp = c->session->curw->window->active; /* could die - do each loop */ + wp = c->session->curw->window->active; /* could die */ server_clear_client_message(c); if (c->prompt_string != NULL) { @@ -686,7 +688,7 @@ server_lost_client(struct client *c) ARRAY_SET(&clients, i, NULL); } - tty_free(&c->tty); + tty_free(&c->tty, c->flags & CLIENT_SUSPENDED); if (c->title != NULL) xfree(c->title); diff --git a/tmux.c b/tmux.c index 9f33d7e8..8f636c7b 100644 --- a/tmux.c +++ b/tmux.c @@ -1,4 +1,4 @@ -/* $Id: tmux.c,v 1.97 2009-01-15 23:42:21 nicm Exp $ */ +/* $Id: tmux.c,v 1.98 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -45,6 +45,7 @@ const char *_malloc_options = "AJX"; volatile sig_atomic_t sigwinch; volatile sig_atomic_t sigterm; +volatile sig_atomic_t sigcont; char *cfg_file; struct options global_options; @@ -59,7 +60,6 @@ int be_quiet; time_t start_time; const char *socket_path; -void sighandler(int); __dead void usage(void); #ifdef NO_PROGNAME @@ -105,6 +105,9 @@ sighandler(int sig) case SIGCHLD: waitpid(WAIT_ANY, NULL, WNOHANG); break; + case SIGCONT: + sigcont = 1; + break; } errno = saved_errno; } diff --git a/tmux.h b/tmux.h index 0994c322..eea4446b 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.236 2009-01-17 18:38:12 nicm Exp $ */ +/* $Id: tmux.h,v 1.237 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -358,6 +358,8 @@ enum hdrtype { MSG_READY, MSG_RESIZE, MSG_UNLOCK, + MSG_SUSPEND, + MSG_WAKEUP, }; /* Message header structure. */ @@ -759,6 +761,7 @@ struct client { #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 #define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */ +#define CLIENT_SUSPENDED 0x40 int flags; char *message_string; @@ -959,6 +962,7 @@ char *fgetln(FILE *, size_t *); /* tmux.c */ extern volatile sig_atomic_t sigwinch; extern volatile sig_atomic_t sigterm; +extern volatile sig_atomic_t sigcont; extern struct options global_options; extern struct options global_window_options; extern char *cfg_file; @@ -972,6 +976,7 @@ extern const char *socket_path; void logfile(const char *); void siginit(void); void sigreset(void); +void sighandler(int); /* cfg.c */ int load_cfg(const char *, char **x); @@ -1001,10 +1006,12 @@ void tty_putcode2(struct tty *, enum tty_code_code, int, int); void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, char); void tty_init(struct tty *, char *, char *); +void tty_start_tty(struct tty *); +void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); int tty_open(struct tty *, char **); -void tty_close(struct tty *); -void tty_free(struct tty *); +void tty_close(struct tty *, int); +void tty_free(struct tty *, int); void tty_write(struct tty *, struct screen *, u_int, enum tty_cmd, ...); void tty_vwrite(struct tty *, @@ -1135,6 +1142,7 @@ extern const struct cmd_entry cmd_show_window_options_entry; extern const struct cmd_entry cmd_source_file_entry; extern const struct cmd_entry cmd_split_window_entry; extern const struct cmd_entry cmd_start_server_entry; +extern const struct cmd_entry cmd_suspend_client_entry; extern const struct cmd_entry cmd_swap_window_entry; extern const struct cmd_entry cmd_switch_client_entry; extern const struct cmd_entry cmd_unbind_key_entry; diff --git a/tty-write.c b/tty-write.c index a776ca57..62adb8b2 100644 --- a/tty-write.c +++ b/tty-write.c @@ -1,4 +1,4 @@ -/* $Id: tty-write.c,v 1.6 2009-01-14 19:29:32 nicm Exp $ */ +/* $Id: tty-write.c,v 1.7 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -45,6 +45,8 @@ tty_vwrite_window(void *ptr, enum tty_cmd cmd, va_list ap) c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; + if (c->flags & CLIENT_SUSPENDED) + continue; if (c->session->curw->window == wp->window) { va_copy(aq, ap); diff --git a/tty.c b/tty.c index 79f7a616..73a7e020 100644 --- a/tty.c +++ b/tty.c @@ -1,4 +1,4 @@ -/* $Id: tty.c,v 1.58 2009-01-11 23:31:46 nicm Exp $ */ +/* $Id: tty.c,v 1.59 2009-01-18 12:09:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -98,10 +98,6 @@ tty_init(struct tty *tty, char *path, char *term) int tty_open(struct tty *tty, char **cause) { - struct termios tio; -#ifdef TIOCFLUSH - int what; -#endif int mode; tty->fd = open(tty->path, O_RDWR|O_NONBLOCK); @@ -120,9 +116,6 @@ tty_open(struct tty *tty, char **cause) else tty->log_fd = -1; - tty->cx = UINT_MAX; - tty->cy = UINT_MAX; - if ((tty->term = tty_term_find(tty->termname, tty->fd, cause)) == NULL) goto error; @@ -130,7 +123,29 @@ tty_open(struct tty *tty, char **cause) tty->out = buffer_create(BUFSIZ); tty->flags &= TTY_UTF8; - memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); + + tty_start_tty(tty); + + tty_keys_init(tty); + + tty_fill_acs(tty); + + return (0); + +error: + close(tty->fd); + tty->fd = -1; + + return (-1); +} + +void +tty_start_tty(struct tty *tty) +{ + struct termios tio; +#ifdef TIOCFLUSH + int what; +#endif if (tcgetattr(tty->fd, &tty->tio) != 0) fatal("tcgetattr failed"); @@ -160,17 +175,41 @@ tty_open(struct tty *tty, char **cause) tty_putcode(tty, TTYC_ENACS); tty_putcode(tty, TTYC_CLEAR); - tty_keys_init(tty); + memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); - tty_fill_acs(tty); + tty->cx = UINT_MAX; + tty->cy = UINT_MAX; - return (0); + tty->rlower = UINT_MAX; + tty->rupper = UINT_MAX; +} -error: - close(tty->fd); - tty->fd = -1; +void +tty_stop_tty(struct tty *tty) +{ + struct winsize ws; - return (-1); + /* + * Be flexible about error handling and try not kill the server just + * because the fd is invalid. Things like ssh -t can easily leave us + * with a dead tty. + */ + if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1) { + if (errno != EBADF && errno != ENXIO && errno != ENOTTY) + fatal("ioctl(TIOCGWINSZ)"); + } else if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) { + if (errno != EBADF && errno != ENXIO && errno != ENOTTY) + fatal("tcsetattr failed"); + } else { + tty_raw(tty, + tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); + tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); + tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); + tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); + tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); + tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); + tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); + } } void @@ -198,10 +237,8 @@ tty_get_acs(struct tty *tty, u_char ch) } void -tty_close(struct tty *tty) +tty_close(struct tty *tty, int no_stop) { - struct winsize ws; - if (tty->fd == -1) return; @@ -210,27 +247,8 @@ tty_close(struct tty *tty) tty->log_fd = -1; } - /* - * Be flexible about error handling and try not kill the server just - * because the fd is invalid. Things like ssh -t can easily leave us - * with a dead tty. - */ - if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1) { - if (errno != EBADF && errno != ENXIO && errno != ENOTTY) - fatal("ioctl(TIOCGWINSZ)"); - } else if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) { - if (errno != EBADF && errno != ENXIO && errno != ENOTTY) - fatal("tcsetattr failed"); - } else { - tty_raw(tty, - tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); - tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); - tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); - tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); - tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); - tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); - tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); - } + if (!no_stop) + tty_stop_tty(tty); tty_term_free(tty->term); tty_keys_free(tty); @@ -243,9 +261,9 @@ tty_close(struct tty *tty) } void -tty_free(struct tty *tty) +tty_free(struct tty *tty, int no_stop) { - tty_close(tty); + tty_close(tty, no_stop); if (tty->path != NULL) xfree(tty->path);