1/* $OpenBSD$ */ 2 3/* 4 * Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20#include <sys/wait.h> 21 22#include <signal.h> 23#include <stdlib.h> 24#include <string.h> 25#include <unistd.h> 26 27#include "tmux.h" 28 29struct popup_data { 30 struct client *c; 31 struct cmdq_item *item; 32 int flags; 33 char *title; 34 35 struct grid_cell border_cell; 36 enum box_lines border_lines; 37 38 struct screen s; 39 struct grid_cell defaults; 40 struct colour_palette palette; 41 42 struct job *job; 43 struct input_ctx *ictx; 44 int status; 45 popup_close_cb cb; 46 void *arg; 47 48 struct menu *menu; 49 struct menu_data *md; 50 int close; 51 52 /* Current position and size. */ 53 u_int px; 54 u_int py; 55 u_int sx; 56 u_int sy; 57 58 /* Preferred position and size. */ 59 u_int ppx; 60 u_int ppy; 61 u_int psx; 62 u_int psy; 63 64 enum { OFF, MOVE, SIZE } dragging; 65 u_int dx; 66 u_int dy; 67 68 u_int lx; 69 u_int ly; 70 u_int lb; 71}; 72 73struct popup_editor { 74 char *path; 75 popup_finish_edit_cb cb; 76 void *arg; 77}; 78 79static const struct menu_item popup_menu_items[] = { 80 { "Close", 'q', NULL }, 81 { "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL }, 82 { "", KEYC_NONE, NULL }, 83 { "Fill Space", 'F', NULL }, 84 { "Centre", 'C', NULL }, 85 { "", KEYC_NONE, NULL }, 86 { "To Horizontal Pane", 'h', NULL }, 87 { "To Vertical Pane", 'v', NULL }, 88 89 { NULL, KEYC_NONE, NULL } 90}; 91 92static const struct menu_item popup_internal_menu_items[] = { 93 { "Close", 'q', NULL }, 94 { "", KEYC_NONE, NULL }, 95 { "Fill Space", 'F', NULL }, 96 { "Centre", 'C', NULL }, 97 98 { NULL, KEYC_NONE, NULL } 99}; 100 101static void 102popup_redraw_cb(const struct tty_ctx *ttyctx) 103{ 104 struct popup_data *pd = ttyctx->arg; 105 106 pd->c->flags |= CLIENT_REDRAWOVERLAY; 107} 108 109static int 110popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c) 111{ 112 struct popup_data *pd = ttyctx->arg; 113 114 if (c != pd->c) 115 return (0); 116 if (pd->c->flags & CLIENT_REDRAWOVERLAY) 117 return (0); 118 119 ttyctx->bigger = 0; 120 ttyctx->wox = 0; 121 ttyctx->woy = 0; 122 ttyctx->wsx = c->tty.sx; 123 ttyctx->wsy = c->tty.sy; 124 125 if (pd->border_lines == BOX_LINES_NONE) { 126 ttyctx->xoff = ttyctx->rxoff = pd->px; 127 ttyctx->yoff = ttyctx->ryoff = pd->py; 128 } else { 129 ttyctx->xoff = ttyctx->rxoff = pd->px + 1; 130 ttyctx->yoff = ttyctx->ryoff = pd->py + 1; 131 } 132 133 return (1); 134} 135 136static void 137popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) 138{ 139 struct popup_data *pd = ctx->arg; 140 141 memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults); 142 ttyctx->palette = &pd->palette; 143 ttyctx->redraw_cb = popup_redraw_cb; 144 ttyctx->set_client_cb = popup_set_client_cb; 145 ttyctx->arg = pd; 146} 147 148static struct screen * 149popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy) 150{ 151 struct popup_data *pd = data; 152 153 if (pd->md != NULL) 154 return (menu_mode_cb(c, pd->md, cx, cy)); 155 156 if (pd->border_lines == BOX_LINES_NONE) { 157 *cx = pd->px + pd->s.cx; 158 *cy = pd->py + pd->s.cy; 159 } else { 160 *cx = pd->px + 1 + pd->s.cx; 161 *cy = pd->py + 1 + pd->s.cy; 162 } 163 return (&pd->s); 164} 165 166/* Return parts of the input range which are not obstructed by the popup. */ 167static void 168popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx, 169 struct overlay_ranges *r) 170{ 171 struct popup_data *pd = data; 172 struct overlay_ranges or[2]; 173 u_int i, j, k = 0; 174 175 if (pd->md != NULL) { 176 /* Check each returned range for the menu against the popup. */ 177 menu_check_cb(c, pd->md, px, py, nx, r); 178 for (i = 0; i < 2; i++) { 179 server_client_overlay_range(pd->px, pd->py, pd->sx, 180 pd->sy, r->px[i], py, r->nx[i], &or[i]); 181 } 182 183 /* 184 * or has up to OVERLAY_MAX_RANGES non-overlapping ranges, 185 * ordered from left to right. Collect them in the output. 186 */ 187 for (i = 0; i < 2; i++) { 188 /* Each or[i] only has 2 ranges. */ 189 for (j = 0; j < 2; j++) { 190 if (or[i].nx[j] > 0) { 191 r->px[k] = or[i].px[j]; 192 r->nx[k] = or[i].nx[j]; 193 k++; 194 } 195 } 196 } 197 198 /* Zero remaining ranges if any. */ 199 for (i = k; i < OVERLAY_MAX_RANGES; i++) { 200 r->px[i] = 0; 201 r->nx[i] = 0; 202 } 203 204 return; 205 } 206 207 server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx, 208 r); 209} 210 211static void 212popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx) 213{ 214 struct popup_data *pd = data; 215 struct tty *tty = &c->tty; 216 struct screen s; 217 struct screen_write_ctx ctx; 218 u_int i, px = pd->px, py = pd->py; 219 struct colour_palette *palette = &pd->palette; 220 struct grid_cell defaults; 221 222 screen_init(&s, pd->sx, pd->sy, 0); 223 screen_write_start(&ctx, &s); 224 screen_write_clearscreen(&ctx, 8); 225 226 if (pd->border_lines == BOX_LINES_NONE) { 227 screen_write_cursormove(&ctx, 0, 0, 0); 228 screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy); 229 } else if (pd->sx > 2 && pd->sy > 2) { 230 screen_write_box(&ctx, pd->sx, pd->sy, pd->border_lines, 231 &pd->border_cell, pd->title); 232 screen_write_cursormove(&ctx, 1, 1, 0); 233 screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2, 234 pd->sy - 2); 235 } 236 screen_write_stop(&ctx); 237 238 memcpy(&defaults, &pd->defaults, sizeof defaults); 239 if (defaults.fg == 8) 240 defaults.fg = palette->fg; 241 if (defaults.bg == 8) 242 defaults.bg = palette->bg; 243 244 if (pd->md != NULL) { 245 c->overlay_check = menu_check_cb; 246 c->overlay_data = pd->md; 247 } else { 248 c->overlay_check = NULL; 249 c->overlay_data = NULL; 250 } 251 for (i = 0; i < pd->sy; i++) { 252 tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults, 253 palette); 254 } 255 if (pd->md != NULL) { 256 c->overlay_check = NULL; 257 c->overlay_data = NULL; 258 menu_draw_cb(c, pd->md, rctx); 259 } 260 c->overlay_check = popup_check_cb; 261 c->overlay_data = pd; 262} 263 264static void 265popup_free_cb(struct client *c, void *data) 266{ 267 struct popup_data *pd = data; 268 struct cmdq_item *item = pd->item; 269 270 if (pd->md != NULL) 271 menu_free_cb(c, pd->md); 272 273 if (pd->cb != NULL) 274 pd->cb(pd->status, pd->arg); 275 276 if (item != NULL) { 277 if (cmdq_get_client(item) != NULL && 278 cmdq_get_client(item)->session == NULL) 279 cmdq_get_client(item)->retval = pd->status; 280 cmdq_continue(item); 281 } 282 server_client_unref(pd->c); 283 284 if (pd->job != NULL) 285 job_free(pd->job); 286 input_free(pd->ictx); 287 288 screen_free(&pd->s); 289 colour_palette_free(&pd->palette); 290 291 free(pd->title); 292 free(pd); 293} 294 295static void 296popup_resize_cb(__unused struct client *c, void *data) 297{ 298 struct popup_data *pd = data; 299 struct tty *tty = &c->tty; 300 301 if (pd == NULL) 302 return; 303 if (pd->md != NULL) 304 menu_free_cb(c, pd->md); 305 306 /* Adjust position and size. */ 307 if (pd->psy > tty->sy) 308 pd->sy = tty->sy; 309 else 310 pd->sy = pd->psy; 311 if (pd->psx > tty->sx) 312 pd->sx = tty->sx; 313 else 314 pd->sx = pd->psx; 315 if (pd->ppy + pd->sy > tty->sy) 316 pd->py = tty->sy - pd->sy; 317 else 318 pd->py = pd->ppy; 319 if (pd->ppx + pd->sx > tty->sx) 320 pd->px = tty->sx - pd->sx; 321 else 322 pd->px = pd->ppx; 323 324 /* Avoid zero size screens. */ 325 if (pd->border_lines == BOX_LINES_NONE) { 326 screen_resize(&pd->s, pd->sx, pd->sy, 0); 327 if (pd->job != NULL) 328 job_resize(pd->job, pd->sx, pd->sy ); 329 } else if (pd->sx > 2 && pd->sy > 2) { 330 screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); 331 if (pd->job != NULL) 332 job_resize(pd->job, pd->sx - 2, pd->sy - 2); 333 } 334} 335 336static void 337popup_make_pane(struct popup_data *pd, enum layout_type type) 338{ 339 struct client *c = pd->c; 340 struct session *s = c->session; 341 struct window *w = s->curw->window; 342 struct layout_cell *lc; 343 struct window_pane *wp = w->active, *new_wp; 344 u_int hlimit; 345 const char *shell; 346 347 window_unzoom(w); 348 349 lc = layout_split_pane(wp, type, -1, 0); 350 hlimit = options_get_number(s->options, "history-limit"); 351 new_wp = window_add_pane(wp->window, NULL, hlimit, 0); 352 layout_assign_pane(lc, new_wp, 0); 353 354 new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty, 355 sizeof new_wp->tty); 356 pd->job = NULL; 357 358 screen_set_title(&pd->s, new_wp->base.title); 359 screen_free(&new_wp->base); 360 memcpy(&new_wp->base, &pd->s, sizeof wp->base); 361 screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1); 362 screen_init(&pd->s, 1, 1, 0); 363 364 shell = options_get_string(s->options, "default-shell"); 365 if (!checkshell(shell)) 366 shell = _PATH_BSHELL; 367 new_wp->shell = xstrdup(shell); 368 369 window_pane_set_event(new_wp); 370 window_set_active_pane(w, new_wp, 1); 371 new_wp->flags |= PANE_CHANGED; 372 373 pd->close = 1; 374} 375 376static void 377popup_menu_done(__unused struct menu *menu, __unused u_int choice, 378 key_code key, void *data) 379{ 380 struct popup_data *pd = data; 381 struct client *c = pd->c; 382 struct paste_buffer *pb; 383 const char *buf; 384 size_t len; 385 386 pd->md = NULL; 387 pd->menu = NULL; 388 server_redraw_client(pd->c); 389 390 switch (key) { 391 case 'p': 392 pb = paste_get_top(NULL); 393 if (pb != NULL) { 394 buf = paste_buffer_data(pb, &len); 395 bufferevent_write(job_get_event(pd->job), buf, len); 396 } 397 break; 398 case 'F': 399 pd->sx = c->tty.sx; 400 pd->sy = c->tty.sy; 401 pd->px = 0; 402 pd->py = 0; 403 server_redraw_client(c); 404 break; 405 case 'C': 406 pd->px = c->tty.sx / 2 - pd->sx / 2; 407 pd->py = c->tty.sy / 2 - pd->sy / 2; 408 server_redraw_client(c); 409 break; 410 case 'h': 411 popup_make_pane(pd, LAYOUT_LEFTRIGHT); 412 break; 413 case 'v': 414 popup_make_pane(pd, LAYOUT_TOPBOTTOM); 415 break; 416 case 'q': 417 pd->close = 1; 418 break; 419 } 420} 421 422static void 423popup_handle_drag(struct client *c, struct popup_data *pd, 424 struct mouse_event *m) 425{ 426 u_int px, py; 427 428 if (!MOUSE_DRAG(m->b)) 429 pd->dragging = OFF; 430 else if (pd->dragging == MOVE) { 431 if (m->x < pd->dx) 432 px = 0; 433 else if (m->x - pd->dx + pd->sx > c->tty.sx) 434 px = c->tty.sx - pd->sx; 435 else 436 px = m->x - pd->dx; 437 if (m->y < pd->dy) 438 py = 0; 439 else if (m->y - pd->dy + pd->sy > c->tty.sy) 440 py = c->tty.sy - pd->sy; 441 else 442 py = m->y - pd->dy; 443 pd->px = px; 444 pd->py = py; 445 pd->dx = m->x - pd->px; 446 pd->dy = m->y - pd->py; 447 pd->ppx = px; 448 pd->ppy = py; 449 server_redraw_client(c); 450 } else if (pd->dragging == SIZE) { 451 if (pd->border_lines == BOX_LINES_NONE) { 452 if (m->x < pd->px + 1) 453 return; 454 if (m->y < pd->py + 1) 455 return; 456 } else { 457 if (m->x < pd->px + 3) 458 return; 459 if (m->y < pd->py + 3) 460 return; 461 } 462 pd->sx = m->x - pd->px; 463 pd->sy = m->y - pd->py; 464 pd->psx = pd->sx; 465 pd->psy = pd->sy; 466 467 if (pd->border_lines == BOX_LINES_NONE) { 468 screen_resize(&pd->s, pd->sx, pd->sy, 0); 469 if (pd->job != NULL) 470 job_resize(pd->job, pd->sx, pd->sy); 471 } else { 472 screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); 473 if (pd->job != NULL) 474 job_resize(pd->job, pd->sx - 2, pd->sy - 2); 475 } 476 server_redraw_client(c); 477 } 478} 479 480static int 481popup_key_cb(struct client *c, void *data, struct key_event *event) 482{ 483 struct popup_data *pd = data; 484 struct mouse_event *m = &event->m; 485 const char *buf; 486 size_t len; 487 u_int px, py, x; 488 enum { NONE, LEFT, RIGHT, TOP, BOTTOM } border = NONE; 489 490 if (pd->md != NULL) { 491 if (menu_key_cb(c, pd->md, event) == 1) { 492 pd->md = NULL; 493 pd->menu = NULL; 494 if (pd->close) 495 server_client_clear_overlay(c); 496 else 497 server_redraw_client(c); 498 } 499 return (0); 500 } 501 502 if (KEYC_IS_MOUSE(event->key)) { 503 if (pd->dragging != OFF) { 504 popup_handle_drag(c, pd, m); 505 goto out; 506 } 507 if (m->x < pd->px || 508 m->x > pd->px + pd->sx - 1 || 509 m->y < pd->py || 510 m->y > pd->py + pd->sy - 1) { 511 if (MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3) 512 goto menu; 513 return (0); 514 } 515 if (pd->border_lines != BOX_LINES_NONE) { 516 if (m->x == pd->px) 517 border = LEFT; 518 else if (m->x == pd->px + pd->sx - 1) 519 border = RIGHT; 520 else if (m->y == pd->py) 521 border = TOP; 522 else if (m->y == pd->py + pd->sy - 1) 523 border = BOTTOM; 524 } 525 if ((m->b & MOUSE_MASK_MODIFIERS) == 0 && 526 MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3 && 527 (border == LEFT || border == TOP)) 528 goto menu; 529 if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) || 530 border != NONE) { 531 if (!MOUSE_DRAG(m->b)) 532 goto out; 533 if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_1) 534 pd->dragging = MOVE; 535 else if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_3) 536 pd->dragging = SIZE; 537 pd->dx = m->lx - pd->px; 538 pd->dy = m->ly - pd->py; 539 goto out; 540 } 541 } 542 if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) || 543 pd->job == NULL) && 544 (event->key == '\033' || event->key == '\003')) 545 return (1); 546 if (pd->job != NULL) { 547 if (KEYC_IS_MOUSE(event->key)) { 548 /* Must be inside, checked already. */ 549 if (pd->border_lines == BOX_LINES_NONE) { 550 px = m->x - pd->px; 551 py = m->y - pd->py; 552 } else { 553 px = m->x - pd->px - 1; 554 py = m->y - pd->py - 1; 555 } 556 if (!input_key_get_mouse(&pd->s, m, px, py, &buf, &len)) 557 return (0); 558 bufferevent_write(job_get_event(pd->job), buf, len); 559 return (0); 560 } 561 input_key(&pd->s, job_get_event(pd->job), event->key); 562 } 563 return (0); 564 565menu: 566 pd->menu = menu_create(""); 567 if (pd->flags & POPUP_INTERNAL) { 568 menu_add_items(pd->menu, popup_internal_menu_items, NULL, c, 569 NULL); 570 } else 571 menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL); 572 if (m->x >= (pd->menu->width + 4) / 2) 573 x = m->x - (pd->menu->width + 4) / 2; 574 else 575 x = 0; 576 pd->md = menu_prepare(pd->menu, 0, NULL, x, m->y, c, NULL, 577 popup_menu_done, pd); 578 c->flags |= CLIENT_REDRAWOVERLAY; 579 580out: 581 pd->lx = m->x; 582 pd->ly = m->y; 583 pd->lb = m->b; 584 return (0); 585} 586 587static void 588popup_job_update_cb(struct job *job) 589{ 590 struct popup_data *pd = job_get_data(job); 591 struct evbuffer *evb = job_get_event(job)->input; 592 struct client *c = pd->c; 593 struct screen *s = &pd->s; 594 void *data = EVBUFFER_DATA(evb); 595 size_t size = EVBUFFER_LENGTH(evb); 596 597 if (size == 0) 598 return; 599 600 if (pd->md != NULL) { 601 c->overlay_check = menu_check_cb; 602 c->overlay_data = pd->md; 603 } else { 604 c->overlay_check = NULL; 605 c->overlay_data = NULL; 606 } 607 input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size); 608 c->overlay_check = popup_check_cb; 609 c->overlay_data = pd; 610 611 evbuffer_drain(evb, size); 612} 613 614static void 615popup_job_complete_cb(struct job *job) 616{ 617 struct popup_data *pd = job_get_data(job); 618 int status; 619 620 status = job_get_status(pd->job); 621 if (WIFEXITED(status)) 622 pd->status = WEXITSTATUS(status); 623 else if (WIFSIGNALED(status)) 624 pd->status = WTERMSIG(status); 625 else 626 pd->status = 0; 627 pd->job = NULL; 628 629 if ((pd->flags & POPUP_CLOSEEXIT) || 630 ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0)) 631 server_client_clear_overlay(pd->c); 632} 633 634int 635popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, 636 u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd, 637 int argc, char **argv, const char *cwd, const char *title, struct client *c, 638 struct session *s, const char* style, const char* border_style, 639 popup_close_cb cb, void *arg) 640{ 641 struct popup_data *pd; 642 u_int jx, jy; 643 struct options *o; 644 struct style sytmp; 645 646 if (s != NULL) 647 o = s->curw->window->options; 648 else 649 o = c->session->curw->window->options; 650 651 if (lines == BOX_LINES_DEFAULT) 652 lines = options_get_number(o, "popup-border-lines"); 653 if (lines == BOX_LINES_NONE) { 654 if (sx < 1 || sy < 1) 655 return (-1); 656 jx = sx; 657 jy = sy; 658 } else { 659 if (sx < 3 || sy < 3) 660 return (-1); 661 jx = sx - 2; 662 jy = sy - 2; 663 } 664 if (c->tty.sx < sx || c->tty.sy < sy) 665 return (-1); 666 667 pd = xcalloc(1, sizeof *pd); 668 pd->item = item; 669 pd->flags = flags; 670 if (title != NULL) 671 pd->title = xstrdup(title); 672 673 pd->c = c; 674 pd->c->references++; 675 676 pd->cb = cb; 677 pd->arg = arg; 678 pd->status = 128 + SIGHUP; 679 680 pd->border_lines = lines; 681 memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell); 682 style_apply(&pd->border_cell, o, "popup-border-style", NULL); 683 if (border_style != NULL) { 684 style_set(&sytmp, &grid_default_cell); 685 if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) { 686 pd->border_cell.fg = sytmp.gc.fg; 687 pd->border_cell.bg = sytmp.gc.bg; 688 } 689 } 690 pd->border_cell.attr = 0; 691 692 screen_init(&pd->s, jx, jy, 0); 693 colour_palette_init(&pd->palette); 694 colour_palette_from_option(&pd->palette, global_w_options); 695 696 memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults); 697 style_apply(&pd->defaults, o, "popup-style", NULL); 698 if (style != NULL) { 699 style_set(&sytmp, &grid_default_cell); 700 if (style_parse(&sytmp, &pd->defaults, style) == 0) { 701 pd->defaults.fg = sytmp.gc.fg; 702 pd->defaults.bg = sytmp.gc.bg; 703 } 704 } 705 pd->defaults.attr = 0; 706 707 pd->px = px; 708 pd->py = py; 709 pd->sx = sx; 710 pd->sy = sy; 711 712 pd->ppx = px; 713 pd->ppy = py; 714 pd->psx = sx; 715 pd->psy = sy; 716 717 pd->job = job_run(shellcmd, argc, argv, env, s, cwd, 718 popup_job_update_cb, popup_job_complete_cb, NULL, pd, 719 JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, jx, jy); 720 pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette); 721 722 server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, 723 popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); 724 return (0); 725} 726 727static void 728popup_editor_free(struct popup_editor *pe) 729{ 730 unlink(pe->path); 731 free(pe->path); 732 free(pe); 733} 734 735static void 736popup_editor_close_cb(int status, void *arg) 737{ 738 struct popup_editor *pe = arg; 739 FILE *f; 740 char *buf = NULL; 741 off_t len = 0; 742 743 if (status != 0) { 744 pe->cb(NULL, 0, pe->arg); 745 popup_editor_free(pe); 746 return; 747 } 748 749 f = fopen(pe->path, "r"); 750 if (f != NULL) { 751 fseeko(f, 0, SEEK_END); 752 len = ftello(f); 753 fseeko(f, 0, SEEK_SET); 754 755 if (len == 0 || 756 (uintmax_t)len > (uintmax_t)SIZE_MAX || 757 (buf = malloc(len)) == NULL || 758 fread(buf, len, 1, f) != 1) { 759 free(buf); 760 buf = NULL; 761 len = 0; 762 } 763 fclose(f); 764 } 765 pe->cb(buf, len, pe->arg); /* callback now owns buffer */ 766 popup_editor_free(pe); 767} 768 769int 770popup_editor(struct client *c, const char *buf, size_t len, 771 popup_finish_edit_cb cb, void *arg) 772{ 773 struct popup_editor *pe; 774 int fd; 775 FILE *f; 776 char *cmd; 777 char path[] = _PATH_TMP "tmux.XXXXXXXX"; 778 const char *editor; 779 u_int px, py, sx, sy; 780 781 editor = options_get_string(global_options, "editor"); 782 if (*editor == '\0') 783 return (-1); 784 785 fd = mkstemp(path); 786 if (fd == -1) 787 return (-1); 788 f = fdopen(fd, "w"); 789 if (fwrite(buf, len, 1, f) != 1) { 790 fclose(f); 791 return (-1); 792 } 793 fclose(f); 794 795 pe = xcalloc(1, sizeof *pe); 796 pe->path = xstrdup(path); 797 pe->cb = cb; 798 pe->arg = arg; 799 800 sx = c->tty.sx * 9 / 10; 801 sy = c->tty.sy * 9 / 10; 802 px = (c->tty.sx / 2) - (sx / 2); 803 py = (c->tty.sy / 2) - (sy / 2); 804 805 xasprintf(&cmd, "%s %s", editor, path); 806 if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, BOX_LINES_DEFAULT, 807 NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, NULL, c, NULL, 808 NULL, NULL, popup_editor_close_cb, pe) != 0) { 809 popup_editor_free(pe); 810 free(cmd); 811 return (-1); 812 } 813 free(cmd); 814 return (0); 815} 816