Coverage Report

Created: 2026-10-05 06:03

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