1*20c4f615Snicm /* $OpenBSD: popup.c,v 1.57 2025/01/12 14:36:28 nicm Exp $ */ 20cac3d2dSnicm 30cac3d2dSnicm /* 40cac3d2dSnicm * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com> 50cac3d2dSnicm * 60cac3d2dSnicm * Permission to use, copy, modify, and distribute this software for any 70cac3d2dSnicm * purpose with or without fee is hereby granted, provided that the above 80cac3d2dSnicm * copyright notice and this permission notice appear in all copies. 90cac3d2dSnicm * 100cac3d2dSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 110cac3d2dSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 120cac3d2dSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 130cac3d2dSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 140cac3d2dSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 150cac3d2dSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 160cac3d2dSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 170cac3d2dSnicm */ 180cac3d2dSnicm 190cac3d2dSnicm #include <sys/types.h> 200cac3d2dSnicm #include <sys/wait.h> 210cac3d2dSnicm 22f67736f8Snicm #include <paths.h> 230cac3d2dSnicm #include <signal.h> 240cac3d2dSnicm #include <stdlib.h> 250cac3d2dSnicm #include <string.h> 26f67736f8Snicm #include <unistd.h> 270cac3d2dSnicm 280cac3d2dSnicm #include "tmux.h" 290cac3d2dSnicm 300cac3d2dSnicm struct popup_data { 310cac3d2dSnicm struct client *c; 320cac3d2dSnicm struct cmdq_item *item; 330cac3d2dSnicm int flags; 34dad8e5c0Snicm char *title; 350cac3d2dSnicm 36f7bae471Snicm struct grid_cell border_cell; 37f7bae471Snicm enum box_lines border_lines; 38f7bae471Snicm 390cac3d2dSnicm struct screen s; 40f7bae471Snicm struct grid_cell defaults; 4133a1e283Snicm struct colour_palette palette; 42f7bae471Snicm 430cac3d2dSnicm struct job *job; 440cac3d2dSnicm struct input_ctx *ictx; 450cac3d2dSnicm int status; 46a6c9106fSnicm popup_close_cb cb; 47a6c9106fSnicm void *arg; 480cac3d2dSnicm 49f38784aeSnicm struct menu *menu; 50f38784aeSnicm struct menu_data *md; 51f38784aeSnicm int close; 52f38784aeSnicm 53c0287628Snicm /* Current position and size. */ 540cac3d2dSnicm u_int px; 550cac3d2dSnicm u_int py; 560cac3d2dSnicm u_int sx; 570cac3d2dSnicm u_int sy; 580cac3d2dSnicm 59c0287628Snicm /* Preferred position and size. */ 60c0287628Snicm u_int ppx; 61c0287628Snicm u_int ppy; 62c0287628Snicm u_int psx; 63c0287628Snicm u_int psy; 64c0287628Snicm 650cac3d2dSnicm enum { OFF, MOVE, SIZE } dragging; 660cac3d2dSnicm u_int dx; 670cac3d2dSnicm u_int dy; 680cac3d2dSnicm 690cac3d2dSnicm u_int lx; 700cac3d2dSnicm u_int ly; 710cac3d2dSnicm u_int lb; 720cac3d2dSnicm }; 730cac3d2dSnicm 74f67736f8Snicm struct popup_editor { 75f67736f8Snicm char *path; 76f67736f8Snicm popup_finish_edit_cb cb; 77f67736f8Snicm void *arg; 78f67736f8Snicm }; 79f67736f8Snicm 80f38784aeSnicm static const struct menu_item popup_menu_items[] = { 81f38784aeSnicm { "Close", 'q', NULL }, 82f38784aeSnicm { "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL }, 83f38784aeSnicm { "", KEYC_NONE, NULL }, 84f38784aeSnicm { "Fill Space", 'F', NULL }, 85f38784aeSnicm { "Centre", 'C', NULL }, 867caf7dd1Snicm { "", KEYC_NONE, NULL }, 875edeef37Snicm { "To Horizontal Pane", 'h', NULL }, 885edeef37Snicm { "To Vertical Pane", 'v', NULL }, 897caf7dd1Snicm 907caf7dd1Snicm { NULL, KEYC_NONE, NULL } 917caf7dd1Snicm }; 927caf7dd1Snicm 937caf7dd1Snicm static const struct menu_item popup_internal_menu_items[] = { 947caf7dd1Snicm { "Close", 'q', NULL }, 957caf7dd1Snicm { "", KEYC_NONE, NULL }, 967caf7dd1Snicm { "Fill Space", 'F', NULL }, 977caf7dd1Snicm { "Centre", 'C', NULL }, 98f38784aeSnicm 99f38784aeSnicm { NULL, KEYC_NONE, NULL } 100f38784aeSnicm }; 101f38784aeSnicm 1020cac3d2dSnicm static void 10383e83a91Snicm popup_redraw_cb(const struct tty_ctx *ttyctx) 10483e83a91Snicm { 10583e83a91Snicm struct popup_data *pd = ttyctx->arg; 10683e83a91Snicm 10783e83a91Snicm pd->c->flags |= CLIENT_REDRAWOVERLAY; 10883e83a91Snicm } 10983e83a91Snicm 11083e83a91Snicm static int 11183e83a91Snicm popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) 11283e83a91Snicm { 11383e83a91Snicm struct popup_data *pd = ttyctx->arg; 11483e83a91Snicm 115572e1496Snicm if (c != pd->c) 116572e1496Snicm return (0); 11783e83a91Snicm if (pd->c->flags & CLIENT_REDRAWOVERLAY) 118572e1496Snicm return (0); 11983e83a91Snicm 12083e83a91Snicm ttyctx->bigger = 0; 12183e83a91Snicm ttyctx->wox = 0; 12283e83a91Snicm ttyctx->woy = 0; 12383e83a91Snicm ttyctx->wsx = c->tty.sx; 12483e83a91Snicm ttyctx->wsy = c->tty.sy; 12583e83a91Snicm 126f7bae471Snicm if (pd->border_lines == BOX_LINES_NONE) { 12701a3f482Snicm ttyctx->xoff = ttyctx->rxoff = pd->px; 12801a3f482Snicm ttyctx->yoff = ttyctx->ryoff = pd->py; 12901a3f482Snicm } else { 13083e83a91Snicm ttyctx->xoff = ttyctx->rxoff = pd->px + 1; 13183e83a91Snicm ttyctx->yoff = ttyctx->ryoff = pd->py + 1; 13201a3f482Snicm } 13383e83a91Snicm 13483e83a91Snicm return (1); 13583e83a91Snicm } 13683e83a91Snicm 13783e83a91Snicm static void 13883e83a91Snicm popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) 13983e83a91Snicm { 14083e83a91Snicm struct popup_data *pd = ctx->arg; 14183e83a91Snicm 142f7bae471Snicm memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults); 14333a1e283Snicm ttyctx->palette = &pd->palette; 14483e83a91Snicm ttyctx->redraw_cb = popup_redraw_cb; 14583e83a91Snicm ttyctx->set_client_cb = popup_set_client_cb; 14683e83a91Snicm ttyctx->arg = pd; 14783e83a91Snicm } 14883e83a91Snicm 14983e83a91Snicm static struct screen * 150f38784aeSnicm popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) 1510cac3d2dSnicm { 152f38784aeSnicm struct popup_data *pd = data; 153f38784aeSnicm 154f38784aeSnicm if (pd->md != NULL) 155f38784aeSnicm return (menu_mode_cb(c, pd->md, cx, cy)); 1560cac3d2dSnicm 157f7bae471Snicm if (pd->border_lines == BOX_LINES_NONE) { 15801a3f482Snicm *cx = pd->px + pd->s.cx; 15901a3f482Snicm *cy = pd->py + pd->s.cy; 16001a3f482Snicm } else { 1610cac3d2dSnicm *cx = pd->px + 1 + pd->s.cx; 1620cac3d2dSnicm *cy = pd->py + 1 + pd->s.cy; 16301a3f482Snicm } 16483e83a91Snicm return (&pd->s); 1650cac3d2dSnicm } 1660cac3d2dSnicm 1677f16de32Snicm /* Return parts of the input range which are not obstructed by the popup. */ 1687f16de32Snicm static void 1697f16de32Snicm popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx, 1707f16de32Snicm struct overlay_ranges *r) 1710cac3d2dSnicm { 172f38784aeSnicm struct popup_data *pd = data; 1737f16de32Snicm struct overlay_ranges or[2]; 1747f16de32Snicm u_int i, j, k = 0; 1750cac3d2dSnicm 1767f16de32Snicm if (pd->md != NULL) { 1777f16de32Snicm /* Check each returned range for the menu against the popup. */ 1787f16de32Snicm menu_check_cb(c, pd->md, px, py, nx, r); 1797f16de32Snicm for (i = 0; i < 2; i++) { 1807f16de32Snicm server_client_overlay_range(pd->px, pd->py, pd->sx, 1817f16de32Snicm pd->sy, r->px[i], py, r->nx[i], &or[i]); 1827f16de32Snicm } 1837f16de32Snicm 1847f16de32Snicm /* 1857f16de32Snicm * or has up to OVERLAY_MAX_RANGES non-overlapping ranges, 1867f16de32Snicm * ordered from left to right. Collect them in the output. 1877f16de32Snicm */ 1887f16de32Snicm for (i = 0; i < 2; i++) { 1897f16de32Snicm /* Each or[i] only has 2 ranges. */ 1907f16de32Snicm for (j = 0; j < 2; j++) { 1917f16de32Snicm if (or[i].nx[j] > 0) { 1927f16de32Snicm r->px[k] = or[i].px[j]; 1937f16de32Snicm r->nx[k] = or[i].nx[j]; 1947f16de32Snicm k++; 1957f16de32Snicm } 1967f16de32Snicm } 1977f16de32Snicm } 1987f16de32Snicm 1997f16de32Snicm /* Zero remaining ranges if any. */ 2007f16de32Snicm for (i = k; i < OVERLAY_MAX_RANGES; i++) { 2017f16de32Snicm r->px[i] = 0; 2027f16de32Snicm r->nx[i] = 0; 2037f16de32Snicm } 2047f16de32Snicm 2057f16de32Snicm return; 2067f16de32Snicm } 2077f16de32Snicm 2087f16de32Snicm server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx, 2097f16de32Snicm r); 2100cac3d2dSnicm } 2110cac3d2dSnicm 2120cac3d2dSnicm static void 213f38784aeSnicm popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) 2140cac3d2dSnicm { 215f38784aeSnicm struct popup_data *pd = data; 2160cac3d2dSnicm struct tty *tty = &c->tty; 2170cac3d2dSnicm struct screen s; 2180cac3d2dSnicm struct screen_write_ctx ctx; 2190cac3d2dSnicm u_int i, px = pd->px, py = pd->py; 22033a1e283Snicm struct colour_palette *palette = &pd->palette; 221f7bae471Snicm struct grid_cell defaults; 2220cac3d2dSnicm 2230cac3d2dSnicm screen_init(&s, pd->sx, pd->sy, 0); 22483e83a91Snicm screen_write_start(&ctx, &s); 2250cac3d2dSnicm screen_write_clearscreen(&ctx, 8); 226c0287628Snicm 227f7bae471Snicm if (pd->border_lines == BOX_LINES_NONE) { 22801a3f482Snicm screen_write_cursormove(&ctx, 0, 0, 0); 22901a3f482Snicm screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy); 23001a3f482Snicm } else if (pd->sx > 2 && pd->sy > 2) { 231f7bae471Snicm screen_write_box(&ctx, pd->sx, pd->sy, pd->border_lines, 232f7bae471Snicm &pd->border_cell, pd->title); 2330cac3d2dSnicm screen_write_cursormove(&ctx, 1, 1, 0); 234c0287628Snicm screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, 235c0287628Snicm pd->sy - 2); 236c0287628Snicm } 2370cac3d2dSnicm screen_write_stop(&ctx); 2380cac3d2dSnicm 239f7bae471Snicm memcpy(&defaults, &pd->defaults, sizeof defaults); 240ffda99cdSnicm if (defaults.fg == 8) 241f7bae471Snicm defaults.fg = palette->fg; 242ffda99cdSnicm if (defaults.bg == 8) 243f7bae471Snicm defaults.bg = palette->bg; 24433a1e283Snicm 245f38784aeSnicm if (pd->md != NULL) { 246f38784aeSnicm c->overlay_check = menu_check_cb; 247f38784aeSnicm c->overlay_data = pd->md; 248f38784aeSnicm } else { 2490cac3d2dSnicm c->overlay_check = NULL; 250f38784aeSnicm c->overlay_data = NULL; 251f38784aeSnicm } 252f7bae471Snicm for (i = 0; i < pd->sy; i++) { 253f7bae471Snicm tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults, 254f7bae471Snicm palette); 255f7bae471Snicm } 2566987aaaaSnicm screen_free(&s); 257f38784aeSnicm if (pd->md != NULL) { 258f38784aeSnicm c->overlay_check = NULL; 259f38784aeSnicm c->overlay_data = NULL; 260f38784aeSnicm menu_draw_cb(c, pd->md, rctx); 261f38784aeSnicm } 2620cac3d2dSnicm c->overlay_check = popup_check_cb; 263f38784aeSnicm c->overlay_data = pd; 2640cac3d2dSnicm } 2650cac3d2dSnicm 2660cac3d2dSnicm static void 267f38784aeSnicm popup_free_cb(struct client *c, void *data) 2680cac3d2dSnicm { 269f38784aeSnicm struct popup_data *pd = data; 2700cac3d2dSnicm struct cmdq_item *item = pd->item; 2710cac3d2dSnicm 272f38784aeSnicm if (pd->md != NULL) 273f38784aeSnicm menu_free_cb(c, pd->md); 274f38784aeSnicm 275a6c9106fSnicm if (pd->cb != NULL) 276a6c9106fSnicm pd->cb(pd->status, pd->arg); 277a6c9106fSnicm 2780cac3d2dSnicm if (item != NULL) { 279309e6403Snicm if (cmdq_get_client(item) != NULL && 280040343aeSnicm cmdq_get_client(item)->session == NULL) 281040343aeSnicm cmdq_get_client(item)->retval = pd->status; 2820cac3d2dSnicm cmdq_continue(item); 2830cac3d2dSnicm } 2840cac3d2dSnicm server_client_unref(pd->c); 2850cac3d2dSnicm 2860cac3d2dSnicm if (pd->job != NULL) 2870cac3d2dSnicm job_free(pd->job); 2880cac3d2dSnicm input_free(pd->ictx); 2890cac3d2dSnicm 2900cac3d2dSnicm screen_free(&pd->s); 29133a1e283Snicm colour_palette_free(&pd->palette); 292f38784aeSnicm 293dad8e5c0Snicm free(pd->title); 2940cac3d2dSnicm free(pd); 2950cac3d2dSnicm } 2960cac3d2dSnicm 2970cac3d2dSnicm static void 298f38784aeSnicm popup_resize_cb(__unused struct client *c, void *data) 299c0287628Snicm { 300f38784aeSnicm struct popup_data *pd = data; 301c0287628Snicm struct tty *tty = &c->tty; 302c0287628Snicm 303c0287628Snicm if (pd == NULL) 304c0287628Snicm return; 305f38784aeSnicm if (pd->md != NULL) 306f38784aeSnicm menu_free_cb(c, pd->md); 307c0287628Snicm 308c0287628Snicm /* Adjust position and size. */ 309c0287628Snicm if (pd->psy > tty->sy) 310c0287628Snicm pd->sy = tty->sy; 311c0287628Snicm else 312c0287628Snicm pd->sy = pd->psy; 313c0287628Snicm if (pd->psx > tty->sx) 314c0287628Snicm pd->sx = tty->sx; 315c0287628Snicm else 316c0287628Snicm pd->sx = pd->psx; 317c0287628Snicm if (pd->ppy + pd->sy > tty->sy) 318c0287628Snicm pd->py = tty->sy - pd->sy; 319c0287628Snicm else 320c0287628Snicm pd->py = pd->ppy; 321c0287628Snicm if (pd->ppx + pd->sx > tty->sx) 322c0287628Snicm pd->px = tty->sx - pd->sx; 323c0287628Snicm else 324c0287628Snicm pd->px = pd->ppx; 325c0287628Snicm 326c0287628Snicm /* Avoid zero size screens. */ 327f7bae471Snicm if (pd->border_lines == BOX_LINES_NONE) { 32801a3f482Snicm screen_resize(&pd->s, pd->sx, pd->sy, 0); 32901a3f482Snicm if (pd->job != NULL) 33001a3f482Snicm job_resize(pd->job, pd->sx, pd->sy ); 33101a3f482Snicm } else if (pd->sx > 2 && pd->sy > 2) { 332c0287628Snicm screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); 333c0287628Snicm if (pd->job != NULL) 334c0287628Snicm job_resize(pd->job, pd->sx - 2, pd->sy - 2); 335c0287628Snicm } 336c0287628Snicm } 337c0287628Snicm 338c0287628Snicm static void 3397caf7dd1Snicm popup_make_pane(struct popup_data *pd, enum layout_type type) 3407caf7dd1Snicm { 3417caf7dd1Snicm struct client *c = pd->c; 3427caf7dd1Snicm struct session *s = c->session; 3437caf7dd1Snicm struct window *w = s->curw->window; 3447caf7dd1Snicm struct layout_cell *lc; 3457caf7dd1Snicm struct window_pane *wp = w->active, *new_wp; 3467caf7dd1Snicm u_int hlimit; 3475edeef37Snicm const char *shell; 3487caf7dd1Snicm 3490ad0daf4Snicm window_unzoom(w, 1); 3507caf7dd1Snicm 3517caf7dd1Snicm lc = layout_split_pane(wp, type, -1, 0); 3527caf7dd1Snicm hlimit = options_get_number(s->options, "history-limit"); 3537caf7dd1Snicm new_wp = window_add_pane(wp->window, NULL, hlimit, 0); 3547caf7dd1Snicm layout_assign_pane(lc, new_wp, 0); 3557caf7dd1Snicm 356*20c4f615Snicm if (pd->job != NULL) { 3575edeef37Snicm new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty, 3585edeef37Snicm sizeof new_wp->tty); 3597caf7dd1Snicm pd->job = NULL; 360*20c4f615Snicm } 3617caf7dd1Snicm 3625edeef37Snicm screen_set_title(&pd->s, new_wp->base.title); 3637caf7dd1Snicm screen_free(&new_wp->base); 3647caf7dd1Snicm memcpy(&new_wp->base, &pd->s, sizeof wp->base); 3657caf7dd1Snicm screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1); 3667caf7dd1Snicm screen_init(&pd->s, 1, 1, 0); 3677caf7dd1Snicm 3685edeef37Snicm shell = options_get_string(s->options, "default-shell"); 3695edeef37Snicm if (!checkshell(shell)) 3705edeef37Snicm shell = _PATH_BSHELL; 3715edeef37Snicm new_wp->shell = xstrdup(shell); 3725edeef37Snicm 3737caf7dd1Snicm window_pane_set_event(new_wp); 3747caf7dd1Snicm window_set_active_pane(w, new_wp, 1); 3755edeef37Snicm new_wp->flags |= PANE_CHANGED; 3767caf7dd1Snicm 3777caf7dd1Snicm pd->close = 1; 3787caf7dd1Snicm } 3797caf7dd1Snicm 3807caf7dd1Snicm static void 381f38784aeSnicm popup_menu_done(__unused struct menu *menu, __unused u_int choice, 382f38784aeSnicm key_code key, void *data) 383f38784aeSnicm { 384f38784aeSnicm struct popup_data *pd = data; 385f38784aeSnicm struct client *c = pd->c; 386f38784aeSnicm struct paste_buffer *pb; 387f38784aeSnicm const char *buf; 388f38784aeSnicm size_t len; 389f38784aeSnicm 390f38784aeSnicm pd->md = NULL; 391f38784aeSnicm pd->menu = NULL; 392f38784aeSnicm server_redraw_client(pd->c); 393f38784aeSnicm 394f38784aeSnicm switch (key) { 395f38784aeSnicm case 'p': 396f38784aeSnicm pb = paste_get_top(NULL); 397f38784aeSnicm if (pb != NULL) { 398f38784aeSnicm buf = paste_buffer_data(pb, &len); 399f38784aeSnicm bufferevent_write(job_get_event(pd->job), buf, len); 400f38784aeSnicm } 401f38784aeSnicm break; 402f38784aeSnicm case 'F': 403f38784aeSnicm pd->sx = c->tty.sx; 404f38784aeSnicm pd->sy = c->tty.sy; 405f38784aeSnicm pd->px = 0; 406f38784aeSnicm pd->py = 0; 407f38784aeSnicm server_redraw_client(c); 408f38784aeSnicm break; 409f38784aeSnicm case 'C': 410f38784aeSnicm pd->px = c->tty.sx / 2 - pd->sx / 2; 411f38784aeSnicm pd->py = c->tty.sy / 2 - pd->sy / 2; 412f38784aeSnicm server_redraw_client(c); 413f38784aeSnicm break; 4147caf7dd1Snicm case 'h': 4157caf7dd1Snicm popup_make_pane(pd, LAYOUT_LEFTRIGHT); 4167caf7dd1Snicm break; 4177caf7dd1Snicm case 'v': 4187caf7dd1Snicm popup_make_pane(pd, LAYOUT_TOPBOTTOM); 4197caf7dd1Snicm break; 420f38784aeSnicm case 'q': 421f38784aeSnicm pd->close = 1; 422f38784aeSnicm break; 423f38784aeSnicm } 424f38784aeSnicm } 425f38784aeSnicm 426f38784aeSnicm static void 4270cac3d2dSnicm popup_handle_drag(struct client *c, struct popup_data *pd, 4280cac3d2dSnicm struct mouse_event *m) 4290cac3d2dSnicm { 4300cac3d2dSnicm u_int px, py; 4310cac3d2dSnicm 4320cac3d2dSnicm if (!MOUSE_DRAG(m->b)) 4330cac3d2dSnicm pd->dragging = OFF; 4340cac3d2dSnicm else if (pd->dragging == MOVE) { 4350cac3d2dSnicm if (m->x < pd->dx) 4360cac3d2dSnicm px = 0; 4370cac3d2dSnicm else if (m->x - pd->dx + pd->sx > c->tty.sx) 4380cac3d2dSnicm px = c->tty.sx - pd->sx; 4390cac3d2dSnicm else 4400cac3d2dSnicm px = m->x - pd->dx; 4410cac3d2dSnicm if (m->y < pd->dy) 4420cac3d2dSnicm py = 0; 4430cac3d2dSnicm else if (m->y - pd->dy + pd->sy > c->tty.sy) 4440cac3d2dSnicm py = c->tty.sy - pd->sy; 4450cac3d2dSnicm else 4460cac3d2dSnicm py = m->y - pd->dy; 4470cac3d2dSnicm pd->px = px; 4480cac3d2dSnicm pd->py = py; 4490cac3d2dSnicm pd->dx = m->x - pd->px; 4500cac3d2dSnicm pd->dy = m->y - pd->py; 451c0287628Snicm pd->ppx = px; 452c0287628Snicm pd->ppy = py; 4530cac3d2dSnicm server_redraw_client(c); 4540cac3d2dSnicm } else if (pd->dragging == SIZE) { 455f7bae471Snicm if (pd->border_lines == BOX_LINES_NONE) { 45601a3f482Snicm if (m->x < pd->px + 1) 45701a3f482Snicm return; 45801a3f482Snicm if (m->y < pd->py + 1) 45901a3f482Snicm return; 46001a3f482Snicm } else { 4611311647bSnicm if (m->x < pd->px + 3) 4620cac3d2dSnicm return; 4631311647bSnicm if (m->y < pd->py + 3) 4640cac3d2dSnicm return; 46501a3f482Snicm } 4660cac3d2dSnicm pd->sx = m->x - pd->px; 4670cac3d2dSnicm pd->sy = m->y - pd->py; 468c0287628Snicm pd->psx = pd->sx; 469c0287628Snicm pd->psy = pd->sy; 4700cac3d2dSnicm 471f7bae471Snicm if (pd->border_lines == BOX_LINES_NONE) { 47201a3f482Snicm screen_resize(&pd->s, pd->sx, pd->sy, 0); 47301a3f482Snicm if (pd->job != NULL) 47401a3f482Snicm job_resize(pd->job, pd->sx, pd->sy); 47501a3f482Snicm } else { 476e2265670Snicm screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); 477309e6403Snicm if (pd->job != NULL) 4780cac3d2dSnicm job_resize(pd->job, pd->sx - 2, pd->sy - 2); 47901a3f482Snicm } 4800cac3d2dSnicm server_redraw_client(c); 4810cac3d2dSnicm } 4820cac3d2dSnicm } 4830cac3d2dSnicm 4840cac3d2dSnicm static int 485f38784aeSnicm popup_key_cb(struct client *c, void *data, struct key_event *event) 4860cac3d2dSnicm { 487f38784aeSnicm struct popup_data *pd = data; 4880cac3d2dSnicm struct mouse_event *m = &event->m; 489309e6403Snicm const char *buf; 49029724ccbSnicm size_t len; 491f38784aeSnicm u_int px, py, x; 49228a7cae8Snicm enum { NONE, LEFT, RIGHT, TOP, BOTTOM } border = NONE; 493f38784aeSnicm 494f38784aeSnicm if (pd->md != NULL) { 495f38784aeSnicm if (menu_key_cb(c, pd->md, event) == 1) { 496f38784aeSnicm pd->md = NULL; 497f38784aeSnicm pd->menu = NULL; 498f38784aeSnicm if (pd->close) 499f38784aeSnicm server_client_clear_overlay(c); 500f38784aeSnicm else 501f38784aeSnicm server_redraw_client(c); 502f38784aeSnicm } 503f38784aeSnicm return (0); 504f38784aeSnicm } 5050cac3d2dSnicm 5060cac3d2dSnicm if (KEYC_IS_MOUSE(event->key)) { 5070cac3d2dSnicm if (pd->dragging != OFF) { 5080cac3d2dSnicm popup_handle_drag(c, pd, m); 5090cac3d2dSnicm goto out; 5100cac3d2dSnicm } 5110cac3d2dSnicm if (m->x < pd->px || 5120cac3d2dSnicm m->x > pd->px + pd->sx - 1 || 5130cac3d2dSnicm m->y < pd->py || 5140cac3d2dSnicm m->y > pd->py + pd->sy - 1) { 5154e5846a2Snicm if (MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3) 516f38784aeSnicm goto menu; 5170cac3d2dSnicm return (0); 5180cac3d2dSnicm } 519f7bae471Snicm if (pd->border_lines != BOX_LINES_NONE) { 52028a7cae8Snicm if (m->x == pd->px) 52128a7cae8Snicm border = LEFT; 52228a7cae8Snicm else if (m->x == pd->px + pd->sx - 1) 52328a7cae8Snicm border = RIGHT; 52428a7cae8Snicm else if (m->y == pd->py) 52528a7cae8Snicm border = TOP; 52628a7cae8Snicm else if (m->y == pd->py + pd->sy - 1) 52728a7cae8Snicm border = BOTTOM; 52828a7cae8Snicm } 52928a7cae8Snicm if ((m->b & MOUSE_MASK_MODIFIERS) == 0 && 5304e5846a2Snicm MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3 && 53128a7cae8Snicm (border == LEFT || border == TOP)) 532f38784aeSnicm goto menu; 53328a7cae8Snicm if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) || 53428a7cae8Snicm border != NONE) { 5350cac3d2dSnicm if (!MOUSE_DRAG(m->b)) 5360cac3d2dSnicm goto out; 5374e5846a2Snicm if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_1) 5380cac3d2dSnicm pd->dragging = MOVE; 5394e5846a2Snicm else if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_3) 5400cac3d2dSnicm pd->dragging = SIZE; 5410cac3d2dSnicm pd->dx = m->lx - pd->px; 5420cac3d2dSnicm pd->dy = m->ly - pd->py; 5430cac3d2dSnicm goto out; 5440cac3d2dSnicm } 5450cac3d2dSnicm } 546309e6403Snicm if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) || 5479a253fadSnicm pd->job == NULL) && 548719f5715Snicm (event->key == '\033' || event->key == ('c'|KEYC_CTRL))) 5490cac3d2dSnicm return (1); 550309e6403Snicm if (pd->job != NULL) { 55129724ccbSnicm if (KEYC_IS_MOUSE(event->key)) { 55229724ccbSnicm /* Must be inside, checked already. */ 553f7bae471Snicm if (pd->border_lines == BOX_LINES_NONE) { 55401a3f482Snicm px = m->x - pd->px; 55501a3f482Snicm py = m->y - pd->py; 55601a3f482Snicm } else { 55701a3f482Snicm px = m->x - pd->px - 1; 55801a3f482Snicm py = m->y - pd->py - 1; 55901a3f482Snicm } 56001a3f482Snicm if (!input_key_get_mouse(&pd->s, m, px, py, &buf, &len)) 56129724ccbSnicm return (0); 56229724ccbSnicm bufferevent_write(job_get_event(pd->job), buf, len); 56329724ccbSnicm return (0); 56429724ccbSnicm } 5659265d1acSnicm input_key(&pd->s, job_get_event(pd->job), event->key); 566309e6403Snicm } 5670cac3d2dSnicm return (0); 5680cac3d2dSnicm 569f38784aeSnicm menu: 570f38784aeSnicm pd->menu = menu_create(""); 5717caf7dd1Snicm if (pd->flags & POPUP_INTERNAL) { 5726a9342afSnicm menu_add_items(pd->menu, popup_internal_menu_items, NULL, c, 5737caf7dd1Snicm NULL); 5747caf7dd1Snicm } else 5756a9342afSnicm menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL); 576f38784aeSnicm if (m->x >= (pd->menu->width + 4) / 2) 577f38784aeSnicm x = m->x - (pd->menu->width + 4) / 2; 578f38784aeSnicm else 579f38784aeSnicm x = 0; 58017d7ce67Snicm pd->md = menu_prepare(pd->menu, 0, 0, NULL, x, m->y, c, 58119c94b00Snicm BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL, popup_menu_done, pd); 582f38784aeSnicm c->flags |= CLIENT_REDRAWOVERLAY; 583f38784aeSnicm 5840cac3d2dSnicm out: 5850cac3d2dSnicm pd->lx = m->x; 5860cac3d2dSnicm pd->ly = m->y; 5870cac3d2dSnicm pd->lb = m->b; 5880cac3d2dSnicm return (0); 5890cac3d2dSnicm } 5900cac3d2dSnicm 5910cac3d2dSnicm static void 5920cac3d2dSnicm popup_job_update_cb(struct job *job) 5930cac3d2dSnicm { 5940cac3d2dSnicm struct popup_data *pd = job_get_data(job); 5950cac3d2dSnicm struct evbuffer *evb = job_get_event(job)->input; 59683e83a91Snicm struct client *c = pd->c; 5970cac3d2dSnicm struct screen *s = &pd->s; 5980cac3d2dSnicm void *data = EVBUFFER_DATA(evb); 5990cac3d2dSnicm size_t size = EVBUFFER_LENGTH(evb); 6000cac3d2dSnicm 60183e83a91Snicm if (size == 0) 60283e83a91Snicm return; 60383e83a91Snicm 60452922e71Snicm if (pd->md != NULL) { 60552922e71Snicm c->overlay_check = menu_check_cb; 60652922e71Snicm c->overlay_data = pd->md; 60752922e71Snicm } else { 60883e83a91Snicm c->overlay_check = NULL; 60952922e71Snicm c->overlay_data = NULL; 61052922e71Snicm } 61183e83a91Snicm input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size); 61283e83a91Snicm c->overlay_check = popup_check_cb; 61352922e71Snicm c->overlay_data = pd; 61483e83a91Snicm 6150cac3d2dSnicm evbuffer_drain(evb, size); 6160cac3d2dSnicm } 6170cac3d2dSnicm 6180cac3d2dSnicm static void 6190cac3d2dSnicm popup_job_complete_cb(struct job *job) 6200cac3d2dSnicm { 6210cac3d2dSnicm struct popup_data *pd = job_get_data(job); 6220cac3d2dSnicm int status; 6230cac3d2dSnicm 6240cac3d2dSnicm status = job_get_status(pd->job); 6250cac3d2dSnicm if (WIFEXITED(status)) 6260cac3d2dSnicm pd->status = WEXITSTATUS(status); 6270cac3d2dSnicm else if (WIFSIGNALED(status)) 6280cac3d2dSnicm pd->status = WTERMSIG(status); 6290cac3d2dSnicm else 6300cac3d2dSnicm pd->status = 0; 6310cac3d2dSnicm pd->job = NULL; 6320cac3d2dSnicm 6331e55951bSnicm if ((pd->flags & POPUP_CLOSEEXIT) || 6341e55951bSnicm ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0)) 6359a253fadSnicm server_client_clear_overlay(pd->c); 6360cac3d2dSnicm } 6370cac3d2dSnicm 6380cac3d2dSnicm int 639ccb627cdSnicm popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, 640ccb627cdSnicm u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd, 641dad8e5c0Snicm int argc, char **argv, const char *cwd, const char *title, struct client *c, 642ffda99cdSnicm struct session *s, const char *style, const char *border_style, 643ffda99cdSnicm popup_close_cb cb, void *arg) 6440cac3d2dSnicm { 6450cac3d2dSnicm struct popup_data *pd; 64601a3f482Snicm u_int jx, jy; 647ccb627cdSnicm struct options *o; 648ffda99cdSnicm struct style sytmp; 6490cac3d2dSnicm 650ccb627cdSnicm if (s != NULL) 651ccb627cdSnicm o = s->curw->window->options; 652ccb627cdSnicm else 653ccb627cdSnicm o = c->session->curw->window->options; 654f7bae471Snicm 655f7bae471Snicm if (lines == BOX_LINES_DEFAULT) 656ccb627cdSnicm lines = options_get_number(o, "popup-border-lines"); 657ccb627cdSnicm if (lines == BOX_LINES_NONE) { 65801a3f482Snicm if (sx < 1 || sy < 1) 65901a3f482Snicm return (-1); 66001a3f482Snicm jx = sx; 66101a3f482Snicm jy = sy; 66201a3f482Snicm } else { 6630cac3d2dSnicm if (sx < 3 || sy < 3) 6640cac3d2dSnicm return (-1); 66501a3f482Snicm jx = sx - 2; 66601a3f482Snicm jy = sy - 2; 66701a3f482Snicm } 6680cac3d2dSnicm if (c->tty.sx < sx || c->tty.sy < sy) 6690cac3d2dSnicm return (-1); 6700cac3d2dSnicm 6710cac3d2dSnicm pd = xcalloc(1, sizeof *pd); 6720cac3d2dSnicm pd->item = item; 6730cac3d2dSnicm pd->flags = flags; 674f81bc88fSnicm if (title != NULL) 675dad8e5c0Snicm pd->title = xstrdup(title); 6760cac3d2dSnicm 6770cac3d2dSnicm pd->c = c; 6780cac3d2dSnicm pd->c->references++; 6790cac3d2dSnicm 680a6c9106fSnicm pd->cb = cb; 681a6c9106fSnicm pd->arg = arg; 6820cac3d2dSnicm pd->status = 128 + SIGHUP; 6830cac3d2dSnicm 684f7bae471Snicm pd->border_lines = lines; 685f7bae471Snicm memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell); 686f7bae471Snicm style_apply(&pd->border_cell, o, "popup-border-style", NULL); 687ffda99cdSnicm if (border_style != NULL) { 688ffda99cdSnicm style_set(&sytmp, &grid_default_cell); 689ffda99cdSnicm if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) { 690ffda99cdSnicm pd->border_cell.fg = sytmp.gc.fg; 691ffda99cdSnicm pd->border_cell.bg = sytmp.gc.bg; 692ffda99cdSnicm } 693ffda99cdSnicm } 694f7bae471Snicm pd->border_cell.attr = 0; 695f7bae471Snicm 69692cab55cSnicm screen_init(&pd->s, jx, jy, 0); 6979b5345daSnicm screen_set_default_cursor(&pd->s, global_w_options); 69833a1e283Snicm colour_palette_init(&pd->palette); 69933a1e283Snicm colour_palette_from_option(&pd->palette, global_w_options); 7000cac3d2dSnicm 701f7bae471Snicm memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults); 702f7bae471Snicm style_apply(&pd->defaults, o, "popup-style", NULL); 703ffda99cdSnicm if (style != NULL) { 704ffda99cdSnicm style_set(&sytmp, &grid_default_cell); 705ffda99cdSnicm if (style_parse(&sytmp, &pd->defaults, style) == 0) { 706ffda99cdSnicm pd->defaults.fg = sytmp.gc.fg; 707ffda99cdSnicm pd->defaults.bg = sytmp.gc.bg; 708ffda99cdSnicm } 709ffda99cdSnicm } 710f7bae471Snicm pd->defaults.attr = 0; 711f7bae471Snicm 7120cac3d2dSnicm pd->px = px; 7130cac3d2dSnicm pd->py = py; 7140cac3d2dSnicm pd->sx = sx; 7150cac3d2dSnicm pd->sy = sy; 7160cac3d2dSnicm 717c0287628Snicm pd->ppx = px; 718c0287628Snicm pd->ppy = py; 719c0287628Snicm pd->psx = sx; 720c0287628Snicm pd->psy = sy; 721c0287628Snicm 72203a1f8ddSnicm pd->job = job_run(shellcmd, argc, argv, env, s, cwd, 723309e6403Snicm popup_job_update_cb, popup_job_complete_cb, NULL, pd, 724668a68adSnicm JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy); 72533a1e283Snicm pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette); 7260cac3d2dSnicm 7270cac3d2dSnicm server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, 728c0287628Snicm popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); 7290cac3d2dSnicm return (0); 7300cac3d2dSnicm } 731f67736f8Snicm 732f67736f8Snicm static void 733f67736f8Snicm popup_editor_free(struct popup_editor *pe) 734f67736f8Snicm { 735f67736f8Snicm unlink(pe->path); 736f67736f8Snicm free(pe->path); 737f67736f8Snicm free(pe); 738f67736f8Snicm } 739f67736f8Snicm 740f67736f8Snicm static void 741f67736f8Snicm popup_editor_close_cb(int status, void *arg) 742f67736f8Snicm { 743f67736f8Snicm struct popup_editor *pe = arg; 744f67736f8Snicm FILE *f; 745f67736f8Snicm char *buf = NULL; 746f67736f8Snicm off_t len = 0; 747f67736f8Snicm 748f67736f8Snicm if (status != 0) { 749f67736f8Snicm pe->cb(NULL, 0, pe->arg); 750f67736f8Snicm popup_editor_free(pe); 751f67736f8Snicm return; 752f67736f8Snicm } 753f67736f8Snicm 754f67736f8Snicm f = fopen(pe->path, "r"); 755f67736f8Snicm if (f != NULL) { 756f67736f8Snicm fseeko(f, 0, SEEK_END); 757f67736f8Snicm len = ftello(f); 758f67736f8Snicm fseeko(f, 0, SEEK_SET); 759f67736f8Snicm 760f67736f8Snicm if (len == 0 || 761f67736f8Snicm (uintmax_t)len > (uintmax_t)SIZE_MAX || 762f67736f8Snicm (buf = malloc(len)) == NULL || 763f67736f8Snicm fread(buf, len, 1, f) != 1) { 764f67736f8Snicm free(buf); 765f67736f8Snicm buf = NULL; 766f67736f8Snicm len = 0; 767f67736f8Snicm } 768f67736f8Snicm fclose(f); 769f67736f8Snicm } 770f67736f8Snicm pe->cb(buf, len, pe->arg); /* callback now owns buffer */ 771f67736f8Snicm popup_editor_free(pe); 772f67736f8Snicm } 773f67736f8Snicm 774f67736f8Snicm int 775f67736f8Snicm popup_editor(struct client *c, const char *buf, size_t len, 776f67736f8Snicm popup_finish_edit_cb cb, void *arg) 777f67736f8Snicm { 778f67736f8Snicm struct popup_editor *pe; 779f67736f8Snicm int fd; 780f67736f8Snicm FILE *f; 781f67736f8Snicm char *cmd; 782f67736f8Snicm char path[] = _PATH_TMP "tmux.XXXXXXXX"; 783f67736f8Snicm const char *editor; 784f67736f8Snicm u_int px, py, sx, sy; 785f67736f8Snicm 786f67736f8Snicm editor = options_get_string(global_options, "editor"); 787f67736f8Snicm if (*editor == '\0') 788f67736f8Snicm return (-1); 789f67736f8Snicm 790f67736f8Snicm fd = mkstemp(path); 791f67736f8Snicm if (fd == -1) 792f67736f8Snicm return (-1); 793f67736f8Snicm f = fdopen(fd, "w"); 7940b091dd3Snicm if (f == NULL) 7950b091dd3Snicm return (-1); 796f67736f8Snicm if (fwrite(buf, len, 1, f) != 1) { 797f67736f8Snicm fclose(f); 798f67736f8Snicm return (-1); 799f67736f8Snicm } 800f67736f8Snicm fclose(f); 801f67736f8Snicm 802f67736f8Snicm pe = xcalloc(1, sizeof *pe); 803f67736f8Snicm pe->path = xstrdup(path); 804f67736f8Snicm pe->cb = cb; 805f67736f8Snicm pe->arg = arg; 806f67736f8Snicm 807f67736f8Snicm sx = c->tty.sx * 9 / 10; 808f67736f8Snicm sy = c->tty.sy * 9 / 10; 809f67736f8Snicm px = (c->tty.sx / 2) - (sx / 2); 810f67736f8Snicm py = (c->tty.sy / 2) - (sy / 2); 811f67736f8Snicm 812f67736f8Snicm xasprintf(&cmd, "%s %s", editor, path); 813ccb627cdSnicm if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, BOX_LINES_DEFAULT, 814dad8e5c0Snicm NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, NULL, c, NULL, 815ffda99cdSnicm NULL, NULL, popup_editor_close_cb, pe) != 0) { 816f67736f8Snicm popup_editor_free(pe); 817f67736f8Snicm free(cmd); 818f67736f8Snicm return (-1); 819f67736f8Snicm } 820f67736f8Snicm free(cmd); 821f67736f8Snicm return (0); 822f67736f8Snicm } 823