Coverage Report

Created: 2026-06-10 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/cmd-display-panes.c
Line
Count
Source
1
/* $OpenBSD$ */
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
#include <string.h>
23
24
#include "tmux.h"
25
26
/*
27
 * Display panes on a client.
28
 */
29
30
static enum args_parse_type cmd_display_panes_args_parse(struct args *,
31
            u_int, char **);
32
static enum cmd_retval    cmd_display_panes_exec(struct cmd *,
33
            struct cmdq_item *);
34
35
const struct cmd_entry cmd_display_panes_entry = {
36
  .name = "display-panes",
37
  .alias = "displayp",
38
39
  .args = { "bd:Nt:", 0, 1, cmd_display_panes_args_parse },
40
  .usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
41
42
  .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
43
  .exec = cmd_display_panes_exec
44
};
45
46
struct cmd_display_panes_data {
47
  struct cmdq_item    *item;
48
  struct args_command_state *state;
49
};
50
51
static enum args_parse_type
52
cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
53
    __unused char **cause)
54
0
{
55
0
  return (ARGS_PARSE_COMMANDS_OR_STRING);
56
0
}
57
58
static void
59
cmd_display_panes_put(struct screen_redraw_ctx *ctx,
60
    struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len)
61
0
{
62
0
  struct client   *c = ctx->c;
63
0
  struct tty    *tty = &c->tty;
64
0
  struct visible_ranges *r;
65
0
  struct visible_range  *ri;
66
0
  u_int      i, j;
67
68
0
  r = screen_redraw_get_visible_ranges(wp, ctx->ox + cx, ctx->oy + cy,
69
0
      len, NULL);
70
0
  for (i = 0; i < r->used; i++) {
71
0
    ri = &r->ranges[i];
72
0
    for (j = ri->px; j < ri->px + ri->nx; j++) {
73
0
      tty_cursor(tty, j - ctx->ox, cy);
74
0
      tty_putn(tty, &buf[j - ri->px], 1, 1);
75
0
    }
76
0
  }
77
0
}
78
79
static void
80
cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx,
81
    struct window_pane *wp, u_int xoff, u_int yoff, u_int sx,
82
    const struct grid_cell *gc)
83
0
{
84
0
  struct client   *c = ctx->c;
85
0
  struct tty    *tty = &c->tty;
86
0
  struct session    *s = c->session;
87
0
  struct screen    screen;
88
0
  struct screen_write_ctx  sctx;
89
0
  struct visible_ranges *r;
90
0
  struct visible_range  *ri;
91
0
  const char    *format;
92
0
  char      *expanded;
93
0
  u_int      i, px = ctx->ox + xoff;
94
95
0
  format = options_get_string(s->options, "display-panes-format");
96
0
  expanded = format_single(NULL, format, c, s, s->curw, wp);
97
98
0
  screen_init(&screen, sx, 1, 0);
99
0
  screen_write_start(&sctx, &screen);
100
0
  format_draw(&sctx, gc, sx, expanded, NULL, 0);
101
0
  screen_write_stop(&sctx);
102
0
  free(expanded);
103
104
0
  r = screen_redraw_get_visible_ranges(wp, px, wp->yoff, sx, NULL);
105
0
  for (i = 0; i < r->used; i++) {
106
0
    ri = &r->ranges[i];
107
0
    tty_draw_line(tty, &screen, ri->px - px, 0, ri->nx,
108
0
        ri->px - ctx->ox, yoff, gc, NULL);
109
0
  }
110
0
  screen_free(&screen);
111
0
}
112
113
static void
114
cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
115
    struct window_pane *wp)
116
0
{
117
0
  struct client   *c = ctx->c;
118
0
  struct tty    *tty = &c->tty;
119
0
  struct session    *s = c->session;
120
0
  struct options    *oo = s->options;
121
0
  struct window   *w = wp->window;
122
0
  struct grid_cell   fgc, bgc;
123
0
  u_int      pane, idx, px, py, i, j, xoff, yoff, sx, sy;
124
0
  u_int      cx, cy;
125
0
  int      colour, active_colour;
126
0
  char       buf[16], lbuf[16], *ptr;
127
0
  size_t       len, llen;
128
129
0
  if (wp->xoff + (int)wp->sx <= ctx->ox ||
130
0
      wp->xoff >= ctx->ox + (int)ctx->sx ||
131
0
      wp->yoff + (int)wp->sy <= ctx->oy ||
132
0
      wp->yoff >= ctx->oy + (int)ctx->sy)
133
0
    return;
134
135
0
  if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
136
    /* All visible. */
137
0
    xoff = wp->xoff - ctx->ox;
138
0
    sx = wp->sx;
139
0
  } else if (wp->xoff < ctx->ox &&
140
0
      wp->xoff + wp->sx > ctx->ox + ctx->sx) {
141
    /* Both left and right not visible. */
142
0
    xoff = 0;
143
0
    sx = ctx->sx;
144
0
  } else if (wp->xoff < ctx->ox) {
145
    /* Left not visible. */
146
0
    xoff = 0;
147
0
    sx = wp->sx - (ctx->ox - wp->xoff);
148
0
  } else {
149
    /* Right not visible. */
150
0
    xoff = wp->xoff - ctx->ox;
151
0
    sx = wp->sx - xoff;
152
0
  }
153
0
  if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) {
154
    /* All visible. */
155
0
    yoff = wp->yoff - ctx->oy;
156
0
    sy = wp->sy;
157
0
  } else if (wp->yoff < ctx->oy &&
158
0
      wp->yoff + wp->sy > ctx->oy + ctx->sy) {
159
    /* Both top and bottom not visible. */
160
0
    yoff = 0;
161
0
    sy = ctx->sy;
162
0
  } else if (wp->yoff < ctx->oy) {
163
    /* Top not visible. */
164
0
    yoff = 0;
165
0
    sy = wp->sy - (ctx->oy - wp->yoff);
166
0
  } else {
167
    /* Bottom not visible. */
168
0
    yoff = wp->yoff - ctx->oy;
169
0
    sy = wp->sy - yoff;
170
0
  }
171
172
0
  if (ctx->statustop)
173
0
    yoff += ctx->statuslines;
174
0
  px = sx / 2;
175
0
  py = sy / 2;
176
177
0
  if (window_pane_index(wp, &pane) != 0)
178
0
    fatalx("index not found");
179
0
  len = xsnprintf(buf, sizeof buf, "%u", pane);
180
181
0
  if (sx < len)
182
0
    return;
183
0
  colour = options_get_number(oo, "display-panes-colour");
184
0
  active_colour = options_get_number(oo, "display-panes-active-colour");
185
186
0
  memcpy(&fgc, &grid_default_cell, sizeof fgc);
187
0
  memcpy(&bgc, &grid_default_cell, sizeof bgc);
188
0
  if (w->active == wp) {
189
0
    fgc.fg = active_colour;
190
0
    bgc.bg = active_colour;
191
0
  } else {
192
0
    fgc.fg = colour;
193
0
    bgc.bg = colour;
194
0
  }
195
196
0
  if (pane > 9 && pane < 35)
197
0
    llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
198
0
  else
199
0
    llen = 0;
200
201
0
  if (sx < len * 6 || sy < 5) {
202
0
    tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
203
0
    if (sx >= len + llen + 1) {
204
0
      len += llen + 1;
205
0
      cx = xoff + px - len / 2;
206
0
      cy = yoff + py;
207
0
      cmd_display_panes_put(ctx, wp, cx, cy, buf, len);
208
0
      cx += len;
209
0
      cmd_display_panes_put(ctx, wp, cx, cy, " ", 1);
210
0
      cx++;
211
0
      cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen);
212
0
    } else {
213
0
      cx = xoff + px - len / 2;
214
0
      cy = yoff + py;
215
0
      cmd_display_panes_put(ctx, wp, cx, cy, buf, len);
216
0
    }
217
0
    goto out;
218
0
  }
219
220
0
  px -= len * 3;
221
0
  py -= 2;
222
223
0
  tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
224
0
  for (ptr = buf; *ptr != '\0'; ptr++) {
225
0
    if (*ptr < '0' || *ptr > '9')
226
0
      continue;
227
0
    idx = *ptr - '0';
228
229
0
    for (j = 0; j < 5; j++) {
230
0
      for (i = px; i < px + 5; i++) {
231
0
        if (!window_clock_table[idx][j][i - px])
232
0
          continue;
233
0
        cx = xoff + i;
234
0
        cy = yoff + py + j;
235
0
        cmd_display_panes_put(ctx, wp, cx, cy, " ", 1);
236
0
      }
237
0
    }
238
0
    px += 6;
239
0
  }
240
241
0
  if (sy <= 6)
242
0
    goto out;
243
0
  cmd_display_panes_draw_format(ctx, wp, xoff, yoff, sx, &fgc);
244
0
  if (llen != 0) {
245
0
    tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
246
0
    cx = xoff + sx / 2 + len * 3 - llen - 1;
247
0
    cy = yoff + py + 5;
248
0
    cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen);
249
0
  }
250
251
0
out:
252
0
  tty_cursor(tty, 0, 0);
253
0
}
254
255
static void
256
cmd_display_panes_draw(struct client *c, __unused void *data,
257
    struct screen_redraw_ctx *ctx)
258
0
{
259
0
  struct window   *w = c->session->curw->window;
260
0
  struct window_pane  *wp;
261
262
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
263
264
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
265
0
    if (window_pane_visible(wp))
266
0
      cmd_display_panes_draw_pane(ctx, wp);
267
0
  }
268
0
}
269
270
static void
271
cmd_display_panes_free(__unused struct client *c, void *data)
272
0
{
273
0
  struct cmd_display_panes_data *cdata = data;
274
275
0
  if (cdata->item != NULL)
276
0
    cmdq_continue(cdata->item);
277
0
  args_make_commands_free(cdata->state);
278
0
  free(cdata);
279
0
}
280
281
static int
282
cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
283
0
{
284
0
  struct cmd_display_panes_data *cdata = data;
285
0
  char        *expanded, *error;
286
0
  struct cmdq_item    *item = cdata->item, *new_item;
287
0
  struct cmd_list     *cmdlist;
288
0
  struct window     *w = c->session->curw->window;
289
0
  struct window_pane    *wp;
290
0
  u_int        index;
291
0
  key_code       key;
292
293
0
  if (event->key >= '0' && event->key <= '9')
294
0
    index = event->key - '0';
295
0
  else if ((event->key & KEYC_MASK_MODIFIERS) == 0) {
296
0
    key = (event->key & KEYC_MASK_KEY);
297
0
    if (key >= 'a' && key <= 'z')
298
0
      index = 10 + (key - 'a');
299
0
    else
300
0
      return (-1);
301
0
  } else
302
0
    return (-1);
303
304
0
  wp = window_pane_at_index(w, index);
305
0
  if (wp == NULL)
306
0
    return (1);
307
0
  window_unzoom(w, 1);
308
309
0
  xasprintf(&expanded, "%%%u", wp->id);
310
311
0
  cmdlist = args_make_commands(cdata->state, 1, &expanded, &error);
312
0
  if (cmdlist == NULL) {
313
0
    cmdq_append(c, cmdq_get_error(error));
314
0
    free(error);
315
0
  } else if (item == NULL) {
316
0
    new_item = cmdq_get_command(cmdlist, NULL);
317
0
    cmdq_append(c, new_item);
318
0
  } else {
319
0
    new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
320
0
    cmdq_insert_after(item, new_item);
321
0
  }
322
323
0
  free(expanded);
324
0
  return (1);
325
0
}
326
327
static enum cmd_retval
328
cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
329
0
{
330
0
  struct args     *args = cmd_get_args(self);
331
0
  struct client     *tc = cmdq_get_target_client(item);
332
0
  struct session      *s = tc->session;
333
0
  u_int        delay;
334
0
  char        *cause;
335
0
  struct cmd_display_panes_data *cdata;
336
0
  int        wait = !args_has(args, 'b');
337
338
0
  if (tc->overlay_draw != NULL)
339
0
    return (CMD_RETURN_NORMAL);
340
341
0
  if (args_has(args, 'd')) {
342
0
    delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
343
0
    if (cause != NULL) {
344
0
      cmdq_error(item, "delay %s", cause);
345
0
      free(cause);
346
0
      return (CMD_RETURN_ERROR);
347
0
    }
348
0
  } else
349
0
    delay = options_get_number(s->options, "display-panes-time");
350
351
0
  cdata = xcalloc(1, sizeof *cdata);
352
0
  if (wait)
353
0
    cdata->item = item;
354
0
  cdata->state = args_make_commands_prepare(self, item, 0,
355
0
      "select-pane -t \"%%%\"", wait, 0);
356
357
0
  if (args_has(args, 'N')) {
358
0
    server_client_set_overlay(tc, delay, NULL, NULL,
359
0
        cmd_display_panes_draw, NULL, cmd_display_panes_free, NULL,
360
0
        cdata);
361
0
  } else {
362
0
    server_client_set_overlay(tc, delay, NULL, NULL,
363
0
        cmd_display_panes_draw, cmd_display_panes_key,
364
0
        cmd_display_panes_free, NULL, cdata);
365
0
  }
366
367
0
  if (!wait)
368
0
    return (CMD_RETURN_NORMAL);
369
0
  return (CMD_RETURN_WAIT);
370
0
}