Since coreutils 9.12, env shell-quotes values written to a terminal
by default (a pty counts as one, see coreutils issue #355 for the
history: https://github.com/coreutils/coreutils/issues/355).
This makes the literal, unquoted match for TSET= in
test_ssh_env_vars no longer find the value, so the test times out
waiting for it and eventually surfaces as an EIO reading from the pty
once the child has already exited. Accept the shlex-quoted form of
the value as well, since the ssh kitten's own env-passing code does
not use env and is unaffected.
Observed failure (Fedora rawhide buildroot with coreutils-9.12-1.fc46):
======================================================================
ERROR: test_ssh_env_vars (kitty_tests.ssh.SSHKitten.test_ssh_env_vars)
----------------------------------------------------------------------
Traceback (most recent call last):
File "kitty_tests/__init__.py", line 461, in wait_till
self.process_input_from_child(timeout=end_time - time.monotonic())
File "kitty_tests/__init__.py", line 449, in process_input_from_child
data = os.read(self.master_fd, io.DEFAULT_BUFFER_SIZE)
OSError: [Errno 5] Input/output error
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "kitty_tests/__init__.py", line 250, in wrapper
return func(*args, **kwargs)
File "kitty_tests/ssh.py", line 146, in test_ssh_env_vars
pty.wait_till(lambda: 'TSET={}'.format(tset.replace('$A', 'AAA')) in pty.screen_contents())
File "kitty_tests/__init__.py", line 464, in wait_till
raise Exception(f'Failed to read from pty with error: {e}. {self.screen_contents_for_error()}') from e
Exception: Failed to read from pty with error: [Errno 5] Input/output error. Screen contents as repr:
'...\nA=AAA\nTSET=\'AAA-$(echo no)-`echo no2` !Q5 "something else"\'\n...'
Ran 337 tests in 19.341s
FAILED (errors=1, skipped=3)
Assisted-By: Claude Sonnet 5
Fixes found while reviewing #10471:
- A window squeezed to zero size (by maximize or an extreme bias) was
indistinguishable from a removed one, so closing an unrelated window
handed its share to windows in sub-trees that lost nothing. Track
survivors separately from the numeric weight.
- Pruning a batch of windows gave a different layout than closing them
one by one, since a perpendicular sub-tree stops being perpendicular
once removals collapse it. Apply the policy one window at a time.
- Windows reconciled by do_layout(), rather than split off an existing
window, bypassed the policy entirely. Route them through it too.
Also fix two pre-existing bugs on the same code paths:
- collapse_redundant_pairs() collapsed top down, so a pair whose
children only became empty during the recursion was never
re-examined and was left in the tree, consuming its share of the
space.
- do_layout() kept using a stale pairs root when removing windows
collapsed the tree, so windows added in the same relayout were
attached to the discarded root and vanished for a frame.
These two code points have Grapheme_Cluster_Break=SpacingMark but a
width of one, the only such code points in Unicode. Grapheme
segmentation (GB9a) adds them to the previous cell, so "จำ" occupied a
single cell in kitty while wcwidth() based programs count two. As a
result the SARA AA glyph was clipped, only the nikhahit was visible and
the cursor drifted right by one cell per AM.
Now, when such a code point combines into a plain one cell wide cell,
the cell is widened to two cells, using the same mechanism as U+FE0F
does for narrow emoji. The C and Go wcswidth() implementations are
updated to match so that kitty's own width calculations agree with the
screen.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
fill_key() sizes its scratch buffer assuming every cell holds at most
MAX_NUM_CODEPOINTS_PER_CELL codepoints. The draw paths in screen.c
enforce that, but Line.add_combining_char() does not, so a line built via
the Python API with more codepoints in a cell overflows the key buffer
once the run is long enough. Bail out of caching such runs instead.
Follow-ups to the drag_or_normal_select selected text drag support:
- Reset the pending drag at the top of handle_button_event again, before
the scrollbar, missing screen and mouse position early returns. Moving
it into a helper placed it after those returns, so a scrollbar track
click (with scrollbar_jump_on_click disabled) no longer cleared an
armed URL drag, letting it fire on a later mouse move with no button
held. Split the helper in two, as arming the drag needs an up to date
mouse position but clearing it does not.
- Clear the click queue for all drag kinds, not just text drags. A native
drag consumes mouse-up, so the press lingered and turned the next press
into a double press, selecting a word instead of starting a new
gesture. Reproducible with link drags before this change.
- Note that handle_potential_drag can call into Python, which can
re-alloc the windows array, so w must not be used after it returns.
Also share the drag thumbnail construction between drag_url and
drag_selection (and skip the thumbnail rather than dividing by zero on a
zero width render), use None instead of -1 for the mock event pixel
position so negative positions remain testable, and tighten the
off-by-one bound in clear_click_queue.
The ceiling in change_font_size is ten times the configured font_size, so
the largest size you can reach depends on the size you configured. With
font_size 6 zooming stops at 60pt, while font_size 20 reaches 200pt. That
is backwards: a small configured font is if anything a reason to want a
larger temporary zoom, not a smaller one.
Use the larger of that multiple and an absolute 256pt. No existing setup
loses range, since the multiple still wins whenever it is the bigger of
the two. The bounds and the clamp move into options/utils.py so the
numbers are in one place and can be tested directly.
increase_font_size also had its own cap of five times font_size. That was
the old limit, missed when 25d8d9fe6 raised change_font_size to ten, and
because the wrapper clamps before delegating, the stale bound is the one
that wins. Both legacy wrappers can simply drop their bounds: they go
through change_font_size, which already clamps.
This is actually not a security boundary for this protocol. Since it is
explicitly *not* designed to be proof against malicious clients.
However, to avoid more AI generated security reports against it...
Alpha blending of graphics protocol images and animation frames is
2-3.5x faster and glyph alpha masks are composited onto canvases using
the same vectorized primitives