diff options
author | Nicholas Marriott <nicm@cvs.openbsd.org> | 2015-12-08 01:10:32 +0000 |
---|---|---|
committer | Nicholas Marriott <nicm@cvs.openbsd.org> | 2015-12-08 01:10:32 +0000 |
commit | 4b154a643b4ef5a4448fd2e5b4d8e2e11d596eb5 (patch) | |
tree | 397291b7e8d108dc8fce3eb7cfe8ddf516b5f7d1 | |
parent | 124e67113db228290a9a8ff903f6eb4ab3b34e60 (diff) |
Add hooks infrastructure, basic commands (set-hook, show-hooks) and a
couple of not very useful client hooks. This will eventually let
commands be run at various points and on notifications. Joint work with
Thomas Adam.
-rw-r--r-- | usr.bin/tmux/Makefile | 4 | ||||
-rw-r--r-- | usr.bin/tmux/cmd-attach-session.c | 7 | ||||
-rw-r--r-- | usr.bin/tmux/cmd-detach-client.c | 14 | ||||
-rw-r--r-- | usr.bin/tmux/cmd-set-hook.c | 116 | ||||
-rw-r--r-- | usr.bin/tmux/cmd.c | 6 | ||||
-rw-r--r-- | usr.bin/tmux/hooks.c | 139 | ||||
-rw-r--r-- | usr.bin/tmux/server-client.c | 17 | ||||
-rw-r--r-- | usr.bin/tmux/session.c | 6 | ||||
-rw-r--r-- | usr.bin/tmux/tmux.1 | 50 | ||||
-rw-r--r-- | usr.bin/tmux/tmux.c | 5 | ||||
-rw-r--r-- | usr.bin/tmux/tmux.h | 33 |
11 files changed, 374 insertions, 23 deletions
diff --git a/usr.bin/tmux/Makefile b/usr.bin/tmux/Makefile index 8138a15842b..276440669b6 100644 --- a/usr.bin/tmux/Makefile +++ b/usr.bin/tmux/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.82 2015/11/13 08:09:28 nicm Exp $ +# $OpenBSD: Makefile,v 1.83 2015/12/08 01:10:31 nicm Exp $ PROG= tmux SRCS= alerts.c \ @@ -56,6 +56,7 @@ SRCS= alerts.c \ cmd-send-keys.c \ cmd-set-buffer.c \ cmd-set-environment.c \ + cmd-set-hook.c \ cmd-set-option.c \ cmd-show-environment.c \ cmd-show-messages.c \ @@ -78,6 +79,7 @@ SRCS= alerts.c \ format.c \ grid-view.c \ grid.c \ + hooks.c \ input-keys.c \ input.c \ job.c \ diff --git a/usr.bin/tmux/cmd-attach-session.c b/usr.bin/tmux/cmd-attach-session.c index b1f3cef4cb6..98582995407 100644 --- a/usr.bin/tmux/cmd-attach-session.c +++ b/usr.bin/tmux/cmd-attach-session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-attach-session.c,v 1.50 2015/12/07 09:47:41 nicm Exp $ */ +/* $OpenBSD: cmd-attach-session.c,v 1.51 2015/12/08 01:10:31 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> @@ -108,7 +108,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c_loop->peer, MSG_DETACH, s->name); + server_client_detach(c, MSG_DETACH); } } @@ -139,7 +139,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c_loop->peer, MSG_DETACH, s->name); + server_client_detach(c_loop, MSG_DETACH); } } @@ -159,6 +159,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); + hooks_run(c->session->hooks, "client-attached", c); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/usr.bin/tmux/cmd-detach-client.c b/usr.bin/tmux/cmd-detach-client.c index a0b83543dd7..23694816a9a 100644 --- a/usr.bin/tmux/cmd-detach-client.c +++ b/usr.bin/tmux/cmd-detach-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-detach-client.c,v 1.22 2015/11/24 20:40:51 nicm Exp $ */ +/* $OpenBSD: cmd-detach-client.c,v 1.23 2015/12/08 01:10:31 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> @@ -72,9 +72,8 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session != s) - continue; - proc_send_s(cloop->peer, msgtype, cloop->session->name); + if (cloop->session == s) + server_client_detach(cloop, msgtype); } return (CMD_RETURN_STOP); } @@ -85,13 +84,12 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'a')) { TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session == NULL || cloop == c) - continue; - proc_send_s(cloop->peer, msgtype, cloop->session->name); + if (cloop->session != NULL && cloop != c) + server_client_detach(cloop, msgtype); } return (CMD_RETURN_NORMAL); } - proc_send_s(c->peer, msgtype, c->session->name); + server_client_detach(c, msgtype); return (CMD_RETURN_STOP); } diff --git a/usr.bin/tmux/cmd-set-hook.c b/usr.bin/tmux/cmd-set-hook.c new file mode 100644 index 00000000000..fb021dd92d8 --- /dev/null +++ b/usr.bin/tmux/cmd-set-hook.c @@ -0,0 +1,116 @@ +/* $OpenBSD: cmd-set-hook.c,v 1.1 2015/12/08 01:10:31 nicm Exp $ */ + +/* + * Copyright (c) 2012 Thomas Adam <thomas@xteddy.org> + * + * 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" + +/* + * Set or show global or session hooks. + */ + +enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmd_q *); + +const struct cmd_entry cmd_set_hook_entry = { + "set-hook", NULL, + "gt:u", 1, 2, + "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", + 0, + cmd_set_hook_exec +}; + +const struct cmd_entry cmd_show_hooks_entry = { + "show-hooks", NULL, + "gt:", 0, 1, + "[-g] " CMD_TARGET_SESSION_USAGE, + 0, + cmd_set_hook_exec +}; + +enum cmd_retval +cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) +{ + struct args *args = self->args; + struct session *s; + struct cmd_list *cmdlist; + struct hooks *hooks; + struct hook *hook; + char *cause, *tmp; + const char *name, *cmd; + + if (args_has(args, 'g')) + hooks = global_hooks; + else { + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) + return (CMD_RETURN_ERROR); + hooks = s->hooks; + } + + if (self->entry == &cmd_show_hooks_entry) { + hook = hooks_first(hooks); + while (hook != NULL) { + tmp = cmd_list_print(hook->cmdlist); + cmdq_print(cmdq, "%s -> %s", hook->name, tmp); + free(tmp); + + hook = hooks_next(hook); + } + return (CMD_RETURN_NORMAL); + } + + name = args->argv[0]; + if (*name == '\0') { + cmdq_error(cmdq, "invalid hook name"); + return (CMD_RETURN_ERROR); + } + if (args->argc < 2) + cmd = NULL; + else + cmd = args->argv[1]; + + if (args_has(args, 'u')) { + if (cmd != NULL) { + cmdq_error(cmdq, "command passed to unset hook: %s", + name); + return (CMD_RETURN_ERROR); + } + if ((hook = hooks_find(hooks, name)) != NULL) + hooks_remove(hooks, hook); + return (CMD_RETURN_NORMAL); + } + + if (cmd == NULL) { + cmdq_error(cmdq, "no command to set hook: %s", name); + return (CMD_RETURN_ERROR); + } + if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { + if (cause != NULL) { + cmdq_error(cmdq, "%s", cause); + free(cause); + } + return (CMD_RETURN_ERROR); + } + hooks_add(hooks, name, cmdlist); + cmd_list_free(cmdlist); + + return (CMD_RETURN_NORMAL); +} diff --git a/usr.bin/tmux/cmd.c b/usr.bin/tmux/cmd.c index 6b0a5e1e49e..e9b82c0eadd 100644 --- a/usr.bin/tmux/cmd.c +++ b/usr.bin/tmux/cmd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd.c,v 1.106 2015/11/27 15:06:43 nicm Exp $ */ +/* $OpenBSD: cmd.c,v 1.107 2015/12/08 01:10:31 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> @@ -96,10 +96,12 @@ 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_environment_entry; +extern const struct cmd_entry cmd_set_hook_entry; extern const struct cmd_entry cmd_set_option_entry; extern const struct cmd_entry cmd_set_window_option_entry; extern const struct cmd_entry cmd_show_buffer_entry; extern const struct cmd_entry cmd_show_environment_entry; +extern const struct cmd_entry cmd_show_hooks_entry; extern const struct cmd_entry cmd_show_messages_entry; extern const struct cmd_entry cmd_show_options_entry; extern const struct cmd_entry cmd_show_window_options_entry; @@ -183,10 +185,12 @@ const struct cmd_entry *cmd_table[] = { &cmd_server_info_entry, &cmd_set_buffer_entry, &cmd_set_environment_entry, + &cmd_set_hook_entry, &cmd_set_option_entry, &cmd_set_window_option_entry, &cmd_show_buffer_entry, &cmd_show_environment_entry, + &cmd_show_hooks_entry, &cmd_show_messages_entry, &cmd_show_options_entry, &cmd_show_window_options_entry, diff --git a/usr.bin/tmux/hooks.c b/usr.bin/tmux/hooks.c new file mode 100644 index 00000000000..25391b999bb --- /dev/null +++ b/usr.bin/tmux/hooks.c @@ -0,0 +1,139 @@ +/* $OpenBSD: hooks.c,v 1.1 2015/12/08 01:10:31 nicm Exp $ */ + +/* + * Copyright (c) 2012 Thomas Adam <thomas@xteddy.org> + * + * 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" + +struct hooks { + RB_HEAD(hooks_tree, hook) tree; + struct hooks *parent; +}; + +static int hooks_cmp(struct hook *, struct hook *); +RB_PROTOTYPE(hooks_tree, hook, entry, hooks_cmp); +RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); + +struct hook *hooks_find1(struct hooks *, const char *); + +static int +hooks_cmp(struct hook *hook1, struct hook *hook2) +{ + return (strcmp(hook1->name, hook2->name)); +} + +struct hooks * +hooks_create(struct hooks *parent) +{ + struct hooks *hooks; + + hooks = xcalloc(1, sizeof *hooks); + RB_INIT(&hooks->tree); + hooks->parent = parent; + return (hooks); +} + +void +hooks_free(struct hooks *hooks) +{ + struct hook *hook, *hook1; + + RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1) + hooks_remove(hooks, hook); + free(hooks); +} + +struct hook * +hooks_first(struct hooks *hooks) +{ + return (RB_MIN(hooks_tree, &hooks->tree)); +} + +struct hook * +hooks_next(struct hook *hook) +{ + return (RB_NEXT(hooks_tree, &hooks->tree, hook)); +} + +void +hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) +{ + struct hook *hook; + + if ((hook = hooks_find1(hooks, name)) != NULL) + hooks_remove(hooks, hook); + + hook = xcalloc(1, sizeof *hook); + hook->name = xstrdup(name); + hook->cmdlist = cmdlist; + hook->cmdlist->references++; + RB_INSERT(hooks_tree, &hooks->tree, hook); +} + +void +hooks_remove(struct hooks *hooks, struct hook *hook) +{ + RB_REMOVE(hooks_tree, &hooks->tree, hook); + cmd_list_free(hook->cmdlist); + free((char *) hook->name); + free(hook); +} + +struct hook * +hooks_find1(struct hooks *hooks, const char *name) +{ + struct hook hook; + + hook.name = name; + return (RB_FIND(hooks_tree, &hooks->tree, &hook)); +} + +struct hook * +hooks_find(struct hooks *hooks, const char *name) +{ + struct hook hook0, *hook; + + hook0.name = name; + hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); + while (hook == NULL) { + hooks = hooks->parent; + if (hooks == NULL) + break; + hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); + } + return (hook); +} + +void +hooks_run(struct hooks *hooks, const char *name, struct client *c) +{ + struct hook *hook; + struct cmd_q *cmdq; + + hook = hooks_find(hooks, name); + if (hook == NULL) + return; + log_debug("running hook %s", name); + + cmdq = cmdq_new(c); + cmdq_run(cmdq, hook->cmdlist, NULL); + cmdq_free(cmdq); +} diff --git a/usr.bin/tmux/server-client.c b/usr.bin/tmux/server-client.c index 12d2a630d6a..22f90d93726 100644 --- a/usr.bin/tmux/server-client.c +++ b/usr.bin/tmux/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.173 2015/12/01 09:41:03 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.174 2015/12/08 01:10:31 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> @@ -256,6 +256,19 @@ server_client_free(__unused int fd, __unused short events, void *arg) free(c); } +/* Detach a client. */ +void +server_client_detach(struct client *c, enum msgtype msgtype) +{ + struct session *s = c->session; + + if (s == NULL) + return; + + hooks_run(c->session->hooks, "client-detached", c); + proc_send_s(c->peer, msgtype, s->name); +} + /* Check for mouse keys. */ key_code server_client_check_mouse(struct client *c) @@ -995,6 +1008,8 @@ server_client_dispatch(struct imsg *imsg, void *arg) recalculate_sizes(); server_redraw_client(c); } + if (c->session != NULL) + hooks_run(c->session->hooks, "client-resized", c); break; case MSG_EXITING: if (datalen != 0) diff --git a/usr.bin/tmux/session.c b/usr.bin/tmux/session.c index fe65fb47121..9e905b44e73 100644 --- a/usr.bin/tmux/session.c +++ b/usr.bin/tmux/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.60 2015/11/18 14:27:44 nicm Exp $ */ +/* $OpenBSD: session.c,v 1.61 2015/12/08 01:10:31 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> @@ -123,7 +123,9 @@ session_create(const char *name, int argc, char **argv, const char *path, s->environ = environ_create(); if (env != NULL) environ_copy(env, s->environ); + s->options = options_create(global_s_options); + s->hooks = hooks_create(global_hooks); s->tio = NULL; if (tio != NULL) { @@ -189,7 +191,9 @@ session_free(__unused int fd, __unused short events, void *arg) if (s->references == 0) { environ_free(s->environ); + options_free(s->options); + hooks_free(s->hooks); free(s->name); free(s); diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1 index 6529207d44f..46ca7fdc22a 100644 --- a/usr.bin/tmux/tmux.1 +++ b/usr.bin/tmux/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.468 2015/11/29 17:06:59 guenther Exp $ +.\" $OpenBSD: tmux.1,v 1.469 2015/12/08 01:10:31 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: November 29 2015 $ +.Dd $Mdocdate: December 8 2015 $ .Dt TMUX 1 .Os .Sh NAME @@ -3193,6 +3193,52 @@ is used. .Fl v shows only the option value, not the name. .El +.Sh HOOKS +.Nm +allows commands to run on various triggers, called +.Em hooks . +Each hook has a +.Em name . +The following hooks are available: +.Bl -tag -width "XXXXXXXXXXXXXXXX" +.It client-attached +Run when a client is attached. +.It client-detached +Run when a client is detached +.It client-resized +Run when a client is resized. +.El +.Pp +Hooks are managed with these commands: +.Bl -tag -width Ds +.It Xo Ic set-hook +.Op Fl g +.Op Fl t Ar target-session +.Ar hook-name +.Ar command +.Xc +Sets hook +.Ar hook-name +to +.Ar command . +If +.Fl g +is given, +.Em hook-name +is added to the global list of hooks, otherwise it is added to the session +hooks (for +.Ar target-session +with +.Fl t ) . +Like options, session hooks inherit from the global ones. +.It Xo Ic show-hooks +.Op Fl g +.Op Fl t Ar target-session +.Xc +Shows the global list of hooks with +.Fl g , +otherwise the session hooks. +.Ed .Sh MOUSE SUPPORT If the .Ic mouse diff --git a/usr.bin/tmux/tmux.c b/usr.bin/tmux/tmux.c index 5df75994b08..a0c4c5e85e9 100644 --- a/usr.bin/tmux/tmux.c +++ b/usr.bin/tmux/tmux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.c,v 1.162 2015/11/24 23:46:15 nicm Exp $ */ +/* $OpenBSD: tmux.c,v 1.163 2015/12/08 01:10:31 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> @@ -38,6 +38,7 @@ struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ struct environ *global_environ; +struct hooks *global_hooks; struct timeval start_time; const char *socket_path; @@ -269,6 +270,8 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; } + global_hooks = hooks_create(NULL); + global_environ = environ_create(); for (var = environ; *var != NULL; var++) environ_put(global_environ, *var); diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h index 3cad1742b29..9379effe2fc 100644 --- a/usr.bin/tmux/tmux.h +++ b/usr.bin/tmux/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.594 2015/12/07 09:47:41 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.595 2015/12/08 01:10:31 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> @@ -691,6 +691,14 @@ struct grid { struct grid_line *linedata; }; +/* Hook data structures. */ +struct hook { + const char *name; + struct cmd_q *cmdq; + struct cmd_list *cmdlist; + RB_ENTRY(hook) entry; +}; + /* Option data structures. */ struct options_entry { char *name; @@ -1011,6 +1019,7 @@ struct session { struct winlink_stack lastw; struct winlinks windows; + struct hooks *hooks; struct options *options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ @@ -1427,10 +1436,11 @@ struct options_table_entry { #define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ -extern struct options *global_options; -extern struct options *global_s_options; -extern struct options *global_w_options; -extern struct environ *global_environ; +extern struct hooks *global_hooks; +extern struct options *global_options; +extern struct options *global_s_options; +extern struct options *global_w_options; +extern struct environ *global_environ; extern struct timeval start_time; extern const char *socket_path; const char *getshell(void); @@ -1495,6 +1505,18 @@ void format_defaults_pane(struct format_tree *, void format_defaults_paste_buffer(struct format_tree *, struct paste_buffer *); +/* hooks.c */ +struct hook; +struct hooks *hooks_create(struct hooks *); +void hooks_free(struct hooks *); +struct hook *hooks_first(struct hooks *); +struct hook *hooks_next(struct hook *); +void hooks_add(struct hooks *, const char *, struct cmd_list *); +void hooks_copy(struct hooks *, struct hooks *); +void hooks_remove(struct hooks *, struct hook *); +struct hook *hooks_find(struct hooks *, const char *); +void hooks_run(struct hooks *, const char *, struct client *); + /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; extern struct mode_key_tree mode_key_tree_vi_edit; @@ -1782,6 +1804,7 @@ void server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); +void server_client_detach(struct client *, enum msgtype); void server_client_loop(void); void server_client_push_stdout(struct client *); void server_client_push_stderr(struct client *); |