Annotation of src/usr.bin/tmux/popup.c, Revision 1.30
1.30 ! nicm 1: /* $OpenBSD: popup.c,v 1.29 2021/08/13 19:27:25 nicm Exp $ */
1.1 nicm 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:
1.17 nicm 22: #include <paths.h>
1.1 nicm 23: #include <signal.h>
24: #include <stdlib.h>
25: #include <string.h>
1.17 nicm 26: #include <unistd.h>
1.1 nicm 27:
28: #include "tmux.h"
29:
30: struct popup_data {
31: struct client *c;
32: struct cmdq_item *item;
33: int flags;
34:
35: struct screen s;
1.25 nicm 36: struct colour_palette palette;
1.1 nicm 37: struct job *job;
38: struct input_ctx *ictx;
39: int status;
1.14 nicm 40: popup_close_cb cb;
41: void *arg;
1.1 nicm 42:
1.27 nicm 43: struct menu *menu;
44: struct menu_data *md;
45: int close;
46:
1.23 nicm 47: /* Current position and size. */
1.1 nicm 48: u_int px;
49: u_int py;
50: u_int sx;
51: u_int sy;
52:
1.23 nicm 53: /* Preferred position and size. */
54: u_int ppx;
55: u_int ppy;
56: u_int psx;
57: u_int psy;
58:
1.1 nicm 59: enum { OFF, MOVE, SIZE } dragging;
60: u_int dx;
61: u_int dy;
62:
63: u_int lx;
64: u_int ly;
65: u_int lb;
66: };
67:
1.17 nicm 68: struct popup_editor {
69: char *path;
70: popup_finish_edit_cb cb;
71: void *arg;
72: };
73:
1.27 nicm 74: static const struct menu_item popup_menu_items[] = {
75: { "Close", 'q', NULL },
76: { "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL },
77: { "", KEYC_NONE, NULL },
78: { "Fill Space", 'F', NULL },
79: { "Centre", 'C', NULL },
1.29 nicm 80: { "", KEYC_NONE, NULL },
1.30 ! nicm 81: { "To Horizontal Pane", 'h', NULL },
! 82: { "To Vertical Pane", 'v', NULL },
1.29 nicm 83:
84: { NULL, KEYC_NONE, NULL }
85: };
86:
87: static const struct menu_item popup_internal_menu_items[] = {
88: { "Close", 'q', NULL },
89: { "", KEYC_NONE, NULL },
90: { "Fill Space", 'F', NULL },
91: { "Centre", 'C', NULL },
1.27 nicm 92:
93: { NULL, KEYC_NONE, NULL }
94: };
95:
1.1 nicm 96: static void
1.15 nicm 97: popup_redraw_cb(const struct tty_ctx *ttyctx)
98: {
99: struct popup_data *pd = ttyctx->arg;
100:
101: pd->c->flags |= CLIENT_REDRAWOVERLAY;
102: }
103:
104: static int
105: popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
106: {
107: struct popup_data *pd = ttyctx->arg;
108:
1.16 nicm 109: if (c != pd->c)
110: return (0);
1.15 nicm 111: if (pd->c->flags & CLIENT_REDRAWOVERLAY)
1.16 nicm 112: return (0);
1.15 nicm 113:
114: ttyctx->bigger = 0;
115: ttyctx->wox = 0;
116: ttyctx->woy = 0;
117: ttyctx->wsx = c->tty.sx;
118: ttyctx->wsy = c->tty.sy;
119:
1.26 nicm 120: if (pd->flags & POPUP_NOBORDER) {
121: ttyctx->xoff = ttyctx->rxoff = pd->px;
122: ttyctx->yoff = ttyctx->ryoff = pd->py;
123: } else {
124: ttyctx->xoff = ttyctx->rxoff = pd->px + 1;
125: ttyctx->yoff = ttyctx->ryoff = pd->py + 1;
126: }
1.15 nicm 127:
128: return (1);
129: }
130:
131: static void
132: popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
133: {
134: struct popup_data *pd = ctx->arg;
135:
1.25 nicm 136: ttyctx->palette = &pd->palette;
1.15 nicm 137: ttyctx->redraw_cb = popup_redraw_cb;
138: ttyctx->set_client_cb = popup_set_client_cb;
139: ttyctx->arg = pd;
140: }
141:
142: static struct screen *
1.27 nicm 143: popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
1.1 nicm 144: {
1.27 nicm 145: struct popup_data *pd = data;
146:
147: if (pd->md != NULL)
148: return (menu_mode_cb(c, pd->md, cx, cy));
1.1 nicm 149:
1.26 nicm 150: if (pd->flags & POPUP_NOBORDER) {
151: *cx = pd->px + pd->s.cx;
152: *cy = pd->py + pd->s.cy;
153: } else {
154: *cx = pd->px + 1 + pd->s.cx;
155: *cy = pd->py + 1 + pd->s.cy;
156: }
1.15 nicm 157: return (&pd->s);
1.1 nicm 158: }
159:
160: static int
1.27 nicm 161: popup_check_cb(struct client *c, void *data, u_int px, u_int py)
1.1 nicm 162: {
1.27 nicm 163: struct popup_data *pd = data;
1.1 nicm 164:
165: if (px < pd->px || px > pd->px + pd->sx - 1)
166: return (1);
167: if (py < pd->py || py > pd->py + pd->sy - 1)
168: return (1);
1.27 nicm 169: if (pd->md != NULL)
170: return (menu_check_cb(c, pd->md, px, py));
1.1 nicm 171: return (0);
172: }
173:
174: static void
1.27 nicm 175: popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
1.1 nicm 176: {
1.27 nicm 177: struct popup_data *pd = data;
1.1 nicm 178: struct tty *tty = &c->tty;
179: struct screen s;
180: struct screen_write_ctx ctx;
181: u_int i, px = pd->px, py = pd->py;
1.25 nicm 182: struct colour_palette *palette = &pd->palette;
183: struct grid_cell gc;
1.1 nicm 184:
185: screen_init(&s, pd->sx, pd->sy, 0);
1.15 nicm 186: screen_write_start(&ctx, &s);
1.1 nicm 187: screen_write_clearscreen(&ctx, 8);
1.23 nicm 188:
1.26 nicm 189: if (pd->flags & POPUP_NOBORDER) {
190: screen_write_cursormove(&ctx, 0, 0, 0);
191: screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy);
192: } else if (pd->sx > 2 && pd->sy > 2) {
1.23 nicm 193: screen_write_box(&ctx, pd->sx, pd->sy);
194: screen_write_cursormove(&ctx, 1, 1, 0);
195: screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2,
196: pd->sy - 2);
197: }
1.1 nicm 198: screen_write_stop(&ctx);
199:
1.25 nicm 200: memcpy(&gc, &grid_default_cell, sizeof gc);
201: gc.fg = pd->palette.fg;
202: gc.bg = pd->palette.bg;
203:
1.27 nicm 204: if (pd->md != NULL) {
205: c->overlay_check = menu_check_cb;
206: c->overlay_data = pd->md;
207: } else {
208: c->overlay_check = NULL;
209: c->overlay_data = NULL;
210: }
1.25 nicm 211: for (i = 0; i < pd->sy; i++)
212: tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &gc, palette);
1.27 nicm 213: if (pd->md != NULL) {
214: c->overlay_check = NULL;
215: c->overlay_data = NULL;
216: menu_draw_cb(c, pd->md, rctx);
217: }
1.1 nicm 218: c->overlay_check = popup_check_cb;
1.27 nicm 219: c->overlay_data = pd;
1.1 nicm 220: }
221:
222: static void
1.27 nicm 223: popup_free_cb(struct client *c, void *data)
1.1 nicm 224: {
1.27 nicm 225: struct popup_data *pd = data;
1.1 nicm 226: struct cmdq_item *item = pd->item;
227:
1.27 nicm 228: if (pd->md != NULL)
229: menu_free_cb(c, pd->md);
230:
1.14 nicm 231: if (pd->cb != NULL)
232: pd->cb(pd->status, pd->arg);
233:
1.1 nicm 234: if (item != NULL) {
1.22 nicm 235: if (cmdq_get_client(item) != NULL &&
1.8 nicm 236: cmdq_get_client(item)->session == NULL)
237: cmdq_get_client(item)->retval = pd->status;
1.1 nicm 238: cmdq_continue(item);
239: }
240: server_client_unref(pd->c);
241:
242: if (pd->job != NULL)
243: job_free(pd->job);
1.22 nicm 244: input_free(pd->ictx);
1.1 nicm 245:
246: screen_free(&pd->s);
1.25 nicm 247: colour_palette_free(&pd->palette);
1.27 nicm 248:
1.1 nicm 249: free(pd);
250: }
251:
252: static void
1.27 nicm 253: popup_resize_cb(__unused struct client *c, void *data)
1.23 nicm 254: {
1.27 nicm 255: struct popup_data *pd = data;
1.23 nicm 256: struct tty *tty = &c->tty;
257:
258: if (pd == NULL)
259: return;
1.27 nicm 260: if (pd->md != NULL)
261: menu_free_cb(c, pd->md);
1.23 nicm 262:
263: /* Adjust position and size. */
264: if (pd->psy > tty->sy)
265: pd->sy = tty->sy;
266: else
267: pd->sy = pd->psy;
268: if (pd->psx > tty->sx)
269: pd->sx = tty->sx;
270: else
271: pd->sx = pd->psx;
272: if (pd->ppy + pd->sy > tty->sy)
273: pd->py = tty->sy - pd->sy;
274: else
275: pd->py = pd->ppy;
276: if (pd->ppx + pd->sx > tty->sx)
277: pd->px = tty->sx - pd->sx;
278: else
279: pd->px = pd->ppx;
280:
281: /* Avoid zero size screens. */
1.26 nicm 282: if (pd->flags & POPUP_NOBORDER) {
283: screen_resize(&pd->s, pd->sx, pd->sy, 0);
284: if (pd->job != NULL)
285: job_resize(pd->job, pd->sx, pd->sy );
286: } else if (pd->sx > 2 && pd->sy > 2) {
1.23 nicm 287: screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
288: if (pd->job != NULL)
289: job_resize(pd->job, pd->sx - 2, pd->sy - 2);
290: }
291: }
292:
293: static void
1.29 nicm 294: popup_make_pane(struct popup_data *pd, enum layout_type type)
295: {
296: struct client *c = pd->c;
297: struct session *s = c->session;
298: struct window *w = s->curw->window;
299: struct layout_cell *lc;
300: struct window_pane *wp = w->active, *new_wp;
301: u_int hlimit;
1.30 ! nicm 302: const char *shell;
1.29 nicm 303:
304: window_unzoom(w);
305:
306: lc = layout_split_pane(wp, type, -1, 0);
307: hlimit = options_get_number(s->options, "history-limit");
308: new_wp = window_add_pane(wp->window, NULL, hlimit, 0);
309: layout_assign_pane(lc, new_wp, 0);
310:
1.30 ! nicm 311: new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty,
! 312: sizeof new_wp->tty);
1.29 nicm 313: pd->job = NULL;
314:
1.30 ! nicm 315: screen_set_title(&pd->s, new_wp->base.title);
1.29 nicm 316: screen_free(&new_wp->base);
317: memcpy(&new_wp->base, &pd->s, sizeof wp->base);
318: screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1);
319: screen_init(&pd->s, 1, 1, 0);
320:
1.30 ! nicm 321: shell = options_get_string(s->options, "default-shell");
! 322: if (!checkshell(shell))
! 323: shell = _PATH_BSHELL;
! 324: new_wp->shell = xstrdup(shell);
! 325:
1.29 nicm 326: window_pane_set_event(new_wp);
327: window_set_active_pane(w, new_wp, 1);
1.30 ! nicm 328: new_wp->flags |= PANE_CHANGED;
1.29 nicm 329:
330: pd->close = 1;
331: }
332:
333: static void
1.27 nicm 334: popup_menu_done(__unused struct menu *menu, __unused u_int choice,
335: key_code key, void *data)
336: {
337: struct popup_data *pd = data;
338: struct client *c = pd->c;
339: struct paste_buffer *pb;
340: const char *buf;
341: size_t len;
342:
343: pd->md = NULL;
344: pd->menu = NULL;
345: server_redraw_client(pd->c);
346:
347: switch (key) {
348: case 'p':
349: pb = paste_get_top(NULL);
350: if (pb != NULL) {
351: buf = paste_buffer_data(pb, &len);
352: bufferevent_write(job_get_event(pd->job), buf, len);
353: }
354: break;
355: case 'F':
356: pd->sx = c->tty.sx;
357: pd->sy = c->tty.sy;
358: pd->px = 0;
359: pd->py = 0;
360: server_redraw_client(c);
361: break;
362: case 'C':
363: pd->px = c->tty.sx / 2 - pd->sx / 2;
364: pd->py = c->tty.sy / 2 - pd->sy / 2;
365: server_redraw_client(c);
366: break;
1.29 nicm 367: case 'h':
368: popup_make_pane(pd, LAYOUT_LEFTRIGHT);
369: break;
370: case 'v':
371: popup_make_pane(pd, LAYOUT_TOPBOTTOM);
372: break;
1.27 nicm 373: case 'q':
374: pd->close = 1;
375: break;
376: }
377: }
378:
379: static void
1.1 nicm 380: popup_handle_drag(struct client *c, struct popup_data *pd,
381: struct mouse_event *m)
382: {
383: u_int px, py;
384:
385: if (!MOUSE_DRAG(m->b))
386: pd->dragging = OFF;
387: else if (pd->dragging == MOVE) {
388: if (m->x < pd->dx)
389: px = 0;
390: else if (m->x - pd->dx + pd->sx > c->tty.sx)
391: px = c->tty.sx - pd->sx;
392: else
393: px = m->x - pd->dx;
394: if (m->y < pd->dy)
395: py = 0;
396: else if (m->y - pd->dy + pd->sy > c->tty.sy)
397: py = c->tty.sy - pd->sy;
398: else
399: py = m->y - pd->dy;
400: pd->px = px;
401: pd->py = py;
402: pd->dx = m->x - pd->px;
403: pd->dy = m->y - pd->py;
1.23 nicm 404: pd->ppx = px;
405: pd->ppy = py;
1.1 nicm 406: server_redraw_client(c);
407: } else if (pd->dragging == SIZE) {
1.26 nicm 408: if (pd->flags & POPUP_NOBORDER) {
409: if (m->x < pd->px + 1)
410: return;
411: if (m->y < pd->py + 1)
412: return;
413: } else {
414: if (m->x < pd->px + 3)
415: return;
416: if (m->y < pd->py + 3)
417: return;
418: }
1.1 nicm 419: pd->sx = m->x - pd->px;
420: pd->sy = m->y - pd->py;
1.23 nicm 421: pd->psx = pd->sx;
422: pd->psy = pd->sy;
1.1 nicm 423:
1.26 nicm 424: if (pd->flags & POPUP_NOBORDER) {
425: screen_resize(&pd->s, pd->sx, pd->sy, 0);
426: if (pd->job != NULL)
427: job_resize(pd->job, pd->sx, pd->sy);
428: } else {
429: screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
430: if (pd->job != NULL)
431: job_resize(pd->job, pd->sx - 2, pd->sy - 2);
432: }
1.1 nicm 433: server_redraw_client(c);
434: }
435: }
436:
437: static int
1.27 nicm 438: popup_key_cb(struct client *c, void *data, struct key_event *event)
1.1 nicm 439: {
1.27 nicm 440: struct popup_data *pd = data;
1.1 nicm 441: struct mouse_event *m = &event->m;
1.22 nicm 442: const char *buf;
1.6 nicm 443: size_t len;
1.27 nicm 444: u_int px, py, x;
445:
446: if (pd->md != NULL) {
447: if (menu_key_cb(c, pd->md, event) == 1) {
448: pd->md = NULL;
449: pd->menu = NULL;
450: if (pd->close)
451: server_client_clear_overlay(c);
452: else
453: server_redraw_client(c);
454: }
455: return (0);
456: }
1.1 nicm 457:
458: if (KEYC_IS_MOUSE(event->key)) {
459: if (pd->dragging != OFF) {
460: popup_handle_drag(c, pd, m);
461: goto out;
462: }
463: if (m->x < pd->px ||
464: m->x > pd->px + pd->sx - 1 ||
465: m->y < pd->py ||
466: m->y > pd->py + pd->sy - 1) {
1.27 nicm 467: if (MOUSE_BUTTONS(m->b) == 2)
468: goto menu;
1.1 nicm 469: return (0);
470: }
1.27 nicm 471: if ((~pd->flags & POPUP_NOBORDER) &&
472: (~m->b & MOUSE_MASK_META) &&
473: MOUSE_BUTTONS(m->b) == 2 &&
474: (m->x == pd->px ||
475: m->x == pd->px + pd->sx - 1 ||
476: m->y == pd->py ||
477: m->y == pd->py + pd->sy - 1))
478: goto menu;
1.1 nicm 479: if ((m->b & MOUSE_MASK_META) ||
1.26 nicm 480: ((~pd->flags & POPUP_NOBORDER) &&
481: (m->x == pd->px ||
1.1 nicm 482: m->x == pd->px + pd->sx - 1 ||
483: m->y == pd->py ||
1.26 nicm 484: m->y == pd->py + pd->sy - 1))) {
1.1 nicm 485: if (!MOUSE_DRAG(m->b))
486: goto out;
487: if (MOUSE_BUTTONS(m->lb) == 0)
488: pd->dragging = MOVE;
489: else if (MOUSE_BUTTONS(m->lb) == 2)
490: pd->dragging = SIZE;
491: pd->dx = m->lx - pd->px;
492: pd->dy = m->ly - pd->py;
493: goto out;
494: }
495: }
1.22 nicm 496: if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) ||
497: pd->job == NULL) &&
498: (event->key == '\033' || event->key == '\003'))
499: return (1);
500: if (pd->job != NULL) {
1.6 nicm 501: if (KEYC_IS_MOUSE(event->key)) {
502: /* Must be inside, checked already. */
1.26 nicm 503: if (pd->flags & POPUP_NOBORDER) {
504: px = m->x - pd->px;
505: py = m->y - pd->py;
506: } else {
507: px = m->x - pd->px - 1;
508: py = m->y - pd->py - 1;
509: }
510: if (!input_key_get_mouse(&pd->s, m, px, py, &buf, &len))
1.6 nicm 511: return (0);
512: bufferevent_write(job_get_event(pd->job), buf, len);
513: return (0);
514: }
1.18 nicm 515: input_key(&pd->s, job_get_event(pd->job), event->key);
1.1 nicm 516: }
1.22 nicm 517: return (0);
1.27 nicm 518:
519: menu:
520: pd->menu = menu_create("");
1.29 nicm 521: if (pd->flags & POPUP_INTERNAL) {
522: menu_add_items(pd->menu, popup_internal_menu_items, NULL, NULL,
523: NULL);
524: } else
525: menu_add_items(pd->menu, popup_menu_items, NULL, NULL, NULL);
1.27 nicm 526: if (m->x >= (pd->menu->width + 4) / 2)
527: x = m->x - (pd->menu->width + 4) / 2;
528: else
529: x = 0;
530: pd->md = menu_prepare(pd->menu, 0, NULL, x, m->y, c, NULL,
531: popup_menu_done, pd);
532: c->flags |= CLIENT_REDRAWOVERLAY;
1.1 nicm 533:
534: out:
535: pd->lx = m->x;
536: pd->ly = m->y;
537: pd->lb = m->b;
538: return (0);
539: }
540:
541: static void
542: popup_job_update_cb(struct job *job)
543: {
544: struct popup_data *pd = job_get_data(job);
545: struct evbuffer *evb = job_get_event(job)->input;
1.15 nicm 546: struct client *c = pd->c;
1.1 nicm 547: struct screen *s = &pd->s;
548: void *data = EVBUFFER_DATA(evb);
549: size_t size = EVBUFFER_LENGTH(evb);
550:
1.15 nicm 551: if (size == 0)
552: return;
553:
1.28 nicm 554: if (pd->md != NULL) {
555: c->overlay_check = menu_check_cb;
556: c->overlay_data = pd->md;
557: } else {
558: c->overlay_check = NULL;
559: c->overlay_data = NULL;
560: }
1.15 nicm 561: input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size);
562: c->overlay_check = popup_check_cb;
1.28 nicm 563: c->overlay_data = pd;
1.15 nicm 564:
565: evbuffer_drain(evb, size);
1.1 nicm 566: }
567:
568: static void
569: popup_job_complete_cb(struct job *job)
570: {
571: struct popup_data *pd = job_get_data(job);
572: int status;
573:
574: status = job_get_status(pd->job);
575: if (WIFEXITED(status))
576: pd->status = WEXITSTATUS(status);
577: else if (WIFSIGNALED(status))
578: pd->status = WTERMSIG(status);
579: else
580: pd->status = 0;
581: pd->job = NULL;
582:
1.4 nicm 583: if ((pd->flags & POPUP_CLOSEEXIT) ||
584: ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0))
1.1 nicm 585: server_client_clear_overlay(pd->c);
586: }
587:
588: int
589: popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx,
1.22 nicm 590: u_int sy, const char *shellcmd, int argc, char **argv, const char *cwd,
591: struct client *c, struct session *s, popup_close_cb cb, void *arg)
1.1 nicm 592: {
593: struct popup_data *pd;
1.26 nicm 594: u_int jx, jy;
1.1 nicm 595:
1.26 nicm 596: if (flags & POPUP_NOBORDER) {
597: if (sx < 1 || sy < 1)
598: return (-1);
599: jx = sx;
600: jy = sy;
601: } else {
602: if (sx < 3 || sy < 3)
603: return (-1);
604: jx = sx - 2;
605: jy = sy - 2;
606: }
1.1 nicm 607: if (c->tty.sx < sx || c->tty.sy < sy)
608: return (-1);
609:
610: pd = xcalloc(1, sizeof *pd);
611: pd->item = item;
612: pd->flags = flags;
613:
614: pd->c = c;
615: pd->c->references++;
616:
1.14 nicm 617: pd->cb = cb;
618: pd->arg = arg;
1.1 nicm 619: pd->status = 128 + SIGHUP;
620:
621: screen_init(&pd->s, sx - 2, sy - 2, 0);
1.25 nicm 622: colour_palette_init(&pd->palette);
623: colour_palette_from_option(&pd->palette, global_w_options);
1.1 nicm 624:
625: pd->px = px;
626: pd->py = py;
627: pd->sx = sx;
628: pd->sy = sy;
629:
1.23 nicm 630: pd->ppx = px;
631: pd->ppy = py;
632: pd->psx = sx;
633: pd->psy = sy;
634:
1.22 nicm 635: pd->job = job_run(shellcmd, argc, argv, s, cwd,
636: popup_job_update_cb, popup_job_complete_cb, NULL, pd,
1.26 nicm 637: JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, jx, jy);
1.25 nicm 638: pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette);
1.1 nicm 639:
640: server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
1.23 nicm 641: popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);
1.17 nicm 642: return (0);
643: }
644:
645: static void
646: popup_editor_free(struct popup_editor *pe)
647: {
648: unlink(pe->path);
649: free(pe->path);
650: free(pe);
651: }
652:
653: static void
654: popup_editor_close_cb(int status, void *arg)
655: {
656: struct popup_editor *pe = arg;
657: FILE *f;
658: char *buf = NULL;
659: off_t len = 0;
660:
661: if (status != 0) {
662: pe->cb(NULL, 0, pe->arg);
663: popup_editor_free(pe);
664: return;
665: }
666:
667: f = fopen(pe->path, "r");
668: if (f != NULL) {
669: fseeko(f, 0, SEEK_END);
670: len = ftello(f);
671: fseeko(f, 0, SEEK_SET);
672:
673: if (len == 0 ||
674: (uintmax_t)len > (uintmax_t)SIZE_MAX ||
675: (buf = malloc(len)) == NULL ||
676: fread(buf, len, 1, f) != 1) {
677: free(buf);
678: buf = NULL;
679: len = 0;
680: }
681: fclose(f);
682: }
683: pe->cb(buf, len, pe->arg); /* callback now owns buffer */
684: popup_editor_free(pe);
685: }
686:
687: int
688: popup_editor(struct client *c, const char *buf, size_t len,
689: popup_finish_edit_cb cb, void *arg)
690: {
691: struct popup_editor *pe;
692: int fd;
693: FILE *f;
694: char *cmd;
695: char path[] = _PATH_TMP "tmux.XXXXXXXX";
696: const char *editor;
697: u_int px, py, sx, sy;
698:
699: editor = options_get_string(global_options, "editor");
700: if (*editor == '\0')
701: return (-1);
702:
703: fd = mkstemp(path);
704: if (fd == -1)
705: return (-1);
706: f = fdopen(fd, "w");
707: if (fwrite(buf, len, 1, f) != 1) {
708: fclose(f);
709: return (-1);
710: }
711: fclose(f);
712:
713: pe = xcalloc(1, sizeof *pe);
714: pe->path = xstrdup(path);
715: pe->cb = cb;
716: pe->arg = arg;
717:
718: sx = c->tty.sx * 9 / 10;
719: sy = c->tty.sy * 9 / 10;
720: px = (c->tty.sx / 2) - (sx / 2);
721: py = (c->tty.sy / 2) - (sy / 2);
722:
723: xasprintf(&cmd, "%s %s", editor, path);
1.29 nicm 724: if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, NULL, px, py, sx, sy,
725: cmd, 0, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) {
1.17 nicm 726: popup_editor_free(pe);
727: free(cmd);
728: return (-1);
729: }
730: free(cmd);
1.1 nicm 731: return (0);
732: }