Coverage Report

Created: 2026-10-17 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/window-visible.c
Line
Count
Source
1
/* $OpenBSD: window-visible.c,v 1.6 2026/10/02 15:20:41 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2007 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
 * Check if a single character is within a visible range (not obscured by a
28
 * menu or floating pane).
29
 */
30
int
31
window_position_is_visible(struct visible_ranges *r, u_int px)
32
0
{
33
0
  u_int      i;
34
0
  struct visible_range  *ri;
35
36
0
  if (r == NULL)
37
0
    return (1);
38
0
  for (i = 0; i < r->used; i++) {
39
0
    ri = &r->ranges[i];
40
0
    if (ri->nx != 0 && px >= ri->px && px < ri->px + ri->nx)
41
0
      return (1);
42
0
  }
43
0
  return (0);
44
0
}
45
46
/* Remove width cells starting at px from the visible ranges. */
47
static void
48
window_visible_ranges_remove(struct visible_ranges *r, int px, u_int width)
49
0
{
50
0
  struct visible_range  *ri;
51
0
  u_int      i;
52
0
  int      start, end, right = px + width;
53
54
0
  if (width == 0)
55
0
    return;
56
57
0
  for (i = 0; i < r->used; i++) {
58
0
    ri = &r->ranges[i];
59
0
    start = ri->px;
60
0
    end = start + ri->nx;
61
0
    if (ri->nx == 0 || right <= start || px >= end)
62
0
      continue;
63
0
    if (px <= start) {
64
      /* Remove the left part or the whole range. */
65
0
      ri->px = right < end ? right : end;
66
0
      ri->nx = end - ri->px;
67
0
    } else {
68
      /* Keep the left part, and split off any right part. */
69
0
      ri->nx = px - start;
70
0
      if (right < end) {
71
0
        server_client_ensure_ranges(r, r->used + 1);
72
0
        memmove(&r->ranges[i + 2], &r->ranges[i + 1],
73
0
            (r->used - i - 1) * sizeof *r->ranges);
74
0
        r->ranges[i + 1].px = right;
75
0
        r->ranges[i + 1].nx = end - right;
76
0
        r->used++;
77
0
      }
78
0
    }
79
0
  }
80
0
}
81
82
/*
83
 * Construct ranges array for the line at starting at px,py of width cells of
84
 * base_wp that are unobstructed. All ranges are in window coordinates.
85
 */
86
struct visible_ranges *
87
window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
88
    struct visible_ranges *r)
89
0
{
90
0
  struct window_pane    *wp;
91
0
  struct window     *w;
92
0
  struct menu_data    *md;
93
0
  static struct visible_ranges   sr = { NULL, 0, 0 };
94
0
  int        found_self, sb_w, sb_pos;
95
0
  int        lb, rb, tb, bb, no_border;
96
97
0
  if (py < 0 || width == 0)
98
0
    goto empty;
99
0
  if (px < 0) {
100
0
    if ((u_int)-px >= width)
101
0
      goto empty;
102
0
    width -= (u_int)-px;
103
0
    px = 0;
104
0
  }
105
106
0
  if (base_wp == NULL) {
107
0
    if (r != NULL)
108
0
      return (r);
109
0
    if (sr.ranges == NULL)
110
0
      sr.ranges = xcalloc(1, sizeof *sr.ranges);
111
0
    sr.ranges[0].px = px;
112
0
    sr.ranges[0].nx = width;
113
0
    sr.size = 1;
114
0
    sr.used = 1;
115
0
    return (&sr);
116
0
  }
117
118
0
  w = base_wp->window;
119
0
  if ((u_int)py >= w->sy || (u_int)px >= w->sx)
120
0
    goto empty;
121
0
  if (px + width > w->sx)
122
0
    width = w->sx - px;
123
124
0
  if (r == NULL) {
125
    /* Start with the entire width of the range. */
126
0
    server_client_ensure_ranges(&base_wp->r, 1);
127
0
    r = &base_wp->r;
128
0
    r->ranges[0].px = px;
129
0
    r->ranges[0].nx = width;
130
0
    r->used = 1;
131
0
  }
132
133
  /* The menu is above every pane in the window. */
134
0
  md = w->menu;
135
0
  if (md != NULL &&
136
0
      (u_int)py >= menu_y(md) &&
137
0
      (u_int)py - menu_y(md) < menu_height(md)) {
138
0
    window_visible_ranges_remove(r, menu_x(md), menu_width(md));
139
0
  }
140
141
0
  found_self = 0;
142
0
  TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) {
143
0
    if (wp == base_wp) {
144
0
      found_self = 1;
145
0
      continue;
146
0
    }
147
148
0
    if (window_pane_is_floating(wp) &&
149
0
        window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
150
0
      no_border = 1;
151
0
    else
152
0
      no_border = 0;
153
0
    if (no_border) {
154
0
      tb = wp->yoff;
155
0
      bb = wp->yoff + (int)wp->sy - 1;
156
0
    } else {
157
0
      tb = wp->yoff > 0 ? wp->yoff - 1 : 0;
158
0
      bb = wp->yoff + (int)wp->sy;
159
0
    }
160
0
    if (!found_self ||
161
0
        !window_pane_is_visible(wp) ||
162
0
        py < tb ||
163
0
        py > bb)
164
0
      continue;
165
0
    if (!window_pane_is_floating(wp) && (py == tb || py == bb))
166
0
      continue;
167
168
0
    sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
169
0
    if (window_pane_scrollbar_reserve(wp))
170
0
      sb_pos = w->sb_pos;
171
0
    else
172
0
      sb_w = sb_pos = 0;
173
174
0
    if (no_border) {
175
0
      lb = wp->xoff;
176
0
      rb = wp->xoff + (int)wp->sx - 1;
177
0
    } else if (sb_pos == PANE_SCROLLBARS_LEFT) {
178
0
      if (wp->xoff > sb_w)
179
0
        lb = wp->xoff - 1 - sb_w;
180
0
      else
181
0
        lb = 0;
182
0
    } else { /* PANE_SCROLLBARS_RIGHT or none. */
183
0
      if (wp->xoff > 0)
184
0
        lb = wp->xoff - 1;
185
0
      else
186
0
        lb = 0;
187
0
    }
188
0
    if (!no_border) {
189
0
      if (sb_pos == PANE_SCROLLBARS_LEFT)
190
0
        rb = wp->xoff + (int)wp->sx;
191
0
      else /* PANE_SCROLLBARS_RIGHT or none. */
192
0
        rb = wp->xoff + (int)wp->sx + sb_w;
193
0
    }
194
0
    if (lb < 0)
195
0
      lb = 0;
196
0
    if (rb < 0)
197
0
      continue;
198
0
    if (no_border && rb >= (int)w->sx)
199
0
      rb = w->sx - 1;
200
0
    else if (!no_border && rb > (int)w->sx)
201
0
      rb = w->sx - 1;
202
0
    if (lb <= rb)
203
0
      window_visible_ranges_remove(r, lb, rb - lb + 1);
204
0
  }
205
0
  return (r);
206
207
0
empty:
208
0
  if (r == NULL) {
209
0
    if (sr.ranges == NULL)
210
0
      sr.ranges = xcalloc(1, sizeof *sr.ranges);
211
0
    sr.size = 1;
212
0
    sr.used = 0;
213
0
    return (&sr);
214
0
  }
215
0
  r->used = 0;
216
0
  return (r);
217
0
}