Skip to content

Add "box" option to pane-border-indicators#4940

Open
patrick-motard wants to merge 9 commits into
tmux:masterfrom
patrick-motard:feature/pane-box-borders
Open

Add "box" option to pane-border-indicators#4940
patrick-motard wants to merge 9 commits into
tmux:masterfrom
patrick-motard:feature/pane-box-borders

Conversation

@patrick-motard

@patrick-motard patrick-motard commented Mar 19, 2026

Copy link
Copy Markdown

Reopening #4747 with additional fixes since the original PR.

Add a new "box" option for pane-border-indicators that draws a complete box border around the active pane instead of using shared separator borders between panes. Also adds "box-all" to draw borders around every pane.

Screen.Recording.2026-03-18.at.10.59.36.PM.mov

Changes

  • Add PANE_BORDER_BOX / PANE_BORDER_BOX_ALL constants and window_pane_box_mode() helper
  • Reduce internal screen buffer and PTY by 2 in each dimension to reserve space for box borders
  • Offset pane content drawing by 1 to account for box border
  • Adjust cursor positioning for box mode offset in screen-write, tty, and server-client
  • Draw styled box border for active pane, blank borders for inactive panes
  • Skip normal pane separator borders when box mode is active
  • Clear separator cells between panes that would otherwise show old borders
  • Handle mouse clicks on box borders correctly
  • Add viewport clipping support so box borders work correctly when the window is larger than the terminal
  • Add "box-all" variant that draws borders around all panes, not just the active one
  • Document both options in tmux.1

Test plan

  • Single pane: box mode inactive, no borders drawn
  • Horizontal split: box borders on active pane, blank on inactive
  • Vertical split: same behavior
  • box-all: borders on all panes
  • Switch between off/box/box-all dynamically
  • Mouse clicks on borders and within panes work correctly
  • Resize terminal: borders update correctly
  • Viewport panning works with box borders

@patrick-motard patrick-motard force-pushed the feature/pane-box-borders branch from 64bd580 to ed89b69 Compare March 19, 2026 03:49
@patrick-motard

Copy link
Copy Markdown
Author

Still seeing a few unwanted artifacts when panes are larger than the viewport. Working on fixing those.

@Isaqdn03

Copy link
Copy Markdown

I was literally just talking to a friend about how much we've wanted a more containerized approach to managing panes. This is exactly it. Really hoping they merge this

@Isaqdn03

Copy link
Copy Markdown
image

working quite well, built it from the branch and it's working well on Arch/Hyprland with heavy borders.

@Isaqdn03

Copy link
Copy Markdown

Hey @patrick-motard, great work on this PR! I've been testing it on Arch Linux with Hyprland + Ghostty and the box borders look amazing.

I found a content clipping bug and have a fix for it:

Bug: In screen_redraw_draw_pane(), the tty_draw_line source offset calculation doesn't account for the box mode offset. When box mode is active, rr->px includes the +1 box offset, but the screen buffer was already resized to sx-2 (starting at column 0). So rr->px - wp->xoff evaluates to 1 instead of 0, causing the first column of content to be skipped and the content to appear shifted/clipped by the left border.

Visible symptom: Content in panes gets cut off at the left edge and bottom — the box border overlaps the leftmost column of text.

Fix (in screen-redraw.c, inside screen_redraw_draw_pane):

// Before:
tty_draw_line(tty, s, rr->px - wp->xoff, j,
    rr->nx, rr->px, y, &defaults, palette);

// After:
u_int   src_x;
src_x = rr->px - wp->xoff;
if (box_mode)
    src_x -= 1;
tty_draw_line(tty, s, src_x, j,
    rr->nx, rr->px, y, &defaults, palette);

Tested with pane-border-indicators box-all + pane-border-lines heavy, multiple pane layouts (horizontal, vertical, mixed). Content now renders correctly within the box borders.

Add infrastructure for a new "box" pane border indicator mode:

- Add PANE_BORDER_BOX constant (value 4) to tmux.h
- Add window_pane_box_mode() function to detect when box mode is active
- Box mode requires: pane-border-indicators set to "box", more than
  one pane in the window, and pane size at least 3x3

The detection function will be used by subsequent commits to adjust
screen sizing, content positioning, and border drawing.
When box mode is active, reduce both the screen buffer and PTY size
by 2 in each dimension to reserve space for the border:

- window_pane_resize(): Reduce screen buffer to (sx-2, sy-2)
- window_pane_send_resize(): Report PTY size as (sx-2, sy-2)
- options_push_changes(): Resize screen/PTY when option changes

This ensures the shell receives the correct terminal dimensions and
content cannot be written in the border area.
Adjust content and cursor positioning when box mode is active:

- screen_write_set_client_cb(): Apply +1 offset to xoff/yoff for
  content positioning inside the border
- server_client_reset_state(): Account for box mode offset when
  calculating cursor position
- tty_window_offset1(): Account for box mode offset in view offset
  calculation

This ensures content is rendered inside the border area and the
cursor appears at the correct position.
Add screen_redraw_draw_active_box() to draw a complete box border
around the active pane when box mode is enabled:

- Draw styled border (using pane-active-border-style) for active pane
- Draw blank spaces for inactive pane border areas
- Skip normal pane separator borders when box mode is active
- Adjust pane content drawing to account for box mode offset
- Call box drawing on border redraws, window redraws, and pane redraws

The box border uses the configured pane-border-lines style for the
line drawing characters.
Add "box" as a valid choice for the pane-border-indicators window
option. Update the option description to mention the new box mode.
Update tmux.1 man page to document the new "box" value for
pane-border-indicators, explaining that it draws a complete box
around the active pane and that content is inset to prevent reflow.
When box mode is active, the separator columns and rows between panes
were not being cleared, causing old border characters to persist.
Each pane's box is drawn within its allocated space, but the layout
system places separator cells between panes that were left untouched.

Add code to clear:
- The separator column immediately to the right of each pane
- The separator row immediately below each pane
- The corner cell where separators intersect
Add MouseDown1Border key binding so clicking on any pane border
selects that pane. Extend window_get_active_at() to include box
border areas and adjacent separator cells in hit detection, which
allows drag-to-resize to work correctly in box mode. Add box border
edge detection as a fallback in server_client_check_mouse_in_pane()
after traditional separator detection.
@patrick-motard patrick-motard force-pushed the feature/pane-box-borders branch from ed89b69 to 97cdb40 Compare May 4, 2026 19:08
@patrick-motard

Copy link
Copy Markdown
Author

Hey @Isaqdn03 - thanks for the feedback! Glad it's working well for you. Appreciate the bug report too! Should be working now 🤞

Fix border rendering when windows are larger than the terminal by
adding viewport offset calculations (ctx->ox/oy) to all coordinate
computations in screen_redraw_draw_active_box(). Borders are now
clipped to the visible viewport and only drawn when within bounds.

Add "box-all" option to pane-border-indicators that draws visible
borders around all panes, not just the active one. Active pane uses
pane-active-border-style, inactive panes use pane-border-style.

Update man page and add basic regression test for option parsing.
@patrick-motard patrick-motard force-pushed the feature/pane-box-borders branch from 97cdb40 to cddfeb6 Compare May 4, 2026 19:38
@patrick-motard

Copy link
Copy Markdown
Author

@nicm I think this is in  good place for review once again.

@nicm

nicm commented May 12, 2026

Copy link
Copy Markdown
Member

Something seems up with this if I also set -g pane-border-status top, the bottom pane has no status line here and the top two seem like they are in the wrong place?

image

If I change the terminal size sometimes I get a stray line drawing character in the top left as well.

@nicm

nicm commented May 12, 2026

Copy link
Copy Markdown
Member

ASAN is also not happy when you make a couple of panes and then start doing exit in them, but I'm not sure if this is an existing problem:

=================================================================
==1304993==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x51f000005384 at pc 0x652237b8e44f bp 0x7ffdd6989230 sp 0x7ffdd6989220
READ of size 4 at 0x51f000005384 thread T0 (tmux: server)
    #0 0x652237b8e44e in tty_draw_line /home/nicholas/tmux/tty-draw.c:158
    #1 0x652237b23de7 in screen_redraw_draw_pane /home/nicholas/tmux/screen-redraw.c:1270
    #2 0x652237b227eb in screen_redraw_draw_panes /home/nicholas/tmux/screen-redraw.c:1160
    #3 0x652237b1e6cb in screen_redraw_screen /home/nicholas/tmux/screen-redraw.c:676
    #4 0x652237b588bb in server_client_check_redraw /home/nicholas/tmux/server-client.c:2086
    #5 0x652237b53a72 in server_client_loop /home/nicholas/tmux/server-client.c:1495
    #6 0x652237b60dfc in server_loop /home/nicholas/tmux/server.c:279
    #7 0x652237b15273 in proc_loop /home/nicholas/tmux/proc.c:214
    #8 0x652237b60c82 in server_start /home/nicholas/tmux/server.c:254
    #9 0x6522379e7900 in client_connect /home/nicholas/tmux/client.c:164
    #10 0x6522379e7ffa in client_main /home/nicholas/tmux/client.c:290
    #11 0x652237b8d31d in main /home/nicholas/tmux/tmux.c:556
    #12 0x794a23e2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #13 0x794a23e2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #14 0x6522379d8f64 in _start (/home/nicholas/tmux/tmux+0xaff64) (BuildId: bd89207ec66397f1a8b5e1199b3cff146be3f404)

0x51f000005384 is located 12 bytes after 3320-byte region [0x51f000004680,0x51f000005378)
allocated by thread T0 (tmux: server) here:
    #0 0x794a242fc91d in reallocarray ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:92
    #1 0x652237c2e343 in xreallocarray /home/nicholas/tmux/xmalloc.c:67
    #2 0x652237a9a382 in grid_adjust_lines /home/nicholas/tmux/grid.c:198
    #3 0x652237b3deb9 in screen_resize_y /home/nicholas/tmux/screen.c:448
    #4 0x652237b3d6ee in screen_resize_cursor /home/nicholas/tmux/screen.c:362
    #5 0x652237b3db62 in screen_resize /home/nicholas/tmux/screen.c:390
    #6 0x652237c27d66 in window_pane_resize /home/nicholas/tmux/window.c:1194
    #7 0x652237ad94c0 in layout_fix_panes /home/nicholas/tmux/layout.c:340
    #8 0x652237add50c in layout_close_pane /home/nicholas/tmux/layout.c:1094
    #9 0x652237b5f53e in server_destroy_pane /home/nicholas/tmux/server-fn.c:381
    #10 0x652237c2776b in window_pane_error_callback /home/nicholas/tmux/window.c:1141
    #11 0x794a2490361f  (/lib/x86_64-linux-gnu/libevent_core-2.1.so.7+0x1561f) (BuildId: c2fbb2410dd28aeae41e58c440dd9ca2e6a65d8c)
    #12 0x794a2490d38b  (/lib/x86_64-linux-gnu/libevent_core-2.1.so.7+0x1f38b) (BuildId: c2fbb2410dd28aeae41e58c440dd9ca2e6a65d8c)
    #13 0x794a2490efae in event_base_loop (/lib/x86_64-linux-gnu/libevent_core-2.1.so.7+0x20fae) (BuildId: c2fbb2410dd28aeae41e58c440dd9ca2e6a65d8c)
    #14 0x652237b15228 in proc_loop /home/nicholas/tmux/proc.c:213
    #15 0x652237b60c82 in server_start /home/nicholas/tmux/server.c:254
    #16 0x6522379e7900 in client_connect /home/nicholas/tmux/client.c:164
    #17 0x6522379e7ffa in client_main /home/nicholas/tmux/client.c:290
    #18 0x652237b8d31d in main /home/nicholas/tmux/tmux.c:556
    #19 0x794a23e2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #20 0x794a23e2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #21 0x6522379d8f64 in _start (/home/nicholas/tmux/tmux+0xaff64) (BuildId: bd89207ec66397f1a8b5e1199b3cff146be3f404)

@nicm nicm moved this from Not Started to Waiting in All Issues & PRs Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Waiting

Development

Successfully merging this pull request may close these issues.

4 participants