Coverage Report

Created: 2026-07-16 06:53

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.4 2026/06/29 19:03:34 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
 * 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
/*
47
 * Construct ranges array for the line at starting at px,py of width cells of
48
 * base_wp that are unobsructed. All ranges are in window coordinates.
49
 */
50
struct visible_ranges *
51
window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
52
    struct visible_ranges *r)
53
0
{
54
0
  struct window_pane    *wp;
55
0
  struct window     *w;
56
0
  struct visible_range    *ri;
57
0
  static struct visible_ranges   sr = { NULL, 0, 0 };
58
0
  int        found_self, sb_w, sb_pos;
59
0
  int        lb, rb, tb, bb, sx, ex, no_border;
60
0
  u_int        i, s;
61
62
0
  if (py < 0 || width == 0)
63
0
    goto empty;
64
0
  if (px < 0) {
65
0
    if ((u_int)-px >= width)
66
0
      goto empty;
67
0
    width -= (u_int)-px;
68
0
    px = 0;
69
0
  }
70
71
0
  if (base_wp == NULL) {
72
0
    if (r != NULL)
73
0
      return (r);
74
0
    if (sr.ranges == NULL)
75
0
      sr.ranges = xcalloc(1, sizeof *sr.ranges);
76
0
    sr.ranges[0].px = px;
77
0
    sr.ranges[0].nx = width;
78
0
    sr.size = 1;
79
0
    sr.used = 1;
80
0
    return (&sr);
81
0
  }
82
83
0
  w = base_wp->window;
84
0
  if ((u_int)py >= w->sy)
85
0
    goto empty;
86
0
  if (px + width > w->sx)
87
0
    width = w->sx - px;
88
89
0
  if (r == NULL) {
90
    /* Start with the entire width of the range. */
91
0
    server_client_ensure_ranges(&base_wp->r, 1);
92
0
    r = &base_wp->r;
93
0
    r->ranges[0].px = px;
94
0
    r->ranges[0].nx = width;
95
0
    r->used = 1;
96
0
  }
97
98
99
0
  found_self = 0;
100
0
  TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) {
101
0
    if (wp == base_wp) {
102
0
      found_self = 1;
103
0
      continue;
104
0
    }
105
106
0
    if (window_pane_is_floating(wp) &&
107
0
        window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
108
0
      no_border = 1;
109
0
    else
110
0
      no_border = 0;
111
0
    if (no_border) {
112
0
      tb = wp->yoff;
113
0
      bb = wp->yoff + (int)wp->sy - 1;
114
0
    } else {
115
0
      tb = wp->yoff > 0 ? wp->yoff - 1 : 0;
116
0
      bb = wp->yoff + (int)wp->sy;
117
0
    }
118
0
    if (!found_self ||
119
0
        !window_pane_is_visible(wp) ||
120
0
        py < tb ||
121
0
        py > bb)
122
0
      continue;
123
0
    if (!window_pane_is_floating(wp) && (py == tb || py == bb))
124
0
      continue;
125
126
0
    sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
127
0
    if (window_pane_scrollbar_reserve(wp))
128
0
      sb_pos = w->sb_pos;
129
0
    else
130
0
      sb_w = sb_pos = 0;
131
132
0
    for (i = 0; i < r->used; i++) {
133
0
      ri = &r->ranges[i];
134
0
      if (ri->nx == 0)
135
0
        continue;
136
0
      if (no_border) {
137
0
        lb = wp->xoff;
138
0
        rb = wp->xoff + (int)wp->sx - 1;
139
0
      } else if (sb_pos == PANE_SCROLLBARS_LEFT) {
140
0
        if (wp->xoff > sb_w)
141
0
          lb = wp->xoff - 1 - sb_w;
142
0
        else
143
0
          lb = 0;
144
0
      } else { /* PANE_SCROLLBARS_RIGHT or none. */
145
0
        if (wp->xoff > 0)
146
0
          lb = wp->xoff - 1;
147
0
        else
148
0
          lb = 0;
149
0
      }
150
0
      if (!no_border) {
151
0
        if (sb_pos == PANE_SCROLLBARS_LEFT)
152
0
          rb = wp->xoff + (int)wp->sx;
153
0
        else /* PANE_SCROLLBARS_RIGHT or none. */
154
0
          rb = wp->xoff + (int)wp->sx + sb_w;
155
0
      }
156
0
      if (lb < 0)
157
0
        lb = 0;
158
0
      if (rb < 0)
159
0
        continue;
160
0
      if (no_border && rb >= (int)w->sx)
161
0
        rb = w->sx - 1;
162
0
      else if (!no_border && rb > (int)w->sx)
163
0
        rb = w->sx - 1;
164
0
      if (lb > rb)
165
0
        continue;
166
167
0
      sx = ri->px;
168
0
      ex = sx + ri->nx - 1;
169
0
      if (lb > sx && lb <= ex && rb > ex) {
170
        /*
171
         * If the left edge of floating pane falls
172
         * inside this range and right edge covers up
173
         * to right of range, then shrink left edge of
174
         * range.
175
         */
176
0
        ri->nx = lb - sx;
177
0
      } else if (rb >= sx && rb <= ex && lb <= sx) {
178
        /*
179
         * Else if the right edge of floating pane falls
180
         * inside of this range and left edge covers
181
         * the left of range, then move px forward to
182
         * right edge of pane.
183
         */
184
0
        ri->nx = ex - rb;
185
0
        ri->px = rb + 1;
186
0
      } else if (lb > sx && rb <= ex) {
187
        /*
188
         * Else if pane fully inside range then split
189
         * into 2 ranges.
190
         */
191
0
        server_client_ensure_ranges(r, r->used + 1);
192
0
        for (s = r->used; s > i; s--) {
193
0
          memcpy(&r->ranges[s], &r->ranges[s - 1],
194
0
              sizeof *r->ranges);
195
0
        }
196
0
        ri = &r->ranges[i];
197
0
        r->ranges[i + 1].px = rb + 1;
198
0
        r->ranges[i + 1].nx = ex - rb;
199
        /* ri->px was copied, unchanged. */
200
0
        ri->nx = lb - sx;
201
0
        r->used++;
202
0
      } else if (lb <= sx && rb > ex) {
203
        /*
204
         * If floating pane completely covers this range
205
         * then delete it (make it 0 length).
206
         */
207
0
        ri->nx = 0;
208
0
      } else {
209
        /*
210
         * The range is already obscured, do
211
         * nothing.
212
         */
213
0
      }
214
0
    }
215
0
  }
216
0
  return (r);
217
218
0
empty:
219
0
  if (r == NULL) {
220
0
    if (sr.ranges == NULL)
221
0
      sr.ranges = xcalloc(1, sizeof *sr.ranges);
222
0
    sr.size = 1;
223
0
    sr.used = 0;
224
0
    return (&sr);
225
0
  }
226
0
  r->used = 0;
227
0
  return (r);
228
0
}