mirror of
https://github.com/tmux/tmux.git
synced 2026-06-24 04:47:57 +00:00
is generated and cached in the client: it holds positions and sizes of panes, borders and so on. The scene is invalidated when a pane is moved or resized or relevant option is changed. This scene is then drawn to the client as needed and text and colours are filled in. With Michael Grant.
859 lines
21 KiB
C
859 lines
21 KiB
C
/* $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 <paths.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "tmux.h"
|
|
|
|
struct popup_data {
|
|
struct client *c;
|
|
struct cmdq_item *item;
|
|
int flags;
|
|
char *title;
|
|
|
|
char *style;
|
|
char *border_style;
|
|
struct grid_cell border_cell;
|
|
enum box_lines border_lines;
|
|
|
|
struct screen s;
|
|
struct grid_cell defaults;
|
|
struct colour_palette palette;
|
|
|
|
struct visible_ranges r;
|
|
struct visible_ranges or[2];
|
|
|
|
struct job *job;
|
|
struct input_ctx *ictx;
|
|
int status;
|
|
popup_close_cb cb;
|
|
void *arg;
|
|
|
|
struct menu *menu;
|
|
struct menu_data *md;
|
|
int close;
|
|
|
|
/* Current position and size. */
|
|
u_int px;
|
|
u_int py;
|
|
u_int sx;
|
|
u_int sy;
|
|
|
|
/* Preferred position and size. */
|
|
u_int ppx;
|
|
u_int ppy;
|
|
u_int psx;
|
|
u_int psy;
|
|
|
|
enum { OFF, MOVE, SIZE } dragging;
|
|
u_int dx;
|
|
u_int dy;
|
|
|
|
u_int lx;
|
|
u_int ly;
|
|
u_int lb;
|
|
};
|
|
|
|
static const struct menu_item popup_menu_items[] = {
|
|
{ "Close", 'q', NULL },
|
|
{ "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL },
|
|
{ "", KEYC_NONE, NULL },
|
|
{ "Fill Space", 'F', NULL },
|
|
{ "Centre", 'C', NULL },
|
|
{ "", KEYC_NONE, NULL },
|
|
{ "To Horizontal Pane", 'h', NULL },
|
|
{ "To Vertical Pane", 'v', NULL },
|
|
|
|
{ NULL, KEYC_NONE, NULL }
|
|
};
|
|
|
|
static void
|
|
popup_free(struct popup_data *pd)
|
|
{
|
|
server_client_unref(pd->c);
|
|
|
|
if (pd->job != NULL)
|
|
job_free(pd->job);
|
|
input_free(pd->ictx);
|
|
|
|
free(pd->or[0].ranges);
|
|
free(pd->or[1].ranges);
|
|
free(pd->r.ranges);
|
|
screen_free(&pd->s);
|
|
colour_palette_free(&pd->palette);
|
|
|
|
free(pd->title);
|
|
free(pd->style);
|
|
free(pd->border_style);
|
|
free(pd);
|
|
}
|
|
|
|
static void
|
|
popup_reapply_styles(struct popup_data *pd)
|
|
{
|
|
struct client *c = pd->c;
|
|
struct session *s = c->session;
|
|
struct options *o;
|
|
struct format_tree *ft;
|
|
struct style sytmp;
|
|
|
|
if (s == NULL)
|
|
return;
|
|
o = s->curw->window->options;
|
|
|
|
ft = format_create_defaults(NULL, c, s, s->curw, NULL);
|
|
|
|
/* Reapply popup style from options. */
|
|
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
|
|
style_apply(&pd->defaults, o, "popup-style", ft);
|
|
if (pd->style != NULL) {
|
|
style_set(&sytmp, &grid_default_cell);
|
|
if (style_parse(&sytmp, &pd->defaults, pd->style) == 0) {
|
|
pd->defaults.fg = sytmp.gc.fg;
|
|
pd->defaults.bg = sytmp.gc.bg;
|
|
}
|
|
}
|
|
pd->defaults.attr = 0;
|
|
|
|
/* Reapply border style from options. */
|
|
memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell);
|
|
style_apply(&pd->border_cell, o, "popup-border-style", ft);
|
|
if (pd->border_style != NULL) {
|
|
style_set(&sytmp, &grid_default_cell);
|
|
if (style_parse(&sytmp, &pd->border_cell,
|
|
pd->border_style) == 0) {
|
|
pd->border_cell.fg = sytmp.gc.fg;
|
|
pd->border_cell.bg = sytmp.gc.bg;
|
|
}
|
|
}
|
|
pd->border_cell.attr = 0;
|
|
|
|
format_free(ft);
|
|
}
|
|
|
|
static void
|
|
popup_redraw_cb(const struct tty_ctx *ttyctx)
|
|
{
|
|
struct popup_data *pd = ttyctx->arg;
|
|
|
|
pd->c->flags |= CLIENT_REDRAWOVERLAY;
|
|
}
|
|
|
|
static int
|
|
popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
|
{
|
|
struct popup_data *pd = ttyctx->arg;
|
|
|
|
if (c != pd->c)
|
|
return (0);
|
|
if (pd->c->flags & CLIENT_REDRAWOVERLAY)
|
|
return (0);
|
|
|
|
ttyctx->wox = 0;
|
|
ttyctx->woy = 0;
|
|
ttyctx->wsx = c->tty.sx;
|
|
ttyctx->wsy = c->tty.sy;
|
|
|
|
if (pd->border_lines == BOX_LINES_NONE) {
|
|
ttyctx->xoff = ttyctx->rxoff = pd->px;
|
|
ttyctx->yoff = ttyctx->ryoff = pd->py;
|
|
} else {
|
|
ttyctx->xoff = ttyctx->rxoff = pd->px + 1;
|
|
ttyctx->yoff = ttyctx->ryoff = pd->py + 1;
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
static void
|
|
popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
|
|
{
|
|
struct popup_data *pd = ctx->arg;
|
|
|
|
memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults);
|
|
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
|
|
ttyctx->style_ctx.defaults = &ttyctx->defaults;
|
|
ttyctx->style_ctx.palette = &pd->palette;
|
|
ttyctx->redraw_cb = popup_redraw_cb;
|
|
ttyctx->set_client_cb = popup_set_client_cb;
|
|
ttyctx->arg = pd;
|
|
}
|
|
|
|
static struct screen *
|
|
popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
|
|
{
|
|
struct popup_data *pd = data;
|
|
|
|
if (pd->md != NULL)
|
|
return (menu_mode_cb(c, pd->md, cx, cy));
|
|
|
|
if (pd->border_lines == BOX_LINES_NONE) {
|
|
*cx = pd->px + pd->s.cx;
|
|
*cy = pd->py + pd->s.cy;
|
|
} else {
|
|
*cx = pd->px + 1 + pd->s.cx;
|
|
*cy = pd->py + 1 + pd->s.cy;
|
|
}
|
|
return (&pd->s);
|
|
}
|
|
|
|
/* Return parts of the input range which are not obstructed by the popup. */
|
|
static struct visible_ranges *
|
|
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
|
|
{
|
|
struct popup_data *pd = data;
|
|
struct visible_ranges *r = &pd->r;
|
|
struct visible_ranges *mr;
|
|
u_int i, j, k = 0;
|
|
|
|
if (pd->md != NULL) {
|
|
/*
|
|
* Work out the visible ranges for the menu (that is, the
|
|
* ranges not covered by the menu). A menu should have at most
|
|
* two ranges and we rely on this being the case.
|
|
*/
|
|
mr = menu_check_cb(c, pd->md, px, py, nx);
|
|
if (mr->used > 2)
|
|
fatalx("too many menu ranges");
|
|
|
|
/*
|
|
* Walk the ranges still visible under the menu and check if
|
|
* each is visible under the popup as well. At most there can be
|
|
* three total ranges if popup and menu do not intersect.
|
|
*/
|
|
for (i = 0; i < mr->used; i++) {
|
|
server_client_overlay_range(pd->px, pd->py, pd->sx,
|
|
pd->sy, r->ranges[i].px, py, r->ranges[i].nx,
|
|
&pd->or[i]);
|
|
}
|
|
|
|
/*
|
|
* We now have nonoverlapping ranges from left to right.
|
|
* Combine them together into the output.
|
|
*/
|
|
server_client_ensure_ranges(r, 3);
|
|
for (i = 0; i < mr->used; i++) {
|
|
for (j = 0; j < pd->or[i].used; j++) {
|
|
if (pd->or[i].ranges[j].nx == 0)
|
|
continue;
|
|
if (k >= 3)
|
|
fatalx("too many popup & menu ranges");
|
|
r->ranges[k].px = pd->or[i].ranges[j].px;
|
|
r->ranges[k].nx = pd->or[i].ranges[j].nx;
|
|
k++;
|
|
}
|
|
}
|
|
return (r);
|
|
}
|
|
|
|
server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
|
|
r);
|
|
return (r);
|
|
}
|
|
|
|
static void
|
|
popup_draw_cb(struct client *c, void *data)
|
|
{
|
|
struct popup_data *pd = data;
|
|
struct tty *tty = &c->tty;
|
|
struct screen s;
|
|
struct screen_write_ctx ctx;
|
|
u_int i, px = pd->px, py = pd->py;
|
|
struct grid_cell defaults;
|
|
struct tty_style_ctx style_ctx;
|
|
|
|
popup_reapply_styles(pd);
|
|
|
|
screen_init(&s, pd->sx, pd->sy, 0);
|
|
if (pd->s.hyperlinks != NULL) {
|
|
hyperlinks_free(s.hyperlinks);
|
|
s.hyperlinks = hyperlinks_copy(pd->s.hyperlinks);
|
|
}
|
|
screen_write_start(&ctx, &s);
|
|
screen_write_clearscreen(&ctx, 8);
|
|
|
|
if (pd->border_lines == BOX_LINES_NONE) {
|
|
screen_write_cursormove(&ctx, 0, 0, 0);
|
|
screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy);
|
|
} else if (pd->sx > 2 && pd->sy > 2) {
|
|
screen_write_box(&ctx, pd->sx, pd->sy, pd->border_lines,
|
|
&pd->border_cell, pd->title);
|
|
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);
|
|
|
|
memcpy(&defaults, &pd->defaults, sizeof defaults);
|
|
if (defaults.fg == 8)
|
|
defaults.fg = pd->palette.fg;
|
|
if (defaults.bg == 8)
|
|
defaults.bg = pd->palette.bg;
|
|
style_ctx.defaults = &defaults;
|
|
style_ctx.palette = &pd->palette;
|
|
style_ctx.dim = 0;
|
|
style_ctx.hyperlinks = s.hyperlinks;
|
|
|
|
if (pd->md != NULL) {
|
|
c->overlay_check = menu_check_cb;
|
|
c->overlay_data = pd->md;
|
|
} else {
|
|
c->overlay_check = NULL;
|
|
c->overlay_data = NULL;
|
|
}
|
|
for (i = 0; i < pd->sy; i++)
|
|
tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &style_ctx);
|
|
screen_free(&s);
|
|
if (pd->md != NULL) {
|
|
c->overlay_check = NULL;
|
|
c->overlay_data = NULL;
|
|
menu_draw_cb(c, pd->md);
|
|
}
|
|
c->overlay_check = popup_check_cb;
|
|
c->overlay_data = pd;
|
|
}
|
|
|
|
static void
|
|
popup_free_cb(struct client *c, void *data)
|
|
{
|
|
struct popup_data *pd = data;
|
|
struct cmdq_item *item = pd->item;
|
|
|
|
if (pd->md != NULL)
|
|
menu_free_cb(c, pd->md);
|
|
|
|
if (pd->cb != NULL)
|
|
pd->cb(pd->status, pd->arg);
|
|
|
|
if (item != NULL) {
|
|
if (cmdq_get_client(item) != NULL &&
|
|
cmdq_get_client(item)->session == NULL)
|
|
cmdq_get_client(item)->retval = pd->status;
|
|
cmdq_continue(item);
|
|
}
|
|
|
|
popup_free(pd);
|
|
}
|
|
|
|
static void
|
|
popup_resize_cb(__unused struct client *c, void *data)
|
|
{
|
|
struct popup_data *pd = data;
|
|
struct tty *tty = &c->tty;
|
|
|
|
if (pd == NULL)
|
|
return;
|
|
if (pd->md != NULL)
|
|
menu_free_cb(c, pd->md);
|
|
|
|
/* Adjust position and size. */
|
|
if (pd->psy > tty->sy)
|
|
pd->sy = tty->sy;
|
|
else
|
|
pd->sy = pd->psy;
|
|
if (pd->psx > tty->sx)
|
|
pd->sx = tty->sx;
|
|
else
|
|
pd->sx = pd->psx;
|
|
if (pd->ppy + pd->sy > tty->sy)
|
|
pd->py = tty->sy - pd->sy;
|
|
else
|
|
pd->py = pd->ppy;
|
|
if (pd->ppx + pd->sx > tty->sx)
|
|
pd->px = tty->sx - pd->sx;
|
|
else
|
|
pd->px = pd->ppx;
|
|
|
|
/* Avoid zero size screens. */
|
|
if (pd->border_lines == BOX_LINES_NONE) {
|
|
screen_resize(&pd->s, pd->sx, pd->sy, 0);
|
|
if (pd->job != NULL)
|
|
job_resize(pd->job, pd->sx, pd->sy );
|
|
} else if (pd->sx > 2 && pd->sy > 2) {
|
|
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
|
|
if (pd->job != NULL)
|
|
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
|
|
}
|
|
}
|
|
|
|
static void
|
|
popup_make_pane(struct popup_data *pd, enum layout_type type)
|
|
{
|
|
struct client *c = pd->c;
|
|
struct session *s = c->session;
|
|
struct window *w = s->curw->window;
|
|
struct layout_cell *lc;
|
|
struct window_pane *wp = w->active, *new_wp;
|
|
u_int hlimit;
|
|
const char *shell;
|
|
|
|
window_unzoom(w, 1);
|
|
|
|
lc = layout_split_pane(wp, type, -1, 0);
|
|
if (lc == NULL)
|
|
return;
|
|
hlimit = options_get_number(s->options, "history-limit");
|
|
new_wp = window_add_pane(wp->window, NULL, hlimit, 0);
|
|
layout_assign_pane(lc, new_wp, 0);
|
|
|
|
if (pd->job != NULL) {
|
|
new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty,
|
|
sizeof new_wp->tty);
|
|
pd->job = NULL;
|
|
}
|
|
|
|
screen_set_title(&pd->s, new_wp->base.title, 0);
|
|
screen_free(&new_wp->base);
|
|
memcpy(&new_wp->base, &pd->s, sizeof wp->base);
|
|
screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1);
|
|
screen_init(&pd->s, 1, 1, 0);
|
|
|
|
shell = options_get_string(s->options, "default-shell");
|
|
if (!checkshell(shell))
|
|
shell = _PATH_BSHELL;
|
|
new_wp->shell = xstrdup(shell);
|
|
|
|
window_pane_set_event(new_wp);
|
|
window_set_active_pane(w, new_wp, 1);
|
|
new_wp->flags |= PANE_CHANGED;
|
|
|
|
pd->close = 1;
|
|
}
|
|
|
|
static void
|
|
popup_menu_done(__unused struct menu *menu, __unused u_int choice,
|
|
key_code key, void *data)
|
|
{
|
|
struct popup_data *pd = data;
|
|
struct client *c = pd->c;
|
|
struct paste_buffer *pb;
|
|
const char *buf;
|
|
size_t len;
|
|
|
|
pd->md = NULL;
|
|
pd->menu = NULL;
|
|
server_redraw_client(pd->c);
|
|
|
|
switch (key) {
|
|
case 'p':
|
|
pb = paste_get_top(NULL);
|
|
if (pb != NULL) {
|
|
buf = paste_buffer_data(pb, &len);
|
|
bufferevent_write(job_get_event(pd->job), buf, len);
|
|
}
|
|
break;
|
|
case 'F':
|
|
pd->sx = c->tty.sx;
|
|
pd->sy = c->tty.sy;
|
|
pd->px = 0;
|
|
pd->py = 0;
|
|
server_redraw_client(c);
|
|
break;
|
|
case 'C':
|
|
pd->px = c->tty.sx / 2 - pd->sx / 2;
|
|
pd->py = c->tty.sy / 2 - pd->sy / 2;
|
|
server_redraw_client(c);
|
|
break;
|
|
case 'h':
|
|
popup_make_pane(pd, LAYOUT_LEFTRIGHT);
|
|
break;
|
|
case 'v':
|
|
popup_make_pane(pd, LAYOUT_TOPBOTTOM);
|
|
break;
|
|
case 'q':
|
|
pd->close = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
pd->ppx = px;
|
|
pd->ppy = py;
|
|
server_redraw_client(c);
|
|
} else if (pd->dragging == SIZE) {
|
|
if (pd->border_lines == BOX_LINES_NONE) {
|
|
if (m->x < pd->px + 1)
|
|
return;
|
|
if (m->y < pd->py + 1)
|
|
return;
|
|
} else {
|
|
if (m->x < pd->px + 3)
|
|
return;
|
|
if (m->y < pd->py + 3)
|
|
return;
|
|
}
|
|
pd->sx = m->x - pd->px;
|
|
pd->sy = m->y - pd->py;
|
|
pd->psx = pd->sx;
|
|
pd->psy = pd->sy;
|
|
|
|
if (pd->border_lines == BOX_LINES_NONE) {
|
|
screen_resize(&pd->s, pd->sx, pd->sy, 0);
|
|
if (pd->job != NULL)
|
|
job_resize(pd->job, pd->sx, pd->sy);
|
|
} else {
|
|
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
|
|
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, void *data, struct key_event *event)
|
|
{
|
|
struct popup_data *pd = data;
|
|
struct mouse_event *m = &event->m;
|
|
const char *buf;
|
|
size_t len;
|
|
u_int px, py, x;
|
|
enum { NONE, LEFT, RIGHT, TOP, BOTTOM } border = NONE;
|
|
|
|
if (pd->md != NULL) {
|
|
if (menu_key_cb(c, pd->md, event) == 1) {
|
|
pd->md = NULL;
|
|
pd->menu = NULL;
|
|
if (pd->close)
|
|
server_client_clear_overlay(c);
|
|
else
|
|
server_redraw_client(c);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
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) == MOUSE_BUTTON_3)
|
|
goto menu;
|
|
return (0);
|
|
}
|
|
if (pd->border_lines != BOX_LINES_NONE) {
|
|
if (m->x == pd->px)
|
|
border = LEFT;
|
|
else if (m->x == pd->px + pd->sx - 1)
|
|
border = RIGHT;
|
|
else if (m->y == pd->py)
|
|
border = TOP;
|
|
else if (m->y == pd->py + pd->sy - 1)
|
|
border = BOTTOM;
|
|
}
|
|
if ((m->b & MOUSE_MASK_MODIFIERS) == 0 &&
|
|
MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3 &&
|
|
(border == LEFT || border == TOP))
|
|
goto menu;
|
|
if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) ||
|
|
(border != NONE && !MOUSE_DRAG(m->lb))) {
|
|
if (!MOUSE_DRAG(m->b))
|
|
goto out;
|
|
if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_1)
|
|
pd->dragging = MOVE;
|
|
else if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_3)
|
|
pd->dragging = SIZE;
|
|
pd->dx = m->lx - pd->px;
|
|
pd->dy = m->ly - pd->py;
|
|
goto out;
|
|
}
|
|
}
|
|
if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) ||
|
|
pd->job == NULL) &&
|
|
(event->key == '\033' || event->key == ('c'|KEYC_CTRL)))
|
|
return (1);
|
|
if (pd->job == NULL && (pd->flags & POPUP_CLOSEANYKEY) &&
|
|
!KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key))
|
|
return (1);
|
|
if (pd->job != NULL) {
|
|
if (KEYC_IS_MOUSE(event->key)) {
|
|
/* Must be inside, checked already. */
|
|
if (pd->border_lines == BOX_LINES_NONE) {
|
|
px = m->x - pd->px;
|
|
py = m->y - pd->py;
|
|
} else {
|
|
px = m->x - pd->px - 1;
|
|
py = m->y - pd->py - 1;
|
|
}
|
|
if (!input_key_get_mouse(&pd->s, m, px, py, &buf, &len))
|
|
return (0);
|
|
bufferevent_write(job_get_event(pd->job), buf, len);
|
|
return (0);
|
|
}
|
|
input_key(&pd->s, job_get_event(pd->job), event->key);
|
|
}
|
|
return (0);
|
|
|
|
menu:
|
|
pd->menu = menu_create("");
|
|
menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL);
|
|
if (m->x >= (pd->menu->width + 4) / 2)
|
|
x = m->x - (pd->menu->width + 4) / 2;
|
|
else
|
|
x = 0;
|
|
pd->md = menu_prepare(pd->menu, 0, 0, NULL, x, m->y, c,
|
|
BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL, popup_menu_done, pd);
|
|
c->flags |= CLIENT_REDRAWOVERLAY;
|
|
|
|
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 client *c = pd->c;
|
|
struct screen *s = &pd->s;
|
|
void *data = EVBUFFER_DATA(evb);
|
|
size_t size = EVBUFFER_LENGTH(evb);
|
|
|
|
if (size == 0)
|
|
return;
|
|
|
|
if (pd->md != NULL) {
|
|
c->overlay_check = menu_check_cb;
|
|
c->overlay_data = pd->md;
|
|
} else {
|
|
c->overlay_check = NULL;
|
|
c->overlay_data = NULL;
|
|
}
|
|
input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size);
|
|
c->overlay_check = popup_check_cb;
|
|
c->overlay_data = pd;
|
|
|
|
evbuffer_drain(evb, size);
|
|
}
|
|
|
|
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) ||
|
|
((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0))
|
|
server_client_clear_overlay(pd->c);
|
|
}
|
|
|
|
int
|
|
popup_present(struct client *c)
|
|
{
|
|
return (c->overlay_draw == popup_draw_cb);
|
|
}
|
|
|
|
int
|
|
popup_modify(struct client *c, const char *title, const char *style,
|
|
const char *border_style, enum box_lines lines, int flags)
|
|
{
|
|
struct popup_data *pd = c->overlay_data;
|
|
struct style sytmp;
|
|
|
|
if (title != NULL) {
|
|
if (pd->title != NULL)
|
|
free(pd->title);
|
|
pd->title = xstrdup(title);
|
|
}
|
|
if (border_style != NULL) {
|
|
free(pd->border_style);
|
|
pd->border_style = xstrdup(border_style);
|
|
style_set(&sytmp, &pd->border_cell);
|
|
if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
|
|
pd->border_cell.fg = sytmp.gc.fg;
|
|
pd->border_cell.bg = sytmp.gc.bg;
|
|
}
|
|
}
|
|
if (style != NULL) {
|
|
free(pd->style);
|
|
pd->style = xstrdup(style);
|
|
style_set(&sytmp, &pd->defaults);
|
|
if (style_parse(&sytmp, &pd->defaults, style) == 0) {
|
|
pd->defaults.fg = sytmp.gc.fg;
|
|
pd->defaults.bg = sytmp.gc.bg;
|
|
}
|
|
}
|
|
if (lines != BOX_LINES_DEFAULT) {
|
|
if (lines == BOX_LINES_NONE && pd->border_lines != lines) {
|
|
screen_resize(&pd->s, pd->sx, pd->sy, 1);
|
|
job_resize(pd->job, pd->sx, pd->sy);
|
|
} else if (pd->border_lines == BOX_LINES_NONE &&
|
|
pd->border_lines != lines) {
|
|
screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 1);
|
|
job_resize(pd->job, pd->sx - 2, pd->sy - 2);
|
|
}
|
|
pd->border_lines = lines;
|
|
tty_resize(&c->tty);
|
|
}
|
|
if (flags != -1)
|
|
pd->flags = flags;
|
|
|
|
server_redraw_client(c);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
|
|
u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd,
|
|
int argc, char **argv, const char *cwd, const char *title, struct client *c,
|
|
struct session *s, const char *style, const char *border_style,
|
|
popup_close_cb cb, void *arg)
|
|
{
|
|
struct popup_data *pd;
|
|
u_int jx, jy;
|
|
struct options *o;
|
|
struct style sytmp;
|
|
|
|
if (s != NULL)
|
|
o = s->curw->window->options;
|
|
else
|
|
o = c->session->curw->window->options;
|
|
|
|
if (lines == BOX_LINES_DEFAULT)
|
|
lines = options_get_number(o, "popup-border-lines");
|
|
if (lines == BOX_LINES_NONE) {
|
|
if (sx < 1 || sy < 1)
|
|
return (-1);
|
|
jx = sx;
|
|
jy = sy;
|
|
} else {
|
|
if (sx < 3 || sy < 3)
|
|
return (-1);
|
|
jx = sx - 2;
|
|
jy = sy - 2;
|
|
}
|
|
if (c->tty.sx < sx || c->tty.sy < sy)
|
|
return (-1);
|
|
|
|
pd = xcalloc(1, sizeof *pd);
|
|
pd->item = item;
|
|
pd->flags = flags;
|
|
|
|
if (title != NULL)
|
|
pd->title = xstrdup(title);
|
|
if (style != NULL)
|
|
pd->style = xstrdup(style);
|
|
if (border_style != NULL)
|
|
pd->border_style = xstrdup(border_style);
|
|
|
|
pd->c = c;
|
|
pd->c->references++;
|
|
|
|
pd->cb = cb;
|
|
pd->arg = arg;
|
|
pd->status = 128 + SIGHUP;
|
|
|
|
pd->border_lines = lines;
|
|
memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell);
|
|
style_apply(&pd->border_cell, o, "popup-border-style", NULL);
|
|
if (border_style != NULL) {
|
|
style_set(&sytmp, &grid_default_cell);
|
|
if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
|
|
pd->border_cell.fg = sytmp.gc.fg;
|
|
pd->border_cell.bg = sytmp.gc.bg;
|
|
}
|
|
}
|
|
pd->border_cell.attr = 0;
|
|
|
|
screen_init(&pd->s, jx, jy, 0);
|
|
screen_set_default_cursor(&pd->s, global_w_options);
|
|
colour_palette_init(&pd->palette);
|
|
colour_palette_from_option(&pd->palette, global_w_options);
|
|
|
|
memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
|
|
style_apply(&pd->defaults, o, "popup-style", NULL);
|
|
if (style != NULL) {
|
|
style_set(&sytmp, &grid_default_cell);
|
|
if (style_parse(&sytmp, &pd->defaults, style) == 0) {
|
|
pd->defaults.fg = sytmp.gc.fg;
|
|
pd->defaults.bg = sytmp.gc.bg;
|
|
}
|
|
}
|
|
pd->defaults.attr = 0;
|
|
|
|
pd->px = px;
|
|
pd->py = py;
|
|
pd->sx = sx;
|
|
pd->sy = sy;
|
|
|
|
pd->ppx = px;
|
|
pd->ppy = py;
|
|
pd->psx = sx;
|
|
pd->psy = sy;
|
|
|
|
pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
|
|
popup_job_update_cb, popup_job_complete_cb, NULL, pd,
|
|
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
|
|
if (pd->job == NULL) {
|
|
popup_free(pd);
|
|
return (-1);
|
|
}
|
|
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette, c);
|
|
|
|
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
|
|
popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);
|
|
return (0);
|
|
}
|