Coverage Report

Created: 2026-10-28 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/cmd-display-menu.c
Line
Count
Source
1
/* $OpenBSD: cmd-display-menu.c,v 1.58 2026/10/16 07:59:06 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2019 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
21
#include <stdlib.h>
22
#include <string.h>
23
24
#include "tmux.h"
25
26
/*
27
 * Display a menu on a client.
28
 */
29
30
static enum args_parse_type cmd_display_menu_args_parse(struct args *,
31
            u_int, char **);
32
static enum cmd_retval    cmd_display_menu_exec(struct cmd *,
33
            struct cmdq_item *);
34
static enum cmd_retval    cmd_display_popup_exec(struct cmd *,
35
            struct cmdq_item *);
36
37
const struct cmd_entry cmd_display_menu_entry = {
38
  .name = "display-menu",
39
  .description = "Display an interactive menu.",
40
  .alias = "menu",
41
42
  .args = { "b:c:C:H:s:S:MOt:T:x:y:", 1, -1, cmd_display_menu_args_parse },
43
  .usage = "[-MO] [-b border-lines] [-c target-client] "
44
     "[-C starting-choice] [-H selected-style] [-s style] "
45
     "[-S border-style] " CMD_TARGET_PANE_USAGE " [-T title] "
46
     "[-x position] [-y position] name [key] [command] ...",
47
48
  .target = { 't', CMD_FIND_PANE, 0 },
49
50
  .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG,
51
  .exec = cmd_display_menu_exec
52
};
53
54
const struct cmd_entry cmd_display_popup_entry = {
55
  .name = "display-popup",
56
  .description = "Create a floating pane (compatibility command).",
57
  .alias = "popup",
58
59
  .args = { "Bb:Cc:d:e:Eh:ks:S:t:T:w:x:y:", 0, -1, NULL },
60
  .usage = "[-BCEk] [-b border-lines] [-c target-client] "
61
     "[-d start-directory] [-e environment] [-h height] "
62
     "[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
63
     " [-T title] [-w width] [-x position] [-y position] "
64
     "[shell-command [argument ...]]",
65
66
  .target = { 't', CMD_FIND_PANE, 0 },
67
68
  .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG,
69
  .exec = cmd_display_popup_exec
70
};
71
72
static enum args_parse_type
73
cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause)
74
3.93k
{
75
3.93k
  u_int      i = 0;
76
3.93k
  enum args_parse_type   type = ARGS_PARSE_STRING;
77
78
388k
  for (;;) {
79
388k
    type = ARGS_PARSE_STRING;
80
388k
    if (i == idx)
81
1.88k
      break;
82
386k
    if (*args_string(args, i++) == '\0')
83
123k
      continue;
84
85
262k
    type = ARGS_PARSE_STRING;
86
262k
    if (i++ == idx)
87
1.03k
      break;
88
89
261k
    type = ARGS_PARSE_COMMANDS_OR_STRING;
90
261k
    if (i++ == idx)
91
1.01k
      break;
92
261k
  }
93
3.93k
  return (type);
94
3.93k
}
95
96
static void
97
cmd_display_menu_set_mouse_pos(struct client *tc, struct mouse_event *m,
98
    struct format_tree *ft, long *mouse_x, long *mouse_y)
99
0
{
100
0
  u_int ox, oy, sx, sy;
101
102
0
  if (!m->valid)
103
0
    return;
104
0
  tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
105
106
0
  if (m->sideat == 0) {
107
0
    if (m->x >= m->sidewidth)
108
0
      *mouse_x = m->x - m->sidewidth + ox;
109
0
    else
110
0
      *mouse_x = ox;
111
0
  } else if (m->sideat > 0 && m->x >= (u_int)m->sideat)
112
0
    *mouse_x = ox + sx - 1;
113
0
  else
114
0
    *mouse_x = m->x + ox;
115
0
  if (m->statusat == 0) {
116
0
    if (m->y >= m->statuslines)
117
0
      *mouse_y = m->y - m->statuslines + oy;
118
0
    else
119
0
      *mouse_y = oy;
120
0
  } else if (m->statusat > 0 && m->y >= (u_int)m->statusat)
121
0
    *mouse_y = oy + sy - 1;
122
0
  else
123
0
    *mouse_y = m->y + oy;
124
0
  format_add(ft, "popup_mouse_x", "%ld", *mouse_x);
125
0
  format_add(ft, "popup_mouse_y", "%ld", *mouse_y);
126
0
}
127
128
static void
129
cmd_display_menu_set_status_pos(struct client *tc, struct cmdq_item *item,
130
    struct format_tree *ft, u_int w, u_int h)
131
0
{
132
0
  struct mouse_event  *m = &cmdq_get_event(item)->m;
133
0
  struct winlink    *wl = cmdq_get_target(item)->wl;
134
0
  struct window   *window = wl->window;
135
0
  struct style_ranges *ranges;
136
0
  struct style_range  *sr = NULL, *clicked;
137
0
  u_int      line, lines, ox, oy, sx, sy, x, y, sidey;
138
0
  int      position, side;
139
140
0
  tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
141
0
  if (m->valid)
142
0
    window = tc->session->curw->window;
143
0
  lines = status_line_size(tc);
144
0
  position = status_at_line(tc);
145
0
  if (position != -1 && lines != 0) {
146
0
    for (line = 0; line < lines; line++) {
147
0
      ranges = &tc->status.entries[line].ranges;
148
0
      TAILQ_FOREACH(sr, ranges, entry) {
149
0
        if (sr->type != STYLE_RANGE_WINDOW)
150
0
          continue;
151
0
        if (sr->argument == (u_int)wl->idx)
152
0
          break;
153
0
      }
154
0
      if (sr != NULL)
155
0
        break;
156
0
    }
157
0
    if (position == 0)
158
0
      y = h;
159
0
    else
160
0
      y = window->sy;
161
0
    if (sr != NULL) {
162
0
      format_add(ft, "popup_window_status_line_x", "%u",
163
0
          sr->start + ox);
164
0
      format_add(ft, "popup_window_status_line_y", "%u", y);
165
0
    }
166
0
    format_add(ft, "popup_status_line_y", "%u", y);
167
0
  }
168
169
0
  side = status_side_at_column(tc);
170
0
  if (side == -1)
171
0
    return;
172
0
  sidey = (position == 0 ? lines : 0);
173
0
  if (m->valid && m->x >= (u_int)side &&
174
0
      m->x < side + status_side_size(tc) &&
175
0
      m->y >= sidey && m->y - sidey < status_side_rows(tc)) {
176
0
    y = m->y - sidey;
177
0
    clicked = status_side_get_range(tc, m->x - side, y);
178
0
    if (clicked == NULL || clicked->type != STYLE_RANGE_WINDOW ||
179
0
        clicked->argument != (u_int)wl->idx)
180
0
      return;
181
0
    sr = clicked;
182
0
  } else {
183
0
    if (sr != NULL)
184
0
      return;
185
0
    ranges = &tc->side_status.ranges;
186
0
    TAILQ_FOREACH(sr, ranges, entry) {
187
0
      if (sr->type == STYLE_RANGE_WINDOW &&
188
0
          sr->argument == (u_int)wl->idx)
189
0
        break;
190
0
    }
191
0
    if (sr == NULL)
192
0
      return;
193
0
  }
194
0
  x = ox;
195
0
  if (side != 0 && sx > w)
196
0
    x += sx - w;
197
0
  format_add(ft, "popup_window_status_line_x", "%u", x);
198
0
  format_add(ft, "popup_window_status_line_y", "%u", oy + sr->y + h);
199
0
}
200
201
static int
202
cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item,
203
    struct args *args, u_int *px, u_int *py, u_int w, u_int h)
204
0
{
205
0
  struct cmd_find_state *target = cmdq_get_target(item);
206
0
  struct key_event  *event = cmdq_get_event(item);
207
0
  struct window   *window = target->w;
208
0
  struct window_pane  *wp = target->wp;
209
0
  const char    *xp, *yp;
210
0
  char      *p;
211
0
  long       n, max_x, max_y, mouse_x = 0, mouse_y = 0;
212
0
  struct format_tree  *ft;
213
214
0
  if (event->m.valid)
215
0
    window = tc->session->curw->window;
216
0
  max_x = window->sx > w ? window->sx - w : 0;
217
0
  max_y = window->sy > h ? window->sy - h : 0;
218
219
0
  ft = format_create_from_target(item);
220
0
  cmd_display_menu_set_mouse_pos(tc, &event->m, ft, &mouse_x, &mouse_y);
221
0
  format_add(ft, "popup_last_x", "%u", window->menu_last_px);
222
0
  format_add(ft, "popup_last_y", "%u", window->menu_last_py + h);
223
224
0
  cmd_display_menu_set_status_pos(tc, item, ft, w, h);
225
226
0
  format_add(ft, "popup_width", "%u", w);
227
0
  format_add(ft, "popup_height", "%u", h);
228
229
0
  n = ((long)window->sx - 1) / 2 - w / 2;
230
0
  if (n < 0)
231
0
    format_add(ft, "popup_centre_x", "%u", 0);
232
0
  else
233
0
    format_add(ft, "popup_centre_x", "%ld", n);
234
0
  n = ((long)window->sy - 1) / 2 + h / 2;
235
0
  if (n >= window->sy)
236
0
    format_add(ft, "popup_centre_y", "%ld", max_y);
237
0
  else
238
0
    format_add(ft, "popup_centre_y", "%ld", n);
239
240
0
  if (event->m.valid) {
241
0
    n = mouse_x - w / 2;
242
0
    if (n < 0)
243
0
      format_add(ft, "popup_mouse_centre_x", "%u", 0);
244
0
    else
245
0
      format_add(ft, "popup_mouse_centre_x", "%ld", n);
246
0
    n = mouse_y - h / 2;
247
0
    if (n + h >= window->sy)
248
0
      format_add(ft, "popup_mouse_centre_y", "%ld", max_y);
249
0
    else
250
0
      format_add(ft, "popup_mouse_centre_y", "%ld", n);
251
0
    n = mouse_y + h;
252
0
    if (n >= window->sy)
253
0
      format_add(ft, "popup_mouse_top", "%u", window->sy - 1);
254
0
    else
255
0
      format_add(ft, "popup_mouse_top", "%ld", n);
256
0
    n = mouse_y - h;
257
0
    if (n < 0)
258
0
      format_add(ft, "popup_mouse_bottom", "%u", 0);
259
0
    else
260
0
      format_add(ft, "popup_mouse_bottom", "%ld", n);
261
0
  }
262
263
0
  n = wp->yoff + h;
264
0
  if (n >= window->sy)
265
0
    format_add(ft, "popup_pane_top", "%ld", max_y);
266
0
  else
267
0
    format_add(ft, "popup_pane_top", "%ld", n);
268
0
  format_add(ft, "popup_pane_bottom", "%u", wp->yoff + wp->sy);
269
0
  format_add(ft, "popup_pane_left", "%u", wp->xoff);
270
0
  n = (long)wp->xoff + wp->sx - w;
271
0
  if (n < 0)
272
0
    format_add(ft, "popup_pane_right", "%u", 0);
273
0
  else
274
0
    format_add(ft, "popup_pane_right", "%ld", n);
275
276
0
  xp = args_get(args, 'x');
277
0
  if (xp == NULL || strcmp(xp, "C") == 0)
278
0
    xp = "#{popup_centre_x}";
279
0
  else if (strcmp(xp, "R") == 0)
280
0
    xp = "#{popup_pane_right}";
281
0
  else if (strcmp(xp, "P") == 0)
282
0
    xp = "#{popup_pane_left}";
283
0
  else if (strcmp(xp, "M") == 0)
284
0
    xp = "#{popup_mouse_centre_x}";
285
0
  else if (strcmp(xp, "L") == 0)
286
0
    xp = "#{popup_last_x}";
287
0
  else if (strcmp(xp, "W") == 0)
288
0
    xp = "#{popup_window_status_line_x}";
289
0
  p = format_expand(ft, xp);
290
0
  n = strtol(p, NULL, 10);
291
0
  if (n < 0)
292
0
    n = 0;
293
0
  if (n > max_x)
294
0
    n = max_x;
295
0
  *px = n;
296
0
  log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w);
297
0
  free(p);
298
299
0
  yp = args_get(args, 'y');
300
0
  if (yp == NULL || strcmp(yp, "C") == 0)
301
0
    yp = "#{popup_centre_y}";
302
0
  else if (strcmp(yp, "P") == 0)
303
0
    yp = "#{popup_pane_bottom}";
304
0
  else if (strcmp(yp, "M") == 0)
305
0
    yp = "#{popup_mouse_top}";
306
0
  else if (strcmp(yp, "L") == 0)
307
0
    yp = "#{popup_last_y}";
308
0
  else if (strcmp(yp, "S") == 0)
309
0
    yp = "#{popup_status_line_y}";
310
0
  else if (strcmp(yp, "W") == 0)
311
0
    yp = "#{popup_window_status_line_y}";
312
0
  p = format_expand(ft, yp);
313
0
  n = strtol(p, NULL, 10);
314
0
  if (n < h)
315
0
    n = 0;
316
0
  else
317
0
    n -= h;
318
0
  if (n < 0)
319
0
    n = 0;
320
0
  if (n > max_y)
321
0
    n = max_y;
322
0
  *py = n;
323
0
  log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
324
0
  free(p);
325
326
0
  format_free(ft);
327
0
  return (1);
328
0
}
329
330
static enum cmd_retval
331
cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
332
0
{
333
0
  struct args   *args = cmd_get_args(self);
334
0
  struct cmd_find_state *target = cmdq_get_target(item);
335
0
  struct key_event  *event = cmdq_get_event(item);
336
0
  struct client   *tc = cmdq_get_target_client(item);
337
0
  struct menu   *menu = NULL;
338
0
  struct menu_item   menu_item;
339
0
  const char    *key, *name, *value;
340
0
  const char    *style = args_get(args, 's');
341
0
  const char    *border_style = args_get(args, 'S');
342
0
  const char    *selected_style = args_get(args, 'H');
343
0
  enum box_lines     lines = BOX_LINES_DEFAULT;
344
0
  char      *title, *cause = NULL;
345
0
  int      flags = 0, starting_choice = 0;
346
0
  u_int      px, py, sx, sy, i, count = args_count(args);
347
0
  struct options    *o = target->w->options;
348
0
  struct options_entry  *oe;
349
350
0
  if (args_has(args, 'C')) {
351
0
    if (strcmp(args_get(args, 'C'), "-") == 0)
352
0
      starting_choice = -1;
353
0
    else {
354
0
      starting_choice = args_strtonum(args, 'C', 0, UINT_MAX,
355
0
          &cause);
356
0
      if (cause != NULL) {
357
0
        cmdq_error(item, "starting choice %s", cause);
358
0
        goto fail;
359
0
      }
360
0
    }
361
0
  }
362
363
0
  if (args_has(args, 'T'))
364
0
    title = format_single_from_target(item, args_get(args, 'T'));
365
0
  else
366
0
    title = xstrdup("");
367
0
  menu = menu_create(title);
368
0
  free(title);
369
370
0
  for (i = 0; i != count; /* nothing */) {
371
0
    name = args_string(args, i++);
372
0
    if (*name == '\0') {
373
0
      menu_add_item(menu, NULL, item, tc, target);
374
0
      continue;
375
0
    }
376
377
0
    if (count - i < 2) {
378
0
      cmdq_error(item, "not enough arguments");
379
0
      goto fail;
380
0
    }
381
0
    key = args_string(args, i++);
382
383
0
    menu_item.name = name;
384
0
    menu_item.key = key_string_lookup_string(key);
385
0
    menu_item.command = args_string(args, i++);
386
387
0
    menu_add_item(menu, &menu_item, item, tc, target);
388
0
  }
389
0
  if (menu == NULL) {
390
0
    cmdq_error(item, "invalid menu arguments");
391
0
    goto fail;
392
0
  }
393
0
  if (menu->count == 0)
394
0
    goto out;
395
396
0
  value = args_get(args, 'b');
397
0
  if (value != NULL) {
398
0
    oe = options_get(o, "menu-border-lines");
399
0
    lines = options_find_choice(options_table_entry(oe), value,
400
0
        &cause);
401
0
    if (lines == -1) {
402
0
      cmdq_error(item, "menu-border-lines %s", cause);
403
0
      goto fail;
404
0
    }
405
0
  } else
406
0
    lines = options_get_number(o, "menu-border-lines");
407
0
  menu_get_size(menu, lines, &sx, &sy);
408
0
  if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py, sx, sy))
409
0
    goto out;
410
411
0
  if (args_has(args, 'O'))
412
0
    flags |= MENU_STAYOPEN;
413
0
  if (!event->m.valid && !args_has(args, 'M'))
414
0
    flags |= MENU_NOMOUSE;
415
0
  if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
416
0
      style, selected_style, border_style, target, NULL, NULL) != 0)
417
0
    goto out;
418
0
  return (CMD_RETURN_NORMAL);
419
420
0
out:
421
0
  menu_free(menu);
422
0
  return (CMD_RETURN_NORMAL);
423
424
0
fail:
425
0
  free(cause);
426
0
  menu_free(menu);
427
0
  return (CMD_RETURN_ERROR);
428
0
}
429
430
static enum pane_lines
431
cmd_display_popup_get_lines(const char *value, char **cause)
432
0
{
433
0
  const struct options_table_entry  *oe;
434
435
0
  if (value == NULL)
436
0
    value = "single";
437
0
  else if (strcmp(value, "rounded") == 0)
438
0
    value = "single";
439
0
  else if (strcmp(value, "padded") == 0)
440
0
    value = "spaces";
441
442
0
  oe = options_search("pane-border-lines");
443
0
  return (options_find_choice(oe, value, cause));
444
0
}
445
446
static enum cmd_retval
447
cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
448
0
{
449
0
  struct args   *args = cmd_get_args(self);
450
0
  struct cmd_find_state *target = cmdq_get_target(item);
451
0
  struct session    *s = target->s;
452
0
  struct client   *tc = cmdq_get_target_client(item);
453
0
  struct winlink    *wl = target->wl;
454
0
  struct window   *w = wl->window;
455
0
  struct window_pane  *wp = target->wp, *new_wp = NULL;
456
0
  struct spawn_context   sc = { 0 };
457
0
  struct layout_cell  *lc = NULL;
458
0
  struct layout_geometry   lg;
459
0
  struct event_payload  *ep;
460
0
  struct cmd_find_state  fs;
461
0
  const char    *value, *style = args_get(args, 's');
462
0
  const char    *border_style = args_get(args, 'S');
463
0
  char      *cause = NULL, *title = NULL;
464
0
  enum pane_lines    lines = PANE_LINES_SINGLE;
465
0
  u_int      px, py, sx, sy, count = args_count(args);
466
0
  struct args_value *av;
467
0
  long long    ll;
468
469
0
  if (args_has(args, 'C')) {
470
0
    if (w->modal != NULL)
471
0
      server_kill_pane(w->modal);
472
0
    return (CMD_RETURN_NORMAL);
473
0
  }
474
0
  if (tc->flags & CLIENT_CONTROL)
475
0
    return (CMD_RETURN_NORMAL);
476
0
  if (w->modal != NULL)
477
0
    return (CMD_RETURN_NORMAL);
478
479
0
  value = args_get(args, 'b');
480
0
  if (args_has(args, 'B'))
481
0
    lines = PANE_LINES_NONE;
482
0
  else if (value != NULL) {
483
0
    lines = cmd_display_popup_get_lines(value, &cause);
484
0
    if (cause != NULL) {
485
0
      cmdq_error(item, "pane-border-lines %s", cause);
486
0
      goto fail;
487
0
    }
488
0
  }
489
490
0
  sy = w->sy / 2;
491
0
  if (args_has(args, 'h')) {
492
0
    ll = args_percentage(args, 'h', 1, w->sy, w->sy, &cause);
493
0
    if (cause != NULL) {
494
0
      cmdq_error(item, "height %s", cause);
495
0
      goto fail;
496
0
    }
497
0
    sy = ll;
498
0
  }
499
500
0
  sx = w->sx / 2;
501
0
  if (args_has(args, 'w')) {
502
0
    ll = args_percentage(args, 'w', 1, w->sx, w->sx, &cause);
503
0
    if (cause != NULL) {
504
0
      cmdq_error(item, "width %s", cause);
505
0
      goto fail;
506
0
    }
507
0
    sx = ll;
508
0
  }
509
510
0
  if (sx > w->sx)
511
0
    sx = w->sx;
512
0
  if (sy > w->sy)
513
0
    sy = w->sy;
514
0
  if ((lines == PANE_LINES_NONE && (sx < 1 || sy < 1)) ||
515
0
      (lines != PANE_LINES_NONE && (sx < 3 || sy < 3)))
516
0
    goto out;
517
0
  if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py, sx, sy))
518
0
    goto out;
519
520
0
  lg.sx = sx;
521
0
  lg.sy = sy;
522
0
  lg.xoff = px;
523
0
  lg.yoff = py;
524
0
  if (lines != PANE_LINES_NONE) {
525
0
    lg.sx -= 2;
526
0
    lg.sy -= 2;
527
0
    lg.xoff++;
528
0
    lg.yoff++;
529
0
  }
530
531
0
  window_push_zoom(w, 0, 1);
532
0
  lc = layout_floating_pane(w, wp, &lg);
533
0
  if (lc == NULL) {
534
0
    window_pop_zoom(w);
535
0
    goto out;
536
0
  }
537
538
0
  sc.item = item;
539
0
  sc.s = s;
540
0
  sc.wl = wl;
541
0
  sc.tc = tc;
542
0
  sc.wp0 = wp;
543
0
  sc.lc = lc;
544
0
  sc.idx = -1;
545
0
  sc.cwd = args_get(args, 'd');
546
0
  sc.flags = (SPAWN_FLOATING|SPAWN_MODAL|SPAWN_FLOATOVERZOOM);
547
548
0
  if (count != 1 || *args_string(args, 0) != '\0')
549
0
    args_to_vector(args, &sc.argc, &sc.argv);
550
0
  sc.environ = environ_create();
551
0
  av = args_first_value(args, 'e');
552
0
  while (av != NULL) {
553
0
    environ_put(sc.environ, av->string, 0);
554
0
    av = args_next_value(av);
555
0
  }
556
557
0
  new_wp = spawn_pane(&sc, &cause);
558
0
  if (new_wp == NULL) {
559
0
    cmdq_error(item, "create pane failed: %s", cause);
560
0
    free(cause);
561
0
    cause = NULL;
562
0
    window_pop_zoom(w);
563
0
    goto fail;
564
0
  }
565
0
  window_pop_zoom(w);
566
0
  new_wp->flags |= PANE_CAPTUREALLKEYS;
567
0
  if (!args_has(args, 'E'))
568
0
    new_wp->flags |= PANE_CLOSEONCANCEL;
569
570
0
  options_set_number(new_wp->options, "pane-border-lines", lines);
571
0
  if (args_has(args, 'E') > 1) {
572
0
    if (args_has(args, 'k'))
573
0
      options_set_number(new_wp->options, "remain-on-exit", 4);
574
0
    else
575
0
      options_set_number(new_wp->options, "remain-on-exit", 2);
576
0
  } else if (args_has(args, 'E'))
577
0
    options_set_number(new_wp->options, "remain-on-exit", 0);
578
0
  else if (args_has(args, 'k'))
579
0
    options_set_number(new_wp->options, "remain-on-exit", 3);
580
0
  else
581
0
    options_set_number(new_wp->options, "remain-on-exit", 1);
582
0
  options_set_string(new_wp->options, "remain-on-exit-format", 0, "%s", "");
583
584
0
  if (style != NULL) {
585
0
    if (options_set_string(new_wp->options, "window-style", 0,
586
0
        "%s", style) == NULL) {
587
0
      cmdq_error(item, "bad style: %s", style);
588
0
      goto fail;
589
0
    }
590
0
    options_set_string(new_wp->options, "window-active-style", 0,
591
0
        "%s", style);
592
0
    new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
593
0
        PANE_THEMECHANGED);
594
0
  }
595
0
  if (border_style != NULL) {
596
0
    if (options_set_string(new_wp->options, "pane-border-style", 0,
597
0
        "%s", border_style) == NULL) {
598
0
      cmdq_error(item, "bad border style: %s", border_style);
599
0
      goto fail;
600
0
    }
601
0
    options_set_string(new_wp->options, "pane-active-border-style",
602
0
        0, "%s", border_style);
603
0
  }
604
0
  if (args_has(args, 'T'))
605
0
    title = format_single_from_target(item, args_get(args, 'T'));
606
0
  if (title != NULL) {
607
0
    options_set_number(new_wp->options, "pane-border-status",
608
0
        PANE_STATUS_TOP);
609
0
    options_set_string(new_wp->options, "pane-border-format", 0,
610
0
        "%s", "#{pane_title}");
611
0
    screen_set_title(&new_wp->base, title, 0);
612
0
    ep = event_payload_create();
613
0
    cmd_find_from_pane(&fs, new_wp, 0);
614
0
    event_payload_set_target(ep, &fs);
615
0
    event_payload_set_pane(ep, "pane", new_wp);
616
0
    event_payload_set_window(ep, "window", new_wp->window);
617
0
    event_payload_set_string(ep, "new_title", "%s", title);
618
0
    events_fire("pane-title-changed", ep);
619
0
  }
620
621
0
  new_wp->wait_item = item;
622
0
  server_redraw_session(s);
623
0
  if (sc.argv != NULL)
624
0
    cmd_free_argv(sc.argc, sc.argv);
625
0
  environ_free(sc.environ);
626
0
  free(title);
627
0
  return (CMD_RETURN_WAIT);
628
629
0
out:
630
0
  if (sc.argv != NULL)
631
0
    cmd_free_argv(sc.argc, sc.argv);
632
0
  environ_free(sc.environ);
633
0
  free(title);
634
0
  return (CMD_RETURN_NORMAL);
635
636
0
fail:
637
0
  free(cause);
638
0
  if (new_wp != NULL) {
639
0
    server_client_remove_pane(new_wp);
640
0
    layout_close_pane(new_wp);
641
0
    window_remove_pane(new_wp->window, new_wp);
642
0
  }
643
0
  if (sc.argv != NULL)
644
0
    cmd_free_argv(sc.argc, sc.argv);
645
0
  environ_free(sc.environ);
646
0
  free(title);
647
0
  return (CMD_RETURN_ERROR);
648
0
}