Coverage Report

Created: 2025-11-19 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/popup.c
Line
Count
Source
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
29
struct 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
73
struct popup_editor {
74
  char      *path;
75
  popup_finish_edit_cb   cb;
76
  void      *arg;
77
};
78
79
static 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
92
static 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
101
static void
102
popup_redraw_cb(const struct tty_ctx *ttyctx)
103
0
{
104
0
  struct popup_data *pd = ttyctx->arg;
105
106
0
  pd->c->flags |= CLIENT_REDRAWOVERLAY;
107
0
}
108
109
static int
110
popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
111
0
{
112
0
  struct popup_data *pd = ttyctx->arg;
113
114
0
  if (c != pd->c)
115
0
    return (0);
116
0
  if (pd->c->flags & CLIENT_REDRAWOVERLAY)
117
0
    return (0);
118
119
0
  ttyctx->bigger = 0;
120
0
  ttyctx->wox = 0;
121
0
  ttyctx->woy = 0;
122
0
  ttyctx->wsx = c->tty.sx;
123
0
  ttyctx->wsy = c->tty.sy;
124
125
0
  if (pd->border_lines == BOX_LINES_NONE) {
126
0
    ttyctx->xoff = ttyctx->rxoff = pd->px;
127
0
    ttyctx->yoff = ttyctx->ryoff = pd->py;
128
0
  } else {
129
0
    ttyctx->xoff = ttyctx->rxoff = pd->px + 1;
130
0
    ttyctx->yoff = ttyctx->ryoff = pd->py + 1;
131
0
  }
132
133
0
  return (1);
134
0
}
135
136
static void
137
popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
138
0
{
139
0
  struct popup_data *pd = ctx->arg;
140
141
0
  memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults);
142
0
  ttyctx->palette = &pd->palette;
143
0
  ttyctx->redraw_cb = popup_redraw_cb;
144
0
  ttyctx->set_client_cb = popup_set_client_cb;
145
0
  ttyctx->arg = pd;
146
0
}
147
148
static struct screen *
149
popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
150
0
{
151
0
  struct popup_data *pd = data;
152
153
0
  if (pd->md != NULL)
154
0
    return (menu_mode_cb(c, pd->md, cx, cy));
155
156
0
  if (pd->border_lines == BOX_LINES_NONE) {
157
0
    *cx = pd->px + pd->s.cx;
158
0
    *cy = pd->py + pd->s.cy;
159
0
  } else {
160
0
    *cx = pd->px + 1 + pd->s.cx;
161
0
    *cy = pd->py + 1 + pd->s.cy;
162
0
  }
163
0
  return (&pd->s);
164
0
}
165
166
/* Return parts of the input range which are not obstructed by the popup. */
167
static void
168
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx,
169
    struct overlay_ranges *r)
170
0
{
171
0
  struct popup_data *pd = data;
172
0
  struct overlay_ranges  or[2];
173
0
  u_int      i, j, k = 0;
174
175
0
  if (pd->md != NULL) {
176
    /* Check each returned range for the menu against the popup. */
177
0
    menu_check_cb(c, pd->md, px, py, nx, r);
178
0
    for (i = 0; i < 2; i++) {
179
0
      server_client_overlay_range(pd->px, pd->py, pd->sx,
180
0
          pd->sy, r->px[i], py, r->nx[i], &or[i]);
181
0
    }
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
0
    for (i = 0; i < 2; i++) {
188
      /* Each or[i] only has 2 ranges. */
189
0
      for (j = 0; j < 2; j++) {
190
0
        if (or[i].nx[j] > 0) {
191
0
          r->px[k] = or[i].px[j];
192
0
          r->nx[k] = or[i].nx[j];
193
0
          k++;
194
0
        }
195
0
      }
196
0
    }
197
198
    /* Zero remaining ranges if any. */
199
0
    for (i = k; i < OVERLAY_MAX_RANGES; i++) {
200
0
      r->px[i] = 0;
201
0
      r->nx[i] = 0;
202
0
    }
203
204
0
    return;
205
0
  }
206
207
0
  server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
208
0
      r);
209
0
}
210
211
static void
212
popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
213
0
{
214
0
  struct popup_data *pd = data;
215
0
  struct tty    *tty = &c->tty;
216
0
  struct screen    s;
217
0
  struct screen_write_ctx  ctx;
218
0
  u_int      i, px = pd->px, py = pd->py;
219
0
  struct colour_palette *palette = &pd->palette;
220
0
  struct grid_cell   defaults;
221
222
0
  screen_init(&s, pd->sx, pd->sy, 0);
223
0
  screen_write_start(&ctx, &s);
224
0
  screen_write_clearscreen(&ctx, 8);
225
226
0
  if (pd->border_lines == BOX_LINES_NONE) {
227
0
    screen_write_cursormove(&ctx, 0, 0, 0);
228
0
    screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx, pd->sy);
229
0
  } else if (pd->sx > 2 && pd->sy > 2) {
230
0
    screen_write_box(&ctx, pd->sx, pd->sy, pd->border_lines,
231
0
        &pd->border_cell, pd->title);
232
0
    screen_write_cursormove(&ctx, 1, 1, 0);
233
0
    screen_write_fast_copy(&ctx, &pd->s, 0, 0, pd->sx - 2,
234
0
        pd->sy - 2);
235
0
  }
236
0
  screen_write_stop(&ctx);
237
238
0
  memcpy(&defaults, &pd->defaults, sizeof defaults);
239
0
  if (defaults.fg == 8)
240
0
    defaults.fg = palette->fg;
241
0
  if (defaults.bg == 8)
242
0
    defaults.bg = palette->bg;
243
244
0
  if (pd->md != NULL) {
245
0
    c->overlay_check = menu_check_cb;
246
0
    c->overlay_data = pd->md;
247
0
  } else {
248
0
    c->overlay_check = NULL;
249
0
    c->overlay_data = NULL;
250
0
  }
251
0
  for (i = 0; i < pd->sy; i++) {
252
0
    tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults,
253
0
        palette);
254
0
  }
255
0
  screen_free(&s);
256
0
  if (pd->md != NULL) {
257
0
    c->overlay_check = NULL;
258
0
    c->overlay_data = NULL;
259
0
    menu_draw_cb(c, pd->md, rctx);
260
0
  }
261
0
  c->overlay_check = popup_check_cb;
262
0
  c->overlay_data = pd;
263
0
}
264
265
static void
266
popup_free_cb(struct client *c, void *data)
267
0
{
268
0
  struct popup_data *pd = data;
269
0
  struct cmdq_item  *item = pd->item;
270
271
0
  if (pd->md != NULL)
272
0
    menu_free_cb(c, pd->md);
273
274
0
  if (pd->cb != NULL)
275
0
    pd->cb(pd->status, pd->arg);
276
277
0
  if (item != NULL) {
278
0
    if (cmdq_get_client(item) != NULL &&
279
0
        cmdq_get_client(item)->session == NULL)
280
0
      cmdq_get_client(item)->retval = pd->status;
281
0
    cmdq_continue(item);
282
0
  }
283
0
  server_client_unref(pd->c);
284
285
0
  if (pd->job != NULL)
286
0
    job_free(pd->job);
287
0
  input_free(pd->ictx);
288
289
0
  screen_free(&pd->s);
290
0
  colour_palette_free(&pd->palette);
291
292
0
  free(pd->title);
293
0
  free(pd);
294
0
}
295
296
static void
297
popup_resize_cb(__unused struct client *c, void *data)
298
0
{
299
0
  struct popup_data *pd = data;
300
0
  struct tty    *tty = &c->tty;
301
302
0
  if (pd == NULL)
303
0
    return;
304
0
  if (pd->md != NULL)
305
0
    menu_free_cb(c, pd->md);
306
307
  /* Adjust position and size. */
308
0
  if (pd->psy > tty->sy)
309
0
    pd->sy = tty->sy;
310
0
  else
311
0
    pd->sy = pd->psy;
312
0
  if (pd->psx > tty->sx)
313
0
    pd->sx = tty->sx;
314
0
  else
315
0
    pd->sx = pd->psx;
316
0
  if (pd->ppy + pd->sy > tty->sy)
317
0
    pd->py = tty->sy - pd->sy;
318
0
  else
319
0
    pd->py = pd->ppy;
320
0
  if (pd->ppx + pd->sx > tty->sx)
321
0
    pd->px = tty->sx - pd->sx;
322
0
  else
323
0
    pd->px = pd->ppx;
324
325
  /* Avoid zero size screens. */
326
0
  if (pd->border_lines == BOX_LINES_NONE) {
327
0
    screen_resize(&pd->s, pd->sx, pd->sy, 0);
328
0
    if (pd->job != NULL)
329
0
      job_resize(pd->job, pd->sx, pd->sy );
330
0
  } else if (pd->sx > 2 && pd->sy > 2) {
331
0
    screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
332
0
    if (pd->job != NULL)
333
0
      job_resize(pd->job, pd->sx - 2, pd->sy - 2);
334
0
  }
335
0
}
336
337
static void
338
popup_make_pane(struct popup_data *pd, enum layout_type type)
339
0
{
340
0
  struct client   *c = pd->c;
341
0
  struct session    *s = c->session;
342
0
  struct window   *w = s->curw->window;
343
0
  struct layout_cell  *lc;
344
0
  struct window_pane  *wp = w->active, *new_wp;
345
0
  u_int      hlimit;
346
0
  const char    *shell;
347
348
0
  window_unzoom(w, 1);
349
350
0
  lc = layout_split_pane(wp, type, -1, 0);
351
0
  if (lc == NULL)
352
0
    return;
353
0
  hlimit = options_get_number(s->options, "history-limit");
354
0
  new_wp = window_add_pane(wp->window, NULL, hlimit, 0);
355
0
  layout_assign_pane(lc, new_wp, 0);
356
357
0
  if (pd->job != NULL) {
358
0
    new_wp->fd = job_transfer(pd->job, &new_wp->pid, new_wp->tty,
359
0
        sizeof new_wp->tty);
360
0
    pd->job = NULL;
361
0
  }
362
363
0
  screen_set_title(&pd->s, new_wp->base.title);
364
0
  screen_free(&new_wp->base);
365
0
  memcpy(&new_wp->base, &pd->s, sizeof wp->base);
366
0
  screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1);
367
0
  screen_init(&pd->s, 1, 1, 0);
368
369
0
  shell = options_get_string(s->options, "default-shell");
370
0
  if (!checkshell(shell))
371
0
    shell = _PATH_BSHELL;
372
0
  new_wp->shell = xstrdup(shell);
373
374
0
  window_pane_set_event(new_wp);
375
0
  window_set_active_pane(w, new_wp, 1);
376
0
  new_wp->flags |= PANE_CHANGED;
377
378
0
  pd->close = 1;
379
0
}
380
381
static void
382
popup_menu_done(__unused struct menu *menu, __unused u_int choice,
383
    key_code key, void *data)
384
0
{
385
0
  struct popup_data *pd = data;
386
0
  struct client   *c = pd->c;
387
0
  struct paste_buffer *pb;
388
0
  const char    *buf;
389
0
  size_t       len;
390
391
0
  pd->md = NULL;
392
0
  pd->menu = NULL;
393
0
  server_redraw_client(pd->c);
394
395
0
  switch (key) {
396
0
  case 'p':
397
0
    pb = paste_get_top(NULL);
398
0
    if (pb != NULL) {
399
0
      buf = paste_buffer_data(pb, &len);
400
0
      bufferevent_write(job_get_event(pd->job), buf, len);
401
0
    }
402
0
    break;
403
0
  case 'F':
404
0
    pd->sx = c->tty.sx;
405
0
    pd->sy = c->tty.sy;
406
0
    pd->px = 0;
407
0
    pd->py = 0;
408
0
    server_redraw_client(c);
409
0
    break;
410
0
  case 'C':
411
0
    pd->px = c->tty.sx / 2 - pd->sx / 2;
412
0
    pd->py = c->tty.sy / 2 - pd->sy / 2;
413
0
    server_redraw_client(c);
414
0
    break;
415
0
  case 'h':
416
0
    popup_make_pane(pd, LAYOUT_LEFTRIGHT);
417
0
    break;
418
0
  case 'v':
419
0
    popup_make_pane(pd, LAYOUT_TOPBOTTOM);
420
0
    break;
421
0
  case 'q':
422
0
    pd->close = 1;
423
0
    break;
424
0
  }
425
0
}
426
427
static void
428
popup_handle_drag(struct client *c, struct popup_data *pd,
429
    struct mouse_event *m)
430
0
{
431
0
  u_int px, py;
432
433
0
  if (!MOUSE_DRAG(m->b))
434
0
    pd->dragging = OFF;
435
0
  else if (pd->dragging == MOVE) {
436
0
    if (m->x < pd->dx)
437
0
      px = 0;
438
0
    else if (m->x - pd->dx + pd->sx > c->tty.sx)
439
0
      px = c->tty.sx - pd->sx;
440
0
    else
441
0
      px = m->x - pd->dx;
442
0
    if (m->y < pd->dy)
443
0
      py = 0;
444
0
    else if (m->y - pd->dy + pd->sy > c->tty.sy)
445
0
      py = c->tty.sy - pd->sy;
446
0
    else
447
0
      py = m->y - pd->dy;
448
0
    pd->px = px;
449
0
    pd->py = py;
450
0
    pd->dx = m->x - pd->px;
451
0
    pd->dy = m->y - pd->py;
452
0
    pd->ppx = px;
453
0
    pd->ppy = py;
454
0
    server_redraw_client(c);
455
0
  } else if (pd->dragging == SIZE) {
456
0
    if (pd->border_lines == BOX_LINES_NONE) {
457
0
      if (m->x < pd->px + 1)
458
0
        return;
459
0
      if (m->y < pd->py + 1)
460
0
        return;
461
0
    } else {
462
0
      if (m->x < pd->px + 3)
463
0
        return;
464
0
      if (m->y < pd->py + 3)
465
0
        return;
466
0
    }
467
0
    pd->sx = m->x - pd->px;
468
0
    pd->sy = m->y - pd->py;
469
0
    pd->psx = pd->sx;
470
0
    pd->psy = pd->sy;
471
472
0
    if (pd->border_lines == BOX_LINES_NONE) {
473
0
      screen_resize(&pd->s, pd->sx, pd->sy, 0);
474
0
      if (pd->job != NULL)
475
0
        job_resize(pd->job, pd->sx, pd->sy);
476
0
    } else {
477
0
      screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0);
478
0
      if (pd->job != NULL)
479
0
        job_resize(pd->job, pd->sx - 2, pd->sy - 2);
480
0
    }
481
0
    server_redraw_client(c);
482
0
  }
483
0
}
484
485
static int
486
popup_key_cb(struct client *c, void *data, struct key_event *event)
487
0
{
488
0
  struct popup_data *pd = data;
489
0
  struct mouse_event  *m = &event->m;
490
0
  const char    *buf;
491
0
  size_t       len;
492
0
  u_int      px, py, x;
493
0
  enum { NONE, LEFT, RIGHT, TOP, BOTTOM } border = NONE;
494
495
0
  if (pd->md != NULL) {
496
0
    if (menu_key_cb(c, pd->md, event) == 1) {
497
0
      pd->md = NULL;
498
0
      pd->menu = NULL;
499
0
      if (pd->close)
500
0
        server_client_clear_overlay(c);
501
0
      else
502
0
        server_redraw_client(c);
503
0
    }
504
0
    return (0);
505
0
  }
506
507
0
  if (KEYC_IS_MOUSE(event->key)) {
508
0
    if (pd->dragging != OFF) {
509
0
      popup_handle_drag(c, pd, m);
510
0
      goto out;
511
0
    }
512
0
    if (m->x < pd->px ||
513
0
        m->x > pd->px + pd->sx - 1 ||
514
0
        m->y < pd->py ||
515
0
        m->y > pd->py + pd->sy - 1) {
516
0
      if (MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3)
517
0
        goto menu;
518
0
      return (0);
519
0
    }
520
0
    if (pd->border_lines != BOX_LINES_NONE) {
521
0
      if (m->x == pd->px)
522
0
        border = LEFT;
523
0
      else if (m->x == pd->px + pd->sx - 1)
524
0
        border = RIGHT;
525
0
      else if (m->y == pd->py)
526
0
        border = TOP;
527
0
      else if (m->y == pd->py + pd->sy - 1)
528
0
        border = BOTTOM;
529
0
    }
530
0
    if ((m->b & MOUSE_MASK_MODIFIERS) == 0 &&
531
0
        MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_3 &&
532
0
        (border == LEFT || border == TOP))
533
0
        goto menu;
534
0
    if (((m->b & MOUSE_MASK_MODIFIERS) == MOUSE_MASK_META) ||
535
0
        (border != NONE && !MOUSE_DRAG(m->lb))) {
536
0
      if (!MOUSE_DRAG(m->b))
537
0
        goto out;
538
0
      if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_1)
539
0
        pd->dragging = MOVE;
540
0
      else if (MOUSE_BUTTONS(m->lb) == MOUSE_BUTTON_3)
541
0
        pd->dragging = SIZE;
542
0
      pd->dx = m->lx - pd->px;
543
0
      pd->dy = m->ly - pd->py;
544
0
      goto out;
545
0
    }
546
0
  }
547
0
  if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) ||
548
0
      pd->job == NULL) &&
549
0
      (event->key == '\033' || event->key == ('c'|KEYC_CTRL)))
550
0
    return (1);
551
0
  if (pd->job == NULL && (pd->flags & POPUP_CLOSEANYKEY) &&
552
0
            !KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key))
553
0
    return (1);
554
0
  if (pd->job != NULL) {
555
0
    if (KEYC_IS_MOUSE(event->key)) {
556
      /* Must be inside, checked already. */
557
0
      if (pd->border_lines == BOX_LINES_NONE) {
558
0
        px = m->x - pd->px;
559
0
        py = m->y - pd->py;
560
0
      } else {
561
0
        px = m->x - pd->px - 1;
562
0
        py = m->y - pd->py - 1;
563
0
      }
564
0
      if (!input_key_get_mouse(&pd->s, m, px, py, &buf, &len))
565
0
        return (0);
566
0
      bufferevent_write(job_get_event(pd->job), buf, len);
567
0
      return (0);
568
0
    }
569
0
    input_key(&pd->s, job_get_event(pd->job), event->key);
570
0
  }
571
0
  return (0);
572
573
0
menu:
574
0
  pd->menu = menu_create("");
575
0
  if (pd->flags & POPUP_INTERNAL) {
576
0
    menu_add_items(pd->menu, popup_internal_menu_items, NULL, c,
577
0
        NULL);
578
0
  } else
579
0
    menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL);
580
0
  if (m->x >= (pd->menu->width + 4) / 2)
581
0
    x = m->x - (pd->menu->width + 4) / 2;
582
0
  else
583
0
    x = 0;
584
0
  pd->md = menu_prepare(pd->menu, 0, 0, NULL, x, m->y, c,
585
0
      BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL, popup_menu_done, pd);
586
0
  c->flags |= CLIENT_REDRAWOVERLAY;
587
588
0
out:
589
0
  pd->lx = m->x;
590
0
  pd->ly = m->y;
591
0
  pd->lb = m->b;
592
0
  return (0);
593
0
}
594
595
static void
596
popup_job_update_cb(struct job *job)
597
0
{
598
0
  struct popup_data *pd = job_get_data(job);
599
0
  struct evbuffer   *evb = job_get_event(job)->input;
600
0
  struct client   *c = pd->c;
601
0
  struct screen   *s = &pd->s;
602
0
  void      *data = EVBUFFER_DATA(evb);
603
0
  size_t       size = EVBUFFER_LENGTH(evb);
604
605
0
  if (size == 0)
606
0
    return;
607
608
0
  if (pd->md != NULL) {
609
0
    c->overlay_check = menu_check_cb;
610
0
    c->overlay_data = pd->md;
611
0
  } else {
612
0
    c->overlay_check = NULL;
613
0
    c->overlay_data = NULL;
614
0
  }
615
0
  input_parse_screen(pd->ictx, s, popup_init_ctx_cb, pd, data, size);
616
0
  c->overlay_check = popup_check_cb;
617
0
  c->overlay_data = pd;
618
619
0
  evbuffer_drain(evb, size);
620
0
}
621
622
static void
623
popup_job_complete_cb(struct job *job)
624
0
{
625
0
  struct popup_data *pd = job_get_data(job);
626
0
  int      status;
627
628
0
  status = job_get_status(pd->job);
629
0
  if (WIFEXITED(status))
630
0
    pd->status = WEXITSTATUS(status);
631
0
  else if (WIFSIGNALED(status))
632
0
    pd->status = WTERMSIG(status);
633
0
  else
634
0
    pd->status = 0;
635
0
  pd->job = NULL;
636
637
0
  if ((pd->flags & POPUP_CLOSEEXIT) ||
638
0
      ((pd->flags & POPUP_CLOSEEXITZERO) && pd->status == 0))
639
0
    server_client_clear_overlay(pd->c);
640
0
}
641
642
int
643
popup_present(struct client *c)
644
0
{
645
0
  return (c->overlay_draw == popup_draw_cb);
646
0
}
647
648
int
649
popup_modify(struct client *c, const char *title, const char *style,
650
  const char *border_style, enum box_lines lines, int flags)
651
0
{
652
0
  struct popup_data   *pd = c->overlay_data;
653
0
  struct style    sytmp;
654
655
0
  if (title != NULL) {
656
0
    if (pd->title != NULL)
657
0
      free(pd->title);
658
0
    pd->title = xstrdup(title);
659
0
  }
660
0
  if (border_style != NULL) {
661
0
    style_set(&sytmp, &pd->border_cell);
662
0
    if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
663
0
      pd->border_cell.fg = sytmp.gc.fg;
664
0
      pd->border_cell.bg = sytmp.gc.bg;
665
0
    }
666
0
  }
667
0
  if (style != NULL) {
668
0
    style_set(&sytmp, &pd->defaults);
669
0
    if (style_parse(&sytmp, &pd->defaults, style) == 0) {
670
0
      pd->defaults.fg = sytmp.gc.fg;
671
0
      pd->defaults.bg = sytmp.gc.bg;
672
0
    }
673
0
  }
674
0
  if (lines != BOX_LINES_DEFAULT) {
675
0
    if (lines == BOX_LINES_NONE && pd->border_lines != lines) {
676
0
      screen_resize(&pd->s, pd->sx, pd->sy, 1);
677
0
      job_resize(pd->job, pd->sx, pd->sy);
678
0
    } else if (pd->border_lines == BOX_LINES_NONE && pd->border_lines != lines) {
679
0
      screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 1);
680
0
      job_resize(pd->job, pd->sx - 2, pd->sy - 2);
681
0
    }
682
0
    pd->border_lines = lines;
683
0
    tty_resize(&c->tty);
684
0
  }
685
0
  if (flags != -1)
686
0
    pd->flags = flags;
687
688
0
  server_redraw_client(c);
689
0
  return (0);
690
0
}
691
692
int
693
popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
694
    u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd,
695
    int argc, char **argv, const char *cwd, const char *title, struct client *c,
696
    struct session *s, const char *style, const char *border_style,
697
    popup_close_cb cb, void *arg)
698
0
{
699
0
  struct popup_data *pd;
700
0
  u_int      jx, jy;
701
0
  struct options    *o;
702
0
  struct style     sytmp;
703
704
0
  if (s != NULL)
705
0
    o = s->curw->window->options;
706
0
  else
707
0
    o = c->session->curw->window->options;
708
709
0
  if (lines == BOX_LINES_DEFAULT)
710
0
    lines = options_get_number(o, "popup-border-lines");
711
0
  if (lines == BOX_LINES_NONE) {
712
0
    if (sx < 1 || sy < 1)
713
0
      return (-1);
714
0
    jx = sx;
715
0
    jy = sy;
716
0
  } else {
717
0
    if (sx < 3 || sy < 3)
718
0
      return (-1);
719
0
    jx = sx - 2;
720
0
    jy = sy - 2;
721
0
  }
722
0
  if (c->tty.sx < sx || c->tty.sy < sy)
723
0
    return (-1);
724
725
0
  pd = xcalloc(1, sizeof *pd);
726
0
  pd->item = item;
727
0
  pd->flags = flags;
728
0
  if (title != NULL)
729
0
    pd->title = xstrdup(title);
730
731
0
  pd->c = c;
732
0
  pd->c->references++;
733
734
0
  pd->cb = cb;
735
0
  pd->arg = arg;
736
0
  pd->status = 128 + SIGHUP;
737
738
0
  pd->border_lines = lines;
739
0
  memcpy(&pd->border_cell, &grid_default_cell, sizeof pd->border_cell);
740
0
  style_apply(&pd->border_cell, o, "popup-border-style", NULL);
741
0
  if (border_style != NULL) {
742
0
    style_set(&sytmp, &grid_default_cell);
743
0
    if (style_parse(&sytmp, &pd->border_cell, border_style) == 0) {
744
0
      pd->border_cell.fg = sytmp.gc.fg;
745
0
      pd->border_cell.bg = sytmp.gc.bg;
746
0
    }
747
0
  }
748
0
  pd->border_cell.attr = 0;
749
750
0
  screen_init(&pd->s, jx, jy, 0);
751
0
  screen_set_default_cursor(&pd->s, global_w_options);
752
0
  colour_palette_init(&pd->palette);
753
0
  colour_palette_from_option(&pd->palette, global_w_options);
754
755
0
  memcpy(&pd->defaults, &grid_default_cell, sizeof pd->defaults);
756
0
  style_apply(&pd->defaults, o, "popup-style", NULL);
757
0
  if (style != NULL) {
758
0
    style_set(&sytmp, &grid_default_cell);
759
0
    if (style_parse(&sytmp, &pd->defaults, style) == 0) {
760
0
      pd->defaults.fg = sytmp.gc.fg;
761
0
      pd->defaults.bg = sytmp.gc.bg;
762
0
    }
763
0
  }
764
0
  pd->defaults.attr = 0;
765
766
0
  pd->px = px;
767
0
  pd->py = py;
768
0
  pd->sx = sx;
769
0
  pd->sy = sy;
770
771
0
  pd->ppx = px;
772
0
  pd->ppy = py;
773
0
  pd->psx = sx;
774
0
  pd->psy = sy;
775
776
0
  pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
777
0
      popup_job_update_cb, popup_job_complete_cb, NULL, pd,
778
0
      JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
779
0
  pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette);
780
781
0
  server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
782
0
      popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);
783
0
  return (0);
784
0
}
785
786
static void
787
popup_editor_free(struct popup_editor *pe)
788
0
{
789
0
  unlink(pe->path);
790
0
  free(pe->path);
791
0
  free(pe);
792
0
}
793
794
static void
795
popup_editor_close_cb(int status, void *arg)
796
0
{
797
0
  struct popup_editor *pe = arg;
798
0
  FILE      *f;
799
0
  char      *buf = NULL;
800
0
  off_t      len = 0;
801
802
0
  if (status != 0) {
803
0
    pe->cb(NULL, 0, pe->arg);
804
0
    popup_editor_free(pe);
805
0
    return;
806
0
  }
807
808
0
  f = fopen(pe->path, "r");
809
0
  if (f != NULL) {
810
0
    fseeko(f, 0, SEEK_END);
811
0
    len = ftello(f);
812
0
    fseeko(f, 0, SEEK_SET);
813
814
0
    if (len == 0 ||
815
0
        (uintmax_t)len > (uintmax_t)SIZE_MAX ||
816
0
        (buf = malloc(len)) == NULL ||
817
0
        fread(buf, len, 1, f) != 1) {
818
0
      free(buf);
819
0
      buf = NULL;
820
0
      len = 0;
821
0
    }
822
0
    fclose(f);
823
0
  }
824
0
  pe->cb(buf, len, pe->arg); /* callback now owns buffer */
825
0
  popup_editor_free(pe);
826
0
}
827
828
int
829
popup_editor(struct client *c, const char *buf, size_t len,
830
    popup_finish_edit_cb cb, void *arg)
831
0
{
832
0
  struct popup_editor *pe;
833
0
  int      fd;
834
0
  FILE      *f;
835
0
  char      *cmd;
836
0
  char       path[] = _PATH_TMP "tmux.XXXXXXXX";
837
0
  const char    *editor;
838
0
  u_int      px, py, sx, sy;
839
840
0
  editor = options_get_string(global_options, "editor");
841
0
  if (*editor == '\0')
842
0
    return (-1);
843
844
0
  fd = mkstemp(path);
845
0
  if (fd == -1)
846
0
    return (-1);
847
0
  f = fdopen(fd, "w");
848
0
  if (f == NULL)
849
0
    return (-1);
850
0
  if (fwrite(buf, len, 1, f) != 1) {
851
0
    fclose(f);
852
0
    return (-1);
853
0
  }
854
0
  fclose(f);
855
856
0
  pe = xcalloc(1, sizeof *pe);
857
0
  pe->path = xstrdup(path);
858
0
  pe->cb = cb;
859
0
  pe->arg = arg;
860
861
0
  sx = c->tty.sx * 9 / 10;
862
0
  sy = c->tty.sy * 9 / 10;
863
0
  px = (c->tty.sx / 2) - (sx / 2);
864
0
  py = (c->tty.sy / 2) - (sy / 2);
865
866
0
  xasprintf(&cmd, "%s %s", editor, path);
867
0
  if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, BOX_LINES_DEFAULT,
868
0
      NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, NULL, c, NULL,
869
0
      NULL, NULL, popup_editor_close_cb, pe) != 0) {
870
0
    popup_editor_free(pe);
871
0
    free(cmd);
872
0
    return (-1);
873
0
  }
874
0
  free(cmd);
875
0
  return (0);
876
0
}