Coverage Report

Created: 2026-10-28 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/cmd-swap-pane.c
Line
Count
Source
1
/* $OpenBSD: cmd-swap-pane.c,v 1.57 2026/10/09 13:12:14 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
 * Swap two panes.
27
 */
28
29
static enum cmd_retval  cmd_swap_pane_exec(struct cmd *, struct cmdq_item *);
30
31
const struct cmd_entry cmd_swap_pane_entry = {
32
  .name = "swap-pane",
33
  .description = "Swap two panes.",
34
  .alias = "swapp",
35
36
  .args = { "dDs:t:UZ", 0, 0, NULL },
37
  .usage = "[-dDUZ] " CMD_SRCDST_PANE_USAGE,
38
39
  .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
40
  .target = { 't', CMD_FIND_PANE, 0 },
41
42
  .flags = 0,
43
  .exec = cmd_swap_pane_exec
44
};
45
46
static struct window_pane *
47
cmd_swap_pane_next_tiled_pane(struct window_pane *wp)
48
0
{
49
0
  while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
50
0
    wp = TAILQ_NEXT(wp, entry);
51
0
  return (wp);
52
0
}
53
54
static struct window_pane *
55
cmd_swap_pane_prev_tiled_pane(struct window_pane *wp)
56
0
{
57
0
  while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
58
0
    wp = TAILQ_PREV(wp, window_panes, entry);
59
0
  return (wp);
60
0
}
61
62
static enum cmd_retval
63
cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
64
0
{
65
0
  struct args   *args = cmd_get_args(self);
66
0
  struct cmd_find_state *source = cmdq_get_source(item);
67
0
  struct cmd_find_state *target = cmdq_get_target(item);
68
0
  struct window   *src_w, *dst_w;
69
0
  struct window_pane  *tmp_wp, *src_wp, *dst_wp;
70
0
  struct layout_cell  *src_lc, *dst_lc;
71
0
  u_int      sx, sy, xoff, yoff;
72
0
  int      src_idx, dst_idx;
73
74
0
  dst_w = target->wl->window;
75
0
  dst_wp = target->wp;
76
0
  dst_idx = target->wl->idx;
77
0
  src_w = source->wl->window;
78
0
  src_wp = source->wp;
79
0
  src_idx = source->wl->idx;
80
81
0
  if (src_wp == src_w->modal || dst_wp == dst_w->modal) {
82
0
    cmdq_error(item, "pane is modal");
83
0
    return (CMD_RETURN_ERROR);
84
0
  }
85
86
0
  if (window_push_zoom(dst_w, 0, args_has(args, 'Z')))
87
0
    server_redraw_window(dst_w);
88
89
0
  if (args_has(args, 'D')) {
90
0
    if (window_pane_is_floating(dst_wp)) {
91
0
      cmdq_error(item, "cannot swap down on floating pane");
92
0
      goto error;
93
0
    }
94
0
    src_w = dst_w;
95
0
    src_wp = TAILQ_NEXT(dst_wp, entry);
96
0
    src_wp = cmd_swap_pane_next_tiled_pane(src_wp);
97
0
    if (src_wp == NULL) {
98
0
      src_wp = TAILQ_FIRST(&dst_w->panes);
99
0
      src_wp = cmd_swap_pane_next_tiled_pane(src_wp);
100
0
    }
101
0
  } else if (args_has(args, 'U')) {
102
0
    if (window_pane_is_floating(dst_wp)) {
103
0
      cmdq_error(item, "cannot swap up on floating pane");
104
0
      goto error;
105
0
    }
106
0
    src_w = dst_w;
107
0
    src_wp = TAILQ_PREV(dst_wp, window_panes, entry);
108
0
    src_wp = cmd_swap_pane_prev_tiled_pane(src_wp);
109
0
    if (src_wp == NULL) {
110
0
      src_wp = TAILQ_LAST(&dst_w->panes, window_panes);
111
0
      src_wp = cmd_swap_pane_prev_tiled_pane(src_wp);
112
0
    }
113
0
  }
114
115
0
  if (src_w != dst_w && window_push_zoom(src_w, 0, args_has(args, 'Z')))
116
0
    server_redraw_window(src_w);
117
118
0
  if (src_wp == dst_wp)
119
0
    goto out;
120
121
0
  server_client_remove_pane(src_wp);
122
0
  server_client_remove_pane(dst_wp);
123
124
0
  tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry);
125
0
  TAILQ_REMOVE(&dst_w->panes, dst_wp, entry);
126
0
  TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry);
127
0
  if (tmp_wp == src_wp)
128
0
    tmp_wp = dst_wp;
129
0
  if (tmp_wp == NULL)
130
0
    TAILQ_INSERT_HEAD(&dst_w->panes, src_wp, entry);
131
0
  else
132
0
    TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry);
133
134
0
  tmp_wp = TAILQ_PREV(dst_wp, window_panes, zentry);
135
0
  TAILQ_REMOVE(&dst_w->z_index, dst_wp, zentry);
136
0
  TAILQ_REPLACE(&src_w->z_index, src_wp, dst_wp, zentry);
137
0
  if (tmp_wp == src_wp)
138
0
    tmp_wp = dst_wp;
139
0
  if (tmp_wp == NULL)
140
0
    TAILQ_INSERT_HEAD(&dst_w->z_index, src_wp, zentry);
141
0
  else
142
0
    TAILQ_INSERT_AFTER(&dst_w->z_index, tmp_wp, src_wp, zentry);
143
144
0
  src_lc = src_wp->layout_cell;
145
0
  dst_lc = dst_wp->layout_cell;
146
0
  src_lc->wp = dst_wp;
147
0
  dst_wp->layout_cell = src_lc;
148
0
  dst_lc->wp = src_wp;
149
0
  src_wp->layout_cell = dst_lc;
150
151
0
  src_wp->window = dst_w;
152
0
  options_set_parent(src_wp->options, dst_w->options);
153
0
  src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
154
0
  dst_wp->window = src_w;
155
0
  options_set_parent(dst_wp->options, src_w->options);
156
0
  dst_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
157
158
0
  sx = src_wp->sx; sy = src_wp->sy;
159
0
  xoff = src_wp->xoff; yoff = src_wp->yoff;
160
0
  src_wp->xoff = dst_wp->xoff; src_wp->yoff = dst_wp->yoff;
161
0
  window_pane_resize(src_wp, dst_wp->sx, dst_wp->sy);
162
0
  dst_wp->xoff = xoff; dst_wp->yoff = yoff;
163
0
  window_pane_resize(dst_wp, sx, sy);
164
165
0
  if (!args_has(args, 'd')) {
166
0
    if (src_w != dst_w) {
167
0
      window_set_active_pane(src_w, dst_wp, 1);
168
0
      window_set_active_pane(dst_w, src_wp, 1);
169
0
    } else {
170
0
      tmp_wp = dst_wp;
171
0
      window_set_active_pane(src_w, tmp_wp, 1);
172
0
    }
173
0
  } else {
174
0
    if (src_w->active == src_wp)
175
0
      window_set_active_pane(src_w, dst_wp, 1);
176
0
    if (dst_w->active == dst_wp)
177
0
      window_set_active_pane(dst_w, src_wp, 1);
178
0
  }
179
0
  if (src_w != dst_w) {
180
0
    window_pane_stack_remove(&src_w->last_panes, src_wp);
181
0
    window_pane_stack_remove(&dst_w->last_panes, dst_wp);
182
0
    colour_palette_from_option(&src_wp->palette, src_wp->options);
183
0
    colour_palette_from_option(&dst_wp->palette, dst_wp->options);
184
0
    layout_fix_panes(src_w, NULL);
185
0
    redraw_invalidate_scene(src_w);
186
0
    server_redraw_window(src_w);
187
0
  }
188
0
  layout_fix_panes(dst_w, NULL);
189
0
  redraw_invalidate_scene(dst_w);
190
0
  server_redraw_window(dst_w);
191
192
0
  if (src_w != dst_w) {
193
0
    window_fire_pane_moved(src_wp, src_w, src_idx, dst_w, dst_idx);
194
0
    window_fire_pane_moved(dst_wp, dst_w, dst_idx, src_w, src_idx);
195
0
  }
196
0
  events_fire_window("window-layout-changed", src_w);
197
0
  if (src_w != dst_w)
198
0
    events_fire_window("window-layout-changed", dst_w);
199
200
0
out:
201
0
  if (window_pop_zoom(src_w))
202
0
    server_redraw_window(src_w);
203
0
  if (src_w != dst_w && window_pop_zoom(dst_w))
204
0
    server_redraw_window(dst_w);
205
0
  return (CMD_RETURN_NORMAL);
206
207
0
error:
208
0
  if (window_pop_zoom(dst_w))
209
0
    server_redraw_window(dst_w);
210
0
  return (CMD_RETURN_ERROR);
211
0
}