Change the graphics protocol N key from a boolean into a usage-hints
bitmask. Define the first bit as a transient hint, allowing the terminal
to treat the image data as short-lived and apply optimizations such as
skipping disk cache writes.
Propagate the transient hint through frame coalescing and composition, so
a composed frame is transient if any contributing frame is transient.
The coverage threshold (0.5 in fbd4e5ad1, lowered to 0.3 in d4106ef2d to
get CI green) is environment-dependent: a correctly sized emoji covers
~0.84 of its cell on one box but ~0.39 in CI, while the buggy render is
~0.28, so the margin is thin and font/cell dependent.
Render the same font two ways at one size instead: via its fontconfig
descriptor (which carries the size-fixup matrix) and via its file path
(which does not). They must come out the same size; the bug shrinks the
descriptor one. Only the matrix differs, so the check no longer depends
on the environment or the emoji artwork.
ee937bdd1b routed FC_MATRIX through the cairo font matrix so synthetic
slant reaches color glyphs. But FC_MATRIX is also how fontconfig encodes
the pixel-size fixup of fixed-size faces. Noto Color Emoji is a ~109px
bitmap strike and fontconfig hands consumers a matrix scaling it to the
requested size (factor = requested_px / strike_px). cairo_set_font_size()
already brings the strike to the requested size, so feeding that matrix
into cairo_set_font_matrix() in apply_cairo_font_size() scales it down
again by the fixup factor. At terminal cell sizes that is a large shrink,
up to ~9x for small cells (easing to 1x as the cell nears the strike), so
color emoji render as a dot; fit_cairo_glyph() only shrinks, so it never
grows them back.
Only the shear carries synthetic slant; the diagonal is the size, which
cairo_set_font_size() and fit_cairo_glyph() already handle. Apply only
the shear and fall through to cairo_set_font_size() when there is no
shear. Pure-slant matrices are unchanged. This carries only the shear to
color glyphs, so a non-uniform diagonal scale from a hand-built FC_MATRIX
is dropped on that path; the stock fixup is uniform, so dropping it is
the intended behaviour.
Add a regression test that renders a color emoji and checks it fills its
cells, skipped unless a fixed-size color font with a fontconfig fixup
matrix is present.
Fixes#10144
fontconfig's FcFontList omits FC_MATRIX from its object set
(kitty/fontconfig.c), so a roman font that find_best_match finds there
(e.g. Fira Code, which ships no italic, in both its static and variable
builds) carries no synthetic-italic shear and its "italic" renders upright.
A family that is not found is substituted, and when the substitute
resolves through the listed faces those descriptors are equally
matrix-less, so this attach covers them too. Only raw fc_match
descriptors (runtime glyph-fallback faces via create_fallback_face, and
find_best_match's last-resort return) already carry the matrix from
substitution.
The italic intent for the configured faces exists only during selection,
not at face construction, so attach the matrix at the end of
get_font_files: for an italic slot whose chosen face is upright and has no
matrix, ask fc_match what fontconfig would do. fc_match returns a synthetic
matrix only when there is no real italic to use (no italic face and no
slanted named instance or variable slant axis), so a font that is already
italic, static or variable, is never double-slanted. Face construction
applies the matrix via FT_Set_Transform; the previous commit makes it
survive the size specialization step the render path builds faces from.
Only the matrix is taken, so selection is unchanged.
FontConfigPattern declared matrix as a required key, but pattern_as_dict
sets it only when the pattern has one, so declare it NotRequired. With
that and narrowing on descriptor_type the attach needs no cast.
Add a regression test (test_synthetic_italic_matrix): a roman no-italic
font gets a non-identity matrix on its italic slot while a real-italic
control does not, and the matrix survives specialize_font_descriptor. It
asserts the invariant rather than the exact shear (the value is
fontconfig's, version-dependent) and skips when the synthetic rule is
inactive.
Covers the four configured faces. Limitation: fc_match re-matches by family
name, so under an uncommon config (a multi-face family key plus a user
per-font FC_MATRIX rule keyed on width/style) it can attach a matrix
computed for a different face; the 90-synthetic shear this targets is
weight-independent and unaffected. A production version should re-match the
selected face by path+index+slant.
Add a new graphics protocol key, N=1, to request that transmitted
image/frame data is kept only in memory and not written to the graphics
disk cache file.
This is useful for transient high-frequency updates such as video-like
streams, where the latest frame is the only useful data and persisting
each frame to the disk cache causes unnecessary write traffic.
The implementation keeps the existing graphics cache abstraction intact:
memory-only entries can still be read back by animation, composition, and
frame coalescing paths. Only persistence to the disk cache file is skipped.
The default behavior is unchanged when N is omitted or set to zero.
Adds an `equalize` layout action that redistributes split sizes so each
window receives a proportional share of space along each axis.
Also adds an `equalize_on_close` layout option that automatically
equalizes splits whenever a window is closed, keeping the remaining
windows balanced without requiring an explicit key binding.
These two features compose well. For example, to keep splits balanced
at all times - equalizing on every open and close:
enabled_layouts splits:equalize_on_close=true
map ctrl+' combine : launch --location=hsplit --cwd=current : layout_action equalize
map ctrl+/ combine : launch --location=vsplit --cwd=current : layout_action equalize
A standalone key binding for manual rebalancing is also supported:
map ctrl+shift+e layout_action equalize
Recognize CSI ? 5 W as DECST8C, which resets the active screen's tab
stops to the default of every 8 columns. Other CSI W variants continue
to produce a parse error.
Signed-off-by: Ayman Bagabas <aymanbagabas@gmail.com>
Previously, every window resize reinitialised the tab stops to the
default of every 8 columns, discarding any stops set via HTS or cleared
via TBC. ECMA-48 only treats RIS, DECSTR, and DECCOLM as events that
reset tab stops, and other terminal emulators all preserve user-set
stops across an interactive resize.
Copy the surviving prefix of the previous tab stops into the freshly
allocated array on both main and alt screens. Newly added columns when
growing the window keep the default every 8 columns pattern. Also point
the active tabstops pointer at the alt screen's array when a resize
happens while the alt screen is active, instead of unconditionally
resetting it to the main screen's array.
Signed-off-by: Ayman Bagabas <aymanbagabas@gmail.com>
When dragging an empty file (or a directory containing an empty file)
from a remote Linux machine to macOS Finder, the empty file would not
be copied.
Root cause: in add_payload() in dnd.c, the file is only created with
O_CREAT when payload data arrives. For an empty file, no payload is
ever sent, so the file was never created on disk. When Finder then
tried to hard-link the (non-existent) temp file to the destination, it
failed silently.
Fix: in the "all data received" handler for type 0 (regular file),
check if the fd was ever opened. If not (empty file), create the empty
file explicitly before finishing.
Also add:
- A new test probe drag_remote_item_path:N to retrieve the filesystem
path of a specific remote item by URI index.
- Two regression tests: test_remote_drag_empty_file (verifying the
empty file is created on disk) and test_remote_drag_dir_with_empty_file
(verifying an empty child file inside a directory is created on disk).
Agent-Logs-Url: https://github.com/kovidgoyal/kitty/sessions/da8b4577-3de8-4784-afc0-c1967f605dec
Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com>
Add a regression test that exercises the code path which crashed in
v0.46.2 (#10017): when paused_rendering is active and a selection
extends into the scrollback, the inner loop of apply_selection iterates
with a negative y. Without the recently-added paused_y translation and
the paused_y < 0 guard, the call to linebuf_init_line treats the
negative y as a huge unsigned index_type and reads ~4GB out of bounds
in line_attrs[idx], crashing with SIGBUS.
The test reproduces the trigger deterministically via the Screen Python
bindings and asserts that current_selections() returns the expected
buffer instead of crashing.