Coverage Report

Created: 2026-07-30 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/cmd-break-pane.c
Line
Count
Source
1
/* $OpenBSD: cmd-break-pane.c,v 1.75 2026/07/23 09:38:27 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2009 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
23
#include "tmux.h"
24
25
/*
26
 * Break pane off into a window.
27
 */
28
29
0
#define BREAK_PANE_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
30
31
static enum cmd_retval  cmd_break_pane_exec(struct cmd *, struct cmdq_item *);
32
33
const struct cmd_entry cmd_break_pane_entry = {
34
  .name = "break-pane",
35
  .alias = "breakp",
36
37
  .args = { "abdPF:n:s:t:Wx:X:y:Y:", 0, 0, NULL },
38
  .usage = "[-abdPW] [-F format] [-n window-name] [-s src-pane] "
39
     "[-t dst-window] [-x width] [-y height] [-X x-position] "
40
     "[-Y y-position]",
41
42
  .source = { 's', CMD_FIND_PANE, 0 },
43
  .target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
44
45
  .flags = 0,
46
  .exec = cmd_break_pane_exec
47
};
48
49
static enum cmd_retval
50
cmd_break_pane_float(struct cmdq_item *item, struct args *args,
51
    struct window *w, struct window_pane *wp)
52
0
{
53
0
  struct layout_cell  *lc = wp->layout_cell;
54
0
  char      *cause = NULL;
55
0
  enum pane_lines    lines = window_get_pane_lines(w);
56
0
  struct layout_geometry  *fg = &lc->fg;
57
58
0
  if (window_pane_is_floating(wp)) {
59
0
    cmdq_error(item, "pane is already floating");
60
0
    return (CMD_RETURN_ERROR);
61
0
  }
62
0
  if (w->flags & WINDOW_ZOOMED) {
63
0
    cmdq_error(item, "can't float a pane while window is zoomed");
64
0
    return (CMD_RETURN_ERROR);
65
0
  }
66
67
0
  if (layout_floating_args_parse(item, args, lines, w, fg, &cause) != 0) {
68
0
    cmdq_error(item, "failed to float pane: %s", cause);
69
0
    free(cause);
70
0
    return (CMD_RETURN_ERROR);
71
0
  }
72
0
  layout_remove_tile(w, lc);
73
0
  layout_set_size(lc, fg->sx, fg->sy, fg->xoff, fg->yoff);
74
75
0
  lc->flags |= LAYOUT_CELL_FLOATING;
76
0
  TAILQ_REMOVE(&w->z_index, wp, zentry);
77
0
  TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
78
79
0
  if (!args_has(args, 'd'))
80
0
    window_set_active_pane(w, wp, 1);
81
0
  layout_fix_offsets(w);
82
0
  layout_fix_panes(w, NULL);
83
0
  events_fire_window("window-layout-changed", w);
84
0
  server_redraw_window(w);
85
86
0
  return (CMD_RETURN_NORMAL);
87
0
}
88
89
static enum cmd_retval
90
cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
91
0
{
92
0
  struct args   *args = cmd_get_args(self);
93
0
  struct cmd_find_state *current = cmdq_get_current(item);
94
0
  struct cmd_find_state *target = cmdq_get_target(item);
95
0
  struct cmd_find_state *source = cmdq_get_source(item);
96
0
  struct client   *tc = cmdq_get_target_client(item);
97
0
  struct winlink    *wl = source->wl;
98
0
  struct session    *src_s = source->s;
99
0
  struct session    *dst_s = target->s;
100
0
  struct window_pane  *wp = source->wp;
101
0
  struct window   *w = wl->window, *old_w = w;
102
0
  char      *cause, *cp;
103
0
  int      idx = target->idx, before, old_idx = wl->idx;
104
0
  const char    *template, *name = args_get(args, 'n');
105
106
0
  if (wp == w->modal) {
107
0
    cmdq_error(item, "pane is modal");
108
0
    return (CMD_RETURN_ERROR);
109
0
  }
110
111
0
  if (args_has(args, 'W'))
112
0
    return (cmd_break_pane_float(item, args, w, wp));
113
114
0
  if (name != NULL && !check_name(name)) {
115
0
    cmdq_error(item, "invalid window name: %s", name);
116
0
    return (CMD_RETURN_ERROR);
117
0
  }
118
119
0
  before = args_has(args, 'b');
120
0
  if (args_has(args, 'a') || before) {
121
0
    if (target->wl != NULL)
122
0
      idx = winlink_shuffle_up(dst_s, target->wl, before);
123
0
    else
124
0
      idx = winlink_shuffle_up(dst_s, dst_s->curw, before);
125
0
    if (idx == -1)
126
0
      return (CMD_RETURN_ERROR);
127
0
  }
128
0
  server_unzoom_window(w);
129
130
0
  if (window_count_panes(w, 1) == 1) {
131
0
    if (server_link_window(src_s, wl, dst_s, idx, 0,
132
0
        !args_has(args, 'd'), &cause) != 0) {
133
0
      cmdq_error(item, "%s", cause);
134
0
      free(cause);
135
0
      return (CMD_RETURN_ERROR);
136
0
    }
137
0
    if (name != NULL) {
138
0
      window_set_name(w, name, 0);
139
0
      options_set_number(w->options, "automatic-rename", 0);
140
0
    }
141
0
    server_unlink_window(src_s, wl);
142
0
    wl = winlink_find_by_window(&dst_s->windows, w);
143
0
    if (wl == NULL)
144
0
      return (CMD_RETURN_ERROR);
145
0
    window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
146
0
    goto out;
147
0
  }
148
0
  if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) {
149
0
    cmdq_error(item, "index in use: %d", idx);
150
0
    return (CMD_RETURN_ERROR);
151
0
  }
152
153
0
  TAILQ_REMOVE(&w->panes, wp, entry);
154
0
  TAILQ_REMOVE(&w->z_index, wp, zentry);
155
0
  server_client_remove_pane(wp);
156
0
  window_lost_pane(w, wp);
157
0
  layout_close_pane(wp);
158
159
0
  w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
160
0
  window_add_ref(w, __func__);
161
0
  options_set_parent(wp->options, w->options);
162
0
  wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
163
0
  TAILQ_INSERT_HEAD(&w->panes, wp, entry);
164
0
  TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
165
0
  w->active = wp;
166
0
  w->latest = tc;
167
168
0
  free(w->name);
169
0
  if (name == NULL)
170
0
    w->name = default_window_name(w);
171
0
  else {
172
0
    w->name = clean_name(name, 0);
173
0
    options_set_number(w->options, "automatic-rename", 0);
174
0
  }
175
0
  window_set_fill_cells(w);
176
177
0
  layout_init(w, wp);
178
0
  wp->flags |= PANE_CHANGED;
179
0
  colour_palette_from_option(&wp->palette, wp->options);
180
181
0
  if (idx == -1)
182
0
    idx = -1 - options_get_number(dst_s->options, "base-index");
183
0
  wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
184
0
  window_remove_ref(w, __func__);
185
0
  events_fire_window("window-created", w);
186
0
  window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
187
0
  if (!args_has(args, 'd')) {
188
0
    session_select(dst_s, wl->idx);
189
0
    cmd_find_from_session(current, dst_s, 0);
190
0
  }
191
192
0
  server_redraw_session(src_s);
193
0
  if (src_s != dst_s)
194
0
    server_redraw_session(dst_s);
195
0
  server_status_session_group(src_s);
196
0
  if (src_s != dst_s)
197
0
    server_status_session_group(dst_s);
198
199
0
out:
200
0
  if (args_has(args, 'P')) {
201
0
    if ((template = args_get(args, 'F')) == NULL)
202
0
      template = BREAK_PANE_TEMPLATE;
203
0
    cp = format_single(item, template, tc, dst_s, wl, wp);
204
0
    cmdq_print(item, "%s", cp);
205
0
    free(cp);
206
0
  }
207
0
  return (CMD_RETURN_NORMAL);
208
0
}