summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@cvs.openbsd.org>2017-05-30 21:45:00 +0000
committerNicholas Marriott <nicm@cvs.openbsd.org>2017-05-30 21:45:00 +0000
commita6666d02f028c9c6260b6049c4270683d9a98c0e (patch)
treeae9b9c74da4c76b2ad01aa919ee8a7e1b0b46f12 /usr.bin
parentaef664be42b2524c118e93dfb52b6dd196fe8fbe (diff)
Rewrite of choose mode, both to simplify and tidy the code and to add
some modern features. Now the common code is in mode-tree.c, which provides an API used by the three modes now separated into window-{buffer,client,tree}.c. Buffer mode shows buffers, client mode clients and tree mode a tree of sessions, windows and panes. Each mode has a common set of key bindings plus a few that are specific to the mode. Other changes are: - each mode has a preview pane: for buffers this is the buffer content (very useful), for others it is a preview of the pane; - items may be sorted in different ways ('O' key); - multiple items may be tagged and an operation applied to all of them (for example, to delete multiple buffers at once); - in tree mode a command may be run on the selected item (session, window, pane) or on tagged items (key ':'); - displayed items may be filtered in tree mode by using a format (this is used to implement find-window) (key 'f'); - the custom format (-F) for the display is no longer available; - shortcut keys change from 0-9, a-z, A-Z which was always a bit weird with keys used for other uses to 0-9, M-a to M-z. Now that the code is simpler, other improvements will come later. Primary key bindings for each mode are documented under the commands in the man page (choose-buffer, choose-client, choose-tree). Parts written by Thomas Adam.
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/tmux/Makefile15
-rw-r--r--usr.bin/tmux/arguments.c5
-rw-r--r--usr.bin/tmux/cfg.c5
-rw-r--r--usr.bin/tmux/cmd-choose-buffer.c101
-rw-r--r--usr.bin/tmux/cmd-choose-client.c135
-rw-r--r--usr.bin/tmux/cmd-choose-tree.c221
-rw-r--r--usr.bin/tmux/cmd-copy-mode.c8
-rw-r--r--usr.bin/tmux/cmd-find-window.c249
-rw-r--r--usr.bin/tmux/cmd-queue.c5
-rw-r--r--usr.bin/tmux/cmd-run-shell.c4
-rw-r--r--usr.bin/tmux/cmd-split-window.c5
-rw-r--r--usr.bin/tmux/cmd.c6
-rw-r--r--usr.bin/tmux/format.c6
-rw-r--r--usr.bin/tmux/key-bindings.c6
-rw-r--r--usr.bin/tmux/mode-tree.c705
-rw-r--r--usr.bin/tmux/options-table.c8
-rw-r--r--usr.bin/tmux/screen-write.c120
-rw-r--r--usr.bin/tmux/tmux.1201
-rw-r--r--usr.bin/tmux/tmux.h71
-rw-r--r--usr.bin/tmux/window-buffer.c342
-rw-r--r--usr.bin/tmux/window-choose.c1078
-rw-r--r--usr.bin/tmux/window-client.c335
-rw-r--r--usr.bin/tmux/window-clock.c8
-rw-r--r--usr.bin/tmux/window-copy.c8
-rw-r--r--usr.bin/tmux/window-tree.c713
-rw-r--r--usr.bin/tmux/window.c33
26 files changed, 2461 insertions, 1932 deletions
diff --git a/usr.bin/tmux/Makefile b/usr.bin/tmux/Makefile
index 81d5fbcb097..1d3fca65c3e 100644
--- a/usr.bin/tmux/Makefile
+++ b/usr.bin/tmux/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.87 2017/04/20 17:49:26 nicm Exp $
+# $OpenBSD: Makefile,v 1.88 2017/05/30 21:44:59 nicm Exp $
PROG= tmux
SRCS= alerts.c \
@@ -10,8 +10,6 @@ SRCS= alerts.c \
cmd-bind-key.c \
cmd-break-pane.c \
cmd-capture-pane.c \
- cmd-choose-buffer.c \
- cmd-choose-client.c \
cmd-choose-tree.c \
cmd-command-prompt.c \
cmd-confirm-before.c \
@@ -20,6 +18,7 @@ SRCS= alerts.c \
cmd-display-message.c \
cmd-display-panes.c \
cmd-find-window.c \
+ cmd-find.c \
cmd-if-shell.c \
cmd-join-pane.c \
cmd-kill-pane.c \
@@ -40,6 +39,7 @@ SRCS= alerts.c \
cmd-new-window.c \
cmd-paste-buffer.c \
cmd-pipe-pane.c \
+ cmd-queue.c \
cmd-refresh-client.c \
cmd-rename-session.c \
cmd-rename-window.c \
@@ -69,11 +69,9 @@ SRCS= alerts.c \
cmd-unbind-key.c \
cmd-wait-for.c \
cmd.c \
- cmd-find.c \
- cmd-queue.c \
colour.c \
- control.c \
control-notify.c \
+ control.c \
environ.c \
format.c \
grid-view.c \
@@ -88,6 +86,7 @@ SRCS= alerts.c \
layout-set.c \
layout.c \
log.c \
+ mode-tree.c \
names.c \
notify.c \
options-table.c \
@@ -112,9 +111,11 @@ SRCS= alerts.c \
tty-term.c \
tty.c \
utf8.c \
- window-choose.c \
+ window-buffer.c \
+ window-client.c \
window-clock.c \
window-copy.c \
+ window-tree.c \
window.c \
xmalloc.c \
xterm-keys.c
diff --git a/usr.bin/tmux/arguments.c b/usr.bin/tmux/arguments.c
index f17f15accdf..ac15296cd31 100644
--- a/usr.bin/tmux/arguments.c
+++ b/usr.bin/tmux/arguments.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: arguments.c,v 1.18 2017/04/22 12:08:41 nicm Exp $ */
+/* $OpenBSD: arguments.c,v 1.19 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -35,7 +35,6 @@ struct args_entry {
RB_ENTRY(args_entry) entry;
};
-static void args_set(struct args *, u_char, const char *);
static struct args_entry *args_find(struct args *, u_char);
static int args_cmp(struct args_entry *, struct args_entry *);
@@ -196,7 +195,7 @@ args_has(struct args *args, u_char ch)
}
/* Set argument value in the arguments tree. */
-static void
+void
args_set(struct args *args, u_char ch, const char *value)
{
struct args_entry *entry;
diff --git a/usr.bin/tmux/cfg.c b/usr.bin/tmux/cfg.c
index 6257930b2f2..c3f59b23265 100644
--- a/usr.bin/tmux/cfg.c
+++ b/usr.bin/tmux/cfg.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cfg.c,v 1.59 2017/05/01 12:20:55 nicm Exp $ */
+/* $OpenBSD: cfg.c,v 1.60 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -23,7 +23,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include <util.h>
#include "tmux.h"
@@ -234,7 +233,7 @@ cfg_show_causes(struct session *s)
return;
wp = s->curw->window->active;
- window_pane_set_mode(wp, &window_copy_mode);
+ window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
window_copy_init_for_output(wp);
for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, "%s", cfg_causes[i]);
diff --git a/usr.bin/tmux/cmd-choose-buffer.c b/usr.bin/tmux/cmd-choose-buffer.c
deleted file mode 100644
index 9f1e9435682..00000000000
--- a/usr.bin/tmux/cmd-choose-buffer.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/* $OpenBSD: cmd-choose-buffer.c,v 1.32 2017/04/22 10:22:39 nicm Exp $ */
-
-/*
- * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@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 <ctype.h>
-#include <stdlib.h>
-
-#include "tmux.h"
-
-/*
- * Enter choice mode to choose a buffer.
- */
-
-#define CHOOSE_BUFFER_TEMPLATE \
- "#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}"
-
-static enum cmd_retval cmd_choose_buffer_exec(struct cmd *,
- struct cmdq_item *);
-
-const struct cmd_entry cmd_choose_buffer_entry = {
- .name = "choose-buffer",
- .alias = NULL,
-
- .args = { "F:t:", 0, 1 },
- .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
-
- .target = { 't', CMD_FIND_WINDOW, 0 },
-
- .flags = 0,
- .exec = cmd_choose_buffer_exec
-};
-
-static enum cmd_retval
-cmd_choose_buffer_exec(struct cmd *self, struct cmdq_item *item)
-{
- struct args *args = self->args;
- struct client *c = cmd_find_client(item, NULL, 1);
- struct winlink *wl = item->target.wl;
- struct window_choose_data *cdata;
- struct paste_buffer *pb;
- char *action, *action_data;
- const char *template;
- u_int idx;
-
- if (c == NULL) {
- cmdq_error(item, "no client available");
- return (CMD_RETURN_ERROR);
- }
-
- if ((template = args_get(args, 'F')) == NULL)
- template = CHOOSE_BUFFER_TEMPLATE;
-
- if (paste_get_top(NULL) == NULL)
- return (CMD_RETURN_NORMAL);
-
- if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
- return (CMD_RETURN_NORMAL);
-
- if (args->argc != 0)
- action = xstrdup(args->argv[0]);
- else
- action = xstrdup("paste-buffer -b '%%'");
-
- idx = 0;
- pb = NULL;
- while ((pb = paste_walk(pb)) != NULL) {
- cdata = window_choose_data_create(TREE_OTHER, c, c->session);
- cdata->idx = idx;
-
- cdata->ft_template = xstrdup(template);
- format_defaults_paste_buffer(cdata->ft, pb);
-
- xasprintf(&action_data, "%s", paste_buffer_name(pb));
- cdata->command = cmd_template_replace(action, action_data, 1);
- free(action_data);
-
- window_choose_add(wl->window->active, cdata);
- idx++;
- }
- free(action);
-
- window_choose_ready(wl->window->active, 0, NULL);
-
- return (CMD_RETURN_NORMAL);
-}
diff --git a/usr.bin/tmux/cmd-choose-client.c b/usr.bin/tmux/cmd-choose-client.c
deleted file mode 100644
index 6cb91c03594..00000000000
--- a/usr.bin/tmux/cmd-choose-client.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/* $OpenBSD: cmd-choose-client.c,v 1.33 2017/04/22 10:22:39 nicm Exp $ */
-
-/*
- * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@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 <ctype.h>
-#include <stdlib.h>
-
-#include "tmux.h"
-
-/*
- * Enter choice mode to choose a client.
- */
-
-#define CHOOSE_CLIENT_TEMPLATE \
- "#{client_name}: #{session_name} " \
- "[#{client_width}x#{client_height} #{client_termname}]" \
- "#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \
- "(last used #{t:client_activity})"
-
-static enum cmd_retval cmd_choose_client_exec(struct cmd *,
- struct cmdq_item *);
-
-static void cmd_choose_client_callback(struct window_choose_data *);
-
-const struct cmd_entry cmd_choose_client_entry = {
- .name = "choose-client",
- .alias = NULL,
-
- .args = { "F:t:", 0, 1 },
- .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
-
- .target = { 't', CMD_FIND_WINDOW, 0 },
-
- .flags = 0,
- .exec = cmd_choose_client_exec
-};
-
-struct cmd_choose_client_data {
- struct client *client;
-};
-
-static enum cmd_retval
-cmd_choose_client_exec(struct cmd *self, struct cmdq_item *item)
-{
- struct args *args = self->args;
- struct client *c = cmd_find_client(item, NULL, 1);
- struct client *c1;
- struct window_choose_data *cdata;
- struct winlink *wl = item->target.wl;
- const char *template;
- char *action;
- u_int idx, cur;
-
- if (c == NULL) {
- cmdq_error(item, "no client available");
- return (CMD_RETURN_ERROR);
- }
-
- if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
- return (CMD_RETURN_NORMAL);
-
- if ((template = args_get(args, 'F')) == NULL)
- template = CHOOSE_CLIENT_TEMPLATE;
-
- if (args->argc != 0)
- action = xstrdup(args->argv[0]);
- else
- action = xstrdup("detach-client -t '%%'");
-
- cur = idx = 0;
- TAILQ_FOREACH(c1, &clients, entry) {
- if (c1->session == NULL)
- continue;
- if (c1 == item->client)
- cur = idx;
-
- cdata = window_choose_data_create(TREE_OTHER, c, c->session);
- cdata->idx = idx;
-
- cdata->ft_template = xstrdup(template);
- format_add(cdata->ft, "line", "%u", idx);
- format_defaults(cdata->ft, c1, NULL, NULL, NULL);
-
- cdata->command = cmd_template_replace(action, c1->name, 1);
-
- window_choose_add(wl->window->active, cdata);
-
- idx++;
- }
- free(action);
-
- window_choose_ready(wl->window->active, cur,
- cmd_choose_client_callback);
-
- return (CMD_RETURN_NORMAL);
-}
-
-static void
-cmd_choose_client_callback(struct window_choose_data *cdata)
-{
- struct client *c;
- u_int idx;
-
- if (cdata == NULL)
- return;
- if (cdata->start_client->flags & CLIENT_DEAD)
- return;
-
- idx = 0;
- TAILQ_FOREACH(c, &clients, entry) {
- if (idx == cdata->idx)
- break;
- idx++;
- }
- if (c == NULL || c->session == NULL)
- return;
-
- window_choose_data_run(cdata);
-}
diff --git a/usr.bin/tmux/cmd-choose-tree.c b/usr.bin/tmux/cmd-choose-tree.c
index 50f882b32bb..354fa910f87 100644
--- a/usr.bin/tmux/cmd-choose-tree.c
+++ b/usr.bin/tmux/cmd-choose-tree.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-choose-tree.c,v 1.34 2017/04/22 10:22:39 nicm Exp $ */
+/* $OpenBSD: cmd-choose-tree.c,v 1.35 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
@@ -18,66 +18,48 @@
#include <sys/types.h>
-#include <ctype.h>
-#include <stdlib.h>
-
-#include <string.h>
-
#include "tmux.h"
-#define CMD_CHOOSE_TREE_WINDOW_ACTION "select-window -t '%%'"
-#define CMD_CHOOSE_TREE_SESSION_ACTION "switch-client -t '%%'"
-
/*
- * Enter choice mode to choose a session and/or window.
+ * Enter a mode.
*/
-#define CHOOSE_TREE_SESSION_TEMPLATE \
- "#{session_name}: #{session_windows} windows" \
- "#{?session_grouped, (group ,}" \
- "#{session_group}#{?session_grouped,),}" \
- "#{?session_attached, (attached),}"
-#define CHOOSE_TREE_WINDOW_TEMPLATE \
- "#{window_index}: #{window_name}#{window_flags} " \
- "\"#{pane_title}\""
-
static enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree",
.alias = NULL,
- .args = { "S:W:swub:c:t:", 0, 1 },
- .usage = "[-suw] [-b session-template] [-c window template] "
- "[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE,
+ .args = { "st:w", 0, 1 },
+ .usage = "[-sw] " CMD_TARGET_PANE_USAGE,
- .target = { 't', CMD_FIND_WINDOW, 0 },
+ .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_choose_tree_exec
};
-const struct cmd_entry cmd_choose_session_entry = {
- .name = "choose-session",
+const struct cmd_entry cmd_choose_client_entry = {
+ .name = "choose-client",
.alias = NULL,
- .args = { "F:t:", 0, 1 },
- .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
+ .args = { "t:", 0, 1 },
+ .usage = CMD_TARGET_PANE_USAGE,
- .target = { 't', CMD_FIND_WINDOW, 0 },
+ .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_choose_tree_exec
};
-const struct cmd_entry cmd_choose_window_entry = {
- .name = "choose-window",
+const struct cmd_entry cmd_choose_buffer_entry = {
+ .name = "choose-buffer",
.alias = NULL,
- .args = { "F:t:", 0, 1 },
- .usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]",
+ .args = { "t:", 0, 1 },
+ .usage = CMD_TARGET_PANE_USAGE,
- .target = { 't', CMD_FIND_WINDOW, 0 },
+ .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_choose_tree_exec
@@ -87,167 +69,20 @@ static enum cmd_retval
cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
- struct client *c = cmd_find_client(item, NULL, 1);
- struct winlink *wl = item->target.wl, *wm;
- struct session *s = item->target.s, *s2;
- struct window_choose_data *wcd = NULL;
- const char *ses_template, *win_template;
- char *final_win_action, *cur_win_template;
- char *final_win_template_middle;
- char *final_win_template_last;
- const char *ses_action, *win_action;
- u_int cur_win, idx_ses, win_ses, win_max;
- u_int wflag, sflag;
-
- ses_template = win_template = NULL;
- ses_action = win_action = NULL;
-
- if (c == NULL) {
- cmdq_error(item, "no client available");
- return (CMD_RETURN_ERROR);
- }
-
- if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
- return (CMD_RETURN_NORMAL);
-
- /* Sort out which command this is. */
- wflag = sflag = 0;
- if (self->entry == &cmd_choose_session_entry) {
- sflag = 1;
- if ((ses_template = args_get(args, 'F')) == NULL)
- ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
-
- if (args->argc != 0)
- ses_action = args->argv[0];
- else
- ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
- } else if (self->entry == &cmd_choose_window_entry) {
- wflag = 1;
- if ((win_template = args_get(args, 'F')) == NULL)
- win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
-
- if (args->argc != 0)
- win_action = args->argv[0];
- else
- win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
- } else {
- wflag = args_has(args, 'w');
- sflag = args_has(args, 's');
-
- if ((ses_action = args_get(args, 'b')) == NULL)
- ses_action = CMD_CHOOSE_TREE_SESSION_ACTION;
-
- if ((win_action = args_get(args, 'c')) == NULL)
- win_action = CMD_CHOOSE_TREE_WINDOW_ACTION;
-
- if ((ses_template = args_get(args, 'S')) == NULL)
- ses_template = CHOOSE_TREE_SESSION_TEMPLATE;
-
- if ((win_template = args_get(args, 'W')) == NULL)
- win_template = CHOOSE_TREE_WINDOW_TEMPLATE;
- }
-
- /*
- * If not asking for windows and sessions, assume no "-ws" given and
- * hence display the entire tree outright.
- */
- if (!wflag && !sflag)
- wflag = sflag = 1;
-
- /*
- * If we're drawing in tree mode, including sessions, then pad the
- * window template, otherwise just render the windows as a flat list
- * without any padding.
- */
- if (wflag && sflag) {
- xasprintf(&final_win_template_middle,
- " \001tq\001> %s", win_template);
- xasprintf(&final_win_template_last,
- " \001mq\001> %s", win_template);
- } else if (wflag) {
- final_win_template_middle = xstrdup(win_template);
- final_win_template_last = xstrdup(win_template);
+ struct window_pane *wp = item->target.wp;
+ const struct window_mode *mode;
+
+ if (self->entry == &cmd_choose_buffer_entry) {
+ if (paste_get_top(NULL) == NULL)
+ return (CMD_RETURN_NORMAL);
+ mode = &window_buffer_mode;
+ } else if (self->entry == &cmd_choose_client_entry) {
+ if (server_client_how_many() == 0)
+ return (CMD_RETURN_NORMAL);
+ mode = &window_client_mode;
} else
- final_win_template_middle = final_win_template_last = NULL;
-
- idx_ses = cur_win = -1;
- RB_FOREACH(s2, sessions, &sessions) {
- idx_ses++;
-
- /*
- * If we're just choosing windows, jump straight there. Note
- * that this implies the current session, so only choose
- * windows when the session matches this one.
- */
- if (wflag && !sflag) {
- if (s != s2)
- continue;
- goto windows_only;
- }
-
- wcd = window_choose_add_session(wl->window->active,
- c, s2, ses_template, ses_action, idx_ses);
-
- /* If we're just choosing sessions, skip choosing windows. */
- if (sflag && !wflag) {
- if (s == s2)
- cur_win = idx_ses;
- continue;
- }
-windows_only:
- win_ses = win_max = -1;
- RB_FOREACH(wm, winlinks, &s2->windows)
- win_max++;
- RB_FOREACH(wm, winlinks, &s2->windows) {
- win_ses++;
- if (sflag && wflag)
- idx_ses++;
-
- if (wm == s2->curw && s == s2) {
- if (wflag && !sflag) {
- /*
- * Then we're only counting windows.
- * So remember which is the current
- * window in the list.
- */
- cur_win = win_ses;
- } else
- cur_win = idx_ses;
- }
-
- xasprintf(&final_win_action, "%s %s %s",
- wcd != NULL ? wcd->command : "",
- wcd != NULL ? ";" : "", win_action);
-
- if (win_ses != win_max)
- cur_win_template = final_win_template_middle;
- else
- cur_win_template = final_win_template_last;
-
- window_choose_add_window(wl->window->active,
- c, s2, wm, cur_win_template,
- final_win_action,
- (wflag && !sflag) ? win_ses : idx_ses);
-
- free(final_win_action);
- }
-
- /*
- * If we're just drawing windows, don't consider moving on to
- * other sessions as we only list windows in this session.
- */
- if (wflag && !sflag)
- break;
- }
- free(final_win_template_middle);
- free(final_win_template_last);
-
- window_choose_ready(wl->window->active, cur_win, NULL);
-
- if (args_has(args, 'u')) {
- window_choose_expand_all(wl->window->active);
- window_choose_set_current(wl->window->active, cur_win);
- }
+ mode = &window_tree_mode;
+ window_pane_set_mode(wp, mode, &item->target, args);
return (CMD_RETURN_NORMAL);
}
diff --git a/usr.bin/tmux/cmd-copy-mode.c b/usr.bin/tmux/cmd-copy-mode.c
index ac1a8e78102..6a957daa66a 100644
--- a/usr.bin/tmux/cmd-copy-mode.c
+++ b/usr.bin/tmux/cmd-copy-mode.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-copy-mode.c,v 1.33 2017/04/22 10:22:39 nicm Exp $ */
+/* $OpenBSD: cmd-copy-mode.c,v 1.34 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -60,6 +60,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = item->client;
struct session *s;
struct window_pane *wp = item->target.wp;
+ int flag;
if (args_has(args, 'M')) {
if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL)
@@ -69,12 +70,13 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
}
if (self->entry == &cmd_clock_mode_entry) {
- window_pane_set_mode(wp, &window_clock_mode);
+ window_pane_set_mode(wp, &window_clock_mode, NULL, NULL);
return (CMD_RETURN_NORMAL);
}
if (wp->mode != &window_copy_mode) {
- if (window_pane_set_mode(wp, &window_copy_mode) != 0)
+ flag = window_pane_set_mode(wp, &window_copy_mode, NULL, NULL);
+ if (flag != 0)
return (CMD_RETURN_NORMAL);
window_copy_init_from_pane(wp, args_has(self->args, 'e'));
}
diff --git a/usr.bin/tmux/cmd-find-window.c b/usr.bin/tmux/cmd-find-window.c
index 68a52f3ee4c..8809b5582c6 100644
--- a/usr.bin/tmux/cmd-find-window.c
+++ b/usr.bin/tmux/cmd-find-window.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-find-window.c,v 1.42 2017/05/29 18:06:34 nicm Exp $ */
+/* $OpenBSD: cmd-find-window.c,v 1.43 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -18,9 +18,7 @@
#include <sys/types.h>
-#include <fnmatch.h>
#include <stdlib.h>
-#include <string.h>
#include "tmux.h"
@@ -28,216 +26,69 @@
* Find window containing text.
*/
-#define FIND_WINDOW_TEMPLATE \
- "#{window_index}: #{window_name} " \
- "[#{window_width}x#{window_height}] " \
- "(#{window_panes} panes) #{window_find_matches}"
-
static enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmdq_item *);
-static void cmd_find_window_callback(struct window_choose_data *);
-
-/* Flags for determining matching behavior. */
-#define CMD_FIND_WINDOW_BY_TITLE 0x1
-#define CMD_FIND_WINDOW_BY_CONTENT 0x2
-#define CMD_FIND_WINDOW_BY_NAME 0x4
-
-#define CMD_FIND_WINDOW_ALL \
- (CMD_FIND_WINDOW_BY_TITLE | \
- CMD_FIND_WINDOW_BY_CONTENT | \
- CMD_FIND_WINDOW_BY_NAME)
-
const struct cmd_entry cmd_find_window_entry = {
.name = "find-window",
.alias = "findw",
- .args = { "F:CNt:T", 1, 4 },
- .usage = "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string",
+ .args = { "CNt:T", 1, 1 },
+ .usage = "[-CNT] " CMD_TARGET_PANE_USAGE " match-string",
- .target = { 't', CMD_FIND_WINDOW, 0 },
+ .target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_find_window_exec
};
-struct cmd_find_window_data {
- struct winlink *wl;
- char *list_ctx;
- u_int pane_id;
- TAILQ_ENTRY(cmd_find_window_data) entry;
-};
-TAILQ_HEAD(cmd_find_window_list, cmd_find_window_data);
-
-static u_int cmd_find_window_match_flags(struct args *);
-static void cmd_find_window_match(struct cmd_find_window_list *, int,
- struct winlink *, const char *, const char *);
-
-static u_int
-cmd_find_window_match_flags(struct args *args)
-{
- u_int match_flags = 0;
-
- /* Turn on flags based on the options. */
- if (args_has(args, 'T'))
- match_flags |= CMD_FIND_WINDOW_BY_TITLE;
- if (args_has(args, 'C'))
- match_flags |= CMD_FIND_WINDOW_BY_CONTENT;
- if (args_has(args, 'N'))
- match_flags |= CMD_FIND_WINDOW_BY_NAME;
-
- /* If none of the flags were set, default to matching anything. */
- if (match_flags == 0)
- match_flags = CMD_FIND_WINDOW_ALL;
-
- return (match_flags);
-}
-
-static void
-cmd_find_window_match(struct cmd_find_window_list *find_list,
- int match_flags, struct winlink *wl, const char *str,
- const char *searchstr)
-{
- struct cmd_find_window_data *find_data;
- struct window_pane *wp;
- u_int i, line;
- char *sres;
-
- find_data = xcalloc(1, sizeof *find_data);
-
- i = 0;
- TAILQ_FOREACH(wp, &wl->window->panes, entry) {
- i++;
-
- if ((match_flags & CMD_FIND_WINDOW_BY_NAME) &&
- fnmatch(searchstr, wl->window->name, 0) == 0) {
- find_data->list_ctx = xstrdup("");
- break;
- }
-
- if ((match_flags & CMD_FIND_WINDOW_BY_TITLE) &&
- fnmatch(searchstr, wp->base.title, 0) == 0) {
- xasprintf(&find_data->list_ctx,
- "pane %u title: \"%s\"", i - 1, wp->base.title);
- break;
- }
-
- if (match_flags & CMD_FIND_WINDOW_BY_CONTENT &&
- (sres = window_pane_search_old(wp, str, &line)) != NULL) {
- xasprintf(&find_data->list_ctx,
- "pane %u line %u: \"%s\"", i - 1, line + 1, sres);
- free(sres);
- break;
- }
- }
-
- if (find_data->list_ctx != NULL) {
- find_data->wl = wl;
- find_data->pane_id = i - 1;
- TAILQ_INSERT_TAIL(find_list, find_data, entry);
- } else
- free(find_data);
-}
-
static enum cmd_retval
cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
{
- struct args *args = self->args;
- struct cmd_find_state *current = &item->shared->current;
- struct client *c = cmd_find_client(item, NULL, 1);
- struct window_choose_data *cdata;
- struct session *s = item->target.s;
- struct winlink *wl = item->target.wl, *wm;
- struct cmd_find_window_list find_list;
- struct cmd_find_window_data *find_data;
- struct cmd_find_window_data *find_data1;
- char *str, *searchstr;
- const char *template;
- u_int i, match_flags;
-
- if (c == NULL) {
- cmdq_error(item, "no client available");
- return (CMD_RETURN_ERROR);
- }
-
- if ((template = args_get(args, 'F')) == NULL)
- template = FIND_WINDOW_TEMPLATE;
-
- match_flags = cmd_find_window_match_flags(args);
- str = args->argv[0];
-
- TAILQ_INIT(&find_list);
-
- xasprintf(&searchstr, "*%s*", str);
- RB_FOREACH(wm, winlinks, &s->windows)
- cmd_find_window_match(&find_list, match_flags, wm, str, searchstr);
- free(searchstr);
-
- if (TAILQ_EMPTY(&find_list)) {
- cmdq_error(item, "no windows matching: %s", str);
- return (CMD_RETURN_ERROR);
- }
-
- if (TAILQ_NEXT(TAILQ_FIRST(&find_list), entry) == NULL) {
- if (session_select(s, TAILQ_FIRST(&find_list)->wl->idx) == 0) {
- cmd_find_from_session(current, s);
- server_redraw_session(s);
- }
- recalculate_sizes();
- goto out;
- }
-
- if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0)
- goto out;
-
- i = 0;
- TAILQ_FOREACH(find_data, &find_list, entry) {
- cdata = window_choose_data_create(TREE_OTHER, c, c->session);
- cdata->idx = find_data->wl->idx;
- cdata->wl = find_data->wl;
-
- cdata->ft_template = xstrdup(template);
- cdata->pane_id = find_data->pane_id;
-
- format_add(cdata->ft, "line", "%u", i);
- format_add(cdata->ft, "window_find_matches", "%s",
- find_data->list_ctx);
- format_defaults(cdata->ft, NULL, s, find_data->wl, NULL);
-
- window_choose_add(wl->window->active, cdata);
-
- i++;
- }
-
- window_choose_ready(wl->window->active, 0, cmd_find_window_callback);
-
-out:
- TAILQ_FOREACH_SAFE(find_data, &find_list, entry, find_data1) {
- free(find_data->list_ctx);
- TAILQ_REMOVE(&find_list, find_data, entry);
- free(find_data);
- }
- return (CMD_RETURN_NORMAL);
-}
-
-static void
-cmd_find_window_callback(struct window_choose_data *cdata)
-{
- struct session *s;
- struct window_pane *wp;
-
- if (cdata == NULL)
- return;
+ struct args *args = self->args, *new_args;
+ struct window_pane *wp = item->target.wp;
+ const char *s = args->argv[0];
+ char *filter, *argv = { NULL };
+ int C, N, T;
+
+ C = args_has(args, 'C');
+ N = args_has(args, 'N');
+ T = args_has(args, 'T');
+
+ if (!C && !N && !T)
+ C = N = T = 1;
+
+ if (C && N && T) {
+ xasprintf(&filter,
+ "#{||:"
+ "#{C:%s},#{||:#{m:*%s*,#{window_name}},"
+ "#{m:*%s*,#{pane_title}}}}",
+ s, s, s);
+ } else if (C && N) {
+ xasprintf(&filter,
+ "#{||:#{C:%s},#{m:*%s*,#{window_name}}}",
+ s, s);
+ } else if (C && T) {
+ xasprintf(&filter,
+ "#{||:#{C:%s},#{m:*%s*,#{pane_title}}}",
+ s, s);
+ } else if (N && T) {
+ xasprintf(&filter,
+ "#{||:#{m:*%s*,#{window_name}},#{m:*%s*,#{pane_title}}}",
+ s, s);
+ } else if (C)
+ xasprintf(&filter, "#{C:%s}", s);
+ else if (N)
+ xasprintf(&filter, "#{m:*%s*,#{window_name}}", s);
+ else if (T)
+ xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
+
+ new_args = args_parse("", 1, &argv);
+ args_set(new_args, 'f', filter);
+
+ window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args);
+
+ args_free(new_args);
+ free(filter);
- s = cdata->start_session;
- if (!session_alive(s))
- return;
-
- wp = window_pane_at_index(cdata->wl->window, cdata->pane_id);
- if (wp != NULL && window_pane_visible(wp))
- window_set_active_pane(cdata->wl->window, wp);
-
- if (session_select(s, cdata->idx) == 0) {
- server_redraw_session(s);
- recalculate_sizes();
- }
+ return (CMD_RETURN_NORMAL);
}
diff --git a/usr.bin/tmux/cmd-queue.c b/usr.bin/tmux/cmd-queue.c
index 68109e5c275..9b9e52c64e2 100644
--- a/usr.bin/tmux/cmd-queue.c
+++ b/usr.bin/tmux/cmd-queue.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-queue.c,v 1.55 2017/05/01 12:20:55 nicm Exp $ */
+/* $OpenBSD: cmd-queue.c,v 1.56 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -428,7 +428,8 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
w = c->session->curw->window;
if (w->active->mode != &window_copy_mode) {
window_pane_reset_mode(w->active);
- window_pane_set_mode(w->active, &window_copy_mode);
+ window_pane_set_mode(w->active, &window_copy_mode, NULL,
+ NULL);
window_copy_init_for_output(w->active);
}
window_copy_vadd(w->active, fmt, ap);
diff --git a/usr.bin/tmux/cmd-run-shell.c b/usr.bin/tmux/cmd-run-shell.c
index c85220ce522..18cd0db6dd5 100644
--- a/usr.bin/tmux/cmd-run-shell.c
+++ b/usr.bin/tmux/cmd-run-shell.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-run-shell.c,v 1.49 2017/04/22 10:22:39 nicm Exp $ */
+/* $OpenBSD: cmd-run-shell.c,v 1.50 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -75,7 +75,7 @@ cmd_run_shell_print(struct job *job, const char *msg)
return;
}
- if (window_pane_set_mode(wp, &window_copy_mode) == 0)
+ if (window_pane_set_mode(wp, &window_copy_mode, NULL, NULL) == 0)
window_copy_init_for_output(wp);
if (wp->mode == &window_copy_mode)
window_copy_add(wp, "%s", msg);
diff --git a/usr.bin/tmux/cmd-split-window.c b/usr.bin/tmux/cmd-split-window.c
index fa42ba43bc9..83490ab0dc6 100644
--- a/usr.bin/tmux/cmd-split-window.c
+++ b/usr.bin/tmux/cmd-split-window.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-split-window.c,v 1.84 2017/04/25 15:35:10 nicm Exp $ */
+/* $OpenBSD: cmd-split-window.c,v 1.85 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -134,7 +134,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
goto error;
}
new_wp = window_add_pane(w, wp, args_has(args, 'b'), hlimit);
- layout_assign_pane(lc, new_wp);
+ layout_make_leaf(lc, new_wp);
path = NULL;
if (item->client != NULL && item->client->session == NULL)
@@ -152,6 +152,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
}
environ_free(env);
+ layout_fix_panes(w, w->sx, w->sy);
server_redraw_window(w);
if (!args_has(args, 'd')) {
diff --git a/usr.bin/tmux/cmd.c b/usr.bin/tmux/cmd.c
index 61e5569e7f7..ac69f21db24 100644
--- a/usr.bin/tmux/cmd.c
+++ b/usr.bin/tmux/cmd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd.c,v 1.138 2017/04/22 10:22:39 nicm Exp $ */
+/* $OpenBSD: cmd.c,v 1.139 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,9 +34,7 @@ extern const struct cmd_entry cmd_break_pane_entry;
extern const struct cmd_entry cmd_capture_pane_entry;
extern const struct cmd_entry cmd_choose_buffer_entry;
extern const struct cmd_entry cmd_choose_client_entry;
-extern const struct cmd_entry cmd_choose_session_entry;
extern const struct cmd_entry cmd_choose_tree_entry;
-extern const struct cmd_entry cmd_choose_window_entry;
extern const struct cmd_entry cmd_clear_history_entry;
extern const struct cmd_entry cmd_clock_mode_entry;
extern const struct cmd_entry cmd_command_prompt_entry;
@@ -123,9 +121,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_capture_pane_entry,
&cmd_choose_buffer_entry,
&cmd_choose_client_entry,
- &cmd_choose_session_entry,
&cmd_choose_tree_entry,
- &cmd_choose_window_entry,
&cmd_clear_history_entry,
&cmd_clock_mode_entry,
&cmd_command_prompt_entry,
diff --git a/usr.bin/tmux/format.c b/usr.bin/tmux/format.c
index 7ce43b6ee49..0853b9f8d99 100644
--- a/usr.bin/tmux/format.c
+++ b/usr.bin/tmux/format.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: format.c,v 1.140 2017/05/29 18:06:34 nicm Exp $ */
+/* $OpenBSD: format.c,v 1.141 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -19,11 +19,9 @@
#include <sys/types.h>
#include <sys/wait.h>
-#include <ctype.h>
#include <errno.h>
#include <fnmatch.h>
#include <libgen.h>
-#include <netdb.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -840,7 +838,7 @@ format_choose(char *s, char **left, char **right)
}
/* Is this true? */
-static int
+int
format_true(const char *s)
{
if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
diff --git a/usr.bin/tmux/key-bindings.c b/usr.bin/tmux/key-bindings.c
index ff5530fd748..dd882660d22 100644
--- a/usr.bin/tmux/key-bindings.c
+++ b/usr.bin/tmux/key-bindings.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: key-bindings.c,v 1.79 2017/05/30 08:13:48 nicm Exp $ */
+/* $OpenBSD: key-bindings.c,v 1.80 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -196,9 +196,9 @@ key_bindings_init(void)
"bind p previous-window",
"bind q display-panes",
"bind r refresh-client",
- "bind s choose-tree",
+ "bind s choose-tree -s",
"bind t clock-mode",
- "bind w choose-window",
+ "bind w choose-tree -w",
"bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind z resize-pane -Z",
"bind { swap-pane -U",
diff --git a/usr.bin/tmux/mode-tree.c b/usr.bin/tmux/mode-tree.c
new file mode 100644
index 00000000000..b6e3b6b839f
--- /dev/null
+++ b/usr.bin/tmux/mode-tree.c
@@ -0,0 +1,705 @@
+/* $OpenBSD: mode-tree.c,v 1.1 2017/05/30 21:44:59 nicm Exp $ */
+
+/*
+ * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+struct mode_tree_item;
+TAILQ_HEAD(mode_tree_list, mode_tree_item);
+
+struct mode_tree_data {
+ struct window_pane *wp;
+ void *modedata;
+
+ const char **sort_list;
+ u_int sort_size;
+ u_int sort_type;
+
+ void (*buildcb)(void *, u_int, uint64_t *);
+ struct screen *(*drawcb)(void *, void *, u_int, u_int);
+
+ struct mode_tree_list children;
+ struct mode_tree_list saved;
+
+ struct mode_tree_line *line_list;
+ u_int line_size;
+
+ u_int depth;
+
+ u_int width;
+ u_int height;
+
+ u_int offset;
+ u_int current;
+
+ struct screen screen;
+};
+
+struct mode_tree_item {
+ struct mode_tree_item *parent;
+ void *itemdata;
+ u_int line;
+
+ uint64_t tag;
+ const char *name;
+ const char *text;
+
+ int expanded;
+ int tagged;
+
+ struct mode_tree_list children;
+ TAILQ_ENTRY(mode_tree_item) entry;
+};
+
+struct mode_tree_line {
+ struct mode_tree_item *item;
+ u_int depth;
+ int last;
+ int flat;
+};
+
+static void mode_tree_free_items(struct mode_tree_list *);
+
+static struct mode_tree_item *
+mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
+{
+ struct mode_tree_item *mti, *child;
+
+ TAILQ_FOREACH(mti, mtl, entry) {
+ if (mti->tag == tag)
+ return (mti);
+ child = mode_tree_find_item(&mti->children, tag);
+ if (child != NULL)
+ return (child);
+ }
+ return (NULL);
+}
+
+static void
+mode_tree_free_item(struct mode_tree_item *mti)
+{
+ mode_tree_free_items(&mti->children);
+
+ free((void *)mti->name);
+ free((void *)mti->text);
+
+ free(mti);
+}
+
+static void
+mode_tree_free_items(struct mode_tree_list *mtl)
+{
+ struct mode_tree_item *mti, *mti1;
+
+ TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
+ TAILQ_REMOVE(mtl, mti, entry);
+ mode_tree_free_item(mti);
+ }
+}
+
+static void
+mode_tree_clear_lines(struct mode_tree_data *mtd)
+{
+ free(mtd->line_list);
+ mtd->line_list = NULL;
+ mtd->line_size = 0;
+}
+
+static void
+mode_tree_build_lines(struct mode_tree_data *mtd,
+ struct mode_tree_list *mtl, u_int depth)
+{
+ struct mode_tree_item *mti;
+ struct mode_tree_line *line;
+ u_int i;
+ int flat = 1;
+
+ mtd->depth = depth;
+ TAILQ_FOREACH(mti, mtl, entry) {
+ mtd->line_list = xreallocarray(mtd->line_list,
+ mtd->line_size + 1, sizeof *mtd->line_list);
+
+ line = &mtd->line_list[mtd->line_size++];
+ line->item = mti;
+ line->depth = depth;
+ line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
+
+ mti->line = (mtd->line_size - 1);
+ if (!TAILQ_EMPTY(&mti->children))
+ flat = 0;
+ if (mti->expanded)
+ mode_tree_build_lines(mtd, &mti->children, depth + 1);
+ }
+ TAILQ_FOREACH(mti, mtl, entry) {
+ for (i = 0; i < mtd->line_size; i++) {
+ line = &mtd->line_list[i];
+ if (line->item == mti)
+ line->flat = flat;
+ }
+ }
+}
+
+static void
+mode_tree_clear_tagged(struct mode_tree_list *mtl)
+{
+ struct mode_tree_item *mti;
+
+ TAILQ_FOREACH(mti, mtl, entry) {
+ mti->tagged = 0;
+ mode_tree_clear_tagged(&mti->children);
+ }
+}
+
+void
+mode_tree_up(struct mode_tree_data *mtd, int wrap)
+{
+ if (mtd->current == 0) {
+ if (wrap) {
+ mtd->current = mtd->line_size - 1;
+ if (mtd->line_size >= mtd->height)
+ mtd->offset = mtd->line_size - mtd->height;
+ }
+ } else {
+ mtd->current--;
+ if (mtd->current < mtd->offset)
+ mtd->offset--;
+ }
+}
+
+void
+mode_tree_down(struct mode_tree_data *mtd, int wrap)
+{
+ if (mtd->current == mtd->line_size - 1) {
+ if (wrap) {
+ mtd->current = 0;
+ mtd->offset = 0;
+ }
+ } else {
+ mtd->current++;
+ if (mtd->current > mtd->offset + mtd->height - 1)
+ mtd->offset++;
+ }
+}
+
+void *
+mode_tree_get_current(struct mode_tree_data *mtd)
+{
+ return (mtd->line_list[mtd->current].item->itemdata);
+}
+
+u_int
+mode_tree_count_tagged(struct mode_tree_data *mtd)
+{
+ struct mode_tree_item *mti;
+ u_int i, tagged;
+
+ tagged = 0;
+ for (i = 0; i < mtd->line_size; i++) {
+ mti = mtd->line_list[i].item;
+ if (mti->tagged)
+ tagged++;
+ }
+ return (tagged);
+}
+
+void
+mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *,
+ key_code), key_code key, int current)
+{
+ struct mode_tree_item *mti;
+ u_int i;
+ int fired;
+
+ fired = 0;
+ for (i = 0; i < mtd->line_size; i++) {
+ mti = mtd->line_list[i].item;
+ if (mti->tagged) {
+ fired = 1;
+ cb(mtd->modedata, mti->itemdata, key);
+ }
+ }
+ if (!fired && current) {
+ mti = mtd->line_list[mtd->current].item;
+ cb(mtd->modedata, mti->itemdata, key);
+ }
+}
+
+struct mode_tree_data *
+mode_tree_start(struct window_pane *wp, void (*buildcb)(void *, u_int,
+ uint64_t *), struct screen *(*drawcb)(void *, void *, u_int, u_int),
+ void *modedata, const char **sort_list, u_int sort_size, struct screen **s)
+{
+ struct mode_tree_data *mtd;
+
+ mtd = xcalloc(1, sizeof *mtd);
+ mtd->wp = wp;
+ mtd->modedata = modedata;
+
+ mtd->sort_list = sort_list;
+ mtd->sort_size = sort_size;
+ mtd->sort_type = 0;
+
+ mtd->buildcb = buildcb;
+ mtd->drawcb = drawcb;
+
+ TAILQ_INIT(&mtd->children);
+
+ *s = &mtd->screen;
+ screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
+ (*s)->mode &= ~MODE_CURSOR;
+
+ return (mtd);
+}
+
+void
+mode_tree_build(struct mode_tree_data *mtd)
+{
+ struct screen *s = &mtd->screen;
+ uint64_t tag;
+ u_int i;
+
+ if (mtd->line_list != NULL)
+ tag = mtd->line_list[mtd->current].item->tag;
+ else
+ tag = 0;
+
+ TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
+ TAILQ_INIT(&mtd->children);
+
+ mtd->buildcb(mtd->modedata, mtd->sort_type, &tag);
+
+ mode_tree_free_items(&mtd->saved);
+ TAILQ_INIT(&mtd->saved);
+
+ mode_tree_clear_lines(mtd);
+ mode_tree_build_lines(mtd, &mtd->children, 0);
+
+ for (i = 0; i < mtd->line_size; i++) {
+ if (mtd->line_list[i].item->tag == tag)
+ break;
+ }
+ if (i != mtd->line_size)
+ mtd->current = i;
+ else {
+ mtd->current = 0;
+ mtd->offset = 0;
+ }
+
+ mtd->width = screen_size_x(s);
+ mtd->height = (screen_size_y(s) / 3) * 2;
+ if (mtd->height > mtd->line_size)
+ mtd->height = screen_size_y(s) / 2;
+ if (mtd->height < 10)
+ mtd->height = screen_size_y(s);
+ if (screen_size_y(s) - mtd->height < 2)
+ mtd->height = screen_size_y(s);
+}
+
+void
+mode_tree_free(struct mode_tree_data *mtd)
+{
+ mode_tree_free_items(&mtd->children);
+ mode_tree_clear_lines(mtd);
+ screen_free(&mtd->screen);
+ free(mtd);
+}
+
+void
+mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
+{
+ struct screen *s = &mtd->screen;
+
+ screen_resize(s, sx, sy, 0);
+
+ mode_tree_build(mtd);
+ mode_tree_draw(mtd);
+
+ mtd->wp->flags |= PANE_REDRAW;
+}
+
+struct mode_tree_item *
+mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
+ void *itemdata, uint64_t tag, const char *name, const char *text,
+ int expanded)
+{
+ struct mode_tree_item *mti, *saved;
+
+ log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
+ name, text);
+
+ mti = xcalloc(1, sizeof *mti);
+ mti->parent = parent;
+ mti->itemdata = itemdata;
+
+ mti->tag = tag;
+ mti->name = xstrdup(name);
+ mti->text = xstrdup(text);
+
+ saved = mode_tree_find_item(&mtd->saved, tag);
+ if (saved != NULL) {
+ if (parent == NULL || (parent != NULL && parent->expanded))
+ mti->tagged = saved->tagged;
+ mti->expanded = saved->expanded;
+ } else if (expanded == -1)
+ mti->expanded = 1;
+ else
+ mti->expanded = expanded;
+
+ TAILQ_INIT(&mti->children);
+
+ if (parent != NULL)
+ TAILQ_INSERT_TAIL(&parent->children, mti, entry);
+ else
+ TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
+
+ return (mti);
+}
+
+void
+mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
+{
+ struct mode_tree_item *parent = mti->parent;
+
+ if (parent != NULL)
+ TAILQ_REMOVE(&parent->children, mti, entry);
+ else
+ TAILQ_REMOVE(&mtd->children, mti, entry);
+ mode_tree_free_item(mti);
+}
+
+void
+mode_tree_draw(struct mode_tree_data *mtd)
+{
+ struct window_pane *wp = mtd->wp;
+ struct screen *s = &mtd->screen, *box;
+ struct mode_tree_line *line;
+ struct mode_tree_item *mti;
+ struct options *oo = wp->window->options;
+ struct screen_write_ctx ctx;
+ struct grid_cell gc0, gc;
+ u_int w, h, i, j, sy, box_x, box_y;
+ char *text, *start, key[7];
+ const char *tag, *symbol;
+ size_t size;
+ int keylen;
+
+ if (mtd->line_size == 0)
+ return;
+
+ memcpy(&gc0, &grid_default_cell, sizeof gc0);
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ style_apply(&gc, oo, "mode-style");
+
+ w = mtd->width;
+ h = mtd->height;
+
+ screen_write_start(&ctx, NULL, s);
+ screen_write_clearscreen(&ctx, 8);
+
+ if (mtd->line_size > 10)
+ keylen = 6;
+ else
+ keylen = 4;
+
+ for (i = 0; i < mtd->line_size; i++) {
+ if (i < mtd->offset)
+ continue;
+ if (i > mtd->offset + h - 1)
+ break;
+
+ line = &mtd->line_list[i];
+ mti = line->item;
+
+ screen_write_cursormove(&ctx, 0, i - mtd->offset);
+
+ if (i < 10)
+ snprintf(key, sizeof key, "(%c)", '0' + i);
+ else if (i < 36)
+ snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
+ else
+ *key = '\0';
+
+ if (line->flat)
+ symbol = "";
+ else if (TAILQ_EMPTY(&mti->children))
+ symbol = " ";
+ else if (mti->expanded)
+ symbol = "- ";
+ else
+ symbol = "+ ";
+
+ if (line->depth == 0)
+ start = xstrdup(symbol);
+ else {
+ size = (4 * line->depth) + 32;
+
+ start = xcalloc(1, size);
+ for (j = 1; j < line->depth; j++) {
+ if (mti->parent != NULL &&
+ mtd->line_list[mti->parent->line].last)
+ strlcat(start, " ", size);
+ else
+ strlcat(start, "\001x\001 ", size);
+ }
+ if (line->last)
+ strlcat(start, "\001mq\001> ", size);
+ else
+ strlcat(start, "\001tq\001> ", size);
+ strlcat(start, symbol, size);
+ }
+
+ if (mti->tagged)
+ tag = "*";
+ else
+ tag = "";
+ xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
+ mti->name, tag, mti->text);
+ free(start);
+
+ if (mti->tagged) {
+ gc.attr ^= GRID_ATTR_BRIGHT;
+ gc0.attr ^= GRID_ATTR_BRIGHT;
+ }
+
+ if (i != mtd->current) {
+ screen_write_puts(&ctx, &gc0, "%.*s", w, text);
+ screen_write_clearendofline(&ctx, 8);
+ } else
+ screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text);
+ free(text);
+
+ if (mti->tagged) {
+ gc.attr ^= GRID_ATTR_BRIGHT;
+ gc0.attr ^= GRID_ATTR_BRIGHT;
+ }
+ }
+
+ sy = screen_size_y(s);
+ if (sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
+ screen_write_stop(&ctx);
+ return;
+ }
+
+ line = &mtd->line_list[mtd->current];
+ mti = line->item;
+
+ screen_write_cursormove(&ctx, 0, h);
+ screen_write_box(&ctx, w, sy - h);
+
+ xasprintf(&text, " %s (sort: %s) ", mti->name,
+ mtd->sort_list[mtd->sort_type]);
+ if (w - 2 >= strlen(text)) {
+ screen_write_cursormove(&ctx, 1, h);
+ screen_write_puts(&ctx, &gc0, "%s", text);
+ }
+ free(text);
+
+ box_x = w - 4;
+ box_y = sy - h - 2;
+
+ box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
+ if (box != NULL) {
+ screen_write_cursormove(&ctx, 2, h + 1);
+ screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
+
+ screen_free(box);
+ }
+
+ screen_write_stop(&ctx);
+}
+
+int
+mode_tree_key(struct mode_tree_data *mtd, key_code *key, struct mouse_event *m)
+{
+ struct mode_tree_line *line;
+ struct mode_tree_item *current, *parent;
+ u_int i, x, y;
+ int choice;
+ key_code tmp;
+
+ if (*key == KEYC_MOUSEDOWN1_PANE) {
+ if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
+ *key = KEYC_NONE;
+ return (0);
+ }
+ if (x > mtd->width || y > mtd->height) {
+ *key = KEYC_NONE;
+ return (0);
+ }
+ if (mtd->offset + y < mtd->line_size) {
+ mtd->current = mtd->offset + y;
+ *key = '\r';
+ return (0);
+ }
+ }
+
+ line = &mtd->line_list[mtd->current];
+ current = line->item;
+
+ choice = -1;
+ if (*key >= '0' && *key <= '9')
+ choice = (*key) - '0';
+ else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
+ tmp = (*key) & KEYC_MASK_KEY;
+ if (tmp >= 'a' && tmp <= 'z')
+ choice = 10 + (tmp - 'a');
+ }
+ if (choice != -1) {
+ if ((u_int)choice > mtd->line_size - 1) {
+ *key = KEYC_NONE;
+ return (0);
+ }
+ mtd->current = choice;
+ *key = '\r';
+ return (0);
+ }
+
+ switch (*key) {
+ case 'q':
+ case '\033': /* Escape */
+ return (1);
+ case KEYC_UP:
+ case 'k':
+ case KEYC_WHEELUP_PANE:
+ mode_tree_up(mtd, 1);
+ break;
+ case KEYC_DOWN:
+ case 'j':
+ case KEYC_WHEELDOWN_PANE:
+ mode_tree_down(mtd, 1);
+ break;
+ case KEYC_PPAGE:
+ case '\002': /* C-b */
+ for (i = 0; i < mtd->height; i++) {
+ if (mtd->current == 0)
+ break;
+ mode_tree_up(mtd, 1);
+ }
+ break;
+ case KEYC_NPAGE:
+ case '\006': /* C-f */
+ for (i = 0; i < mtd->height; i++) {
+ if (mtd->current == mtd->line_size - 1)
+ break;
+ mode_tree_down(mtd, 1);
+ }
+ break;
+ case KEYC_HOME:
+ mtd->current = 0;
+ mtd->offset = 0;
+ break;
+ case KEYC_END:
+ mtd->current = mtd->line_size - 1;
+ if (mtd->current > mtd->height - 1)
+ mtd->offset = mtd->current - mtd->height;
+ else
+ mtd->offset = 0;
+ break;
+ case 't':
+ /*
+ * Do not allow parents and children to both be tagged: untag
+ * all parents and children of current.
+ */
+ if (!current->tagged) {
+ parent = current->parent;
+ while (parent != NULL) {
+ parent->tagged = 0;
+ parent = parent->parent;
+ }
+ mode_tree_clear_tagged(&current->children);
+ current->tagged = 1;
+ } else
+ current->tagged = 0;
+ mode_tree_down(mtd, 0);
+ break;
+ case 'T':
+ for (i = 0; i < mtd->line_size; i++)
+ mtd->line_list[i].item->tagged = 0;
+ break;
+ case '\024': /* C-t */
+ for (i = 0; i < mtd->line_size; i++) {
+ if (mtd->line_list[i].item->parent == NULL)
+ mtd->line_list[i].item->tagged = 1;
+ else
+ mtd->line_list[i].item->tagged = 0;
+ }
+ break;
+ case 'O':
+ mtd->sort_type++;
+ if (mtd->sort_type == mtd->sort_size)
+ mtd->sort_type = 0;
+ mode_tree_build(mtd);
+ break;
+ case KEYC_LEFT:
+ case '-':
+ if (line->flat || !current->expanded)
+ current = current->parent;
+ if (current == NULL)
+ mode_tree_up(mtd, 0);
+ else {
+ current->expanded = 0;
+ mtd->current = current->line;
+ mode_tree_build(mtd);
+ }
+ break;
+ case KEYC_RIGHT:
+ case '+':
+ if (line->flat || current->expanded)
+ mode_tree_down(mtd, 0);
+ else if (!line->flat) {
+ current->expanded = 1;
+ mode_tree_build(mtd);
+ }
+ break;
+ }
+ return (0);
+}
+
+void
+mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
+ const char *template, const char *name)
+{
+ struct cmdq_item *new_item;
+ struct cmd_list *cmdlist;
+ char *command, *cause;
+
+ command = cmd_template_replace(template, name, 1);
+ if (command == NULL || *command == '\0')
+ return;
+
+ cmdlist = cmd_string_parse(command, NULL, 0, &cause);
+ if (cmdlist == NULL) {
+ if (cause != NULL && c != NULL) {
+ *cause = toupper((u_char)*cause);
+ status_message_set(c, "%s", cause);
+ }
+ free(cause);
+ } else {
+ new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
+ cmdq_append(c, new_item);
+ cmd_list_free(cmdlist);
+ }
+
+ free(command);
+}
diff --git a/usr.bin/tmux/options-table.c b/usr.bin/tmux/options-table.c
index aaecc902472..798d337678f 100644
--- a/usr.bin/tmux/options-table.c
+++ b/usr.bin/tmux/options-table.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: options-table.c,v 1.87 2017/05/29 20:41:29 nicm Exp $ */
+/* $OpenBSD: options-table.c,v 1.88 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -62,7 +62,7 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.minimum = 1,
.maximum = INT_MAX,
- .default_num = 20
+ .default_num = 50
},
{ .name = "command-alias",
@@ -71,7 +71,9 @@ const struct options_table_entry options_table[] = {
.default_str = "split-pane=split-window,"
"splitp=split-window,"
"server-info=show-messages -JT,"
- "info=show-messages -JT",
+ "info=show-messages -JT,"
+ "choose-window=choose-tree -w,"
+ "choose-session=choose-tree -s",
.separator = ","
},
diff --git a/usr.bin/tmux/screen-write.c b/usr.bin/tmux/screen-write.c
index bb381f2c0ab..63f9985a5f3 100644
--- a/usr.bin/tmux/screen-write.c
+++ b/usr.bin/tmux/screen-write.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: screen-write.c,v 1.124 2017/05/12 14:56:56 nicm Exp $ */
+/* $OpenBSD: screen-write.c,v 1.125 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -362,6 +362,9 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
struct grid_cell gc;
u_int xx, yy, cx, cy, b;
+ if (nx == 0 || ny == 0)
+ return;
+
cx = s->cx;
cy = s->cy;
@@ -384,6 +387,121 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
}
}
+/* Draw a line on screen. */
+void
+screen_write_line(struct screen_write_ctx *ctx, u_int nx, int left, int right)
+{
+ struct screen *s = ctx->s;
+ struct grid_cell gc;
+ u_int cx, cy, i;
+
+ cx = s->cx;
+ cy = s->cy;
+
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ gc.attr |= GRID_ATTR_CHARSET;
+
+ screen_write_putc(ctx, &gc, left ? 't' : 'q');
+ for (i = 1; i < nx - 1; i++)
+ screen_write_putc(ctx, &gc, 'q');
+ screen_write_putc(ctx, &gc, right ? 'u' : 'q');
+
+ screen_write_cursormove(ctx, cx, cy);
+}
+
+/* Draw a box on screen. */
+void
+screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
+{
+ struct screen *s = ctx->s;
+ struct grid_cell gc;
+ u_int cx, cy, i;
+
+ cx = s->cx;
+ cy = s->cy;
+
+ memcpy(&gc, &grid_default_cell, sizeof gc);
+ gc.attr |= GRID_ATTR_CHARSET;
+
+ screen_write_putc(ctx, &gc, 'l');
+ for (i = 1; i < nx - 1; i++)
+ screen_write_putc(ctx, &gc, 'q');
+ screen_write_putc(ctx, &gc, 'k');
+
+ screen_write_cursormove(ctx, cx, cy + ny - 1);
+ screen_write_putc(ctx, &gc, 'm');
+ for (i = 1; i < nx - 1; i++)
+ screen_write_putc(ctx, &gc, 'q');
+ screen_write_putc(ctx, &gc, 'j');
+
+ for (i = 1; i < ny - 1; i++) {
+ screen_write_cursormove(ctx, cx, cy + i);
+ screen_write_putc(ctx, &gc, 'x');
+ }
+ for (i = 1; i < ny - 1; i++) {
+ screen_write_cursormove(ctx, cx + nx - 1, cy + i);
+ screen_write_putc(ctx, &gc, 'x');
+ }
+
+ screen_write_cursormove(ctx, cx, cy);
+}
+
+/* Write a preview version of a window. */
+void
+screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
+ u_int ny)
+{
+ struct screen *s = ctx->s;
+ struct grid_cell gc;
+ u_int cx, cy, px, py;
+
+ cx = s->cx;
+ cy = s->cy;
+
+ /*
+ * If the cursor is on, pick the area around the cursor, otherwise use
+ * the top left.
+ */
+ if (src->mode & MODE_CURSOR) {
+ px = src->cx;
+ if (px < nx / 3)
+ px = 0;
+ else
+ px = px - nx / 3;
+ if (px + nx > screen_size_x(src)) {
+ if (nx > screen_size_x(src))
+ px = 0;
+ else
+ px = screen_size_x(src) - nx;
+ }
+ py = src->cy;
+ if (py < ny / 3)
+ py = 0;
+ else
+ py = py - ny / 3;
+ if (py + ny > screen_size_y(src)) {
+ if (ny > screen_size_y(src))
+ py = 0;
+ else
+ py = screen_size_y(src) - ny;
+ }
+ } else {
+ px = 0;
+ py = 0;
+ }
+
+ screen_write_copy(ctx, src, px, src->grid->hsize + py, nx, ny, NULL,
+ NULL);
+
+ if (src->mode & MODE_CURSOR) {
+ grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
+ gc.attr |= GRID_ATTR_REVERSE;
+ screen_write_cursormove(ctx, cx + (src->cx - px),
+ cy + (src->cy - py));
+ screen_write_cell(ctx, &gc);
+ }
+}
+
/* Set up context for TTY command. */
static void
screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1
index d6cba360db1..ebfcf6b79cd 100644
--- a/usr.bin/tmux/tmux.1
+++ b/usr.bin/tmux/tmux.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.554 2017/05/29 18:06:34 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.555 2017/05/30 21:44:59 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
@@ -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: May 29 2017 $
+.Dd $Mdocdate: May 30 2017 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -1339,136 +1339,76 @@ the end of the visible pane.
The default is to capture only the visible contents of the pane.
.It Xo
.Ic choose-client
-.Op Fl F Ar format
-.Op Fl t Ar target-window
+.Op Fl t Ar target-pane
.Op Ar template
.Xc
-Put a window into client choice mode, allowing a client to be selected
-interactively from a list.
+Put a pane into client mode, allowing a client to be selected interactively from
+a list.
+The following keys may be used in client mode:
+.Bl -column "Key" "Function" -offset indent
+.It Sy "Key" Ta Sy "Function"
+.It Li "Enter" Ta "Choose selected client"
+.It Li "Up" Ta "Select previous client"
+.It Li "Down" Ta "Select next client"
+.It Li "t" Ta "Toggle if client is tagged"
+.It Li "T" Ta "Tag no clients"
+.It Li "C-t" Ta "Tag all clients"
+.It Li "d" Ta "Detach selected client"
+.It Li "D" Ta "Detach tagged clients"
+.It Li "x" Ta "Detach and HUP selected client"
+.It Li "X" Ta "Detach and HUP tagged clients"
+.It Li "z" Ta "Suspend selected client"
+.It Li "Z" Ta "Suspend tagged clients"
+.It Li "O" Ta "Change sort order"
+.It Li "q" Ta "Exit mode"
+.El
+.Pp
After a client is chosen,
.Ql %%
-is replaced by the client
-.Xr pty 4
-path in
+is replaced by the client name in
.Ar template
and the result executed as a command.
If
.Ar template
is not given, "detach-client -t '%%'" is used.
-For the meaning of the
-.Fl F
-flag, see the
-.Sx FORMATS
-section.
+.Pp
This command works only if at least one client is attached.
.It Xo
-.Ic choose-session
-.Op Fl F Ar format
-.Op Fl t Ar target-window
+.Ic choose-tree
+.Op Fl sw
+.Op Fl t Ar target-pane
.Op Ar template
.Xc
-Put a window into session choice mode, where a session may be selected
+Put a pane into tree mode, where a session, window or pane may be chosen
interactively from a list.
-When one is chosen,
-.Ql %%
-is replaced by the session name in
-.Ar template
-and the result executed as a command.
-If
-.Ar template
-is not given, "switch-client -t '%%'" is used.
-For the meaning of the
-.Fl F
-flag, see the
-.Sx FORMATS
-section.
-This command works only if at least one client is attached.
-.It Xo
-.Ic choose-tree
-.Op Fl suw
-.Op Fl b Ar session-template
-.Op Fl c Ar window-template
-.Op Fl S Ar format
-.Op Fl W Ar format
-.Op Fl t Ar target-window
-.Xc
-Put a window into tree choice mode, where either sessions or windows may be
-selected interactively from a list.
-By default, windows belonging to a session are indented to show their
-relationship to a session.
-.Pp
-Note that the
-.Ic choose-window
-and
-.Ic choose-session
-commands are wrappers around
-.Ic choose-tree .
-.Pp
-If
.Fl s
-is given, will show sessions.
-If
+starts with sessions collapsed and
.Fl w
-is given, will show windows.
-.Pp
-By default, the tree is collapsed and sessions must be expanded to windows
-with the right arrow key.
-The
-.Fl u
-option will start with all sessions expanded instead.
-.Pp
-If
-.Fl b
-is given, will override the default session command.
-Note that
-.Ql %%
-can be used and will be replaced with the session name.
-The default option if not specified is "switch-client -t '%%'".
-If
-.Fl c
-is given, will override the default window command.
-Like
-.Fl b ,
-.Ql %%
-can be used and will be replaced with the session name and window index.
-When a window is chosen from the list, the session command is run before the
-window command.
-.Pp
-.Fl S
-uses
-.Ar format
-instead of the default session
-format and
-.Fl W
-instead of the default window format.
-For the meaning of
-.Ar format ,
-see the
-.Sx FORMATS
-section.
+with windows collapsed.
+The following keys may be used in tree mode:
+.Bl -column "Key" "Function" -offset indent
+.It Sy "Key" Ta Sy "Function"
+.It Li "Enter" Ta "Choose selected item"
+.It Li "Up" Ta "Select previous item"
+.It Li "Down" Ta "Select next item"
+.It Li "t" Ta "Toggle if item is tagged"
+.It Li "T" Ta "Tag no items"
+.It Li "C-t" Ta "Tag all items"
+.It Li ":" Ta "Run a command for each tagged item"
+.It Li "f" Ta "Enter a format to filter items"
+.It Li "O" Ta "Change sort order"
+.It Li "q" Ta "Exit mode"
+.El
.Pp
-This command works only if at least one client is attached.
-.It Xo
-.Ic choose-window
-.Op Fl F Ar format
-.Op Fl t Ar target-window
-.Op Ar template
-.Xc
-Put a window into window choice mode, where a window may be chosen
-interactively from a list.
-After a window is selected,
+After a session, window or pane is chosen,
.Ql %%
-is replaced by the session name and window index in
+is replaced by the target in
.Ar template
and the result executed as a command.
If
.Ar template
-is not given, "select-window -t '%%'" is used.
-For the meaning of the
-.Fl F
-flag, see the
-.Sx FORMATS
-section.
+is not given, "switch-client -t '%%'" is used.
+.Pp
This command works only if at least one client is attached.
.It Xo
.Ic display-panes
@@ -1498,8 +1438,7 @@ The default
is "select-pane -t '%%'".
.It Xo Ic find-window
.Op Fl CNT
-.Op Fl F Ar format
-.Op Fl t Ar target-window
+.Op Fl t Ar target-pane
.Ar match-string
.Xc
.D1 (alias: Ic findw )
@@ -1517,13 +1456,7 @@ matches only the window name and
matches only the window title.
The default is
.Fl CNT .
-If only one window is matched, it'll be automatically selected,
-otherwise a choice list is shown.
-For the meaning of the
-.Fl F
-flag, see the
-.Sx FORMATS
-section.
+.Pp
This command works only if at least one client is attached.
.It Xo Ic join-pane
.Op Fl bdhv
@@ -3982,13 +3915,27 @@ The buffer commands are as follows:
.Bl -tag -width Ds
.It Xo
.Ic choose-buffer
-.Op Fl F Ar format
-.Op Fl t Ar target-window
+.Op Fl t Ar target-pane
.Op Ar template
.Xc
-Put a window into buffer choice mode, where a buffer may be chosen
-interactively from a list.
-After a buffer is selected,
+Put a pane into buffer mode, where a buffer may be chosen interactively from
+a list.
+The following keys may be used in buffer mode:
+.Bl -column "Key" "Function" -offset indent
+.It Sy "Key" Ta Sy "Function"
+.It Li "Enter" Ta "Choose selected buffer"
+.It Li "Up" Ta "Select previous buffer"
+.It Li "Down" Ta "Select next buffer"
+.It Li "t" Ta "Toggle if buffer is tagged"
+.It Li "T" Ta "Tag no buffers"
+.It Li "C-t" Ta "Tag all buffers"
+.It Li "d" Ta "Delete selected buffer"
+.It Li "D" Ta "Delete tagged buffers"
+.It Li "O" Ta "Change sort order"
+.It Li "q" Ta "Exit mode"
+.El
+.Pp
+After a buffer is chosen,
.Ql %%
is replaced by the buffer name in
.Ar template
@@ -3996,11 +3943,7 @@ and the result executed as a command.
If
.Ar template
is not given, "paste-buffer -b '%%'" is used.
-For the meaning of the
-.Fl F
-flag, see the
-.Sx FORMATS
-section.
+.Pp
This command works only if at least one client is attached.
.It Ic clear-history Op Fl t Ar target-pane
.D1 (alias: Ic clearhist )
diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h
index 11e0ff55054..a4c11b6a91e 100644
--- a/usr.bin/tmux/tmux.h
+++ b/usr.bin/tmux/tmux.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.774 2017/05/29 20:42:53 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.775 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -40,12 +40,13 @@ extern char **environ;
struct args;
struct client;
+struct cmd_find_state;
struct cmdq_item;
struct cmdq_list;
struct environ;
struct format_job_tree;
struct input_ctx;
-struct mode_key_cmdstr;
+struct mode_tree_data;
struct mouse_event;
struct options;
struct options_entry;
@@ -693,7 +694,8 @@ struct screen_write_ctx {
struct window_mode {
const char *name;
- struct screen *(*init)(struct window_pane *);
+ struct screen *(*init)(struct window_pane *, struct cmd_find_state *,
+ struct args *);
void (*free)(struct window_pane *);
void (*resize)(struct window_pane *, u_int, u_int);
void (*key)(struct window_pane *, struct client *,
@@ -1518,6 +1520,7 @@ char *paste_make_sample(struct paste_buffer *);
#define FORMAT_PANE 0x80000000U
#define FORMAT_WINDOW 0x40000000U
struct format_tree;
+int format_true(const char *);
struct format_tree *format_create(struct client *, struct cmdq_item *, int,
int);
void format_free(struct format_tree *);
@@ -1717,6 +1720,7 @@ void tty_keys_free(struct tty *);
key_code tty_keys_next(struct tty *);
/* arguments.c */
+void args_set(struct args *, u_char, const char *);
struct args *args_parse(const char *, int, char **);
void args_free(struct args *);
char *args_print(struct args *);
@@ -1997,6 +2001,10 @@ void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *,
u_char);
void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int,
u_int, u_int, u_int, bitstr_t *, const struct grid_cell *);
+void screen_write_line(struct screen_write_ctx *, u_int, int, int);
+void screen_write_box(struct screen_write_ctx *, u_int, u_int);
+void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int,
+ u_int);
void screen_write_backspace(struct screen_write_ctx *);
void screen_write_mode_set(struct screen_write_ctx *, int);
void screen_write_mode_clear(struct screen_write_ctx *, int);
@@ -2119,15 +2127,15 @@ void window_pane_unset_palette(struct window_pane *, u_int);
void window_pane_reset_palette(struct window_pane *);
int window_pane_get_palette(const struct window_pane *, int);
int window_pane_set_mode(struct window_pane *,
- const struct window_mode *);
+ const struct window_mode *, struct cmd_find_state *,
+ struct args *);
void window_pane_reset_mode(struct window_pane *);
void window_pane_key(struct window_pane *, struct client *,
struct session *, key_code, struct mouse_event *);
int window_pane_outside(struct window_pane *);
int window_pane_visible(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *);
-char *window_pane_search_old(struct window_pane *, const char *,
- u_int *);
+
const char *window_printable_flags(struct winlink *);
struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *);
@@ -2176,10 +2184,43 @@ u_int layout_set_select(struct window *, u_int);
u_int layout_set_next(struct window *);
u_int layout_set_previous(struct window *);
+/* mode-tree.c */
+u_int mode_tree_count_tagged(struct mode_tree_data *);
+void *mode_tree_get_current(struct mode_tree_data *);
+void mode_tree_each_tagged(struct mode_tree_data *, void (*)(void *, void *,
+ key_code), key_code, int);
+void mode_tree_up(struct mode_tree_data *, int);
+void mode_tree_down(struct mode_tree_data *, int);
+struct mode_tree_data *mode_tree_start(struct window_pane *,
+ void (*)(void *, u_int, uint64_t *), struct screen *(*)(void *,
+ void *, u_int, u_int), void *, const char **, u_int,
+ struct screen **);
+void mode_tree_build(struct mode_tree_data *);
+void mode_tree_free(struct mode_tree_data *);
+void mode_tree_resize(struct mode_tree_data *, u_int, u_int);
+struct mode_tree_item *mode_tree_add(struct mode_tree_data *,
+ struct mode_tree_item *, void *, uint64_t, const char *,
+ const char *, int);
+void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
+void mode_tree_draw(struct mode_tree_data *);
+int mode_tree_key(struct mode_tree_data *, key_code *,
+ struct mouse_event *);
+void mode_tree_run_command(struct client *, struct cmd_find_state *,
+ const char *, const char *);
+
+/* window-buffer.c */
+extern const struct window_mode window_buffer_mode;
+
+/* window-tree.c */
+extern const struct window_mode window_tree_mode;
+
/* window-clock.c */
extern const struct window_mode window_clock_mode;
extern const char window_clock_table[14][5][5];
+/* window-client.c */
+extern const struct window_mode window_client_mode;
+
/* window-copy.c */
extern const struct window_mode window_copy_mode;
void window_copy_init_from_pane(struct window_pane *, int);
@@ -2190,24 +2231,6 @@ void window_copy_pageup(struct window_pane *, int);
void window_copy_start_drag(struct client *, struct mouse_event *);
int window_copy_scroll_position(struct window_pane *);
-/* window-choose.c */
-extern const struct window_mode window_choose_mode;
-void window_choose_add(struct window_pane *,
- struct window_choose_data *);
-void window_choose_ready(struct window_pane *,
- u_int, void (*)(struct window_choose_data *));
-struct window_choose_data *window_choose_data_create (int,
- struct client *, struct session *);
-void window_choose_data_run(struct window_choose_data *);
-struct window_choose_data *window_choose_add_window(struct window_pane *,
- struct client *, struct session *, struct winlink *,
- const char *, const char *, u_int);
-struct window_choose_data *window_choose_add_session(struct window_pane *,
- struct client *, struct session *, const char *,
- const char *, u_int);
-void window_choose_expand_all(struct window_pane *);
-void window_choose_set_current(struct window_pane *, u_int);
-
/* names.c */
void check_window_name(struct window *);
char *default_window_name(struct window *);
diff --git a/usr.bin/tmux/window-buffer.c b/usr.bin/tmux/window-buffer.c
new file mode 100644
index 00000000000..1f534efc8e7
--- /dev/null
+++ b/usr.bin/tmux/window-buffer.c
@@ -0,0 +1,342 @@
+/* $OpenBSD: window-buffer.c,v 1.1 2017/05/30 21:44:59 nicm Exp $ */
+
+/*
+ * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@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 <vis.h>
+
+#include "tmux.h"
+
+static struct screen *window_buffer_init(struct window_pane *,
+ struct cmd_find_state *, struct args *);
+static void window_buffer_free(struct window_pane *);
+static void window_buffer_resize(struct window_pane *, u_int,
+ u_int);
+static void window_buffer_key(struct window_pane *,
+ struct client *, struct session *, key_code,
+ struct mouse_event *);
+
+#define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'"
+
+const struct window_mode window_buffer_mode = {
+ .name = "buffer-mode",
+
+ .init = window_buffer_init,
+ .free = window_buffer_free,
+ .resize = window_buffer_resize,
+ .key = window_buffer_key,
+};
+
+enum window_buffer_sort_type {
+ WINDOW_BUFFER_BY_NAME,
+ WINDOW_BUFFER_BY_TIME,
+ WINDOW_BUFFER_BY_SIZE,
+};
+static const char *window_buffer_sort_list[] = {
+ "name",
+ "time",
+ "size"
+};
+
+struct window_buffer_itemdata {
+ const char *name;
+ time_t created;
+ u_int order;
+ size_t size;
+};
+
+struct window_buffer_modedata {
+ struct mode_tree_data *data;
+ char *command;
+
+ struct window_buffer_itemdata **item_list;
+ u_int item_size;
+};
+
+static struct window_buffer_itemdata *
+window_buffer_add_item(struct window_buffer_modedata *data)
+{
+ struct window_buffer_itemdata *item;
+
+ data->item_list = xreallocarray(data->item_list, data->item_size + 1,
+ sizeof *data->item_list);
+ item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
+ return (item);
+}
+
+static void
+window_buffer_free_item(struct window_buffer_itemdata *item)
+{
+ free((void *)item->name);
+ free(item);
+}
+
+static int
+window_buffer_cmp_name(const void *a0, const void *b0)
+{
+ const struct window_buffer_itemdata *const *a = a0;
+ const struct window_buffer_itemdata *const *b = b0;
+
+ return (strcmp((*a)->name, (*b)->name));
+}
+
+static int
+window_buffer_cmp_time(const void *a0, const void *b0)
+{
+ const struct window_buffer_itemdata *const *a = a0;
+ const struct window_buffer_itemdata *const *b = b0;
+
+ if ((*a)->order > (*b)->order)
+ return (-1);
+ if ((*a)->order < (*b)->order)
+ return (1);
+ return (strcmp((*a)->name, (*b)->name));
+}
+
+static int
+window_buffer_cmp_size(const void *a0, const void *b0)
+{
+ const struct window_buffer_itemdata *const *a = a0;
+ const struct window_buffer_itemdata *const *b = b0;
+
+ if ((*a)->size > (*b)->size)
+ return (-1);
+ if ((*a)->size < (*b)->size)
+ return (1);
+ return (strcmp((*a)->name, (*b)->name));
+}
+
+static void
+window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag)
+{
+ struct window_buffer_modedata *data = modedata;
+ struct window_buffer_itemdata *item;
+ u_int i;
+ struct paste_buffer *pb;
+ char *tim;
+ char *text;
+
+ for (i = 0; i < data->item_size; i++)
+ window_buffer_free_item(data->item_list[i]);
+ free(data->item_list);
+ data->item_list = NULL;
+ data->item_size = 0;
+
+ pb = NULL;
+ while ((pb = paste_walk(pb)) != NULL) {
+ item = window_buffer_add_item(data);
+ item->name = xstrdup(paste_buffer_name(pb));
+ item->created = paste_buffer_created(pb);
+ paste_buffer_data(pb, &item->size);
+ item->order = paste_buffer_order(pb);
+ }
+
+ switch (sort_type) {
+ case WINDOW_BUFFER_BY_NAME:
+ qsort(data->item_list, data->item_size, sizeof *data->item_list,
+ window_buffer_cmp_name);
+ break;
+ case WINDOW_BUFFER_BY_TIME:
+ qsort(data->item_list, data->item_size, sizeof *data->item_list,
+ window_buffer_cmp_time);
+ break;
+ case WINDOW_BUFFER_BY_SIZE:
+ qsort(data->item_list, data->item_size, sizeof *data->item_list,
+ window_buffer_cmp_size);
+ break;
+ }
+
+ for (i = 0; i < data->item_size; i++) {
+ item = data->item_list[i];
+
+ tim = ctime(&item->created);
+ *strchr(tim, '\n') = '\0';
+
+ xasprintf(&text, "%zu bytes (%s)", item->size, tim);
+ mode_tree_add(data->data, NULL, item, item->order, item->name,
+ text, -1);
+ free(text);
+ }
+
+}
+
+static struct screen *
+window_buffer_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
+{
+ struct window_buffer_itemdata *item = itemdata;
+ struct paste_buffer *pb;
+ static struct screen s;
+ struct screen_write_ctx ctx;
+ char line[1024];
+ const char *pdata, *end, *cp;
+ size_t psize, at;
+ u_int i;
+
+ pb = paste_get_name(item->name);
+ if (pb == NULL)
+ return (NULL);
+
+ screen_init(&s, sx, sy, 0);
+
+ screen_write_start(&ctx, NULL, &s);
+ screen_write_clearscreen(&ctx, 8);
+
+ pdata = end = paste_buffer_data (pb, &psize);
+ for (i = 0; i < sy; i++) {
+ at = 0;
+ while (end != pdata + psize && *end != '\n') {
+ if ((sizeof line) - at > 5) {
+ cp = vis(line + at, *end, VIS_TAB|VIS_OCTAL, 0);
+ at = cp - line;
+ }
+ end++;
+ }
+ if (at > sx)
+ at = sx;
+ line[at] = '\0';
+
+ if (*line != '\0') {
+ screen_write_cursormove(&ctx, 0, i);
+ screen_write_puts(&ctx, &grid_default_cell, "%s", line);
+ }
+
+ if (end == pdata + psize)
+ break;
+ end++;
+ }
+
+ screen_write_stop(&ctx);
+ return (&s);
+}
+
+static struct screen *
+window_buffer_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
+ struct args *args)
+{
+ struct window_buffer_modedata *data;
+ struct screen *s;
+
+ wp->modedata = data = xcalloc(1, sizeof *data);
+
+ if (args == NULL || args->argc == 0)
+ data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
+ else
+ data->command = xstrdup(args->argv[0]);
+
+ data->data = mode_tree_start(wp, window_buffer_build,
+ window_buffer_draw, data, window_buffer_sort_list,
+ nitems(window_buffer_sort_list), &s);
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+
+ return (s);
+}
+
+static void
+window_buffer_free(struct window_pane *wp)
+{
+ struct window_buffer_modedata *data = wp->modedata;
+ u_int i;
+
+ if (data == NULL)
+ return;
+
+ mode_tree_free(data->data);
+
+ for (i = 0; i < data->item_size; i++)
+ window_buffer_free_item(data->item_list[i]);
+ free(data->item_list);
+
+ free(data->command);
+ free(data);
+}
+
+static void
+window_buffer_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+ struct window_buffer_modedata *data = wp->modedata;
+
+ mode_tree_resize(data->data, sx, sy);
+}
+
+static void
+window_buffer_do_delete(void* modedata, void *itemdata, __unused key_code key)
+{
+ struct window_buffer_modedata *data = modedata;
+ struct window_buffer_itemdata *item = itemdata;
+ struct paste_buffer *pb;
+
+ if (item == mode_tree_get_current(data->data))
+ mode_tree_down(data->data, 0);
+ if ((pb = paste_get_name(item->name)) != NULL)
+ paste_free(pb);
+}
+
+static void
+window_buffer_key(struct window_pane *wp, struct client *c,
+ __unused struct session *s, key_code key, struct mouse_event *m)
+{
+ struct window_buffer_modedata *data = wp->modedata;
+ struct window_buffer_itemdata *item;
+ char *command, *name;
+ int finished;
+
+ /*
+ * t = toggle tag
+ * T = tag none
+ * C-t = tag all
+ * q = exit
+ * O = change sort order
+ *
+ * d = delete buffer
+ * D = delete tagged buffers
+ * Enter = paste buffer
+ */
+
+ finished = mode_tree_key(data->data, &key, m);
+ switch (key) {
+ case 'd':
+ item = mode_tree_get_current(data->data);
+ window_buffer_do_delete(data, item, key);
+ mode_tree_build(data->data);
+ break;
+ case 'D':
+ mode_tree_each_tagged(data->data, window_buffer_do_delete, key,
+ 0);
+ mode_tree_build(data->data);
+ break;
+ case '\r':
+ item = mode_tree_get_current(data->data);
+ command = xstrdup(data->command);
+ name = xstrdup(item->name);
+ window_pane_reset_mode(wp);
+ mode_tree_run_command(c, NULL, command, name);
+ free(name);
+ free(command);
+ return;
+ }
+ if (finished || paste_get_top(NULL) == NULL)
+ window_pane_reset_mode(wp);
+ else {
+ mode_tree_draw(data->data);
+ wp->flags |= PANE_REDRAW;
+ }
+}
diff --git a/usr.bin/tmux/window-choose.c b/usr.bin/tmux/window-choose.c
deleted file mode 100644
index 09aebf8e9a4..00000000000
--- a/usr.bin/tmux/window-choose.c
+++ /dev/null
@@ -1,1078 +0,0 @@
-/* $OpenBSD: window-choose.c,v 1.91 2017/05/07 22:27:57 nicm Exp $ */
-
-/*
- * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@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 <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "tmux.h"
-
-static struct screen *window_choose_init(struct window_pane *);
-static void window_choose_free(struct window_pane *);
-static void window_choose_resize(struct window_pane *, u_int, u_int);
-static void window_choose_key(struct window_pane *, struct client *,
- struct session *, key_code, struct mouse_event *);
-
-static void window_choose_default_callback(struct window_choose_data *);
-static struct window_choose_mode_item *window_choose_get_item(
- struct window_pane *, key_code, struct mouse_event *);
-
-static void window_choose_fire_callback(struct window_pane *,
- struct window_choose_data *);
-static void window_choose_redraw_screen(struct window_pane *);
-static void window_choose_write_line(struct window_pane *,
- struct screen_write_ctx *, u_int);
-
-static void window_choose_scroll_up(struct window_pane *);
-static void window_choose_scroll_down(struct window_pane *);
-
-static void window_choose_collapse(struct window_pane *, struct session *,
- u_int);
-static void window_choose_expand(struct window_pane *, struct session *,
- u_int);
-static void window_choose_collapse_all(struct window_pane *);
-
-static void window_choose_data_free(struct window_choose_data *);
-
-enum window_choose_input_type {
- WINDOW_CHOOSE_NORMAL = -1,
- WINDOW_CHOOSE_GOTO_ITEM,
-};
-
-const struct window_mode window_choose_mode = {
- .name = "choose-mode",
-
- .init = window_choose_init,
- .free = window_choose_free,
- .resize = window_choose_resize,
- .key = window_choose_key,
-};
-
-struct window_choose_mode_item {
- struct window_choose_data *wcd;
- char *name;
- int pos;
- int state;
-#define TREE_EXPANDED 0x1
-};
-
-struct window_choose_mode_data {
- struct screen screen;
-
- struct window_choose_mode_item *list;
- u_int list_size;
- struct window_choose_mode_item *old_list;
- u_int old_list_size;
-
- int width;
- u_int top;
- u_int selected;
- enum window_choose_input_type input_type;
- const char *input_prompt;
- char *input_str;
-
- void (*callbackfn)(struct window_choose_data *);
-};
-
-static const char window_choose_keys_emacs[] = "0123456789"
- "abcdefghijklmnoprstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-static const char window_choose_keys_vi[] = "0123456789"
- "abcdefimnoprstuvwxyz"
- "ABCDEFIJKMNOPQRSTUVWXYZ";
-
-static void window_choose_free1(struct window_choose_mode_data *);
-static int window_choose_key_index(struct window_pane *, u_int);
-static int window_choose_index_key(struct window_pane *, key_code);
-static void window_choose_prompt_input(enum window_choose_input_type,
- const char *, struct window_pane *, key_code);
-static void window_choose_reset_top(struct window_pane *, u_int);
-
-void
-window_choose_add(struct window_pane *wp, struct window_choose_data *wcd)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct window_choose_mode_item *item;
- char tmp[11];
-
- data->list = xreallocarray(data->list, data->list_size + 1,
- sizeof *data->list);
- item = &data->list[data->list_size++];
-
- item->name = format_expand(wcd->ft, wcd->ft_template);
- item->wcd = wcd;
- item->pos = data->list_size - 1;
- item->state = 0;
-
- data->width = xsnprintf(tmp, sizeof tmp, "%d", item->pos);
-}
-
-void
-window_choose_set_current(struct window_pane *wp, u_int cur)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct screen *s = &data->screen;
-
- data->selected = cur;
- window_choose_reset_top(wp, screen_size_y(s));
-}
-
-static void
-window_choose_reset_top(struct window_pane *wp, u_int sy)
-{
- struct window_choose_mode_data *data = wp->modedata;
-
- data->top = 0;
- if (data->selected > sy - 1)
- data->top = data->selected - (sy - 1);
-
- window_choose_redraw_screen(wp);
-}
-
-void
-window_choose_ready(struct window_pane *wp, u_int cur,
- void (*callbackfn)(struct window_choose_data *))
-{
- struct window_choose_mode_data *data = wp->modedata;
- u_int size;
-
- data->callbackfn = callbackfn;
- if (data->callbackfn == NULL)
- data->callbackfn = window_choose_default_callback;
-
- size = data->old_list_size;
- data->old_list_size += data->list_size;
- data->old_list = xreallocarray(data->old_list, data->old_list_size,
- sizeof *data->old_list);
- memcpy(data->old_list + size, data->list, data->list_size *
- sizeof *data->list);
-
- window_choose_set_current(wp, cur);
- window_choose_collapse_all(wp);
-}
-
-static struct screen *
-window_choose_init(struct window_pane *wp)
-{
- struct window_choose_mode_data *data;
- struct screen *s;
-
- wp->modedata = data = xcalloc(1, sizeof *data);
-
- data->callbackfn = NULL;
- data->input_type = WINDOW_CHOOSE_NORMAL;
- data->input_str = xstrdup("");
- data->input_prompt = NULL;
-
- data->list = NULL;
- data->list_size = 0;
-
- data->old_list = NULL;
- data->old_list_size = 0;
-
- data->top = 0;
-
- s = &data->screen;
- screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
- s->mode &= ~MODE_CURSOR;
-
- return (s);
-}
-
-struct window_choose_data *
-window_choose_data_create(int type, struct client *c, struct session *s)
-{
- struct window_choose_data *wcd;
-
- wcd = xmalloc(sizeof *wcd);
- wcd->type = type;
-
- wcd->ft = format_create(c, NULL, FORMAT_NONE, 0);
- wcd->ft_template = NULL;
-
- wcd->command = NULL;
-
- wcd->wl = NULL;
- wcd->pane_id = -1;
- wcd->idx = -1;
-
- wcd->tree_session = NULL;
-
- wcd->start_client = c;
- wcd->start_client->references++;
- wcd->start_session = s;
- wcd->start_session->references++;
-
- return (wcd);
-}
-
-static void
-window_choose_data_free(struct window_choose_data *wcd)
-{
- server_client_unref(wcd->start_client);
- session_remove_ref(wcd->start_session, __func__);
-
- if (wcd->tree_session != NULL)
- session_remove_ref(wcd->tree_session, __func__);
-
- free(wcd->ft_template);
- format_free(wcd->ft);
-
- free(wcd->command);
- free(wcd);
-}
-
-void
-window_choose_data_run(struct window_choose_data *cdata)
-{
- struct cmd_list *cmdlist;
- char *cause;
- struct cmdq_item *item;
-
- /*
- * The command template will have already been replaced. But if it's
- * NULL, bail here.
- */
- if (cdata->command == NULL)
- return;
-
- cmdlist = cmd_string_parse(cdata->command, NULL, 0, &cause);
- if (cmdlist == NULL) {
- if (cause != NULL) {
- *cause = toupper((u_char) *cause);
- status_message_set(cdata->start_client, "%s", cause);
- free(cause);
- }
- return;
- }
-
- item = cmdq_get_command(cmdlist, NULL, NULL, 0);
- cmdq_append(cdata->start_client, item);
- cmd_list_free(cmdlist);
-}
-
-static void
-window_choose_default_callback(struct window_choose_data *wcd)
-{
- if (wcd == NULL)
- return;
- if (wcd->start_client->flags & CLIENT_DEAD)
- return;
-
- window_choose_data_run(wcd);
-}
-
-static void
-window_choose_free(struct window_pane *wp)
-{
- if (wp->modedata != NULL)
- window_choose_free1(wp->modedata);
-}
-
-static void
-window_choose_free1(struct window_choose_mode_data *data)
-{
- struct window_choose_mode_item *item;
- u_int i;
-
- if (data == NULL)
- return;
-
- for (i = 0; i < data->old_list_size; i++) {
- item = &data->old_list[i];
- window_choose_data_free(item->wcd);
- free(item->name);
- }
- free(data->list);
- free(data->old_list);
-
- free(data->input_str);
-
- screen_free(&data->screen);
- free(data);
-}
-
-static void
-window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct screen *s = &data->screen;
-
- window_choose_reset_top(wp, sy);
- screen_resize(s, sx, sy, 0);
- window_choose_redraw_screen(wp);
-}
-
-static void
-window_choose_fire_callback(struct window_pane *wp,
- struct window_choose_data *wcd)
-{
- struct window_choose_mode_data *data = wp->modedata;
-
- wp->modedata = NULL;
- window_pane_reset_mode(wp);
-
- data->callbackfn(wcd);
-
- window_choose_free1(data);
-}
-
-static void
-window_choose_prompt_input(enum window_choose_input_type input_type,
- const char *prompt, struct window_pane *wp, key_code key)
-{
- struct window_choose_mode_data *data = wp->modedata;
- size_t input_len;
-
- data->input_type = input_type;
- data->input_prompt = prompt;
- input_len = strlen(data->input_str) + 2;
-
- data->input_str = xrealloc(data->input_str, input_len);
- data->input_str[input_len - 2] = key;
- data->input_str[input_len - 1] = '\0';
-
- window_choose_redraw_screen(wp);
-}
-
-static void
-window_choose_collapse(struct window_pane *wp, struct session *s, u_int pos)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct window_choose_mode_item *item, *chosen, *copy = NULL;
- struct window_choose_data *wcd;
- u_int i, copy_size = 0;
-
- chosen = &data->list[pos];
- chosen->state &= ~TREE_EXPANDED;
-
- /*
- * Trying to mangle the &data->list in-place has lots of problems, so
- * assign the actual result we want to render and copy the new one over
- * the top of it.
- */
- for (i = 0; i < data->list_size; i++) {
- item = &data->list[i];
- wcd = item->wcd;
-
- if (s == wcd->tree_session) {
- /* We only show the session when collapsed. */
- if (wcd->type & TREE_SESSION) {
- item->state &= ~TREE_EXPANDED;
-
- copy = xreallocarray(copy, copy_size + 1,
- sizeof *copy);
- memcpy(&copy[copy_size], item, sizeof *copy);
- copy_size++;
-
- /*
- * Update the selection to this session item so
- * we don't end up highlighting a non-existent
- * item.
- */
- data->selected = i;
- }
- } else {
- copy = xreallocarray(copy, copy_size + 1, sizeof *copy);
- memcpy(&copy[copy_size], item, sizeof *copy);
- copy_size++;
- }
- }
-
- if (copy_size != 0) {
- free(data->list);
- data->list = copy;
- data->list_size = copy_size;
- }
-}
-
-static void
-window_choose_collapse_all(struct window_pane *wp)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct window_choose_mode_item *item;
- struct screen *scr = &data->screen;
- struct session *s, *chosen;
- u_int i;
-
- chosen = data->list[data->selected].wcd->start_session;
-
- RB_FOREACH(s, sessions, &sessions)
- window_choose_collapse(wp, s, data->selected);
-
- /* Reset the selection back to the starting session. */
- for (i = 0; i < data->list_size; i++) {
- item = &data->list[i];
-
- if (chosen != item->wcd->tree_session)
- continue;
-
- if (item->wcd->type & TREE_SESSION)
- data->selected = i;
- }
- window_choose_reset_top(wp, screen_size_y(scr));
-}
-
-void
-window_choose_expand_all(struct window_pane *wp)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct window_choose_mode_item *item;
- struct screen *scr = &data->screen;
- struct session *s;
- u_int i;
-
- RB_FOREACH(s, sessions, &sessions) {
- for (i = 0; i < data->list_size; i++) {
- item = &data->list[i];
-
- if (s != item->wcd->tree_session)
- continue;
-
- if (item->wcd->type & TREE_SESSION)
- window_choose_expand(wp, s, i);
- }
- }
-
- window_choose_reset_top(wp, screen_size_y(scr));
-}
-
-static void
-window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct window_choose_mode_item *item, *chosen;
- struct window_choose_data *wcd;
- u_int i, items;
-
- chosen = &data->list[pos];
- items = data->old_list_size - 1;
-
- /* It's not possible to expand anything other than sessions. */
- if (!(chosen->wcd->type & TREE_SESSION))
- return;
-
- /* Don't re-expand a session which is already expanded. */
- if (chosen->state & TREE_EXPANDED)
- return;
-
- /* Mark the session entry as expanded. */
- chosen->state |= TREE_EXPANDED;
-
- /*
- * Go back through the original list of all sessions and windows, and
- * pull out the windows where the session matches the selection chosen
- * to expand.
- */
- for (i = items; i > 0; i--) {
- item = &data->old_list[i];
- item->state |= TREE_EXPANDED;
- wcd = item->wcd;
-
- if (s == wcd->tree_session) {
- /*
- * Since the session is already displayed, we only care
- * to add back in window for it.
- */
- if (wcd->type & TREE_WINDOW) {
- /*
- * If the insertion point for adding the
- * windows to the session falls inside the
- * range of the list, then we insert these
- * entries in order *AFTER* the selected
- * session.
- */
- if (pos < i) {
- data->list = xreallocarray(data->list,
- data->list_size + 1,
- sizeof *data->list);
- memmove(&data->list[pos + 2],
- &data->list[pos + 1],
- (data->list_size - (pos + 1)) *
- sizeof *data->list);
- memcpy(&data->list[pos + 1],
- &data->old_list[i],
- sizeof *data->list);
- data->list_size++;
- } else {
- /* Ran out of room, add to the end. */
- data->list = xreallocarray(data->list,
- data->list_size + 1,
- sizeof *data->list);
- memcpy(&data->list[data->list_size],
- &data->old_list[i],
- sizeof *data->list);
- data->list_size++;
- }
- }
- }
- }
-}
-
-static struct window_choose_mode_item *
-window_choose_get_item(struct window_pane *wp, key_code key,
- struct mouse_event *m)
-{
- struct window_choose_mode_data *data = wp->modedata;
- u_int x, y, idx;
-
- if (!KEYC_IS_MOUSE(key))
- return (&data->list[data->selected]);
-
- if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
- return (NULL);
-
- idx = data->top + y;
- if (idx >= data->list_size)
- return (NULL);
- return (&data->list[idx]);
-}
-
-static key_code
-window_choose_translate_key(key_code key)
-{
- switch (key) {
- case '0'|KEYC_ESCAPE:
- case '1'|KEYC_ESCAPE:
- case '2'|KEYC_ESCAPE:
- case '3'|KEYC_ESCAPE:
- case '4'|KEYC_ESCAPE:
- case '5'|KEYC_ESCAPE:
- case '6'|KEYC_ESCAPE:
- case '7'|KEYC_ESCAPE:
- case '8'|KEYC_ESCAPE:
- case '9'|KEYC_ESCAPE:
- case '\003': /* C-c */
- case 'q':
- case '\n':
- case '\r':
- case KEYC_BSPACE:
- case ' ':
- case KEYC_LEFT|KEYC_CTRL:
- case KEYC_RIGHT|KEYC_CTRL:
- case KEYC_MOUSEDOWN1_PANE:
- case KEYC_MOUSEDOWN3_PANE:
- case KEYC_WHEELUP_PANE:
- case KEYC_WHEELDOWN_PANE:
- return (key);
- case '\031': /* C-y */
- case KEYC_UP|KEYC_CTRL:
- return (KEYC_UP|KEYC_CTRL);
- case '\002': /* C-b */
- case KEYC_PPAGE:
- return (KEYC_PPAGE);
- case '\005': /* C-e */
- case KEYC_DOWN|KEYC_CTRL:
- return (KEYC_DOWN|KEYC_CTRL);
- case '\006': /* C-f */
- case KEYC_NPAGE:
- return (KEYC_NPAGE);
- case 'h':
- case KEYC_LEFT:
- return (KEYC_LEFT);
- case 'j':
- case KEYC_DOWN:
- return (KEYC_DOWN);
- case 'k':
- case KEYC_UP:
- return (KEYC_UP);
- case 'l':
- case KEYC_RIGHT:
- return (KEYC_RIGHT);
- case 'g':
- case KEYC_HOME:
- return (KEYC_HOME);
- case 'G':
- case KEYC_END:
- return (KEYC_END);
- case 'H':
- return ('R'|KEYC_ESCAPE);
- case 'L':
- return ('r'|KEYC_ESCAPE);
- }
- if ((key >= '0' && key <= '9') ||
- (key >= 'a' && key <= 'z') ||
- (key >= 'A' && key <= 'Z'))
- return (key);
- return (KEYC_NONE);
-}
-
-static void
-window_choose_key(struct window_pane *wp, __unused struct client *c,
- __unused struct session *sp, key_code key, struct mouse_event *m)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct screen *s = &data->screen;
- struct screen_write_ctx ctx;
- struct window_choose_mode_item *item;
- size_t input_len;
- u_int items, n;
- int idx, keys;
-
- keys = options_get_number(wp->window->options, "mode-keys");
- if (keys == MODEKEY_VI) {
- key = window_choose_translate_key(key);
- if (key == KEYC_NONE)
- return;
- }
- items = data->list_size;
-
- if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) {
- switch (key) {
- case '\003': /* C-c */
- case '\033': /* Escape */
- case 'q':
- data->input_type = WINDOW_CHOOSE_NORMAL;
- window_choose_redraw_screen(wp);
- break;
- case '\n':
- case '\r':
- n = strtonum(data->input_str, 0, INT_MAX, NULL);
- if (n > items - 1) {
- data->input_type = WINDOW_CHOOSE_NORMAL;
- window_choose_redraw_screen(wp);
- break;
- }
- window_choose_fire_callback(wp, data->list[n].wcd);
- break;
- case KEYC_BSPACE:
- input_len = strlen(data->input_str);
- if (input_len > 0)
- data->input_str[input_len - 1] = '\0';
- window_choose_redraw_screen(wp);
- break;
- default:
- if (key < '0' || key > '9')
- break;
- window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
- "Goto Item", wp, key);
- break;
- }
- return;
- }
-
- switch (key) {
- case '\003': /* C-c */
- case '\033': /* Escape */
- case 'q':
- window_choose_fire_callback(wp, NULL);
- break;
- case '\n':
- case '\r':
- case KEYC_MOUSEDOWN1_PANE:
- if ((item = window_choose_get_item(wp, key, m)) == NULL)
- break;
- window_choose_fire_callback(wp, item->wcd);
- break;
- case ' ':
- case KEYC_MOUSEDOWN3_PANE:
- if ((item = window_choose_get_item(wp, key, m)) == NULL)
- break;
- if (item->state & TREE_EXPANDED) {
- window_choose_collapse(wp, item->wcd->tree_session,
- data->selected);
- } else {
- window_choose_expand(wp, item->wcd->tree_session,
- data->selected);
- }
- window_choose_redraw_screen(wp);
- break;
- case KEYC_LEFT:
- if ((item = window_choose_get_item(wp, key, m)) == NULL)
- break;
- if (item->state & TREE_EXPANDED) {
- window_choose_collapse(wp, item->wcd->tree_session,
- data->selected);
- window_choose_redraw_screen(wp);
- }
- break;
- case KEYC_LEFT|KEYC_CTRL:
- window_choose_collapse_all(wp);
- break;
- case KEYC_RIGHT:
- if ((item = window_choose_get_item(wp, key, m)) == NULL)
- break;
- if (!(item->state & TREE_EXPANDED)) {
- window_choose_expand(wp, item->wcd->tree_session,
- data->selected);
- window_choose_redraw_screen(wp);
- }
- break;
- case KEYC_RIGHT|KEYC_CTRL:
- window_choose_expand_all(wp);
- break;
- case '\020': /* C-p */
- case KEYC_UP:
- case KEYC_WHEELUP_PANE:
- if (items == 0)
- break;
- if (data->selected == 0) {
- data->selected = items - 1;
- if (data->selected > screen_size_y(s) - 1)
- data->top = items - screen_size_y(s);
- window_choose_redraw_screen(wp);
- break;
- }
- data->selected--;
- if (data->selected < data->top)
- window_choose_scroll_up(wp);
- else {
- screen_write_start(&ctx, wp, NULL);
- window_choose_write_line(wp, &ctx,
- data->selected - data->top);
- window_choose_write_line(wp, &ctx,
- data->selected + 1 - data->top);
- screen_write_stop(&ctx);
- }
- break;
- case '\016': /* C-n */
- case KEYC_DOWN:
- case KEYC_WHEELDOWN_PANE:
- if (items == 0)
- break;
- if (data->selected == items - 1) {
- data->selected = 0;
- data->top = 0;
- window_choose_redraw_screen(wp);
- break;
- }
- data->selected++;
-
- if (data->selected < data->top + screen_size_y(s)) {
- screen_write_start(&ctx, wp, NULL);
- window_choose_write_line(wp, &ctx,
- data->selected - data->top);
- window_choose_write_line(wp, &ctx,
- data->selected - 1 - data->top);
- screen_write_stop(&ctx);
- } else
- window_choose_scroll_down(wp);
- break;
- case KEYC_UP|KEYC_CTRL:
- if (items == 0 || data->top == 0)
- break;
- if (data->selected == data->top + screen_size_y(s) - 1) {
- data->selected--;
- window_choose_scroll_up(wp);
- screen_write_start(&ctx, wp, NULL);
- window_choose_write_line(wp, &ctx,
- screen_size_y(s) - 1);
- screen_write_stop(&ctx);
- } else
- window_choose_scroll_up(wp);
- break;
- case KEYC_DOWN|KEYC_CTRL:
- if (items == 0 ||
- data->top + screen_size_y(&data->screen) >= items)
- break;
- if (data->selected == data->top) {
- data->selected++;
- window_choose_scroll_down(wp);
- screen_write_start(&ctx, wp, NULL);
- window_choose_write_line(wp, &ctx, 0);
- screen_write_stop(&ctx);
- } else
- window_choose_scroll_down(wp);
- break;
- case KEYC_PPAGE:
- if (data->selected < screen_size_y(s)) {
- data->selected = 0;
- data->top = 0;
- } else {
- data->selected -= screen_size_y(s);
- if (data->top < screen_size_y(s))
- data->top = 0;
- else
- data->top -= screen_size_y(s);
- }
- window_choose_redraw_screen(wp);
- break;
- case KEYC_NPAGE:
- data->selected += screen_size_y(s);
- if (data->selected > items - 1)
- data->selected = items - 1;
- data->top += screen_size_y(s);
- if (screen_size_y(s) < items) {
- if (data->top + screen_size_y(s) > items)
- data->top = items - screen_size_y(s);
- } else
- data->top = 0;
- if (data->selected < data->top)
- data->top = data->selected;
- window_choose_redraw_screen(wp);
- break;
- case KEYC_BSPACE:
- input_len = strlen(data->input_str);
- if (input_len > 0)
- data->input_str[input_len - 1] = '\0';
- window_choose_redraw_screen(wp);
- break;
- case '0'|KEYC_ESCAPE:
- case '1'|KEYC_ESCAPE:
- case '2'|KEYC_ESCAPE:
- case '3'|KEYC_ESCAPE:
- case '4'|KEYC_ESCAPE:
- case '5'|KEYC_ESCAPE:
- case '6'|KEYC_ESCAPE:
- case '7'|KEYC_ESCAPE:
- case '8'|KEYC_ESCAPE:
- case '9'|KEYC_ESCAPE:
- key &= KEYC_MASK_KEY;
- if (key < '0' || key > '9')
- break;
- window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM,
- "Goto Item", wp, key);
- break;
- case KEYC_HOME:
- case '<'|KEYC_ESCAPE:
- data->selected = 0;
- data->top = 0;
- window_choose_redraw_screen(wp);
- break;
- case 'R'|KEYC_ESCAPE:
- data->selected = data->top;
- window_choose_redraw_screen(wp);
- break;
- case 'r'|KEYC_ESCAPE:
- data->selected = data->top + screen_size_y(s) - 1;
- if (data->selected > items - 1)
- data->selected = items - 1;
- window_choose_redraw_screen(wp);
- break;
- case KEYC_END:
- case '>'|KEYC_ESCAPE:
- data->selected = items - 1;
- if (screen_size_y(s) < items)
- data->top = items - screen_size_y(s);
- else
- data->top = 0;
- window_choose_redraw_screen(wp);
- break;
- default:
- idx = window_choose_index_key(wp, key);
- if (idx < 0 || (u_int) idx >= data->list_size)
- break;
- data->selected = idx;
- window_choose_fire_callback(wp, data->list[idx].wcd);
- break;
- }
-}
-
-static void
-window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
- u_int py)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct window_choose_mode_item *item;
- struct options *oo = wp->window->options;
- struct screen *s = &data->screen;
- struct grid_cell gc;
- size_t last, xoff = 0;
- char hdr[32], label[32];
- int key;
-
- if (data->callbackfn == NULL)
- fatalx("called before callback assigned");
-
- last = screen_size_y(s) - 1;
- memcpy(&gc, &grid_default_cell, sizeof gc);
- gc.flags |= GRID_FLAG_NOPALETTE;
- if (data->selected == data->top + py)
- style_apply(&gc, oo, "mode-style");
-
- screen_write_cursormove(ctx, 0, py);
- if (data->top + py < data->list_size) {
- item = &data->list[data->top + py];
- if (item->wcd->wl != NULL &&
- item->wcd->wl->flags & WINLINK_ALERTFLAGS)
- gc.attr |= GRID_ATTR_BRIGHT;
-
- key = window_choose_key_index(wp, data->top + py);
- if (key != -1)
- xsnprintf(label, sizeof label, "(%c)", key);
- else
- xsnprintf(label, sizeof label, "(%d)", item->pos);
- screen_write_nputs(ctx, screen_size_x(s) - 1, &gc,
- "%*s %s %s", data->width + 2, label,
- /*
- * Add indication to tree if necessary about whether it's
- * expanded or not.
- */
- (item->wcd->type & TREE_SESSION) ?
- ((item->state & TREE_EXPANDED) ? "-" : "+") : "", item->name);
- }
- while (s->cx < screen_size_x(s) - 1)
- screen_write_putc(ctx, &gc, ' ');
-
- if (data->input_type != WINDOW_CHOOSE_NORMAL) {
- style_apply(&gc, oo, "mode-style");
-
- xoff = xsnprintf(hdr, sizeof hdr,
- "%s: %s", data->input_prompt, data->input_str);
- screen_write_cursormove(ctx, 0, last);
- screen_write_puts(ctx, &gc, "%s", hdr);
- screen_write_cursormove(ctx, xoff, py);
- memcpy(&gc, &grid_default_cell, sizeof gc);
- }
-
-}
-
-static int
-window_choose_key_index(struct window_pane *wp, u_int idx)
-{
- const char *ptr;
- int keys;
-
- keys = options_get_number(wp->window->options, "mode-keys");
- if (keys == MODEKEY_VI)
- ptr = window_choose_keys_vi;
- else
- ptr = window_choose_keys_emacs;
- for (; *ptr != '\0'; ptr++) {
- if (idx-- == 0)
- return (*ptr);
- }
- return (-1);
-}
-
-static int
-window_choose_index_key(struct window_pane *wp, key_code key)
-{
- const char *ptr;
- int keys;
- u_int idx = 0;
-
- keys = options_get_number(wp->window->options, "mode-keys");
- if (keys == MODEKEY_VI)
- ptr = window_choose_keys_vi;
- else
- ptr = window_choose_keys_emacs;
- for (; *ptr != '\0'; ptr++) {
- if (key == (key_code)*ptr)
- return (idx);
- idx++;
- }
- return (-1);
-}
-
-static void
-window_choose_redraw_screen(struct window_pane *wp)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct screen *s = &data->screen;
- struct screen_write_ctx ctx;
- u_int i;
-
- screen_write_start(&ctx, wp, NULL);
- for (i = 0; i < screen_size_y(s); i++)
- window_choose_write_line(wp, &ctx, i);
- screen_write_stop(&ctx);
-}
-
-static void
-window_choose_scroll_up(struct window_pane *wp)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct screen_write_ctx ctx;
-
- if (data->top == 0)
- return;
- data->top--;
-
- screen_write_start(&ctx, wp, NULL);
- screen_write_cursormove(&ctx, 0, 0);
- screen_write_insertline(&ctx, 1, 8);
- window_choose_write_line(wp, &ctx, 0);
- if (screen_size_y(&data->screen) > 1)
- window_choose_write_line(wp, &ctx, 1);
- screen_write_stop(&ctx);
-}
-
-static void
-window_choose_scroll_down(struct window_pane *wp)
-{
- struct window_choose_mode_data *data = wp->modedata;
- struct screen *s = &data->screen;
- struct screen_write_ctx ctx;
-
- if (data->top >= data->list_size)
- return;
- data->top++;
-
- screen_write_start(&ctx, wp, NULL);
- screen_write_cursormove(&ctx, 0, 0);
- screen_write_deleteline(&ctx, 1, 8);
- window_choose_write_line(wp, &ctx, screen_size_y(s) - 1);
- if (screen_size_y(&data->screen) > 1)
- window_choose_write_line(wp, &ctx, screen_size_y(s) - 2);
- screen_write_stop(&ctx);
-}
-
-struct window_choose_data *
-window_choose_add_session(struct window_pane *wp, struct client *c,
- struct session *s, const char *template, const char *action, u_int idx)
-{
- struct window_choose_data *wcd;
-
- wcd = window_choose_data_create(TREE_SESSION, c, c->session);
- wcd->idx = s->id;
-
- wcd->tree_session = s;
- wcd->tree_session->references++;
-
- wcd->ft_template = xstrdup(template);
- format_add(wcd->ft, "line", "%u", idx);
- format_defaults(wcd->ft, NULL, s, NULL, NULL);
-
- wcd->command = cmd_template_replace(action, s->name, 1);
-
- window_choose_add(wp, wcd);
-
- return (wcd);
-}
-
-struct window_choose_data *
-window_choose_add_window(struct window_pane *wp, struct client *c,
- struct session *s, struct winlink *wl, const char *template,
- const char *action, u_int idx)
-{
- struct window_choose_data *wcd;
- char *expanded;
-
- wcd = window_choose_data_create(TREE_WINDOW, c, c->session);
- wcd->idx = wl->idx;
-
- wcd->wl = wl;
-
- wcd->tree_session = s;
- wcd->tree_session->references++;
-
- wcd->ft_template = xstrdup(template);
- format_add(wcd->ft, "line", "%u", idx);
- format_defaults(wcd->ft, NULL, s, wl, NULL);
-
- xasprintf(&expanded, "%s:%d", s->name, wl->idx);
- wcd->command = cmd_template_replace(action, expanded, 1);
- free(expanded);
-
- window_choose_add(wp, wcd);
-
- return (wcd);
-}
diff --git a/usr.bin/tmux/window-client.c b/usr.bin/tmux/window-client.c
new file mode 100644
index 00000000000..0411c56af74
--- /dev/null
+++ b/usr.bin/tmux/window-client.c
@@ -0,0 +1,335 @@
+/* $OpenBSD: window-client.c,v 1.1 2017/05/30 21:44:59 nicm Exp $ */
+
+/*
+ * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@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 <sys/time.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+static struct screen *window_client_init(struct window_pane *,
+ struct cmd_find_state *, struct args *);
+static void window_client_free(struct window_pane *);
+static void window_client_resize(struct window_pane *, u_int,
+ u_int);
+static void window_client_key(struct window_pane *,
+ struct client *, struct session *, key_code,
+ struct mouse_event *);
+
+#define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
+
+const struct window_mode window_client_mode = {
+ .name = "client-mode",
+
+ .init = window_client_init,
+ .free = window_client_free,
+ .resize = window_client_resize,
+ .key = window_client_key,
+};
+
+enum window_client_sort_type {
+ WINDOW_CLIENT_BY_NAME,
+ WINDOW_CLIENT_BY_CREATION_TIME,
+ WINDOW_CLIENT_BY_ACTIVITY_TIME,
+};
+static const char *window_client_sort_list[] = {
+ "name",
+ "creation time",
+ "activity time"
+};
+
+struct window_client_itemdata {
+ struct client *c;
+};
+
+struct window_client_modedata {
+ struct mode_tree_data *data;
+ char *command;
+
+ struct window_client_itemdata **item_list;
+ u_int item_size;
+};
+
+static struct window_client_itemdata *
+window_client_add_item(struct window_client_modedata *data)
+{
+ struct window_client_itemdata *item;
+
+ data->item_list = xreallocarray(data->item_list, data->item_size + 1,
+ sizeof *data->item_list);
+ item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
+ return (item);
+}
+
+static void
+window_client_free_item(struct window_client_itemdata *item)
+{
+ server_client_unref(item->c);
+ free(item);
+}
+
+static int
+window_client_cmp_name(const void *a0, const void *b0)
+{
+ const struct window_client_itemdata *const *a = a0;
+ const struct window_client_itemdata *const *b = b0;
+
+ return (strcmp((*a)->c->name, (*b)->c->name));
+}
+
+static int
+window_client_cmp_creation_time(const void *a0, const void *b0)
+{
+ const struct window_client_itemdata *const *a = a0;
+ const struct window_client_itemdata *const *b = b0;
+
+ if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >))
+ return (-1);
+ if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <))
+ return (1);
+ return (0);
+}
+
+static int
+window_client_cmp_activity_time(const void *a0, const void *b0)
+{
+ const struct window_client_itemdata *const *a = a0;
+ const struct window_client_itemdata *const *b = b0;
+
+ if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >))
+ return (-1);
+ if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <))
+ return (1);
+ return (0);
+}
+
+static void
+window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag)
+{
+ struct window_client_modedata *data = modedata;
+ struct window_client_itemdata *item;
+ u_int i;
+ struct client *c;
+ char *tim;
+ char *text;
+
+ for (i = 0; i < data->item_size; i++)
+ window_client_free_item(data->item_list[i]);
+ free(data->item_list);
+ data->item_list = NULL;
+ data->item_size = 0;
+
+ TAILQ_FOREACH(c, &clients, entry) {
+ if (c->session == NULL || (c->flags & (CLIENT_DETACHING)))
+ continue;
+
+ item = window_client_add_item(data);
+ item->c = c;
+
+ c->references++;
+ }
+
+ switch (sort_type) {
+ case WINDOW_CLIENT_BY_NAME:
+ qsort(data->item_list, data->item_size, sizeof *data->item_list,
+ window_client_cmp_name);
+ break;
+ case WINDOW_CLIENT_BY_CREATION_TIME:
+ qsort(data->item_list, data->item_size, sizeof *data->item_list,
+ window_client_cmp_creation_time);
+ break;
+ case WINDOW_CLIENT_BY_ACTIVITY_TIME:
+ qsort(data->item_list, data->item_size, sizeof *data->item_list,
+ window_client_cmp_activity_time);
+ break;
+ }
+
+ for (i = 0; i < data->item_size; i++) {
+ item = data->item_list[i];
+ c = item->c;
+
+ tim = ctime(&c->activity_time.tv_sec);
+ *strchr(tim, '\n') = '\0';
+
+ xasprintf(&text, "session %s (%s)", c->session->name, tim);
+ mode_tree_add(data->data, NULL, item, (uint64_t)c, c->name,
+ text, -1);
+ free(text);
+ }
+}
+
+static struct screen *
+window_client_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
+{
+ struct window_client_itemdata *item = itemdata;
+ struct client *c = item->c;
+ struct window_pane *wp;
+ static struct screen s;
+ struct screen_write_ctx ctx;
+
+ if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING)))
+ return (NULL);
+ wp = c->session->curw->window->active;
+
+ screen_init(&s, sx, sy, 0);
+
+ screen_write_start(&ctx, NULL, &s);
+ screen_write_clearscreen(&ctx, 8);
+
+ screen_write_preview(&ctx, &wp->base, sx, sy - 3);
+
+ screen_write_cursormove(&ctx, 0, sy - 2);
+ screen_write_line(&ctx, sx, 0, 0);
+
+ screen_write_cursormove(&ctx, 0, sy - 1);
+ if (c->old_status != NULL)
+ screen_write_copy(&ctx, c->old_status, 0, 0, sx, 1, NULL, NULL);
+ else
+ screen_write_copy(&ctx, &c->status, 0, 0, sx, 1, NULL, NULL);
+
+ screen_write_stop(&ctx);
+ return (&s);
+}
+
+static struct screen *
+window_client_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
+ struct args *args)
+{
+ struct window_client_modedata *data;
+ struct screen *s;
+
+ wp->modedata = data = xcalloc(1, sizeof *data);
+
+ if (args == NULL || args->argc == 0)
+ data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
+ else
+ data->command = xstrdup(args->argv[0]);
+
+ data->data = mode_tree_start(wp, window_client_build,
+ window_client_draw, data, window_client_sort_list,
+ nitems(window_client_sort_list), &s);
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+
+ return (s);
+}
+
+static void
+window_client_free(struct window_pane *wp)
+{
+ struct window_client_modedata *data = wp->modedata;
+ u_int i;
+
+ if (data == NULL)
+ return;
+
+ mode_tree_free(data->data);
+
+ for (i = 0; i < data->item_size; i++)
+ window_client_free_item(data->item_list[i]);
+ free(data->item_list);
+
+ free(data->command);
+ free(data);
+}
+
+static void
+window_client_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+ struct window_client_modedata *data = wp->modedata;
+
+ mode_tree_resize(data->data, sx, sy);
+}
+
+static void
+window_client_do_detach(void* modedata, void *itemdata, key_code key)
+{
+ struct window_client_modedata *data = modedata;
+ struct window_client_itemdata *item = itemdata;
+
+ if (item == mode_tree_get_current(data->data))
+ mode_tree_down(data->data, 0);
+ if (key == 'd' || key == 'D')
+ server_client_detach(item->c, MSG_DETACH);
+ else if (key == 'x' || key == 'X')
+ server_client_detach(item->c, MSG_DETACHKILL);
+ else if (key == 'z' || key == 'Z')
+ server_client_suspend(item->c);
+}
+
+static void
+window_client_key(struct window_pane *wp, struct client *c,
+ __unused struct session *s, key_code key, struct mouse_event *m)
+{
+ struct window_client_modedata *data = wp->modedata;
+ struct window_client_itemdata *item;
+ char *command, *name;
+ int finished;
+
+ /*
+ * t = toggle tag
+ * T = tag none
+ * C-t = tag all
+ * q = exit
+ * O = change sort order
+ *
+ * d = detach client
+ * D = detach tagged clients
+ * x = detach and kill client
+ * X = detach and kill tagged clients
+ * z = suspend client
+ * Z = suspend tagged clients
+ * Enter = detach client
+ */
+
+ finished = mode_tree_key(data->data, &key, m);
+ switch (key) {
+ case 'd':
+ case 'x':
+ case 'z':
+ item = mode_tree_get_current(data->data);
+ window_client_do_detach(data, item, key);
+ mode_tree_build(data->data);
+ break;
+ case 'D':
+ case 'X':
+ case 'Z':
+ mode_tree_each_tagged(data->data, window_client_do_detach, key,
+ 0);
+ mode_tree_build(data->data);
+ break;
+ case '\r':
+ item = mode_tree_get_current(data->data);
+ command = xstrdup(data->command);
+ name = xstrdup(item->c->ttyname);
+ window_pane_reset_mode(wp);
+ mode_tree_run_command(c, NULL, command, name);
+ free(name);
+ free(command);
+ return;
+ }
+ if (finished || server_client_how_many() == 0)
+ window_pane_reset_mode(wp);
+ else {
+ mode_tree_draw(data->data);
+ wp->flags |= PANE_REDRAW;
+ }
+}
diff --git a/usr.bin/tmux/window-clock.c b/usr.bin/tmux/window-clock.c
index 168cbd0bf93..2c03968bc8a 100644
--- a/usr.bin/tmux/window-clock.c
+++ b/usr.bin/tmux/window-clock.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: window-clock.c,v 1.23 2017/05/07 22:27:57 nicm Exp $ */
+/* $OpenBSD: window-clock.c,v 1.24 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -24,7 +24,8 @@
#include "tmux.h"
-static struct screen *window_clock_init(struct window_pane *);
+static struct screen *window_clock_init(struct window_pane *,
+ struct cmd_find_state *, struct args *);
static void window_clock_free(struct window_pane *);
static void window_clock_resize(struct window_pane *, u_int, u_int);
static void window_clock_key(struct window_pane *, struct client *,
@@ -145,7 +146,8 @@ window_clock_timer_callback(__unused int fd, __unused short events, void *arg)
}
static struct screen *
-window_clock_init(struct window_pane *wp)
+window_clock_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
+ __unused struct args *args)
{
struct window_clock_mode_data *data;
struct screen *s;
diff --git a/usr.bin/tmux/window-copy.c b/usr.bin/tmux/window-copy.c
index e7bb44be73e..4a2aeed8daf 100644
--- a/usr.bin/tmux/window-copy.c
+++ b/usr.bin/tmux/window-copy.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: window-copy.c,v 1.176 2017/05/29 07:58:33 nicm Exp $ */
+/* $OpenBSD: window-copy.c,v 1.177 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -27,7 +27,8 @@
static const char *window_copy_key_table(struct window_pane *);
static void window_copy_command(struct window_pane *, struct client *,
struct session *, struct args *, struct mouse_event *);
-static struct screen *window_copy_init(struct window_pane *);
+static struct screen *window_copy_init(struct window_pane *,
+ struct cmd_find_state *, struct args *);
static void window_copy_free(struct window_pane *);
static int window_copy_pagedown(struct window_pane *, int);
static void window_copy_next_paragraph(struct window_pane *);
@@ -187,7 +188,8 @@ struct window_copy_mode_data {
};
static struct screen *
-window_copy_init(struct window_pane *wp)
+window_copy_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
+ __unused struct args *args)
{
struct window_copy_mode_data *data;
struct screen *s;
diff --git a/usr.bin/tmux/window-tree.c b/usr.bin/tmux/window-tree.c
new file mode 100644
index 00000000000..c0c1e90ab26
--- /dev/null
+++ b/usr.bin/tmux/window-tree.c
@@ -0,0 +1,713 @@
+/* $OpenBSD: window-tree.c,v 1.1 2017/05/30 21:44:59 nicm Exp $ */
+
+/*
+ * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@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"
+
+static struct screen *window_tree_init(struct window_pane *,
+ struct cmd_find_state *, struct args *);
+static void window_tree_free(struct window_pane *);
+static void window_tree_resize(struct window_pane *, u_int, u_int);
+static void window_tree_key(struct window_pane *,
+ struct client *, struct session *, key_code,
+ struct mouse_event *);
+
+#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'"
+
+const struct window_mode window_tree_mode = {
+ .name = "tree-mode",
+
+ .init = window_tree_init,
+ .free = window_tree_free,
+ .resize = window_tree_resize,
+ .key = window_tree_key,
+};
+
+enum window_tree_sort_type {
+ WINDOW_TREE_BY_INDEX,
+ WINDOW_TREE_BY_NAME,
+ WINDOW_TREE_BY_TIME,
+};
+static const char *window_tree_sort_list[] = {
+ "index",
+ "name",
+ "time"
+};
+
+enum window_tree_type {
+ WINDOW_TREE_NONE,
+ WINDOW_TREE_SESSION,
+ WINDOW_TREE_WINDOW,
+ WINDOW_TREE_PANE,
+};
+
+struct window_tree_itemdata {
+ enum window_tree_type type;
+ int session;
+ int winlink;
+ int pane;
+};
+
+struct window_tree_modedata {
+ struct window_pane *wp;
+ int dead;
+ int references;
+
+ struct mode_tree_data *data;
+ char *command;
+
+ struct window_tree_itemdata **item_list;
+ u_int item_size;
+
+ struct client *client;
+ const char *entered;
+
+ char *filter;
+
+ struct cmd_find_state fs;
+ enum window_tree_type type;
+};
+
+static void
+window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
+ struct winlink **wlp, struct window_pane **wp)
+{
+ *wp = NULL;
+ *wlp = NULL;
+ *sp = session_find_by_id(item->session);
+ if (*sp == NULL)
+ return;
+ if (item->type == WINDOW_TREE_SESSION) {
+ *wlp = (*sp)->curw;
+ *wp = (*wlp)->window->active;
+ return;
+ }
+
+ *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
+ if (*wlp == NULL) {
+ *sp = NULL;
+ return;
+ }
+ if (item->type == WINDOW_TREE_WINDOW) {
+ *wp = (*wlp)->window->active;
+ return;
+ }
+
+ *wp = window_pane_find_by_id(item->pane);
+ if (!window_has_pane((*wlp)->window, *wp))
+ *wp = NULL;
+ if (*wp == NULL) {
+ *sp = NULL;
+ *wlp = NULL;
+ return;
+ }
+}
+
+static struct window_tree_itemdata *
+window_tree_add_item(struct window_tree_modedata *data)
+{
+ struct window_tree_itemdata *item;
+
+ data->item_list = xreallocarray(data->item_list, data->item_size + 1,
+ sizeof *data->item_list);
+ item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
+ return (item);
+}
+
+static void
+window_tree_free_item(struct window_tree_itemdata *item)
+{
+ free(item);
+}
+
+static int
+window_tree_cmp_session_name(const void *a0, const void *b0)
+{
+ const struct session *const *a = a0;
+ const struct session *const *b = b0;
+
+ return (strcmp((*a)->name, (*b)->name));
+}
+
+static int
+window_tree_cmp_session_time(const void *a0, const void *b0)
+{
+ const struct session *const *a = a0;
+ const struct session *const *b = b0;
+
+ if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
+ return (-1);
+ if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
+ return (1);
+ return (strcmp((*a)->name, (*b)->name));
+}
+
+static int
+window_tree_cmp_window_name(const void *a0, const void *b0)
+{
+ const struct winlink *const *a = a0;
+ const struct winlink *const *b = b0;
+
+ return (strcmp((*a)->window->name, (*b)->window->name));
+}
+
+static int
+window_tree_cmp_window_time(const void *a0, const void *b0)
+{
+ const struct winlink *const *a = a0;
+ const struct winlink *const *b = b0;
+
+ if (timercmp(&(*a)->window->activity_time,
+ &(*b)->window->activity_time, >))
+ return (-1);
+ if (timercmp(&(*a)->window->activity_time,
+ &(*b)->window->activity_time, <))
+ return (1);
+ return (strcmp((*a)->window->name, (*b)->window->name));
+}
+
+static int
+window_tree_cmp_pane_time(const void *a0, const void *b0)
+{
+ const struct window_pane *const *a = a0;
+ const struct window_pane *const *b = b0;
+
+ if ((*a)->active_point < (*b)->active_point)
+ return (-1);
+ if ((*a)->active_point > (*b)->active_point)
+ return (1);
+ return (0);
+}
+
+static void
+window_tree_build_pane(struct session *s, struct winlink *wl,
+ struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
+{
+ struct window_tree_modedata *data = modedata;
+ struct window_tree_itemdata *item;
+ char *name, *text;
+ u_int idx;
+
+ window_pane_index(wp, &idx);
+
+ item = window_tree_add_item(data);
+ item->type = WINDOW_TREE_PANE;
+ item->session = s->id;
+ item->winlink = wl->idx;
+ item->pane = wp->id;
+
+ text = format_single(NULL,
+ "#{pane_current_command} \"#{pane_title}\"",
+ NULL, s, wl, wp);
+ xasprintf(&name, "%u", idx);
+
+ mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1);
+ free(text);
+ free(name);
+}
+
+static int
+window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
+ u_int sort_type, struct mode_tree_item *parent, int no_filter)
+{
+ struct window_tree_modedata *data = modedata;
+ struct window_tree_itemdata *item;
+ struct mode_tree_item *mti;
+ char *name, *text, *cp;
+ struct window_pane *wp, **l;
+ u_int n, i;
+ int expanded;
+
+ item = window_tree_add_item(data);
+ item->type = WINDOW_TREE_WINDOW;
+ item->session = s->id;
+ item->winlink = wl->idx;
+ item->pane = -1;
+
+ text = format_single(NULL,
+ "#{window_name}#{window_flags} (#{window_panes} panes)",
+ NULL, s, wl, NULL);
+ xasprintf(&name, "%u", wl->idx);
+
+ if (data->type == WINDOW_TREE_SESSION ||
+ data->type == WINDOW_TREE_WINDOW)
+ expanded = 0;
+ else
+ expanded = 1;
+ mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
+ expanded);
+ free(text);
+ free(name);
+
+ l = NULL;
+ n = 0;
+ TAILQ_FOREACH(wp, &wl->window->panes, entry) {
+ if (!no_filter && data->filter != NULL) {
+ cp = format_single(NULL, data->filter, NULL, s, wl, wp);
+ if (!format_true(cp)) {
+ free(cp);
+ continue;
+ }
+ free(cp);
+ }
+ l = xreallocarray(l, n + 1, sizeof *l);
+ l[n++] = wp;
+ }
+ if (n == 0) {
+ window_tree_free_item(item);
+ data->item_size--;
+ mode_tree_remove(data->data, mti);
+ return (0);
+ }
+
+ switch (sort_type) {
+ case WINDOW_TREE_BY_INDEX:
+ break;
+ case WINDOW_TREE_BY_NAME:
+ /* Panes don't have names, so leave in number order. */
+ break;
+ case WINDOW_TREE_BY_TIME:
+ qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
+ break;
+ }
+
+ for (i = 0; i < n; i++)
+ window_tree_build_pane(s, wl, l[i], modedata, mti);
+ free(l);
+ return (1);
+}
+
+static void
+window_tree_build_session(struct session *s, void* modedata,
+ u_int sort_type, int no_filter)
+{
+ struct window_tree_modedata *data = modedata;
+ struct window_tree_itemdata *item;
+ struct mode_tree_item *mti;
+ char *text;
+ struct winlink *wl, **l;
+ u_int n, i, empty;
+ int expanded;
+
+ item = window_tree_add_item(data);
+ item->type = WINDOW_TREE_SESSION;
+ item->session = s->id;
+ item->winlink = -1;
+ item->pane = -1;
+
+ text = format_single(NULL,
+ "#{session_windows} windows"
+ "#{?session_grouped, (group ,}"
+ "#{session_group}#{?session_grouped,),}"
+ "#{?session_attached, (attached),}",
+ NULL, s, NULL, NULL);
+
+ if (data->type == WINDOW_TREE_SESSION)
+ expanded = 0;
+ else
+ expanded = 1;
+ mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
+ expanded);
+ free(text);
+
+ l = NULL;
+ n = 0;
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ l = xreallocarray(l, n + 1, sizeof *l);
+ l[n++] = wl;
+ }
+ switch (sort_type) {
+ case WINDOW_TREE_BY_INDEX:
+ break;
+ case WINDOW_TREE_BY_NAME:
+ qsort(l, n, sizeof *l, window_tree_cmp_window_name);
+ break;
+ case WINDOW_TREE_BY_TIME:
+ qsort(l, n, sizeof *l, window_tree_cmp_window_time);
+ break;
+ }
+
+ empty = 0;
+ for (i = 0; i < n; i++) {
+ if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
+ no_filter))
+ empty++;
+ }
+ if (empty == n) {
+ window_tree_free_item(item);
+ data->item_size--;
+ mode_tree_remove(data->data, mti);
+ }
+ free(l);
+}
+
+static void
+window_tree_build(void *modedata, u_int sort_type, uint64_t *tag)
+{
+ struct window_tree_modedata *data = modedata;
+ struct session *s, **l;
+ u_int n, i;
+ int no_filter = 0;
+
+restart:
+ for (i = 0; i < data->item_size; i++)
+ window_tree_free_item(data->item_list[i]);
+ free(data->item_list);
+ data->item_list = NULL;
+ data->item_size = 0;
+
+ l = NULL;
+ n = 0;
+ RB_FOREACH(s, sessions, &sessions) {
+ l = xreallocarray(l, n + 1, sizeof *l);
+ l[n++] = s;
+ }
+ switch (sort_type) {
+ case WINDOW_TREE_BY_INDEX:
+ break;
+ case WINDOW_TREE_BY_NAME:
+ qsort(l, n, sizeof *l, window_tree_cmp_session_name);
+ break;
+ case WINDOW_TREE_BY_TIME:
+ qsort(l, n, sizeof *l, window_tree_cmp_session_time);
+ break;
+ }
+
+ for (i = 0; i < n; i++)
+ window_tree_build_session(l[i], modedata, sort_type, no_filter);
+ free(l);
+
+ if (!no_filter && data->item_size == 0) {
+ no_filter = 1;
+ goto restart;
+ }
+
+ switch (data->type) {
+ case WINDOW_TREE_NONE:
+ break;
+ case WINDOW_TREE_SESSION:
+ *tag = (uint64_t)data->fs.s;
+ break;
+ case WINDOW_TREE_WINDOW:
+ *tag = (uint64_t)data->fs.wl;
+ break;
+ case WINDOW_TREE_PANE:
+ *tag = (uint64_t)data->fs.wp;
+ break;
+ }
+}
+
+static struct screen *
+window_tree_draw(__unused void *modedata, void *itemdata, u_int sx, u_int sy)
+{
+ struct window_tree_itemdata *item = itemdata;
+ struct session *sp;
+ struct winlink *wlp;
+ struct window_pane *wp;
+ static struct screen s;
+ struct screen_write_ctx ctx;
+
+ window_tree_pull_item(item, &sp, &wlp, &wp);
+ if (wp == NULL)
+ return (NULL);
+
+ screen_init(&s, sx, sy, 0);
+
+ screen_write_start(&ctx, NULL, &s);
+
+ screen_write_preview(&ctx, &wp->base, sx, sy);
+
+ screen_write_stop(&ctx);
+ return (&s);
+}
+
+static struct screen *
+window_tree_init(struct window_pane *wp, struct cmd_find_state *fs,
+ struct args *args)
+{
+ struct window_tree_modedata *data;
+ struct screen *s;
+
+ wp->modedata = data = xcalloc(1, sizeof *data);
+
+ if (args_has(args, 's'))
+ data->type = WINDOW_TREE_SESSION;
+ else if (args_has(args, 'w'))
+ data->type = WINDOW_TREE_WINDOW;
+ else
+ data->type = WINDOW_TREE_PANE;
+ memcpy(&data->fs, fs, sizeof data->fs);
+
+ data->wp = wp;
+ data->references = 1;
+
+ if (args_has(args, 'f'))
+ data->filter = xstrdup(args_get(args, 'f'));
+ else
+ data->filter = NULL;
+
+ if (args == NULL || args->argc == 0)
+ data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
+ else
+ data->command = xstrdup(args->argv[0]);
+
+ data->data = mode_tree_start(wp, window_tree_build,
+ window_tree_draw, data, window_tree_sort_list,
+ nitems(window_tree_sort_list), &s);
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+
+ data->type = WINDOW_TREE_NONE;
+
+ return (s);
+}
+
+static void
+window_tree_destroy(struct window_tree_modedata *data)
+{
+ u_int i;
+
+ if (--data->references != 0)
+ return;
+
+ mode_tree_free(data->data);
+
+ for (i = 0; i < data->item_size; i++)
+ window_tree_free_item(data->item_list[i]);
+ free(data->item_list);
+
+ free(data->filter);
+
+ free(data->command);
+ free(data);
+}
+
+static void
+window_tree_free(struct window_pane *wp)
+{
+ struct window_tree_modedata *data = wp->modedata;
+
+ if (data == NULL)
+ return;
+
+ data->dead = 1;
+ window_tree_destroy(data);
+}
+
+static void
+window_tree_resize(struct window_pane *wp, u_int sx, u_int sy)
+{
+ struct window_tree_modedata *data = wp->modedata;
+
+ mode_tree_resize(data->data, sx, sy);
+}
+
+static char *
+window_tree_get_target(struct window_tree_itemdata *item,
+ struct cmd_find_state *fs)
+{
+ struct session *s;
+ struct winlink *wl;
+ struct window_pane *wp;
+ char *target;
+
+ window_tree_pull_item(item, &s, &wl, &wp);
+
+ target = NULL;
+ switch (item->type) {
+ case WINDOW_TREE_NONE:
+ break;
+ case WINDOW_TREE_SESSION:
+ if (s == NULL)
+ break;
+ xasprintf(&target, "=%s:", s->name);
+ break;
+ case WINDOW_TREE_WINDOW:
+ if (s == NULL || wl == NULL)
+ break;
+ xasprintf(&target, "=%s:%u.", s->name, wl->idx);
+ break;
+ case WINDOW_TREE_PANE:
+ if (s == NULL || wl == NULL || wp == NULL)
+ break;
+ xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
+ break;
+ }
+ if (target == NULL)
+ cmd_find_clear_state(fs, 0);
+ else
+ cmd_find_from_winlink_pane(fs, wl, wp);
+ return (target);
+}
+
+static void
+window_tree_command_each(void* modedata, void* itemdata, __unused key_code key)
+{
+ struct window_tree_modedata *data = modedata;
+ struct window_tree_itemdata *item = itemdata;
+ char *name;
+ struct cmd_find_state fs;
+
+ name = window_tree_get_target(item, &fs);
+ if (name != NULL)
+ mode_tree_run_command(data->client, &fs, data->entered, name);
+ free(name);
+}
+
+static enum cmd_retval
+window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
+{
+ struct window_tree_modedata *data = modedata;
+
+ if (!data->dead) {
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+ }
+ window_tree_destroy(data);
+ return (CMD_RETURN_NORMAL);
+}
+
+static int
+window_tree_command_callback(struct client *c, void *modedata, const char *s,
+ __unused int done)
+{
+ struct window_tree_modedata *data = modedata;
+
+ if (data->dead)
+ return (0);
+
+ data->client = c;
+ data->entered = s;
+
+ mode_tree_each_tagged(data->data, window_tree_command_each, KEYC_NONE,
+ 1);
+
+ data->client = NULL;
+ data->entered = NULL;
+
+ data->references++;
+ cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
+
+ return (0);
+}
+
+static void
+window_tree_command_free(void *modedata)
+{
+ struct window_tree_modedata *data = modedata;
+
+ window_tree_destroy(data);
+}
+
+static int
+window_tree_filter_callback(__unused struct client *c, void *modedata,
+ const char *s, __unused int done)
+{
+ struct window_tree_modedata *data = modedata;
+
+ if (data->dead)
+ return (0);
+
+ if (data->filter != NULL)
+ free(data->filter);
+ if (s == NULL || *s == '\0')
+ data->filter = NULL;
+ else
+ data->filter = xstrdup(s);
+
+ mode_tree_build(data->data);
+ mode_tree_draw(data->data);
+ data->wp->flags |= PANE_REDRAW;
+
+ return (0);
+}
+
+static void
+window_tree_filter_free(void *modedata)
+{
+ struct window_tree_modedata *data = modedata;
+
+ window_tree_destroy(data);
+}
+
+static void
+window_tree_key(struct window_pane *wp, struct client *c,
+ __unused struct session *s, key_code key, struct mouse_event *m)
+{
+ struct window_tree_modedata *data = wp->modedata;
+ struct window_tree_itemdata *item;
+ char *command, *name, *prompt;
+ struct cmd_find_state fs;
+ int finished;
+ u_int tagged;
+
+ /*
+ * t = toggle tag
+ * T = tag none
+ * C-t = tag all
+ * q = exit
+ * O = change sort order
+ *
+ * Enter = select item
+ * : = enter command
+ * f = enter filter
+ */
+
+ finished = mode_tree_key(data->data, &key, m);
+ switch (key) {
+ case 'f':
+ data->references++;
+ status_prompt_set(c, "(filter) ", data->filter,
+ window_tree_filter_callback, window_tree_filter_free, data,
+ PROMPT_NOFORMAT);
+ break;
+ case ':':
+ tagged = mode_tree_count_tagged(data->data);
+ if (tagged != 0)
+ xasprintf(&prompt, "(%u tagged) ", tagged);
+ else
+ xasprintf(&prompt, "(current) ");
+ data->references++;
+ status_prompt_set(c, prompt, "", window_tree_command_callback,
+ window_tree_command_free, data, PROMPT_NOFORMAT);
+ free(prompt);
+ break;
+ case '\r':
+ item = mode_tree_get_current(data->data);
+ command = xstrdup(data->command);
+ name = window_tree_get_target(item, &fs);
+ window_pane_reset_mode(wp);
+ if (name != NULL)
+ mode_tree_run_command(c, &fs, command, name);
+ free(name);
+ free(command);
+ return;
+ }
+ if (finished)
+ window_pane_reset_mode(wp);
+ else {
+ mode_tree_draw(data->data);
+ wp->flags |= PANE_REDRAW;
+ }
+}
diff --git a/usr.bin/tmux/window.c b/usr.bin/tmux/window.c
index 28110f02ce1..49c8f265e96 100644
--- a/usr.bin/tmux/window.c
+++ b/usr.bin/tmux/window.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: window.c,v 1.195 2017/05/29 18:06:34 nicm Exp $ */
+/* $OpenBSD: window.c,v 1.196 2017/05/30 21:44:59 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1171,7 +1171,8 @@ window_pane_mode_timer(__unused int fd, __unused short events, void *arg)
}
int
-window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
+window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode,
+ struct cmd_find_state *fs, struct args *args)
{
struct screen *s;
struct timeval tv = { .tv_sec = 10 };
@@ -1184,7 +1185,7 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
evtimer_set(&wp->modetimer, window_pane_mode_timer, wp);
evtimer_add(&wp->modetimer, &tv);
- if ((s = wp->mode->init(wp)) != NULL)
+ if ((s = wp->mode->init(wp, fs, args)) != NULL)
wp->screen = s;
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
@@ -1291,32 +1292,6 @@ window_pane_search(struct window_pane *wp, const char *searchstr)
return (i + 1);
}
-char *
-window_pane_search_old(struct window_pane *wp, const char *searchstr,
- u_int *lineno)
-{
- struct screen *s = &wp->base;
- char *newsearchstr, *line, *msg;
- u_int i;
-
- msg = NULL;
- xasprintf(&newsearchstr, "*%s*", searchstr);
-
- for (i = 0; i < screen_size_y(s); i++) {
- line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
- if (fnmatch(newsearchstr, line, 0) == 0) {
- msg = line;
- if (lineno != NULL)
- *lineno = i;
- break;
- }
- free(line);
- }
-
- free(newsearchstr);
- return (msg);
-}
-
/* Get MRU pane from a list. */
static struct window_pane *
window_pane_choose_best(struct window_pane **list, u_int size)