aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornicm <nicm>2020-03-24 08:09:43 +0000
committernicm <nicm>2020-03-24 08:09:43 +0000
commit8a838b0372163e1a7c0379991545a55028bb9eba (patch)
treee661c7515692850627863e31a357c2305f4dabcd
parentedca27ae45db7be104bc56a4e48e55cddc40acdb (diff)
downloadrtmux-8a838b0372163e1a7c0379991545a55028bb9eba.tar.gz
rtmux-8a838b0372163e1a7c0379991545a55028bb9eba.tar.bz2
rtmux-8a838b0372163e1a7c0379991545a55028bb9eba.zip
Add support for overlay popup boxes to show text or output temporarily
above the normal layout. These work similarly to menus and are created with the display-popup command.
-rw-r--r--Makefile1
-rw-r--r--cmd-display-menu.c93
-rw-r--r--cmd-display-panes.c2
-rw-r--r--cmd.c2
-rw-r--r--job.c19
-rw-r--r--menu.c17
-rw-r--r--popup.c447
-rw-r--r--screen-redraw.c2
-rw-r--r--server-client.c76
-rw-r--r--tmux.191
-rw-r--r--tmux.h20
-rw-r--r--tty.c19
12 files changed, 751 insertions, 38 deletions
diff --git a/Makefile b/Makefile
index 6645bbfc..24117bf1 100644
--- a/Makefile
+++ b/Makefile
@@ -94,6 +94,7 @@ SRCS= alerts.c \
options-table.c \
options.c \
paste.c \
+ popup.c \
proc.c \
procname.c \
regsub.c \
diff --git a/cmd-display-menu.c b/cmd-display-menu.c
index 2ba674a3..aafe2447 100644
--- a/cmd-display-menu.c
+++ b/cmd-display-menu.c
@@ -29,6 +29,8 @@
static enum cmd_retval cmd_display_menu_exec(struct cmd *,
struct cmdq_item *);
+static enum cmd_retval cmd_display_popup_exec(struct cmd *,
+ struct cmdq_item *);
const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu",
@@ -44,6 +46,21 @@ const struct cmd_entry cmd_display_menu_entry = {
.exec = cmd_display_menu_exec
};
+const struct cmd_entry cmd_display_popup_entry = {
+ .name = "display-popup",
+ .alias = "popup",
+
+ .args = { "CEKc:d:h:R:t:w:x:y:", 0, -1 },
+ .usage = "[-CEK] [-c target-client] [-d start-directory] [-h height] "
+ "[-R shell-command] " CMD_TARGET_PANE_USAGE " [-w width] "
+ "[-x position] [-y position] [command line ...]",
+
+ .target = { 't', CMD_FIND_PANE, 0 },
+
+ .flags = CMD_AFTERHOOK,
+ .exec = cmd_display_popup_exec
+};
+
static void
cmd_display_menu_get_position(struct client *c, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
@@ -190,3 +207,79 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
+
+static enum cmd_retval
+cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
+{
+ struct args *args = self->args;
+ struct client *c;
+ struct cmd_find_state *fs = &item->target;
+ const char *value, *cmd = NULL, **lines = NULL;
+ const char *shellcmd = NULL;
+ char *cwd, *cause;
+ int flags = 0;
+ u_int px, py, w, h, nlines = 0;
+
+ if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL)
+ return (CMD_RETURN_ERROR);
+ if (args_has(args, 'C')) {
+ server_client_clear_overlay(c);
+ return (CMD_RETURN_NORMAL);
+ }
+ if (c->overlay_draw != NULL)
+ return (CMD_RETURN_NORMAL);
+
+ if (args->argc >= 1)
+ cmd = args->argv[0];
+ if (args->argc >= 2) {
+ lines = (const char **)args->argv + 1;
+ nlines = args->argc - 1;
+ }
+
+ if (nlines != 0)
+ h = nlines + 2;
+ else
+ h = c->tty.sy / 2;
+ if (args_has(args, 'h')) {
+ h = args_percentage(args, 'h', 1, c->tty.sy, c->tty.sy, &cause);
+ if (cause != NULL) {
+ cmdq_error(item, "height %s", cause);
+ free(cause);
+ return (CMD_RETURN_ERROR);
+ }
+ }
+
+ if (nlines != 0)
+ w = popup_width(item, nlines, lines, c, fs) + 2;
+ else
+ w = c->tty.sx / 2;
+ if (args_has(args, 'w')) {
+ w = args_percentage(args, 'w', 1, c->tty.sx, c->tty.sx, &cause);
+ if (cause != NULL) {
+ cmdq_error(item, "width %s", cause);
+ free(cause);
+ return (CMD_RETURN_ERROR);
+ }
+ }
+
+ cmd_display_menu_get_position(c, item, args, &px, &py, w, h);
+
+ value = args_get(args, 'd');
+ if (value != NULL)
+ cwd = format_single(NULL, value, c, fs->s, fs->wl, fs->wp);
+ else
+ cwd = xstrdup(server_client_get_cwd(c, fs->s));
+
+ value = args_get(args, 'R');
+ if (value != NULL)
+ shellcmd = format_single(NULL, value, c, fs->s, fs->wl, fs->wp);
+
+ if (args_has(args, 'K'))
+ flags |= POPUP_WRITEKEYS;
+ if (args_has(args, 'E'))
+ flags |= POPUP_CLOSEEXIT;
+ if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd,
+ cmd, cwd, c, fs) != 0)
+ return (CMD_RETURN_NORMAL);
+ return (CMD_RETURN_WAIT);
+}
diff --git a/cmd-display-panes.c b/cmd-display-panes.c
index df97819c..d8d351c2 100644
--- a/cmd-display-panes.c
+++ b/cmd-display-panes.c
@@ -273,7 +273,7 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
else
cdata->item = item;
- server_client_set_overlay(c, delay, cmd_display_panes_draw,
+ server_client_set_overlay(c, delay, NULL, NULL, cmd_display_panes_draw,
cmd_display_panes_key, cmd_display_panes_free, cdata);
if (args_has(args, 'b'))
diff --git a/cmd.c b/cmd.c
index 03a17ab1..52cf5af1 100644
--- a/cmd.c
+++ b/cmd.c
@@ -44,6 +44,7 @@ extern const struct cmd_entry cmd_delete_buffer_entry;
extern const struct cmd_entry cmd_detach_client_entry;
extern const struct cmd_entry cmd_display_menu_entry;
extern const struct cmd_entry cmd_display_message_entry;
+extern const struct cmd_entry cmd_display_popup_entry;
extern const struct cmd_entry cmd_display_panes_entry;
extern const struct cmd_entry cmd_down_pane_entry;
extern const struct cmd_entry cmd_find_window_entry;
@@ -133,6 +134,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_detach_client_entry,
&cmd_display_menu_entry,
&cmd_display_message_entry,
+ &cmd_display_popup_entry,
&cmd_display_panes_entry,
&cmd_find_window_entry,
&cmd_has_session_entry,
diff --git a/job.c b/job.c
index c31d51b2..997a6574 100644
--- a/job.c
+++ b/job.c
@@ -17,6 +17,7 @@
*/
#include <sys/types.h>
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <fcntl.h>
@@ -205,6 +206,24 @@ job_free(struct job *job)
free(job);
}
+/* Resize job. */
+void
+job_resize(struct job *job, u_int sx, u_int sy)
+{
+ struct winsize ws;
+
+ if (job->fd == -1 || (~job->flags & JOB_PTY))
+ return;
+
+ log_debug("resize job %p: %ux%u", job, sx, sy);
+
+ memset(&ws, 0, sizeof ws);
+ ws.ws_col = sx;
+ ws.ws_row = sy;
+ if (ioctl(job->fd, TIOCSWINSZ, &ws) == -1)
+ fatal("ioctl failed");
+}
+
/* Job buffer read callback. */
static void
job_read_callback(__unused struct bufferevent *bufev, void *data)
diff --git a/menu.c b/menu.c
index f70b1d18..7ca6253e 100644
--- a/menu.c
+++ b/menu.c
@@ -130,6 +130,16 @@ menu_free(struct menu *menu)
free(menu);
}
+static int
+menu_mode_cb(struct client *c, __unused u_int *cx, __unused u_int *cy)
+{
+ struct menu_data *md = c->overlay_data;
+
+ if (~md->flags & MENU_NOMOUSE)
+ return (MODE_MOUSE_ALL);
+ return (0);
+}
+
static void
menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0)
{
@@ -147,9 +157,6 @@ menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0)
for (i = 0; i < screen_size_y(&md->s); i++)
tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i);
-
- if (~md->flags & MENU_NOMOUSE)
- tty_update_mode(tty, MODE_MOUSE_ALL, NULL);
}
static void
@@ -317,7 +324,7 @@ menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
md->cb = cb;
md->data = data;
- server_client_set_overlay(c, 0, menu_draw_cb, menu_key_cb, menu_free_cb,
- md);
+ server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb,
+ menu_key_cb, menu_free_cb, md);
return (0);
}
diff --git a/popup.c b/popup.c
new file mode 100644
index 00000000..16b7b260
--- /dev/null
+++ b/popup.c
@@ -0,0 +1,447 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2020 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/wait.h>
+
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tmux.h"
+
+struct popup_data {
+ struct client *c;
+ struct cmdq_item *item;
+ int flags;
+
+ char **lines;
+ u_int nlines;
+
+ char *cmd;
+ struct cmd_find_state fs;
+ struct screen s;
+
+ struct job *job;
+ struct input_ctx *ictx;
+ int status;
+
+ u_int px;
+ u_int py;
+ u_int sx;
+ u_int sy;
+
+ enum { OFF, MOVE, SIZE } dragging;
+ u_int dx;
+ u_int dy;
+
+ u_int lx;
+ u_int ly;
+ u_int lb;
+};
+
+static void
+popup_write_screen(struct client *c, struct popup_data *pd)
+{
+ struct cmdq_item *item = pd->item;
+ struct screen_write_ctx ctx;
+ char *copy, *next, *loop, *tmp;
+ struct format_tree *ft;
+ u_int i, y;
+
+ ft = format_create(item->client, item, FORMAT_NONE, 0);
+ if (cmd_find_valid_state(&pd->fs))
+ format_defaults(ft, c, pd->fs.s, pd->fs.wl, pd->fs.wp);
+ else
+ format_defaults(ft, c, NULL, NULL, NULL);
+
+ screen_write_start(&ctx, NULL, &pd->s);
+ screen_write_clearscreen(&ctx, 8);
+
+ y = 0;
+ for (i = 0; i < pd->nlines; i++) {
+ if (y == pd->sy - 2)
+ break;
+ copy = next = xstrdup(pd->lines[i]);
+ while ((loop = strsep(&next, "\n")) != NULL) {
+ if (y == pd->sy - 2)
+ break;
+ tmp = format_expand(ft, loop);
+ screen_write_cursormove(&ctx, 0, y, 0);
+ format_draw(&ctx, &grid_default_cell, pd->sx - 2, tmp,
+ NULL);
+ free(tmp);
+ y++;
+ }
+ free(copy);
+ }
+
+ format_free(ft);
+ screen_write_cursormove(&ctx, 0, y, 0);
+ screen_write_stop(&ctx);
+}
+
+static int
+popup_mode_cb(struct client *c, u_int *cx, u_int *cy)
+{
+ struct popup_data *pd = c->overlay_data;
+
+ if (pd->ictx == NULL)
+ return (0);
+ *cx = pd->px + 1 + pd->s.cx;
+ *cy = pd->py + 1 + pd->s.cy;
+ return (pd->s.mode);
+}
+
+static int
+popup_check_cb(struct client *c, u_int px, u_int py)
+{
+ struct popup_data *pd = c->overlay_data;
+
+ if (px < pd->px || px > pd->px + pd->sx - 1)
+ return (1);
+ if (py < pd->py || py > pd->py + pd->sy - 1)
+ return (1);
+ return (0);
+}
+
+static void
+popup_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0)
+{
+ struct popup_data *pd = c->overlay_data;
+ struct tty *tty = &c->tty;
+ struct screen s;
+ struct screen_write_ctx ctx;
+ u_int i, px = pd->px, py = pd->py;
+
+ screen_init(&s, pd->sx, pd->sy, 0);
+ screen_write_start(&ctx, NULL, &s);
+ screen_write_clearscreen(&ctx, 8);
+ screen_write_box(&ctx, pd->sx, pd->sy);
+ screen_write_cursormove(&ctx, 1, 1, 0);
+ screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, pd->sy - 2);
+ screen_write_stop(&ctx);
+
+ c->overlay_check = NULL;
+ for (i = 0; i < pd->sy; i++)
+ tty_draw_line(tty, NULL, &s, 0, i, pd->sx, px, py + i);
+ c->overlay_check = popup_check_cb;
+}
+
+static void
+popup_free_cb(struct client *c)
+{
+ struct popup_data *pd = c->overlay_data;
+ struct cmdq_item *item = pd->item;
+ u_int i;
+
+ if (item != NULL) {
+ if (pd->ictx != NULL &&
+ item->client != NULL &&
+ item->client->session == NULL)
+ item->client->retval = pd->status;
+ cmdq_continue(item);
+ }
+ server_client_unref(pd->c);
+
+ if (pd->job != NULL)
+ job_free(pd->job);
+ if (pd->ictx != NULL)
+ input_free(pd->ictx);
+
+ for (i = 0; i < pd->nlines; i++)
+ free(pd->lines[i]);
+ free(pd->lines);
+
+ screen_free(&pd->s);
+ free(pd->cmd);
+ free(pd);
+}
+
+static void
+popup_handle_drag(struct client *c, struct popup_data *pd,
+ struct mouse_event *m)
+{
+ u_int px, py;
+
+ if (!MOUSE_DRAG(m->b))
+ pd->dragging = OFF;
+ else if (pd->dragging == MOVE) {
+ if (m->x < pd->dx)
+ px = 0;
+ else if (m->x - pd->dx + pd->sx > c->tty.sx)
+ px = c->tty.sx - pd->sx;
+ else
+ px = m->x - pd->dx;
+ if (m->y < pd->dy)
+ py = 0;
+ else if (m->y - pd->dy + pd->sy > c->tty.sy)
+ py = c->tty.sy - pd->sy;
+ else
+ py = m->y - pd->dy;
+ pd->px = px;
+ pd->py = py;
+ pd->dx = m->x - pd->px;
+ pd->dy = m->y - pd->py;
+ server_redraw_client(c);
+ } else if (pd->dragging == SIZE) {
+ if (m->x < pd->px + 2)
+ return;
+ if (m->y < pd->py + 2)
+ return;
+ pd->sx = m->x - pd->px;
+ pd->sy = m->y - pd->py;
+
+ screen_resize(&pd->s, pd->sx, pd->sy, 0);
+ if (pd->ictx == NULL)
+ popup_write_screen(c, pd);
+ else if (pd->job != NULL)
+ job_resize(pd->job, pd->sx - 2, pd->sy - 2);
+ server_redraw_client(c);
+ }
+}
+
+static int
+popup_key_cb(struct client *c, struct key_event *event)
+{
+ struct popup_data *pd = c->overlay_data;
+ struct mouse_event *m = &event->m;
+ struct cmd_find_state *fs = &pd->fs;
+ struct cmdq_item *new_item;
+ struct cmd_parse_result *pr;
+ struct format_tree *ft;
+ const char *cmd;
+
+ if (KEYC_IS_MOUSE(event->key)) {
+ if (pd->dragging != OFF) {
+ popup_handle_drag(c, pd, m);
+ goto out;
+ }
+ if (m->x < pd->px ||
+ m->x > pd->px + pd->sx - 1 ||
+ m->y < pd->py ||
+ m->y > pd->py + pd->sy - 1) {
+ if (MOUSE_BUTTONS (m->b) == 1)
+ return (1);
+ return (0);
+ }
+ if ((m->b & MOUSE_MASK_META) ||
+ m->x == pd->px ||
+ m->x == pd->px + pd->sx - 1 ||
+ m->y == pd->py ||
+ m->y == pd->py + pd->sy - 1) {
+ if (!MOUSE_DRAG(m->b))
+ goto out;
+ if (MOUSE_BUTTONS(m->lb) == 0)
+ pd->dragging = MOVE;
+ else if (MOUSE_BUTTONS(m->lb) == 2)
+ pd->dragging = SIZE;
+ pd->dx = m->lx - pd->px;
+ pd->dy = m->ly - pd->py;
+ goto out;
+ }
+ }
+
+ if (pd->ictx != NULL && (pd->flags & POPUP_WRITEKEYS)) {
+ if (KEYC_IS_MOUSE(event->key))
+ return (0);
+ if ((~pd->flags & POPUP_CLOSEEXIT) &&
+ (event->key == '\033' || event->key == '\003'))
+ return (1);
+ if (pd->job == NULL)
+ return (0);
+ input_key(NULL, &pd->s, job_get_event(pd->job), event->key);
+ return (0);
+ }
+
+ if (pd->cmd == NULL)
+ return (1);
+
+ ft = format_create(NULL, pd->item, FORMAT_NONE, 0);
+ if (cmd_find_valid_state(fs))
+ format_defaults(ft, c, fs->s, fs->wl, fs->wp);
+ else
+ format_defaults(ft, c, NULL, NULL, NULL);
+ format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key));
+ if (KEYC_IS_MOUSE(event->key)) {
+ format_add(ft, "popup_mouse", "1");
+ format_add(ft, "popup_mouse_x", "%u", m->x - pd->px);
+ format_add(ft, "popup_mouse_y", "%u", m->y - pd->py);
+ }
+ cmd = format_expand(ft, pd->cmd);
+ format_free(ft);
+
+ pr = cmd_parse_from_string(cmd, NULL);
+ switch (pr->status) {
+ case CMD_PARSE_EMPTY:
+ break;
+ case CMD_PARSE_ERROR:
+ new_item = cmdq_get_error(pr->error);
+ free(pr->error);
+ cmdq_append(c, new_item);
+ break;
+ case CMD_PARSE_SUCCESS:
+ if (pd->item != NULL)
+ m = &pd->item->shared->mouse;
+ else
+ m = NULL;
+ new_item = cmdq_get_command(pr->cmdlist, fs, m, 0);
+ cmd_list_free(pr->cmdlist);
+ cmdq_append(c, new_item);
+ break;
+ }
+ return (1);
+
+out:
+ pd->lx = m->x;
+ pd->ly = m->y;
+ pd->lb = m->b;
+ return (0);
+}
+
+static void
+popup_job_update_cb(struct job *job)
+{
+ struct popup_data *pd = job_get_data(job);
+ struct evbuffer *evb = job_get_event(job)->input;
+ struct screen *s = &pd->s;
+ void *data = EVBUFFER_DATA(evb);
+ size_t size = EVBUFFER_LENGTH(evb);
+
+ if (size != 0) {
+ input_parse_screen(pd->ictx, s, data, size);
+ evbuffer_drain(evb, size);
+ pd->c->flags |= CLIENT_REDRAWOVERLAY;
+ }
+}
+
+static void
+popup_job_complete_cb(struct job *job)
+{
+ struct popup_data *pd = job_get_data(job);
+ int status;
+
+ status = job_get_status(pd->job);
+ if (WIFEXITED(status))
+ pd->status = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ pd->status = WTERMSIG(status);
+ else
+ pd->status = 0;
+ pd->job = NULL;
+
+ if (pd->flags & POPUP_CLOSEEXIT)
+ server_client_clear_overlay(pd->c);
+}
+
+u_int
+popup_width(struct cmdq_item *item, u_int nlines, const char **lines,
+ struct client *c, struct cmd_find_state *fs)
+{
+ char *copy, *next, *loop, *tmp;
+ struct format_tree *ft;
+ u_int i, width = 0, tmpwidth;
+
+ ft = format_create(item->client, item, FORMAT_NONE, 0);
+ if (fs != NULL && cmd_find_valid_state(fs))
+ format_defaults(ft, c, fs->s, fs->wl, fs->wp);
+ else
+ format_defaults(ft, c, NULL, NULL, NULL);
+
+ for (i = 0; i < nlines; i++) {
+ copy = next = xstrdup(lines[i]);
+ while ((loop = strsep(&next, "\n")) != NULL) {
+ tmp = format_expand(ft, loop);
+ tmpwidth = format_width(tmp);
+ if (tmpwidth > width)
+ width = tmpwidth;
+ free(tmp);
+ }
+ }
+ free(copy);
+
+ format_free(ft);
+ return (width);
+}
+
+int
+popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx,
+ u_int sy, u_int nlines, const char **lines, const char *shellcmd,
+ const char *cmd, const char *cwd, struct client *c,
+ struct cmd_find_state *fs)
+{
+ struct popup_data *pd;
+ u_int i;
+ struct session *s;
+ int jobflags;
+
+ if (sx < 3 || sy < 3)
+ return (-1);
+ if (c->tty.sx < sx || c->tty.sy < sy)
+ return (-1);
+ if (nlines > sy - 2)
+ nlines = sy - 2;
+
+ pd = xcalloc(1, sizeof *pd);
+ pd->item = item;
+ pd->flags = flags;
+
+ pd->c = c;
+ pd->c->references++;
+
+ pd->status = 128 + SIGHUP;
+
+ if (fs != NULL)
+ cmd_find_copy_state(&pd->fs, fs);
+ screen_init(&pd->s, sx - 2, sy - 2, 0);
+
+ if (cmd != NULL)
+ pd->cmd = xstrdup(cmd);
+
+ pd->px = px;
+ pd->py = py;
+ pd->sx = sx;
+ pd->sy = sy;
+
+ pd->nlines = nlines;
+ if (pd->nlines != 0)
+ pd->lines = xreallocarray(NULL, pd->nlines, sizeof *pd->lines);
+
+ for (i = 0; i < pd->nlines; i++)
+ pd->lines[i] = xstrdup(lines[i]);
+ popup_write_screen(c, pd);
+
+ if (shellcmd != NULL) {
+ pd->ictx = input_init(NULL);
+
+ if (fs != NULL)
+ s = fs->s;
+ else
+ s = NULL;
+ jobflags = JOB_NOWAIT|JOB_PTY;
+ if (flags & POPUP_WRITEKEYS)
+ jobflags |= JOB_KEEPWRITE;
+ pd->job = job_run(shellcmd, s, cwd, popup_job_update_cb,
+ popup_job_complete_cb, NULL, pd, jobflags, pd->sx - 2,
+ pd->sy - 2);
+ }
+
+ server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
+ popup_draw_cb, popup_key_cb, popup_free_cb, pd);
+ return (0);
+}
diff --git a/screen-redraw.c b/screen-redraw.c
index e7f4f077..211f7f79 100644
--- a/screen-redraw.c
+++ b/screen-redraw.c
@@ -482,6 +482,8 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j,
u_int type, x = ctx->ox + i, y = ctx->oy + j;
int flag, pane_status = ctx->pane_status;
+ if (c->overlay_check != NULL && !c->overlay_check(c, x, y))
+ return;
type = screen_redraw_check_cell(c, x, y, pane_status, &wp);
if (type == CELL_INSIDE)
return;
diff --git a/server-client.c b/server-client.c
index f3a2b044..cd33b547 100644
--- a/server-client.c
+++ b/server-client.c
@@ -43,7 +43,6 @@ static void server_client_check_redraw(struct client *);
static void server_client_set_title(struct client *);
static void server_client_reset_state(struct client *);
static int server_client_assume_paste(struct session *);
-static void server_client_clear_overlay(struct client *);
static void server_client_resize_event(int, short, void *);
static void server_client_dispatch(struct imsg *, void *);
@@ -81,8 +80,10 @@ server_client_overlay_timer(__unused int fd, __unused short events, void *data)
/* Set an overlay on client. */
void
-server_client_set_overlay(struct client *c, u_int delay, overlay_draw_cb drawcb,
- overlay_key_cb keycb, overlay_free_cb freecb, void *data)
+server_client_set_overlay(struct client *c, u_int delay,
+ overlay_check_cb checkcb, overlay_mode_cb modecb,
+ overlay_draw_cb drawcb, overlay_key_cb keycb, overlay_free_cb freecb,
+ void *data)
{
struct timeval tv;
@@ -98,17 +99,21 @@ server_client_set_overlay(struct client *c, u_int delay, overlay_draw_cb drawcb,
if (delay != 0)
evtimer_add(&c->overlay_timer, &tv);
+ c->overlay_check = checkcb;
+ c->overlay_mode = modecb;
c->overlay_draw = drawcb;
c->overlay_key = keycb;
c->overlay_free = freecb;
c->overlay_data = data;
- c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR);
+ c->tty.flags |= TTY_FREEZE;
+ if (c->overlay_mode == NULL)
+ c->tty.flags |= TTY_NOCURSOR;
server_redraw_client(c);
}
/* Clear overlay mode on client. */
-static void
+void
server_client_clear_overlay(struct client *c)
{
if (c->overlay_draw == NULL)
@@ -120,8 +125,12 @@ server_client_clear_overlay(struct client *c)
if (c->overlay_free != NULL)
c->overlay_free(c);
+ c->overlay_check = NULL;
+ c->overlay_mode = NULL;
c->overlay_draw = NULL;
c->overlay_key = NULL;
+ c->overlay_free = NULL;
+ c->overlay_data = NULL;
c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
server_redraw_client(c);
@@ -1485,35 +1494,48 @@ server_client_reset_state(struct client *c)
{
struct window *w = c->session->curw->window;
struct window_pane *wp = w->active, *loop;
- struct screen *s = wp->screen;
+ struct screen *s;
struct options *oo = c->session->options;
int mode, cursor = 0;
u_int cx = 0, cy = 0, ox, oy, sx, sy;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
- if (c->overlay_draw != NULL)
- return;
- mode = s->mode;
+ /* Get mode from overlay if any, else from screen. */
+ if (c->overlay_draw != NULL) {
+ s = NULL;
+ if (c->overlay_mode == NULL)
+ mode = 0;
+ else
+ mode = c->overlay_mode(c, &cx, &cy);
+ } else {
+ s = wp->screen;
+ mode = s->mode;
+ }
+ log_debug("%s: client %s mode %x", __func__, c->name, mode);
+
+ /* Reset region and margin. */
tty_region_off(&c->tty);
tty_margin_off(&c->tty);
/* Move cursor to pane cursor and offset. */
- cursor = 0;
- tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
- if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx &&
- wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) {
- cursor = 1;
-
- cx = wp->xoff + s->cx - ox;
- cy = wp->yoff + s->cy - oy;
-
- if (status_at_line(c) == 0)
- cy += status_line_size(c);
+ if (c->overlay_draw == NULL) {
+ cursor = 0;
+ tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
+ if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx &&
+ wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) {
+ cursor = 1;
+
+ cx = wp->xoff + s->cx - ox;
+ cy = wp->yoff + s->cy - oy;
+
+ if (status_at_line(c) == 0)
+ cy += status_line_size(c);
+ }
+ if (!cursor)
+ mode &= ~MODE_CURSOR;
}
- if (!cursor)
- mode &= ~MODE_CURSOR;
tty_cursor(&c->tty, cx, cy);
/*
@@ -1522,16 +1544,18 @@ server_client_reset_state(struct client *c)
*/
if (options_get_number(oo, "mouse")) {
mode &= ~ALL_MOUSE_MODES;
- TAILQ_FOREACH(loop, &w->panes, entry) {
- if (loop->screen->mode & MODE_MOUSE_ALL)
- mode |= MODE_MOUSE_ALL;
+ if (c->overlay_draw == NULL) {
+ TAILQ_FOREACH(loop, &w->panes, entry) {
+ if (loop->screen->mode & MODE_MOUSE_ALL)
+ mode |= MODE_MOUSE_ALL;
+ }
}
if (~mode & MODE_MOUSE_ALL)
mode |= MODE_MOUSE_BUTTON;
}
/* Clear bracketed paste mode if at the prompt. */
- if (c->prompt_string != NULL)
+ if (c->overlay_draw == NULL && c->prompt_string != NULL)
mode &= ~MODE_BRACKETPASTE;
/* Set the terminal mode and reset attributes. */
diff --git a/tmux.1 b/tmux.1
index 8cd4841f..263bedc1 100644
--- a/tmux.1
+++ b/tmux.1
@@ -4382,7 +4382,10 @@ The following variables are available, where appropriate:
.It Li "pane_top" Ta "" Ta "Top of pane"
.It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane"
.It Li "pane_width" Ta "" Ta "Width of pane"
-.It Li "pid" Ta "" Ta "Server PID"
+.It Li "pid" Ta "" Ta "Server PID"
+.It Li "popup_key" Ta "" Ta "Key pressed in popup"
+.It Li "popup_mouse_x" Ta "" Ta "Mouse X position in popup"
+.It Li "popup_mouse_y" Ta "" Ta "Mouse Y position in popup"
.It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated"
.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode"
.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane"
@@ -4972,6 +4975,92 @@ lists the format variables and their values.
.Fl I
forwards any input read from stdin to the empty pane given by
.Ar target-pane .
+.It Xo Ic display-popup
+.Op Fl CEK
+.Op Fl c Ar target-client
+.Op Fl d Ar start-directory
+.Op Fl h Ar height
+.Op Fl R Ar shell-command
+.Op Fl t Ar target-pane
+.Op Fl w Ar width
+.Op Fl x Ar position
+.Op Fl y Ar position
+.Op Ar command Ar line Ar ...
+.Xc
+.D1 (alias: Ic popup )
+Display a popup on
+.Ar target-client .
+A popup is a rectangular box drawn over the top of any panes.
+Panes are not updated while a popup is present.
+The popup content may be given in two ways:
+.Bl -enum -offset Ds
+.It
+A set of lines as arguments.
+Each line is a format which is expanded using
+.Ar target-pane
+as the target.
+If a line contains newlines it is split into multiple lines.
+Lines may use styles, see the
+.Sx STYLES
+section.
+.It
+A shell command given by
+.Fl R
+which is run and any output shown in the pane.
+.El
+.Pp
+The first argument,
+.Ar command ,
+is a
+.Nm
+command which is run when a key is pressed.
+The key is available in the
+.Ql popup_key
+format.
+After
+.Ar command
+is run, the popup is closed.
+It may be empty to discard any key presses.
+If
+.Fl K
+is given together with
+.Fl R,
+key presses are instead passed to the
+.Fl R
+shell command.
+.Fl E
+closes the popup automatically when
+.Ar shell-command
+exits.
+With
+.Fl K ,
+.Ql Escape
+and
+.Ql C-c
+close the popup unless
+.Fl E
+is also given.
+.Pp
+.Fl x
+and
+.Fl y
+give the position of the popup, they have the same meaning as for the
+.Ic display-menu
+command.
+.Fl w
+and
+.Fl h
+give the width and height - both may be a percentage (followed by
+.Ql % ) .
+If omitted, without
+.Fl R
+they are calculated from the given lines and with
+.Fl R
+they use half the terminal size.
+.Pp
+The
+.Fl C
+flag closes any popup on the client.
.El
.Sh BUFFERS
.Nm
diff --git a/tmux.h b/tmux.h
index 424a0ddf..f885287b 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1515,6 +1515,8 @@ RB_HEAD(client_files, client_file);
/* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *);
+typedef int (*overlay_check_cb)(struct client *, u_int, u_int);
+typedef int (*overlay_mode_cb)(struct client *, u_int *, u_int *);
typedef void (*overlay_draw_cb)(struct client *, struct screen_redraw_ctx *);
typedef int (*overlay_key_cb)(struct client *, struct key_event *);
typedef void (*overlay_free_cb)(struct client *);
@@ -1630,6 +1632,8 @@ struct client {
u_int pan_ox;
u_int pan_oy;
+ overlay_check_cb overlay_check;
+ overlay_mode_cb overlay_mode;
overlay_draw_cb overlay_draw;
overlay_key_cb overlay_key;
overlay_free_cb overlay_free;
@@ -1934,6 +1938,7 @@ struct job *job_run(const char *, struct session *, const char *,
job_update_cb, job_complete_cb, job_free_cb, void *, int,
int, int);
void job_free(struct job *);
+void job_resize(struct job *, u_int, u_int);
void job_check_died(pid_t, int);
int job_get_status(struct job *);
void *job_get_data(struct job *);
@@ -2216,8 +2221,10 @@ void server_add_accept(int);
/* server-client.c */
u_int server_client_how_many(void);
-void server_client_set_overlay(struct client *, u_int, overlay_draw_cb,
- overlay_key_cb, overlay_free_cb, void *);
+void server_client_set_overlay(struct client *, u_int, overlay_check_cb,
+ overlay_mode_cb, overlay_draw_cb, overlay_key_cb,
+ overlay_free_cb, void *);
+void server_client_clear_overlay(struct client *);
void server_client_set_key_table(struct client *, const char *);
const char *server_client_get_key_table(struct client *);
int server_client_check_nested(struct client *);
@@ -2751,6 +2758,15 @@ int menu_display(struct menu *, int, struct cmdq_item *, u_int,
u_int, struct client *, struct cmd_find_state *,
menu_choice_cb, void *);
+/* popup.c */
+#define POPUP_WRITEKEYS 0x1
+#define POPUP_CLOSEEXIT 0x2
+u_int popup_width(struct cmdq_item *, u_int, const char **,
+ struct client *, struct cmd_find_state *);
+int popup_display(int, struct cmdq_item *, u_int, u_int, u_int,
+ u_int, u_int, const char **, const char *, const char *,
+ const char *, struct client *, struct cmd_find_state *);
+
/* style.c */
int style_parse(struct style *,const struct grid_cell *,
const char *);
diff --git a/tty.c b/tty.c
index 862a1266..46ab1283 100644
--- a/tty.c
+++ b/tty.c
@@ -1250,6 +1250,16 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
return (&new);
}
+static int
+tty_check_overlay(struct tty *tty, u_int px, u_int py)
+{
+ struct client *c = tty->client;
+
+ if (c->overlay_check == NULL)
+ return (1);
+ return (c->overlay_check(c, px, py));
+}
+
void
tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
u_int px, u_int py, u_int nx, u_int atx, u_int aty)
@@ -1329,7 +1339,8 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
grid_view_get_cell(gd, px + i, py, &gc);
gcp = tty_check_codeset(tty, &gc);
if (len != 0 &&
- ((gcp->attr & GRID_ATTR_CHARSET) ||
+ (!tty_check_overlay(tty, atx + ux + width, aty) ||
+ (gcp->attr & GRID_ATTR_CHARSET) ||
gcp->flags != last.flags ||
gcp->attr != last.attr ||
gcp->fg != last.fg ||
@@ -1358,7 +1369,9 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
screen_select_cell(s, &last, gcp);
else
memcpy(&last, gcp, sizeof last);
- if (ux + gcp->data.width > nx) {
+ if (!tty_check_overlay(tty, atx + ux, aty))
+ ux += gcp->data.width;
+ else if (ux + gcp->data.width > nx) {
tty_attributes(tty, &last, wp);
tty_cursor(tty, atx + ux, aty);
for (j = 0; j < gcp->data.width; j++) {
@@ -1372,7 +1385,7 @@ tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s,
tty_cursor(tty, atx + ux, aty);
for (j = 0; j < gcp->data.size; j++)
tty_putc(tty, gcp->data.data[j]);
- ux += gc.data.width;
+ ux += gcp->data.width;
} else {
memcpy(buf + len, gcp->data.data, gcp->data.size);
len += gcp->data.size;