mirror of
https://github.com/tmux/tmux.git
synced 2026-09-16 10:11:57 +00:00
Compare commits
72 Commits
3.8-rc
...
image-redr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b3d0946b4 | ||
|
|
588dc54753 | ||
|
|
ec9859b15f | ||
|
|
3f5f9cab90 | ||
|
|
33bbd60d98 | ||
|
|
48658fd659 | ||
|
|
1962365106 | ||
|
|
870dba6664 | ||
|
|
6ef98552da | ||
|
|
a4abd61663 | ||
|
|
b263d13df0 | ||
|
|
dadc6ef69f | ||
|
|
001b10f2b3 | ||
|
|
a5808bf868 | ||
|
|
e97c3d697d | ||
|
|
c6a59c29ae | ||
|
|
0ac7289c48 | ||
|
|
7dfd835c3f | ||
|
|
bbcccee9fc | ||
|
|
4819dc34df | ||
|
|
6535274d98 | ||
|
|
9a3b71ac64 | ||
|
|
4c9523c814 | ||
|
|
7e6c49b5a5 | ||
|
|
468259933f | ||
|
|
4380fceb2b | ||
|
|
08c237f0cb | ||
|
|
5d0d6c29b4 | ||
|
|
64cea48f91 | ||
|
|
bc998ede98 | ||
|
|
5c496c765a | ||
|
|
3cd6526d46 | ||
|
|
1dacc18ce0 | ||
|
|
e3f6c1849d | ||
|
|
a8dfb979e6 | ||
|
|
fb589fd9f9 | ||
|
|
945152029d | ||
|
|
8536e26517 | ||
|
|
01d5c9f420 | ||
|
|
a35871110f | ||
|
|
cdff709ab3 | ||
|
|
2788616b6f | ||
|
|
51f1c0de51 | ||
|
|
045a25c89a | ||
|
|
abfd8cee51 | ||
|
|
ccc90ea220 | ||
|
|
6cb01394ed | ||
|
|
6b8d581135 | ||
|
|
ab591ae430 | ||
|
|
d06eb0bc2b | ||
|
|
2a0e47fef0 | ||
|
|
3d07d479db | ||
|
|
ff9b3e5cd7 | ||
|
|
9416ffe9bf | ||
|
|
748fb9ff1c | ||
|
|
cfab0590a4 | ||
|
|
a4b8d1c822 | ||
|
|
7748e525a4 | ||
|
|
01db040717 | ||
|
|
94f6f3b125 | ||
|
|
da6812d15f | ||
|
|
791752ffff | ||
|
|
fe938ea657 | ||
|
|
f68aad1b41 | ||
|
|
7225365b29 | ||
|
|
d50902b14d | ||
|
|
4c04fc805e | ||
|
|
27f12454a1 | ||
|
|
fe5f775f76 | ||
|
|
4da7f897ea | ||
|
|
9a2060db06 | ||
|
|
f4a57f8b95 |
715
IMAGE-REDRAW-PLAN.md
Normal file
715
IMAGE-REDRAW-PLAN.md
Normal file
@@ -0,0 +1,715 @@
|
||||
# Image Redraw and Damage Plan
|
||||
|
||||
## Purpose
|
||||
|
||||
Make image redraws correct and efficient for tiled panes, floating panes,
|
||||
scrolling, copy-mode selection, pane borders, pane status lines, scrollbars,
|
||||
and clients using different output protocols.
|
||||
|
||||
The intended model is retained: the visible screen is a scene of horizontal
|
||||
cell spans. A change damages cell-aligned regions, and the compositor redraws
|
||||
only scene spans intersecting those regions.
|
||||
|
||||
Synchronized output may hide unavoidable intermediate terminal updates, but
|
||||
it must not be required for correctness.
|
||||
|
||||
## Current state and recovery point
|
||||
|
||||
The last clean, pushed commit on `4902-image-support` is:
|
||||
|
||||
```
|
||||
ec9859b1 sixel: preserve image aspect across client geometries
|
||||
```
|
||||
|
||||
That commit already contains the important persistent model:
|
||||
|
||||
- sparse image placements represented by spans attached to grid lines;
|
||||
- input-protocol semantics and placement ordering;
|
||||
- source coordinates for cropping placement spans;
|
||||
- movement, copying, duplication, and removal of image spans with grid data;
|
||||
- per-client output backends for Kitty, SIXEL, and fallback rendering; and
|
||||
- cached image/rendering data.
|
||||
|
||||
The current uncommitted changes touch eight files and mix several attempted
|
||||
optimizations. They are known to damage SIXEL in Mintty and cause unnecessary
|
||||
redraws with floating panes. Do not commit them as the solution.
|
||||
|
||||
Before discarding them, save one patch outside the branch for reference:
|
||||
|
||||
```
|
||||
git diff -- image.c regress/image-support.sh screen-redraw.c screen-write.c \
|
||||
server-client.c tmux.h tty.c window-copy.c \
|
||||
> /tmp/4902-image-redraw-experiment.patch
|
||||
```
|
||||
|
||||
Then restore those tracked files to `ec9859b1` before beginning the new work.
|
||||
Do this only after the user explicitly approves the rollback.
|
||||
|
||||
### Ideas worth retaining, but reimplementing separately
|
||||
|
||||
- Redraw damage should be restricted to scene-visible spans.
|
||||
- Image-related selection tests are useful.
|
||||
- Floating-pane and pane-status regression scenarios are useful.
|
||||
- Window chrome must be emitted after pane image/text composition whenever
|
||||
the same damaged region contains both.
|
||||
- A placement's complete canvas may need to be represented even where its
|
||||
pixels are transparent; visibility and alpha must then be tested while
|
||||
compositing rather than by losing the placement geometry.
|
||||
- Native scrolling should be selected according to the affected region and
|
||||
backend behaviour.
|
||||
|
||||
### Changes not worth retaining in their present form
|
||||
|
||||
- Promoting an obscured-pane update to `PANE_REDRAW` and then sometimes to
|
||||
`CLIENT_REDRAWWINDOW`.
|
||||
- Repairing images directly from individual `tty_cmd_*` text functions.
|
||||
- Separate ad-hoc redraw paths for images, selection, scrollbars, borders,
|
||||
and floating panes.
|
||||
- The current `TTY_CTX_IMAGE_SCROLL`, `TTY_CTX_PANE_FULL_WIDTH`, and
|
||||
`CLIENT_REDRAWSCROLLBARS` plumbing as a combined patch.
|
||||
- Assuming an unchanged pane-status string does not need physical redraw
|
||||
after some other drawing operation has covered its cells.
|
||||
|
||||
## Required invariants
|
||||
|
||||
These rules should be written down in code comments and enforced throughout
|
||||
the implementation.
|
||||
|
||||
1. **Input determines semantics.** Whether text removes an image layer, lies
|
||||
above it, or lies below it is determined by the protocol received from the
|
||||
application. It must not depend on whether the outer client uses Kitty,
|
||||
SIXEL, or fallback output.
|
||||
|
||||
2. **Output backends encode a scene.** An output backend may choose different
|
||||
escape sequences, caches, or cropping, but it must render the same logical
|
||||
result.
|
||||
|
||||
3. **One compositor owns repair.** No output path may independently erase and
|
||||
reconstruct image cells without going through the same scene ordering.
|
||||
|
||||
4. **Damage is regional.** Ordinary content changes do not become pane-wide
|
||||
or window-wide redraws. Full redraw remains appropriate for attachment,
|
||||
resize, geometry changes, terminal reset, or invalid scene geometry.
|
||||
|
||||
5. **Damage uses cell coordinates.** Pixel offsets are handled when an image
|
||||
backend maps the damaged cells to a source-pixel crop.
|
||||
|
||||
6. **Logical state changes before physical output.** Grid text and image spans
|
||||
are moved, removed, or inserted first. Rendering then reads the completed
|
||||
logical state.
|
||||
|
||||
7. **Chrome is part of composition.** Pane borders, pane status lines,
|
||||
scrollbars, menus, and floating panes must not be repaired independently in
|
||||
an order that allows image output to cover them afterward.
|
||||
|
||||
8. **No-image behaviour remains unchanged.** When a damaged region does not
|
||||
intersect image data or graphical overlays, existing fast text output and
|
||||
native scrolling should remain available.
|
||||
|
||||
## Established image semantics
|
||||
|
||||
The image semantics present at the `ec9859b1` recovery point are a
|
||||
compatibility requirement. They resulted from the review discussion in PR
|
||||
#5445, particularly the temporal-versus-positional model described in
|
||||
[Theory and Practice of Sprixels](https://nick-black.com/dankwiki/index.php?title=Theory_and_Practice_of_Sprixels#Transparency_and_z-ordering),
|
||||
and were accepted after commit `a4abd616`, which added the corresponding
|
||||
manual visual checks.
|
||||
|
||||
- SIXEL input is temporal. Transparent pixels preserve what existed when the
|
||||
SIXEL was emitted. A later text write annihilates the affected SIXEL cells;
|
||||
the SIXEL does not remain logically behind that text.
|
||||
- Kitty input is positional. Negative-z placements remain below the text plane;
|
||||
nonnegative placements remain above it. Text writes do not convert or remove
|
||||
Kitty placements merely because the output client uses SIXEL.
|
||||
- Overlapping placements remain separate ordered layers. Transparent pixels in
|
||||
an upper placement reveal retained lower placements or text.
|
||||
- Input protocol, z-index, and placement order determine the logical scene. The
|
||||
attached client's output protocol only determines how that scene is encoded.
|
||||
- Redraw, damage, scrolling, copy mode, and backend conversion must preserve
|
||||
these rules exactly.
|
||||
|
||||
The manual checks introduced by `a4abd616` and the corresponding regression
|
||||
cases are semantic compatibility tests, not merely visual diagnostics.
|
||||
|
||||
## Coordinate spaces
|
||||
|
||||
Keep conversions explicit. Do not pass ambiguous `x` and `y` values between
|
||||
layers.
|
||||
|
||||
- **Grid coordinates:** include history and belong to a pane's `struct grid`.
|
||||
- **Pane coordinates:** visible cells inside a pane.
|
||||
- **Window coordinates:** include pane offsets and floating-pane positions.
|
||||
- **Client scene coordinates:** window coordinates clipped by the client's
|
||||
viewport (`ox`, `oy`, `sx`, and `sy`).
|
||||
- **Terminal coordinates:** include the client status-line offset and any
|
||||
terminal viewport offset.
|
||||
- **Image source coordinates:** identify cell rows/columns within a placement;
|
||||
the backend maps these to source pixels using terminal cell geometry and
|
||||
placement pixel offsets.
|
||||
|
||||
Store damage in window coordinates. Translate and clip it separately for each
|
||||
client when drawing, because clients may have different viewports, status-line
|
||||
positions, cell geometries, and output backends.
|
||||
|
||||
## Compartmentalization
|
||||
|
||||
Keep image implementation details in the existing image files. The rest of
|
||||
tmux should know only that some scene cells require image-aware composition.
|
||||
|
||||
### `image.c`
|
||||
|
||||
Own all protocol-independent image state and composition policy:
|
||||
|
||||
- placements, spans, input protocol, z-index, serial ordering, and alpha;
|
||||
- applying input semantics when text overwrites image cells;
|
||||
- testing whether a grid rectangle intersects image placements;
|
||||
- mapping a damaged cell rectangle to ordered image fragments;
|
||||
- deciding which logical image layers are below or above the text plane;
|
||||
- generic backend dispatch; and
|
||||
- public image helpers used by the redraw subsystem.
|
||||
|
||||
### `image-sixel.c`
|
||||
|
||||
Own only SIXEL-specific output details:
|
||||
|
||||
- cached quantized/indexed representations;
|
||||
- mapping cell crops to pixel and six-pixel-band crops;
|
||||
- emitting SIXEL fragments;
|
||||
- alpha and palette encoding; and
|
||||
- backend-specific reset, erase, and scrolling capabilities.
|
||||
|
||||
### `image-kitty.c`
|
||||
|
||||
Own only Kitty-specific output details:
|
||||
|
||||
- client-side transmission and placement caches;
|
||||
- placement creation, clipping, movement, and deletion;
|
||||
- Kitty IDs and geometry; and
|
||||
- backend-specific reset and scrolling behaviour.
|
||||
|
||||
### Other tmux files
|
||||
|
||||
- `screen-redraw.c` owns scene construction, damage clipping, and the ordering
|
||||
of pane content versus window chrome. It calls generic `image.c` helpers and
|
||||
contains no Kitty or SIXEL protocol logic.
|
||||
- `screen-write.c` updates logical grid state and reports pane/window damage.
|
||||
It must not render image fragments or select an output backend.
|
||||
- `tty.c` owns terminal text and scrolling primitives. It receives an already
|
||||
selected native-scroll operation; it must not reconstruct image layers or
|
||||
call image repair after individual characters.
|
||||
- `server-client.c` schedules and drains per-client damage. It must not inspect
|
||||
image placements or output protocols.
|
||||
- `window-copy.c` reports copy-mode damage and copies logical image state using
|
||||
generic image helpers; it must not render images itself.
|
||||
- `tmux.h` exposes only the minimum generic structures and function prototypes
|
||||
required across those boundaries.
|
||||
|
||||
Prefer one generic call from `screen-redraw.c` into `image.c` over `ENABLE_IMAGES`
|
||||
branches scattered through tty commands. A build without image support should
|
||||
retain the existing redraw path with minimal conditional code.
|
||||
|
||||
## Damage representation
|
||||
|
||||
Add a small regional damage abstraction owned by the redraw subsystem. Do not
|
||||
add per-cell queues or enlarge `struct grid_cell`.
|
||||
|
||||
A damage entry needs:
|
||||
|
||||
```
|
||||
x, y, sx, sy window-cell rectangle
|
||||
reason content, scroll exposure, pane move, chrome, and so on
|
||||
```
|
||||
|
||||
The exact names should follow tmux style. A short list or dynamic array per
|
||||
client is sufficient. When a new rectangle is added:
|
||||
|
||||
- clip it to the window;
|
||||
- discard empty rectangles;
|
||||
- merge overlapping or directly adjacent rectangles where that does not make
|
||||
the result substantially larger;
|
||||
- cap the list at a modest number; above the cap, replace it with their union;
|
||||
- promote to a full redraw only if geometry is invalid or the union covers
|
||||
most of the client scene.
|
||||
|
||||
A shared window damage list must not be cleared after the first client draws.
|
||||
Either append translated window rectangles to every affected client, or give
|
||||
damage events monotonically increasing sequence numbers so each client can
|
||||
consume them independently. The simpler initial implementation is to append
|
||||
window-coordinate rectangles to each client currently displaying the window.
|
||||
|
||||
Suggested public operations are conceptually:
|
||||
|
||||
```
|
||||
redraw_damage_window(window, x, y, sx, sy, reason)
|
||||
redraw_damage_pane(window_pane, x, y, sx, sy, reason)
|
||||
redraw_damage_client(client, x, y, sx, sy, reason)
|
||||
```
|
||||
|
||||
Do not expose image-backend details through these calls.
|
||||
|
||||
## Regional scene composition
|
||||
|
||||
Extend `screen-redraw.c` so an existing `redraw_scene` can be drawn through a
|
||||
set of clipping rectangles.
|
||||
|
||||
For every damaged line:
|
||||
|
||||
1. Intersect the damage rectangle with that line's scene spans.
|
||||
2. Split only at damage boundaries; do not rebuild the complete scene.
|
||||
3. Draw intersecting pane content in logical layer order.
|
||||
4. Draw window chrome intersecting the same rectangle last.
|
||||
|
||||
For a pane span, the scene has already selected the topmost visible pane at
|
||||
that position. Composition inside that pane should then be explicit:
|
||||
|
||||
1. image placements below the text plane, such as negative Kitty z-indexes;
|
||||
2. the text plane, including the requested copy-mode style;
|
||||
3. surviving temporal SIXEL placements and images above the text plane;
|
||||
4. pane borders, pane status, scrollbars, menus, and other chrome.
|
||||
|
||||
The exact placement order is already represented by `struct image_placement`
|
||||
and its input protocol, z-index, and serial. Do not derive it from the client's
|
||||
output backend.
|
||||
|
||||
Only the topmost pane content is normally needed because panes are opaque.
|
||||
Underlying pane content is retained in its own grid and becomes visible when a
|
||||
floating pane moves, at which time the old floating-pane rectangle is damaged
|
||||
and recomposed.
|
||||
|
||||
### Image fragments
|
||||
|
||||
An `image_span` is placement metadata, not a separately stored bitmap. For an
|
||||
intersection, compute the corresponding source-cell rectangle and ask the
|
||||
backend to emit that crop from its cached rendering.
|
||||
|
||||
- Do not rerun quantization or dithering for every small damage rectangle.
|
||||
- SIXEL output operates in six-pixel vertical bands. Expand or clip the
|
||||
backend operation as required, then ensure any additionally touched cells
|
||||
are included in the same composition pass.
|
||||
- A transparent top image requires lower image layers to be drawn first.
|
||||
- Kitty placements may be deleted, clipped, or replaced only within the
|
||||
damaged placement region.
|
||||
|
||||
## Fast text writes
|
||||
|
||||
Do not route every ordinary character through a deferred full compositor.
|
||||
|
||||
For an application text write:
|
||||
|
||||
1. Apply input-protocol damage rules to the logical image spans. For example,
|
||||
text written after a SIXEL image removes the affected SIXEL coverage.
|
||||
2. Test whether the affected cells still intersect any image layer or scene
|
||||
overlay requiring composition.
|
||||
3. If not, retain the existing direct `tty_cmd_cell`/`tty_cmd_cells` path.
|
||||
4. If they do, suppress conflicting direct terminal repair and enqueue only
|
||||
those cells for regional composition.
|
||||
|
||||
This decision belongs above the backend-specific tty output functions. Avoid
|
||||
callbacks such as `image_draw_after_text()` from several `tty_cmd_*` functions;
|
||||
they reproduce ordering logic in multiple places.
|
||||
|
||||
## Selection and copy mode
|
||||
|
||||
Copy mode must not mutate the application's logical image placements.
|
||||
|
||||
- Treat selection as a temporary tmux UI style on the text plane.
|
||||
- Follow the chosen policy that selection highlighting does not overwrite
|
||||
image-covered cells.
|
||||
- Mouse motion should damage only cells whose selection state actually
|
||||
changed.
|
||||
- If changed selection cells do not intersect visible images, draw them using
|
||||
the normal text fast path.
|
||||
- If they intersect an image canvas, compose those cells once; do not erase a
|
||||
whole image or pane.
|
||||
- Entering or leaving copy mode should damage the visible pane rectangle once,
|
||||
not repeatedly for every unchanged mouse event.
|
||||
|
||||
Copy-mode image spans should be copied before its text view is composed, but
|
||||
this should be implemented through the same logical state and damage API, not
|
||||
through special tty repair calls.
|
||||
|
||||
## Floating panes
|
||||
|
||||
### Focus changes
|
||||
|
||||
Changing the active pane should damage only cells whose appearance changes:
|
||||
|
||||
- affected pane borders;
|
||||
- pane status cells whose style or contents change; and
|
||||
- any active-pane indicators.
|
||||
|
||||
It must not redraw pane interiors or retransmit unrelated SIXEL images.
|
||||
|
||||
### Moving or resizing a floating pane
|
||||
|
||||
For an old rectangle `A` and new rectangle `B`:
|
||||
|
||||
- invalidate the scene geometry;
|
||||
- damage `A union B` (represented as a few rectangles rather than necessarily
|
||||
one large bounding box);
|
||||
- rebuild the scene because pane ownership changed;
|
||||
- recompose only spans intersecting those rectangles.
|
||||
|
||||
The old rectangle restores tiled panes and any image fragments that were
|
||||
previously hidden. The new rectangle draws the floating pane and its chrome.
|
||||
Multiple overlapping floating panes follow their window z-order.
|
||||
|
||||
## Scrolling
|
||||
|
||||
Implement scrolling in three stages. Do not begin with the most complicated
|
||||
native-scroll optimization.
|
||||
|
||||
### Stage 1: correct regional redraw
|
||||
|
||||
1. Move grid text and image spans using the existing grid operations.
|
||||
2. Record the pane scroll rectangle and newly exposed rows.
|
||||
3. Recompose that rectangle from the completed logical scene.
|
||||
|
||||
This may still retransmit more image data than desired, but it establishes a
|
||||
correct regional baseline without pane- or window-wide redraws.
|
||||
|
||||
### Stage 2: native scrolling without obstructions
|
||||
|
||||
Use the terminal's native scroll operation only when:
|
||||
|
||||
- the backend/terminal is known to scroll its image pixels consistently;
|
||||
- the source and destination rectangle belong to the same visible pane;
|
||||
- no floating pane, menu, overlay, pane border/status, or reserved scrollbar
|
||||
intersects it;
|
||||
- required vertical and horizontal margins are available and reliable; and
|
||||
- existing BCE and terminal-size checks permit the operation.
|
||||
|
||||
After native scrolling, compose only newly exposed rows and any protocol-sized
|
||||
boundary expansion.
|
||||
|
||||
### Stage 3: mixed native scrolling around obstructions
|
||||
|
||||
Derive safe rectangles from visible scene spans:
|
||||
|
||||
- a destination cell is native-scrollable only when its source cell and
|
||||
destination cell are both visible parts of the same pane;
|
||||
- coalesce equal horizontal ranges across adjacent rows into rectangles;
|
||||
- native-scroll those maximal rectangles;
|
||||
- recompose cells crossing an obstruction boundary and newly exposed cells.
|
||||
|
||||
With one floating pane, left and right strips can often scroll over the full
|
||||
height. Above and below it, cells whose source crosses behind the floating pane
|
||||
must be recomposed rather than recovered from terminal pixels.
|
||||
|
||||
As a simpler optional strategy, native-scroll the full tiled-pane rectangle
|
||||
and recompose the floating pane's old/current rectangles afterward. This is
|
||||
correct if all disturbed cells are marked, but may visibly flash without
|
||||
synchronized output. Prefer safe-rectangle scrolling once correctness is
|
||||
established.
|
||||
|
||||
If rectangle decomposition becomes too fragmented, fall back to regional
|
||||
composition. It is an optimization, never a separate correctness path.
|
||||
|
||||
## Redraw scheduling
|
||||
|
||||
Do not turn queued regional damage into `CLIENT_REDRAWWINDOW` merely because
|
||||
the tty output buffer is nonempty. Preserve the damage list and process it
|
||||
after output drains.
|
||||
|
||||
Use full redraw only for:
|
||||
|
||||
- initial attachment;
|
||||
- client or terminal geometry changes;
|
||||
- a terminal reset or invalid backend cache;
|
||||
- layout/viewport changes where rebuilding and clipping regions is not yet
|
||||
safe; or
|
||||
- a damage union large enough that full redraw is cheaper.
|
||||
|
||||
Focus changes, selection changes, ordinary scrolling, and moving one floating
|
||||
pane should remain regional.
|
||||
|
||||
## Debugging instrumentation
|
||||
|
||||
Add temporary debug logging before optimizing:
|
||||
|
||||
- damage reason and window rectangle;
|
||||
- client clipping and coordinate translation;
|
||||
- scene generation used;
|
||||
- native-scroll rectangles selected;
|
||||
- spans recomposed in each layer phase; and
|
||||
- image fragments emitted by each backend.
|
||||
|
||||
The log should make it possible to answer, for every SIXEL retransmission,
|
||||
which damage rectangle required it. Remove excessively noisy instrumentation
|
||||
before the final commit or keep it behind normal verbose logging.
|
||||
|
||||
## Tests
|
||||
|
||||
Build the tests before each optimization and keep each test focused on one
|
||||
invariant.
|
||||
|
||||
### Automated regressions
|
||||
|
||||
1. **No-image baseline:** ordinary output, scrolling, copy mode, floating pane
|
||||
focus, borders, status lines, and scrollbars behave unchanged.
|
||||
2. **Focus only:** changing focus with a SIXEL elsewhere emits no SIXEL DCS and
|
||||
updates only the affected border/status cells.
|
||||
3. **Selection outside image:** emits no image output.
|
||||
4. **Selection crossing image:** the image remains intact during and after the
|
||||
drag; only changed non-image cells are highlighted.
|
||||
5. **Unobstructed scroll:** logical image spans move with text; where native
|
||||
scrolling is supported, no complete SIXEL retransmission occurs.
|
||||
6. **Scroll with floating pane:** both pane-status lines, borders, scrollbar,
|
||||
floating-pane contents, and visible image fragments remain correct.
|
||||
7. **Move floating pane:** old and new rectangles are restored; unrelated image
|
||||
regions are not emitted.
|
||||
8. **Multiple floating panes:** overlapping z-order remains correct while the
|
||||
top pane moves.
|
||||
9. **Transparent placement:** lower images/text show through correctly, and
|
||||
moving/selection does not treat transparency as absent placement geometry.
|
||||
10. **Cross-backend clients:** the same input scene produces equivalent results
|
||||
on Kitty, SIXEL, and fallback clients attached simultaneously.
|
||||
11. **Pane status top/bottom:** unchanged status contents are physically
|
||||
restored if their cells were damaged.
|
||||
12. **Scrollbar reserved/overlay:** scroll thumb and track remain present and
|
||||
images do not overwrite them.
|
||||
|
||||
Where possible, capture raw terminal output and assert both presence and
|
||||
absence of DCS/APC sequences. `capture-pane` alone cannot prove physical output
|
||||
ordering because it observes logical grid contents rather than terminal image
|
||||
pixels.
|
||||
|
||||
### Manual matrix
|
||||
|
||||
Test at least:
|
||||
|
||||
- Microsoft Terminal with SIXEL;
|
||||
- Mintty with SIXEL;
|
||||
- WezTerm with SIXEL;
|
||||
- Kitty with Kitty output;
|
||||
- a Kitty client attached to a session created from a SIXEL client and the
|
||||
reverse; and
|
||||
- a terminal without graphical image support.
|
||||
|
||||
For each, test scrolling by application output and mouse wheel, selecting text,
|
||||
entering/leaving copy mode, changing focus, opening/closing/moving a floating
|
||||
pane, pane status at top and bottom, and scrollbars.
|
||||
|
||||
## Implementation steps and commit boundaries
|
||||
|
||||
This must not be implemented as one monolithic change. Each numbered step is a
|
||||
separate commit, review, and testing gate. Do not begin the next step until the
|
||||
current step passes its focused tests. If a step exposes a pre-existing bug,
|
||||
record it before expanding the patch.
|
||||
|
||||
### Step 0: archive the experiment and recover the baseline
|
||||
|
||||
**Goal:** Begin development from known source rather than repairing the mixed
|
||||
experimental diff.
|
||||
|
||||
**Work:**
|
||||
|
||||
- Preserve the current diff and this plan on a clearly named WIP branch.
|
||||
- Return `4902-image-support` to clean commit `ec9859b1`.
|
||||
- Build with and without image support.
|
||||
- Run the existing image regression and ordinary tmux regression suite.
|
||||
|
||||
**Gate:** The branch is clean and existing behaviour is reproduced. No product
|
||||
code is changed in this step.
|
||||
|
||||
### Step 1: add focused regressions and tracing
|
||||
|
||||
**Goal:** Establish observable failures before changing redraw behaviour.
|
||||
|
||||
**Primary files:** `regress/image-support.sh`, temporary debug logging in
|
||||
`screen-redraw.c` if necessary.
|
||||
|
||||
**Work:** Add independent tests for focus-only changes, selection crossing an
|
||||
image, unobstructed scrolling, scrolling beneath a floating pane, moving a
|
||||
floating pane, both pane-status lines, and scrollbars. Raw terminal output must
|
||||
be used where a test needs to prove that SIXEL or Kitty data was or was not
|
||||
emitted.
|
||||
|
||||
**Gate:** Each test either passes on the baseline or has a documented expected
|
||||
failure. Tests must not depend on timing races or visual screenshots. The
|
||||
existing edge-case checks introduced by `a4abd616` must pass unchanged on the
|
||||
recovery baseline and establish the semantic compatibility result expected by
|
||||
later gates.
|
||||
|
||||
**Not included:** No redraw implementation changes.
|
||||
|
||||
### Step 2: introduce regional damage scheduling
|
||||
|
||||
**Goal:** Represent and preserve small window-coordinate damage rectangles
|
||||
without changing the rendered result.
|
||||
|
||||
**Primary files:** `screen-redraw.c`, `server-client.c`, minimal declarations in
|
||||
`tmux.h`.
|
||||
|
||||
**Work:** Add per-client damage storage, clipping, merging, a complexity cap,
|
||||
and verbose logging. Existing callers may initially convert the damage to the
|
||||
same redraw they used before. Pending tty output must retain the region rather
|
||||
than promote it automatically to a full-window redraw.
|
||||
|
||||
**Gate:** No-image and image output remain visually and byte-for-byte equivalent
|
||||
where practical. Unit-style tests verify rectangle clipping and merging.
|
||||
|
||||
**Not included:** No image-layer traversal, selection changes, floating-pane
|
||||
optimization, or native scrolling.
|
||||
|
||||
### Step 3: add a generic image-region composition API
|
||||
|
||||
**Goal:** Give the scene compositor one protocol-independent way to render the
|
||||
image layers intersecting a cell rectangle.
|
||||
|
||||
**Primary files:** `image.c`, `image-sixel.c`, `image-kitty.c`, with only generic
|
||||
prototypes in `tmux.h`.
|
||||
|
||||
**Work:**
|
||||
|
||||
- In `image.c`, intersect damage with placement spans and preserve logical
|
||||
input/z/serial order.
|
||||
- In `image-sixel.c`, emit a crop from cached quantized data and handle SIXEL
|
||||
band alignment.
|
||||
- In `image-kitty.c`, update/delete/place only the intersecting cached
|
||||
placements.
|
||||
- Define clearly separate below-text and above-text passes.
|
||||
|
||||
**Gate:** Rendering a whole image as adjacent damaged rectangles produces the
|
||||
same final scene as rendering it in one operation. Test transparent and
|
||||
overlapping placements on both output backends. The `a4abd616` semantic
|
||||
compatibility checks must continue to pass unchanged.
|
||||
|
||||
**Not included:** No changes to individual tty text commands and no native
|
||||
scroll optimization.
|
||||
|
||||
### Step 4: clip scene composition to damage rectangles
|
||||
|
||||
**Goal:** Make `screen-redraw.c` repaint only scene spans intersecting queued
|
||||
damage.
|
||||
|
||||
**Primary files:** `screen-redraw.c`; generic calls into `image.c`.
|
||||
|
||||
**Work:** For each damaged line, intersect existing redraw spans, compose the
|
||||
selected pane's below-text images, text, and above-text images, then draw
|
||||
intersecting chrome last. Full redraw continues to use the same compositor with
|
||||
a full-client damage rectangle.
|
||||
|
||||
**Gate:** Full and tiled panes redraw correctly; pane borders, pane status,
|
||||
scrollbars, menus, and images have deterministic output order. No floating-pane
|
||||
movement or scroll optimization is added yet.
|
||||
|
||||
### Step 5: route text and selection damage through the compositor
|
||||
|
||||
**Goal:** Repair only cells where text or copy-mode selection interacts with
|
||||
image layers.
|
||||
|
||||
**Primary files:** `screen-write.c`, `window-copy.c`, `screen-redraw.c`, and
|
||||
logical helpers in `image.c`.
|
||||
|
||||
**Work:** Apply input semantics to logical spans first. Preserve direct tty text
|
||||
output when no image placement intersects the cells. Otherwise enqueue those
|
||||
cells for composition. Selection changes damage only cells whose selection
|
||||
state changed and follow the policy of not overwriting image-covered cells.
|
||||
|
||||
**Gate:** Dragging selection across an image never damages it, entering/leaving
|
||||
copy mode restores the same scene, and selection outside images emits no image
|
||||
data. The `a4abd616` semantic compatibility checks must continue to pass
|
||||
unchanged.
|
||||
|
||||
**Not included:** Do not add image callbacks to `tty_cmd_cell`,
|
||||
`tty_cmd_cells`, or `tty_cmd_clearcharacter`.
|
||||
|
||||
### Step 6: make floating-pane changes regional
|
||||
|
||||
**Goal:** Focus, movement, resize, open, and close operations damage only the
|
||||
cells whose scene ownership or chrome changed.
|
||||
|
||||
**Primary files:** floating-pane/window code that reports geometry changes,
|
||||
plus `screen-redraw.c`. Image implementation remains in the image files.
|
||||
|
||||
**Work:** Focus changes enqueue border/status cells only. Movement and resize
|
||||
enqueue the old and new pane rectangles and invalidate scene geometry. Regional
|
||||
composition restores underlying panes/images in the old rectangle and draws
|
||||
the floating pane in the new rectangle.
|
||||
|
||||
**Gate:** Changing focus emits no unrelated SIXEL/APC. Moving a floating pane
|
||||
does not damage images, either pane-status line, borders, or scrollbars. Test
|
||||
one and multiple overlapping floating panes.
|
||||
|
||||
### Step 7: implement correct regional scrolling without native optimization
|
||||
|
||||
**Goal:** Establish correct scrolling using logical state plus regional
|
||||
composition.
|
||||
|
||||
**Primary files:** grid/image state helpers in `image.c`, damage reporting in
|
||||
`screen-write.c`, composition in `screen-redraw.c`.
|
||||
|
||||
**Work:** Move text and image spans first, then damage the scroll rectangle and
|
||||
compose it. Do not use special image repair from `tty.c`.
|
||||
|
||||
**Gate:** Application scrolling, mouse-wheel copy-mode scrolling, partial
|
||||
scroll regions, scrollbars, and scrolling beneath floating panes are correct on
|
||||
Kitty, Microsoft Terminal, Mintty, and WezTerm. This step may redraw more than
|
||||
the final optimized version, but must not redraw outside the scroll rectangle.
|
||||
The `a4abd616` semantic compatibility checks must continue to pass unchanged.
|
||||
|
||||
### Step 8: enable native scrolling for unobstructed rectangles
|
||||
|
||||
**Goal:** Avoid retransmitting image pixels when the terminal can safely move
|
||||
them.
|
||||
|
||||
**Primary files:** scroll planning in `screen-redraw.c`, generic backend
|
||||
capability in `image.c`, backend facts in `image-sixel.c` and `image-kitty.c`,
|
||||
terminal primitive execution in `tty.c`.
|
||||
|
||||
**Work:** Select native scrolling only when source/destination cells belong to
|
||||
the same unobstructed pane and terminal margins/backend behaviour permit it.
|
||||
Compose newly exposed rows afterward. Retain Step 7 as the fallback.
|
||||
|
||||
**Gate:** Raw output proves the native scroll is used and a complete image is
|
||||
not retransmitted. Final output must match Step 7 on every tested backend.
|
||||
|
||||
### Step 9: decompose scrolling around floating panes
|
||||
|
||||
**Goal:** Combine native scrolling of safe rectangles with composition of
|
||||
obstructed and boundary rectangles.
|
||||
|
||||
**Primary files:** scroll planning and scene-span traversal in
|
||||
`screen-redraw.c`; no new protocol logic outside the image files.
|
||||
|
||||
**Work:** Derive maximal rectangles where both source and destination are
|
||||
visible cells of the same pane, coalesce adjacent equal ranges, natively scroll
|
||||
them, and compose everything else. Enforce a fragmentation threshold that
|
||||
falls back to Step 7.
|
||||
|
||||
**Gate:** Scrolling beneath one or several floating panes produces the same
|
||||
final scene as Step 7, reduces emitted image data, and never loses pane status,
|
||||
borders, scrollbars, or images.
|
||||
|
||||
### Step 10: cleanup, performance checks, and full regression
|
||||
|
||||
**Goal:** Remove superseded paths only after all replacements are proven.
|
||||
|
||||
**Work:** Remove obsolete redraw helpers and flags introduced or made unused by
|
||||
the new path. Reduce temporary logging. Check memory growth, rectangle-list
|
||||
bounds, output byte counts, and behaviour without image support. Run the entire
|
||||
regression suite and the manual terminal matrix.
|
||||
|
||||
**Gate:** All completion criteria below are satisfied. Synchronized output may
|
||||
improve appearance, but disabling it does not alter final contents. The
|
||||
`a4abd616` semantic compatibility checks must continue to pass unchanged.
|
||||
|
||||
Do not squash these implementation steps during development. Keeping their
|
||||
boundaries visible makes it possible to bisect correctness and performance
|
||||
regressions. They can be reorganized later only after review.
|
||||
|
||||
## Completion criteria
|
||||
|
||||
The work is complete when:
|
||||
|
||||
- Mintty, Microsoft Terminal, WezTerm, and Kitty show no persistent damage;
|
||||
- focus and selection do not retransmit unrelated images;
|
||||
- floating-pane movement redraws only old/new affected regions;
|
||||
- scrolling preserves images, pane borders, both tiled and floating pane
|
||||
status lines, and scrollbars;
|
||||
- input semantics produce the same composition on every output backend;
|
||||
- no-image performance and behaviour remain unchanged;
|
||||
- full regression tests pass; and
|
||||
- synchronized output improves appearance but disabling it never changes the
|
||||
final screen contents.
|
||||
@@ -5,7 +5,8 @@ CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
|
||||
# Distribution tarball options.
|
||||
EXTRA_DIST = \
|
||||
CHANGES README COPYING example_tmux.conf \
|
||||
osdep-*.c mdoc2man.awk tmux.1
|
||||
osdep-*.c mdoc2man.awk tmux.1 \
|
||||
tools/generate-image-diacritics.sh
|
||||
dist_EXTRA_tmux_SOURCES = compat/*.[ch]
|
||||
|
||||
# Preprocessor flags.
|
||||
@@ -246,9 +247,9 @@ if HAVE_UTF8PROC
|
||||
nodist_tmux_SOURCES += compat/utf8proc.c
|
||||
endif
|
||||
|
||||
# Enable sixel support.
|
||||
if ENABLE_SIXEL
|
||||
dist_tmux_SOURCES += image.c image-sixel.c
|
||||
# Enable image support.
|
||||
if ENABLE_IMAGES
|
||||
dist_tmux_SOURCES += image.c image-sixel.c image-kitty.c
|
||||
endif
|
||||
|
||||
# Add bits for fuzzing if enabled.
|
||||
|
||||
33
configure.ac
33
configure.ac
@@ -530,15 +530,34 @@ if test "x$enable_cgroups" = xyes; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Enable sixel support.
|
||||
# Enable image support.
|
||||
AC_ARG_ENABLE(
|
||||
images,
|
||||
AS_HELP_STRING(--enable-images, enable Kitty/SIXEL/ASCII images)
|
||||
)
|
||||
|
||||
# Keep the old option as an alias for one compatibility cycle.
|
||||
AC_ARG_ENABLE(
|
||||
sixel,
|
||||
AS_HELP_STRING(--enable-sixel, enable sixel images)
|
||||
AS_HELP_STRING(--enable-sixel, deprecated alias for --enable-images)
|
||||
)
|
||||
if test "x$enable_sixel" = xyes; then
|
||||
AC_DEFINE(ENABLE_SIXEL)
|
||||
enable_images=yes
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
|
||||
if test "x$enable_images" = xyes; then
|
||||
PKG_CHECK_MODULES(
|
||||
IMAGE,
|
||||
[libpng >= 1.6 zlib],
|
||||
[
|
||||
AM_CPPFLAGS="$IMAGE_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$IMAGE_LIBS $LIBS"
|
||||
],
|
||||
AC_MSG_ERROR("image support requires libpng 1.6 and zlib")
|
||||
)
|
||||
AC_DEFINE(ENABLE_IMAGES)
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_IMAGES, [test "x$enable_images" = xyes])
|
||||
|
||||
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
|
||||
AC_MSG_CHECKING(for b64_ntop)
|
||||
@@ -1137,10 +1156,10 @@ if test "x$enable_asan" = xyes; then
|
||||
else
|
||||
AC_MSG_NOTICE([ASAN: off])
|
||||
fi
|
||||
if test "x$enable_sixel" = xyes; then
|
||||
AC_MSG_NOTICE([SIXEL: on])
|
||||
if test "x$enable_images" = xyes; then
|
||||
AC_MSG_NOTICE([images: on])
|
||||
else
|
||||
AC_MSG_NOTICE([SIXEL: off])
|
||||
AC_MSG_NOTICE([images: off])
|
||||
fi
|
||||
if test "x$enable_debug" = xyes; then
|
||||
AC_MSG_NOTICE([debug: on])
|
||||
|
||||
14
format.c
14
format.c
@@ -3018,6 +3018,17 @@ format_cb_sixel_support(__unused struct format_tree *ft)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Callback for image_support. */
|
||||
static void *
|
||||
format_cb_image_support(__unused struct format_tree *ft)
|
||||
{
|
||||
#ifdef ENABLE_IMAGES
|
||||
return (xstrdup("1"));
|
||||
#else
|
||||
return (xstrdup("0"));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Callback for active_window_index. */
|
||||
static void *
|
||||
format_cb_active_window_index(struct format_tree *ft)
|
||||
@@ -3681,6 +3692,9 @@ static const struct format_table_entry format_table[] = {
|
||||
{ "host_short", FORMAT_TABLE_STRING,
|
||||
format_cb_host_short
|
||||
},
|
||||
{ "image_support", FORMAT_TABLE_STRING,
|
||||
format_cb_image_support
|
||||
},
|
||||
{ "insert_flag", FORMAT_TABLE_STRING,
|
||||
format_cb_insert_flag
|
||||
},
|
||||
|
||||
82
grid.c
82
grid.c
@@ -86,6 +86,9 @@ grid_check_is_clear(struct grid *gd)
|
||||
assert(gl->extdsize == 0);
|
||||
assert(gl->flags == 0);
|
||||
assert(gl->time == 0);
|
||||
#ifdef ENABLE_IMAGES
|
||||
assert(gl->images == NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -353,6 +356,9 @@ grid_free_line(struct grid *gd, u_int py)
|
||||
assert(gl->cellsize == 0 || gl->celldata != NULL);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_free_line(gd, gl);
|
||||
#endif
|
||||
free(gl->celldata);
|
||||
free(gl->extddata);
|
||||
memset(gl, 0, sizeof *gl);
|
||||
@@ -397,6 +403,9 @@ void
|
||||
grid_destroy(struct grid *gd)
|
||||
{
|
||||
grid_free_lines(gd, 0, gd->hsize + gd->sy);
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_free(gd);
|
||||
#endif
|
||||
free(gd->linedata);
|
||||
free(gd);
|
||||
}
|
||||
@@ -729,6 +738,9 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
|
||||
|
||||
if (nx == 0 || ny == 0)
|
||||
return;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_damage(gd, px, py, nx, ny);
|
||||
#endif
|
||||
|
||||
if (px == 0 && nx == gd->sx) {
|
||||
grid_clear_lines(gd, py, ny, bg);
|
||||
@@ -764,6 +776,10 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
|
||||
void
|
||||
grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
|
||||
{
|
||||
struct grid_line *gl;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image_line *images;
|
||||
#endif
|
||||
u_int yy;
|
||||
|
||||
if (ny == 0)
|
||||
@@ -775,8 +791,18 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
|
||||
return;
|
||||
|
||||
for (yy = py; yy < py + ny; yy++) {
|
||||
grid_free_line(gd, yy);
|
||||
gl = &gd->linedata[yy];
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_damage(gd, 0, yy, gd->sx, 1);
|
||||
images = gl->images;
|
||||
#endif
|
||||
free(gl->celldata);
|
||||
free(gl->extddata);
|
||||
memset(gl, 0, sizeof *gl);
|
||||
grid_empty_line(gd, yy, bg);
|
||||
#ifdef ENABLE_IMAGES
|
||||
gl->images = images;
|
||||
#endif
|
||||
}
|
||||
if (py != 0)
|
||||
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
|
||||
@@ -841,6 +867,9 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
|
||||
|
||||
grid_expand_line(gd, py, px + nx, 8);
|
||||
grid_expand_line(gd, py, dx + nx, 8);
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_move_cells(gd, dx, px, py, nx);
|
||||
#endif
|
||||
memmove(&gl->celldata[dx], &gl->celldata[px],
|
||||
nx * sizeof *gl->celldata);
|
||||
if (dx + nx > gl->cellused)
|
||||
@@ -1213,7 +1242,6 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
|
||||
grid_get_cell(gd, xx, py, &gc);
|
||||
if (gc.flags & GRID_FLAG_PADDING)
|
||||
continue;
|
||||
|
||||
if (lastgc != NULL && (flags & GRID_STRING_WITH_SEQUENCES)) {
|
||||
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
|
||||
flags, s, &has_link);
|
||||
@@ -1280,11 +1308,18 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
||||
{
|
||||
struct grid_line *dstl, *srcl;
|
||||
u_int yy;
|
||||
#ifdef ENABLE_IMAGES
|
||||
u_int original_dy, original_sy;
|
||||
#endif
|
||||
|
||||
if (dy + ny > dst->hsize + dst->sy)
|
||||
ny = dst->hsize + dst->sy - dy;
|
||||
if (sy + ny > src->hsize + src->sy)
|
||||
ny = src->hsize + src->sy - sy;
|
||||
#ifdef ENABLE_IMAGES
|
||||
original_dy = dy;
|
||||
original_sy = sy;
|
||||
#endif
|
||||
grid_free_lines(dst, dy, ny);
|
||||
|
||||
for (yy = 0; yy < ny; yy++) {
|
||||
@@ -1292,6 +1327,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
||||
dstl = &dst->linedata[dy];
|
||||
|
||||
memcpy(dstl, srcl, sizeof *dstl);
|
||||
#ifdef ENABLE_IMAGES
|
||||
dstl->images = NULL;
|
||||
#endif
|
||||
if (srcl->cellsize != 0) {
|
||||
dstl->celldata = xreallocarray(NULL,
|
||||
srcl->cellsize, sizeof *dstl->celldata);
|
||||
@@ -1311,6 +1349,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
||||
sy++;
|
||||
dy++;
|
||||
}
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_duplicate_lines(dst, original_dy, src, original_sy, ny);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Mark line as dead. */
|
||||
@@ -1321,6 +1362,30 @@ grid_reflow_dead(struct grid_line *gl)
|
||||
gl->flags = GRID_LINE_DEAD;
|
||||
}
|
||||
|
||||
/* Image rows are cell-aligned and must not be reflowed like text. */
|
||||
static int
|
||||
grid_reflow_has_image(struct grid_line *gl)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
u_int i;
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (image_grid_line_has_images(gl))
|
||||
return (1);
|
||||
#endif
|
||||
if (~gl->flags & GRID_LINE_EXTENDED)
|
||||
return (0);
|
||||
for (i = 0; i < gl->cellused; i++) {
|
||||
grid_get_cell1(gl, i, &gc);
|
||||
/* Kitty Unicode placeholder base character (U+10EEEE). */
|
||||
if (gc.data.size >= 4 && gc.data.data[0] == 0xf4 &&
|
||||
gc.data.data[1] == 0x8e && gc.data.data[2] == 0xbb &&
|
||||
gc.data.data[3] == 0xae)
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Add lines, return the first new one. */
|
||||
static struct grid_line *
|
||||
grid_reflow_add(struct grid *gd, u_int n)
|
||||
@@ -1383,6 +1448,10 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
break;
|
||||
line = yy + 1 + lines;
|
||||
|
||||
/* Do not join wrapped text to a cell-aligned image row. */
|
||||
if (grid_reflow_has_image(&gd->linedata[line]))
|
||||
break;
|
||||
|
||||
/* If the next line is empty, skip it. */
|
||||
if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
|
||||
wrapped = 0;
|
||||
@@ -1443,8 +1512,7 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
|
||||
/* Remove the lines that were completely consumed. */
|
||||
for (i = yy + 1; i < yy + 1 + lines; i++) {
|
||||
free(gd->linedata[i].celldata);
|
||||
free(gd->linedata[i].extddata);
|
||||
grid_free_line(gd, i);
|
||||
grid_reflow_dead(&gd->linedata[i]);
|
||||
}
|
||||
|
||||
@@ -1546,6 +1614,12 @@ grid_reflow(struct grid *gd, u_int sx)
|
||||
if (gl->flags & GRID_LINE_DEAD)
|
||||
continue;
|
||||
|
||||
/* Keep image layers at their original cell coordinates. */
|
||||
if (grid_reflow_has_image(gl)) {
|
||||
grid_reflow_move(target, gl);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Work out the width of this line. at is the point at which
|
||||
* the available width is hit, and width is the full line
|
||||
|
||||
1308
image-kitty.c
Normal file
1308
image-kitty.c
Normal file
File diff suppressed because it is too large
Load Diff
1171
image-sixel.c
1171
image-sixel.c
File diff suppressed because it is too large
Load Diff
105
input.c
105
input.c
@@ -145,6 +145,9 @@ struct input_ctx {
|
||||
*/
|
||||
struct evbuffer *since_ground;
|
||||
struct event ground_timer;
|
||||
#ifdef ENABLE_IMAGES
|
||||
void *kitty_state;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Helper functions. */
|
||||
@@ -183,6 +186,9 @@ static void input_enter_osc(struct input_ctx *);
|
||||
static void input_exit_osc(struct input_ctx *);
|
||||
static void input_enter_apc(struct input_ctx *);
|
||||
static void input_exit_apc(struct input_ctx *);
|
||||
#ifdef ENABLE_IMAGES
|
||||
static int input_handle_kitty(struct input_ctx *, const u_char *, size_t);
|
||||
#endif
|
||||
static void input_enter_rename(struct input_ctx *);
|
||||
static void input_exit_rename(struct input_ctx *);
|
||||
|
||||
@@ -916,6 +922,9 @@ input_free(struct input_ctx *ictx)
|
||||
event_del(&ictx->request_timer);
|
||||
|
||||
free(ictx->input_buf);
|
||||
#ifdef ENABLE_IMAGES
|
||||
kitty_free_state(ictx->kitty_state);
|
||||
#endif
|
||||
evbuffer_free(ictx->since_ground);
|
||||
event_del(&ictx->ground_timer);
|
||||
|
||||
@@ -938,6 +947,7 @@ input_reset(struct input_ctx *ictx, int clear)
|
||||
screen_write_start_pane(sctx, wp, &wp->base);
|
||||
else
|
||||
screen_write_start(sctx, &wp->base);
|
||||
sctx->flags |= SCREEN_WRITE_INPUT;
|
||||
screen_write_reset(sctx);
|
||||
screen_write_stop(sctx);
|
||||
}
|
||||
@@ -1065,6 +1075,7 @@ input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len)
|
||||
screen_write_start_pane(sctx, wp, &wp->base);
|
||||
else
|
||||
screen_write_start(sctx, &wp->base);
|
||||
sctx->flags |= SCREEN_WRITE_INPUT;
|
||||
|
||||
log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id,
|
||||
ictx->state->name, len, (int)len, buf);
|
||||
@@ -1084,6 +1095,7 @@ input_parse_screen(struct input_ctx *ictx, struct screen *s,
|
||||
return;
|
||||
|
||||
screen_write_start_callback(sctx, s, cb, arg);
|
||||
sctx->flags |= SCREEN_WRITE_INPUT;
|
||||
input_parse(ictx, buf, len);
|
||||
screen_write_stop(sctx);
|
||||
}
|
||||
@@ -1583,7 +1595,7 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
case -1:
|
||||
break;
|
||||
case 0:
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
input_reply(ictx, 1, "\033[?1;2;4c");
|
||||
#else
|
||||
input_reply(ictx, 1, "\033[?1;2c");
|
||||
@@ -2101,7 +2113,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
||||
static void
|
||||
input_csi_dispatch_sm_graphics(__unused struct input_ctx *ictx)
|
||||
{
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
int n, m, o;
|
||||
|
||||
if (ictx->param_list_len > 3)
|
||||
@@ -2626,10 +2638,10 @@ input_dcs_dispatch(struct input_ctx *ictx)
|
||||
const char prefix[] = "tmux;";
|
||||
const u_int prefixlen = (sizeof prefix) - 1;
|
||||
long long allow_passthrough = 0;
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct window *w;
|
||||
struct sixel_image *si;
|
||||
int p2;
|
||||
int p1, p2;
|
||||
#endif
|
||||
|
||||
if (wp == NULL)
|
||||
@@ -2642,15 +2654,18 @@ input_dcs_dispatch(struct input_ctx *ictx)
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (wp != NULL && buf[0] == 'q' && ictx->interm_len == 0) {
|
||||
w = wp->window;
|
||||
if (input_split(ictx) != 0)
|
||||
return (0);
|
||||
p1 = input_get(ictx, 0, 0, 0);
|
||||
if (p1 == -1)
|
||||
p1 = 0;
|
||||
p2 = input_get(ictx, 1, 0, 0);
|
||||
if (p2 == -1)
|
||||
p2 = 0;
|
||||
si = sixel_parse(buf, len, p2, w->xpixel, w->ypixel);
|
||||
si = sixel_parse(buf, len, p1, p2, w->xpixel, w->ypixel);
|
||||
if (si != NULL)
|
||||
screen_write_sixelimage(sctx, si, ictx->cell.cell.bg);
|
||||
}
|
||||
@@ -2789,6 +2804,57 @@ input_enter_apc(struct input_ctx *ictx)
|
||||
}
|
||||
|
||||
/* APC terminator (ST) received. */
|
||||
#ifdef ENABLE_IMAGES
|
||||
static int
|
||||
input_handle_kitty(struct input_ctx *ictx, const u_char *buf, size_t len)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct image *im;
|
||||
u_int image_id = 0, replace_id = 0, quiet = 0;
|
||||
u_int placement_id = 0;
|
||||
int32_t z = 0;
|
||||
char action = '\0', delete = '\0';
|
||||
int status;
|
||||
|
||||
if (wp == NULL)
|
||||
return (0);
|
||||
im = kitty_parse_image(&ictx->kitty_state, buf, len,
|
||||
wp->window->xpixel,
|
||||
wp->window->ypixel, &image_id, &replace_id, &quiet, &action,
|
||||
&delete, &placement_id, &z, &status);
|
||||
if (status == KITTY_PARSE_MORE)
|
||||
return (1);
|
||||
if (status != KITTY_PARSE_OK) {
|
||||
if (quiet < 2 && action != '\0') {
|
||||
if (status == KITTY_PARSE_MISSING)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;ENOENT\033\\",
|
||||
image_id);
|
||||
else
|
||||
input_reply(ictx, 0, "\033_Gi=%u;EINVAL\033\\",
|
||||
image_id);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
if (replace_id != 0)
|
||||
image_clear(sctx, replace_id);
|
||||
if (im != NULL) {
|
||||
if (action == 'd')
|
||||
image_clear_kitty(sctx, delete, image_id, placement_id, z);
|
||||
else
|
||||
image_write_kitty(sctx, im, ictx->cell.cell.bg, image_id,
|
||||
placement_id, z);
|
||||
image_free(image_get_id(im));
|
||||
if (quiet == 0 && image_id != 0)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\", image_id);
|
||||
} else if (action == 'd')
|
||||
image_clear_kitty(sctx, delete, image_id, placement_id, z);
|
||||
else if ((action == 't' || action == 'q' || action == 'u') && quiet == 0)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\", image_id);
|
||||
return (1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
input_exit_apc(struct input_ctx *ictx)
|
||||
{
|
||||
@@ -2799,6 +2865,13 @@ input_exit_apc(struct input_ctx *ictx)
|
||||
return;
|
||||
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (ictx->input_len > 1 && ictx->input_buf[0] == 'G' &&
|
||||
input_handle_kitty(ictx, ictx->input_buf + 1,
|
||||
ictx->input_len - 1))
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (wp != NULL &&
|
||||
options_get_number(wp->options, "allow-set-title") &&
|
||||
screen_set_title(sctx->s, ictx->input_buf, 1)) {
|
||||
@@ -2859,6 +2932,12 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct utf8_data *ud = &ictx->utf8data;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell gc;
|
||||
struct image *im;
|
||||
u_int x, source_x, source_y, image_id, placement_id;
|
||||
int32_t z;
|
||||
#endif
|
||||
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
|
||||
@@ -2886,6 +2965,20 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
utf8_copy(&ictx->cell.cell.data, ud);
|
||||
screen_write_collect_add(sctx, &ictx->cell.cell);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (sctx->s->cx != 0) {
|
||||
x = sctx->s->cx - 1; /* cx-1 is the cell just written. */
|
||||
grid_view_get_cell(sctx->s->grid, x, sctx->s->cy, &gc);
|
||||
if (kitty_placeholder_to_image(ictx->kitty_state,
|
||||
sctx->s->grid, &gc, x, sctx->s->cy, &im, &source_x,
|
||||
&source_y, &image_id, &placement_id, &z)) {
|
||||
grid_view_set_cell(sctx->s->grid, x, sctx->s->cy, &gc);
|
||||
image_place_cell_kitty(sctx, im, x, sctx->s->cy, source_x,
|
||||
source_y, image_id, placement_id, z);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
utf8_copy(&ictx->last, &ictx->cell.cell.data);
|
||||
ictx->flags |= INPUT_LAST;
|
||||
|
||||
|
||||
@@ -541,6 +541,7 @@ const struct options_table_entry options_table[] = {
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_ARRAY,
|
||||
.default_str = "xterm*:clipboard:ccolour:cstyle:focus:title,"
|
||||
"xterm-kitty*:kitty,"
|
||||
"screen*:title,"
|
||||
"rxvt*:ignorefkeys",
|
||||
.separator = ",",
|
||||
|
||||
437
regress/image-support.sh
Executable file
437
regress/image-support.sh
Executable file
@@ -0,0 +1,437 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Grid-resident image layers: Kitty input, history, copy and overwrite.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Limage$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Limage-client$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
FIXTURE=$(pwd)/monkey-2.sixel.txt
|
||||
trap "$TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null; rm -f $TMP" 0 1 15
|
||||
|
||||
$TMUX new-session -d -x 20 -y 8 "
|
||||
printf '\033_Ga=T,q=2,f=32,s=2,v=2,c=2,r=2,m=1;/wAA/wD/\033\\'
|
||||
printf '\033_Gm=0;AP8AAP///////w==\033\\'
|
||||
i=0
|
||||
while [ \$i -lt 10 ]; do printf '\nline-%d' \$i; i=\$((i + 1)); done
|
||||
sleep 10"
|
||||
|
||||
sleep 1
|
||||
[ "$($TMUX display-message -p '#{image_support}')" = 0 ] && exit 0
|
||||
|
||||
# Images scroll as grid cells, while capture output contains ordinary spaces.
|
||||
[ "$($TMUX display-message -p '#{history_size}')" -gt 0 ] || exit 1
|
||||
$TMUX capture-pane -pS- >$TMP || exit 1
|
||||
grep '_G' $TMP >/dev/null && exit 1
|
||||
|
||||
# Copy mode duplicates the backing grid, including image layers.
|
||||
$TMUX copy-mode || exit 1
|
||||
$TMUX send-keys -X history-top || exit 1
|
||||
$TMUX capture-pane -p >$TMP || exit 1
|
||||
$TMUX send-keys -X cancel || exit 1
|
||||
|
||||
# Ordinary text updates the image underlay through the normal grid write path.
|
||||
# Deleting the image reveals the newly written text.
|
||||
$TMUX new-window -d "
|
||||
printf '\033_Ga=T,q=2,f=32,s=2,v=2,c=2,r=2,i=8;/wAA/wD/AP8AAP///////w==\033\\'
|
||||
printf '\033[HXY'
|
||||
printf '\033_Ga=d,d=i,q=2,i=8\033\\'
|
||||
sleep 10"
|
||||
sleep 1
|
||||
[ "$($TMUX capture-pane -pt:1 -S0 -E0)" = "XY" ] || exit 1
|
||||
|
||||
# The protocol default is transmit-only; a later placement uses the retained
|
||||
# immutable image and advances by the same canonical one-cell footprint.
|
||||
$TMUX new-window -d "
|
||||
printf '\033_Gq=2,f=32,s=1,v=1,c=1,r=1,i=9;/wAA/w==\033\\'
|
||||
printf '\033_Ga=p,q=2,i=9\033\\'
|
||||
printf '\033_Ga=d,d=I,q=2,i=9\033\\'
|
||||
sleep 10"
|
||||
sleep 1
|
||||
[ "$($TMUX display-message -pt:2 '#{cursor_y}')" = 1 ] || exit 1
|
||||
|
||||
# PNG Kitty input uses the shared image decoder and canonical cell sizing.
|
||||
$TMUX new-window -d "
|
||||
printf '\033_Ga=T,q=2,f=100;iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGUExURf8AAP///0EdNBEAAAABYktHRAH/Ai3eAAAAB3RJTUUH6ggCDAECH324BwAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=\033\\'
|
||||
sleep 10"
|
||||
sleep 1
|
||||
[ "$($TMUX display-message -pt:3 '#{cursor_y}')" = 1 ] || exit 1
|
||||
|
||||
# SIXEL input reaches the same grid layer and copy-mode paths.
|
||||
$TMUX new-window -d "cat '$FIXTURE'; sleep 10"
|
||||
sleep 1
|
||||
[ "$($TMUX display-message -pt:4 '#{cursor_y}')" -gt 0 ] || exit 1
|
||||
$TMUX copy-mode -t:4 || exit 1
|
||||
$TMUX send-keys -t:4 -X history-top || exit 1
|
||||
$TMUX capture-pane -pt:4 >$TMP || exit 1
|
||||
|
||||
# A 26-pixel raster occupies two default 16-pixel cells, but the final cell
|
||||
# remains only partially filled instead of stretching the raster to 32 pixels.
|
||||
# A client without an image feature exposes this in the sampled text backend.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 new-session -d -x 10 -y 4 "
|
||||
printf '\033Pq\"1;1;26;26#0;2;100;100;100#0!26~-!26~-!26~-!26~-!26B\033\\'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX2 set -as terminal-features ',*:sixel' || exit 1
|
||||
$TMUX new-session -d -x 10 -y 4 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX pipe-pane -O "cat >$TMP" || exit 1
|
||||
$TMUX send-keys -l "$TMUX2 attach-session" || exit 1
|
||||
$TMUX send-keys Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
||||
grep -q '^#=' $TMP || exit 1
|
||||
|
||||
# Re-emitting the image preserves the 26-pixel raster rather than expanding
|
||||
# it to the two complete 16-pixel grid cells occupied by the image.
|
||||
$TMUX pipe-pane || exit 1
|
||||
$TMUX pipe-pane -O "cat >$TMP" || exit 1
|
||||
$TMUX2 refresh-client -R || exit 1
|
||||
sleep 1
|
||||
grep -a '"1;1;26;26' $TMP >/dev/null || exit 1
|
||||
|
||||
# A selection redraw uses the single-cell path. It must leave ASCII image
|
||||
# cells visible (graphical clients skip these cells to avoid erasing pixels).
|
||||
$TMUX2 copy-mode || exit 1
|
||||
$TMUX2 send-keys -X history-top || exit 1
|
||||
$TMUX2 send-keys -X start-of-line || exit 1
|
||||
$TMUX2 send-keys -N 4 -X cursor-right || exit 1
|
||||
$TMUX2 send-keys -X begin-selection || exit 1
|
||||
$TMUX pipe-pane || exit 1
|
||||
$TMUX pipe-pane -O "cat >$TMP" || exit 1
|
||||
$TMUX2 send-keys -X cursor-right || exit 1
|
||||
sleep 1
|
||||
$TMUX pipe-pane || exit 1
|
||||
grep -a '"1;1;' $TMP >/dev/null && exit 1
|
||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
||||
grep -q '^#=' $TMP || exit 1
|
||||
|
||||
# Selection must also leave transparent cells inside the image canvas alone.
|
||||
# This 26-pixel canvas paints only its first pixel column, while the text below
|
||||
# it keeps the copy-mode cursor movable across both occupied terminal cells.
|
||||
$TMUX2 send-keys -X cancel || exit 1
|
||||
$TMUX2 new-window -d "
|
||||
printf 'ab\r'
|
||||
printf '\033Pq\"1;1;26;26#0;2;100;100;100#0~-#0~-#0~-#0~-#0B\033\\'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 select-window -t:1 || exit 1
|
||||
sleep 1
|
||||
$TMUX2 copy-mode || exit 1
|
||||
$TMUX2 send-keys -X top-line || exit 1
|
||||
$TMUX2 send-keys -X start-of-line || exit 1
|
||||
$TMUX2 send-keys -X cursor-right || exit 1
|
||||
$TMUX2 send-keys -X begin-selection || exit 1
|
||||
$TMUX pipe-pane || exit 1
|
||||
$TMUX pipe-pane -O "cat >$TMP" || exit 1
|
||||
$TMUX2 send-keys -X cursor-right || exit 1
|
||||
sleep 1
|
||||
$TMUX pipe-pane || exit 1
|
||||
grep -a 'b' $TMP >/dev/null && exit 1
|
||||
|
||||
# Scrolling moves the terminal's image rows with its text rows. It must not
|
||||
# force tmux to erase and retransmit the complete SIXEL image.
|
||||
$TMUX2 send-keys -X cancel || exit 1
|
||||
SCROLL_WINDOW=$($TMUX2 new-window -P -F '#{window_id}' \
|
||||
"cat '$FIXTURE'; exec sh") || exit 1
|
||||
sleep 1
|
||||
$TMUX pipe-pane || exit 1
|
||||
$TMUX pipe-pane -O "cat >$TMP" || exit 1
|
||||
$TMUX2 send-keys -t"$SCROLL_WINDOW" -l \
|
||||
"i=0; while [ \$i -lt 12 ]; do printf 'scroll-marker-%d\\n' \$i; i=\$((i + 1)); done" || exit 1
|
||||
$TMUX2 send-keys -t"$SCROLL_WINDOW" Enter || exit 1
|
||||
sleep 1
|
||||
grep -a 'scroll-marker-11' $TMP >/dev/null || exit 1
|
||||
grep -a '"1;1;' $TMP >/dev/null && exit 1
|
||||
|
||||
# A retained Kitty image may be placed repeatedly using source rectangles.
|
||||
# Crop the green and white right column from a red/green/blue/white image.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
$TMUX2 new-session -d -x 10 -y 4 "
|
||||
printf '\033_Ga=t,q=2,f=32,s=2,v=2,i=10;/wAA/wD/AP8AAP///////w==\033\\'
|
||||
printf '\033_Ga=p,q=2,i=10,x=1,y=0,w=1,h=2,c=1,r=2\033\\'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX new-session -d -x 10 -y 4 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX send-keys -l "$TMUX2 attach-session" || exit 1
|
||||
$TMUX send-keys Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E1 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "*" ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = "@" ] || exit 1
|
||||
|
||||
# C=1 places the image without moving the cursor or scrolling. The four-row
|
||||
# image is clipped at the bottom of this four-row pane, leaving its white last
|
||||
# row off screen.
|
||||
$TMUX2 new-window -d "
|
||||
printf '\033[2;3H'
|
||||
printf '\033_Ga=T,q=2,C=1,f=32,s=1,v=4,c=1,r=4;AAAA/1VVVf+qqqr//////w==\033\\'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 select-window -t:1 || exit 1
|
||||
sleep 1
|
||||
[ "$($TMUX2 display-message -p '#{cursor_x},#{cursor_y}')" = "2,1" ] || exit 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ "$(sed -n 3p $TMP)" = " -" ] || exit 1
|
||||
[ "$(sed -n 4p $TMP)" = " *" ] || exit 1
|
||||
|
||||
# With normal cursor movement, scrolling is calculated from the full image
|
||||
# height. Rows which scrolled above the pane are then cropped from the top, so
|
||||
# the bottom three source rows remain visible and the cursor is on the last row.
|
||||
$TMUX2 new-window -d "
|
||||
printf '\033[3;1H'
|
||||
printf '\033_Ga=T,q=2,f=32,s=1,v=4,c=1,r=4;AAAA/1VVVf+qqqr//////w==\033\\'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 select-window -t:2 || exit 1
|
||||
sleep 1
|
||||
[ "$($TMUX2 display-message -p '#{cursor_y}')" = 3 ] || exit 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "-" ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = "*" ] || exit 1
|
||||
[ "$(sed -n 3p $TMP)" = "@" ] || exit 1
|
||||
|
||||
# Image rows remain cell-aligned when a narrower terminal causes text
|
||||
# reflow. The ten-column rows are clipped to five columns, not split into four
|
||||
# wrapped rows.
|
||||
$TMUX2 new-window -d "
|
||||
printf '\033_Ga=T,q=2,C=1,f=32,s=1,v=2,c=10,r=2;/wAA//////8=\033\\'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 select-window -t:3 || exit 1
|
||||
sleep 1
|
||||
$TMUX resize-window -x 5 -y 4 || exit 1
|
||||
sleep 1
|
||||
[ "$($TMUX2 display-message -p '#{window_width}x#{window_height}')" = "5x4" ] || exit 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "....." ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = "@@@@@" ] || exit 1
|
||||
[ -z "$(sed -n 3p $TMP)" ] || exit 1
|
||||
|
||||
# Kitty virtual placements use U+10EEEE placeholder cells. Keep their rows at
|
||||
# fixed coordinates when narrowing the terminal, clipping instead of reflowing
|
||||
# the second half onto the following row.
|
||||
$TMUX resize-window -x 10 -y 4 || exit 1
|
||||
sleep 1
|
||||
$TMUX2 new-window -d "
|
||||
printf '\\364\\216\\273\\256\\314\\205\\364\\216\\273\\256\\314\\205\\364\\216\\273\\256\\314\\205\\364\\216\\273\\256\\314\\205\\364\\216\\273\\256\\314\\205'
|
||||
printf '\\364\\216\\273\\256\\314\\205\\364\\216\\273\\256\\314\\205\\364\\216\\273\\256\\314\\205\\364\\216\\273\\256\\314\\205\\364\\216\\273\\256\\314\\205'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 select-window -t:4 || exit 1
|
||||
sleep 1
|
||||
$TMUX resize-window -x 5 -y 4 || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ -n "$(sed -n 1p $TMP)" ] || exit 1
|
||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
||||
$TMUX resize-window -x 10 -y 4 || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP | wc -c)" = 61 ] || exit 1
|
||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
||||
|
||||
# A placement ID supplied with transmit-and-place is reused by a later place.
|
||||
# This is the sequence used by chawan: the newline moves the cursor down before
|
||||
# the second command, so the folder moves down one row without leaving a copy.
|
||||
PLACEMENT_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf '\033_GC=1,s=26,v=26,p=1,q=2,i=1,a=T,f=100,m=0;iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAACmUlEQVR4XmOgF2CEWVRfX8/w//9/BkZGRgy7YeKNjY1kuwtsam1tLQMTE5MG0MBsoEWW6KYBxf8DxScD6UVNTU1kWcZYU1MD0qjLzMx8EmgYJy5TgJb8/ffvn+2vX7+Od3Z2kmwZC9AnIE0FIEuAhl0H4ulA9k9kk4Bi4UB1TkA8H2iRYXl5+Xd2dna8loGCG2Q2LLgZQXED1PEIaLjs379/g4Ds9VDLwQYBxRiAPpFmZWW9DFQjCDRgLxDfJeQloJpdQHPWA9X9A1kGs+gZ0BBJoKQtUOIIeqSDgheoKR6IF5ASZkAH3gKaCzLzFQsxGltaWhjq6uoWguIJ6hi82oCG8wIV+AEdpga0LBfIrmUh1oWg1AYM5iVA9UuI1JMGVDcTiCNIsghkOChIQ0NDwfasXr0ap33QeL8GUgD0HTeIZmIgAUAtYQZqYYZZSKx2oi2CWaKpqbkIhEm1jOg40tLSAjleEBgUUSAGkJ8PpN8Q6yOCFoF8AvQBTvOAqZHh+vXrDPjijGAcIcXJUqBPQOXda5iNQP5rkBiQv5SYYMTrIxUVFZC5IkDCCk8QWQHVgdS8xBeMeBNDe3s7A7CwfQl0uSmwKDIBYhdQGQbCIDZIDCQHUgNSS7ZFII2gUgEU6UADzwJz+kUgDa6zoOyzIDmoGgaKLAJpBheKkArxC7BIeQDCQPEvIEuJrQyJzkdQA38ADbcHYaBFP0ipcVGqCWDYBwPxOkYs1TkDCeA/pEkQBDRnLZD9GKhVjgUkCGTsBgomAekWIJYG4p8MlAFQrZgJMgJo/m4QzQJ1fT9QIBLI1gTiSRRaAm/gAM38DjSvH2QeuHECLW1xNk7IsRhoyXGgJVOBem+AExPMEHzNLTItYiAlVTJQCwAAJxkMkgbHo2UAAAAASUVORK5CYII=\033\\'
|
||||
printf '\n'
|
||||
sleep 1
|
||||
printf '\033_GC=1,s=26,v=26,p=1,q=2,i=1,a=p;\033\\'
|
||||
printf '\n'
|
||||
sleep 10") || exit 1
|
||||
$TMUX2 select-window -t"$PLACEMENT_WINDOW" || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ -z "$(sed -n 1p $TMP)" ] || exit 1
|
||||
[ -n "$(sed -n 2p $TMP)" ] || exit 1
|
||||
|
||||
# A nonzero Kitty placement ID identifies one placement of an image. Reusing
|
||||
# the same image and placement IDs moves it rather than leaving the old cells.
|
||||
PLACEMENT_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf '\033_Ga=t,q=2,f=32,s=1,v=1,i=11;/////w==\033\\'
|
||||
printf '\033[2;2H\033_Ga=p,q=2,C=1,i=11,p=7,c=2,r=1\033\\'
|
||||
printf '\033[4;6H\033_Ga=p,q=2,C=1,i=11,p=7,c=2,r=1\033\\'
|
||||
sleep 10") || exit 1
|
||||
$TMUX2 select-window -t"$PLACEMENT_WINDOW" || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
||||
[ "$(sed -n 4p $TMP)" = " @@" ] || exit 1
|
||||
|
||||
# Deleting one placement ID leaves other placements of the image intact.
|
||||
PLACEMENT_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf '\033_Ga=t,q=2,f=32,s=1,v=1,i=12;/////w==\033\\'
|
||||
printf '\033[2;2H\033_Ga=p,q=2,C=1,i=12,p=7,c=2,r=1\033\\'
|
||||
printf '\033[4;6H\033_Ga=p,q=2,C=1,i=12,p=8,c=2,r=1\033\\'
|
||||
printf '\033_Ga=d,d=i,q=2,i=12,p=7\033\\'
|
||||
sleep 10") || exit 1
|
||||
$TMUX2 select-window -t"$PLACEMENT_WINDOW" || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
||||
[ "$(sed -n 4p $TMP)" = " @@" ] || exit 1
|
||||
|
||||
# Image layers retain the cells beneath them. Deleting a transparent image
|
||||
# must reveal the original text rather than replacing it with spaces.
|
||||
PLACEMENT_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf 'XY'
|
||||
printf '\033[H'
|
||||
printf '\033_Ga=T,q=2,C=1,f=32,s=1,v=1,c=2,r=1,i=13,p=7;////AA==\033\\'
|
||||
printf '\033_Ga=d,d=i,q=2,i=13,p=7\033\\'
|
||||
sleep 10") || exit 1
|
||||
$TMUX2 select-window -t"$PLACEMENT_WINDOW" || exit 1
|
||||
sleep 1
|
||||
[ "$($TMUX2 capture-pane -pS0 -E0)" = "XY" ] || exit 1
|
||||
|
||||
# Text written over a Kitty placement updates its underlay without deleting
|
||||
# the image. Deleting the placement afterwards reveals the updated text.
|
||||
TEXT_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf 'test\r'
|
||||
printf '\033_Ga=T,q=2,C=1,f=32,s=2,v=2,c=2,r=2,i=14,p=7;/wAA/wD/AP8AAP///////w==\033\\'
|
||||
printf '\r'
|
||||
printf 'test\n'
|
||||
printf 'test\n'
|
||||
$TEST_TMUX wait-for image-text-delete-$$
|
||||
printf '\033_Ga=d,d=i,q=2,i=14,p=7\033\\'
|
||||
sleep 10") || exit 1
|
||||
$TMUX2 select-window -t"$TEXT_WINDOW" || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E1 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = ".*st" ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = " @st" ] || exit 1
|
||||
$TMUX2 wait-for -S image-text-delete-$$ || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E1 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "test" ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = "test" ] || exit 1
|
||||
|
||||
# Kitty z-indexes and SIXEL's temporal plane are properties of the input, not
|
||||
# of the outer terminal. Exercise the logical scene through the fallback
|
||||
# backend: higher nonnegative z-indexes cover lower ones and text, ordinary
|
||||
# negative z-indexes cover backgrounds but not text, and very negative ones
|
||||
# remain below the background.
|
||||
Z_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf '\033_Ga=t,q=2,f=32,s=1,v=1,i=21;/wAA/w==\033\\'
|
||||
printf '\033_Ga=t,q=2,f=32,s=1,v=1,i=22;/////w==\033\\'
|
||||
printf 'X\r'
|
||||
printf '\033_Ga=p,q=2,C=1,i=21,p=1,z=2,c=1,r=1\033\\'
|
||||
printf '\033_Ga=p,q=2,C=1,i=22,p=2,z=1,c=1,r=1\033\\'
|
||||
printf '\033[2;1H\033[41m \033[0m\r'
|
||||
printf '\033_Ga=p,q=2,C=1,i=21,p=3,z=-1,c=1,r=1\033\\'
|
||||
printf '\033[3;1HY\r'
|
||||
printf '\033_Ga=p,q=2,C=1,i=21,p=4,z=-1,c=1,r=1\033\\'
|
||||
printf '\033[4;1H\033[41m \033[0m\r'
|
||||
printf '\033_Ga=p,q=2,C=1,i=21,p=5,z=-1073741825,c=1,r=1\033\\'
|
||||
$TEST_TMUX wait-for image-z-top-$$
|
||||
printf '\033_Ga=d,d=z,q=2,z=2\033\\'
|
||||
$TEST_TMUX wait-for image-z-lower-$$
|
||||
printf '\033_Ga=d,d=z,q=2,z=1\033\\'
|
||||
sleep 10") || exit 1
|
||||
$TMUX2 select-window -t"$Z_WINDOW" || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "." ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = "." ] || exit 1
|
||||
[ "$(sed -n 3p $TMP)" = "Y" ] || exit 1
|
||||
[ -z "$(sed -n 4p $TMP)" ] || exit 1
|
||||
$TMUX2 wait-for -S image-z-top-$$ || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "@" ] || exit 1
|
||||
$TMUX2 wait-for -S image-z-lower-$$ || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "X" ] || exit 1
|
||||
|
||||
# A weighted median at the maximum channel level must still leave colours on
|
||||
# both sides of the split. This skewed black, grey and white image used to stop
|
||||
# palette generation after one colour instead of producing three.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
$TMUX2 new-session -d -x 20 -y 4 "
|
||||
printf '\033_Ga=T,q=2,C=1,f=24,s=100,v=1,c=10,r=1;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\033\\'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX2 set -as terminal-features ',*:sixel' || exit 1
|
||||
$TMUX new-session -d -x 20 -y 4 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX pipe-pane -O "cat >$TMP" || exit 1
|
||||
$TMUX send-keys -l "$TMUX2 attach-session" || exit 1
|
||||
$TMUX send-keys Enter || exit 1
|
||||
sleep 1
|
||||
grep -a '#2;2;' $TMP >/dev/null || exit 1
|
||||
|
||||
# A pane redraw on a SIXEL terminal must not clear the status line, which is
|
||||
# outside the window scene and may not be redrawn after the pane scrolls.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
$TMUX2 new-session -d -x 20 -y 8 || exit 1
|
||||
$TMUX2 set -g 'status-format[0]' 'STATUS' || exit 1
|
||||
$TMUX2 set -as terminal-features ',*:sixel' || exit 1
|
||||
$TMUX new-session -d -x 20 -y 8 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX send-keys -l "$TMUX2 attach-session" || exit 1
|
||||
$TMUX send-keys Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX2 send-keys -l 'seq 30' || exit 1
|
||||
$TMUX2 send-keys Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -p >$TMP || exit 1
|
||||
[ "$(tail -n 1 $TMP)" = "STATUS" ] || exit 1
|
||||
|
||||
# A partial redraw of a tiled pane must clear only its visible scene spans.
|
||||
# Clearing its entire rectangle would erase a floating pane above it, while
|
||||
# the tiled pane redraw correctly skips the obscured area and cannot restore it.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
$TMUX2 new-session -d -x 40 -y 15 \
|
||||
"printf 'TILED PANE'; sleep 10" || exit 1
|
||||
TILED_PANE=$($TMUX2 display-message -p '#{pane_id}') || exit 1
|
||||
FLOATING_PANE=$($TMUX2 new-pane -dP -F '#{pane_id}' -x 20 -y 6 -X 10 -Y 4 \
|
||||
"printf 'FLOATING PANE'; sleep 10") || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX2 set -as terminal-features ',*:sixel' || exit 1
|
||||
$TMUX2 select-pane -t"$FLOATING_PANE" || exit 1
|
||||
$TMUX new-session -d -x 40 -y 15 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX send-keys -l "$TMUX2 attach-session" || exit 1
|
||||
$TMUX send-keys Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX2 copy-mode -t"$TILED_PANE" || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -p >$TMP || exit 1
|
||||
grep 'TILED PANE' $TMP >/dev/null || exit 1
|
||||
grep 'FLOATING PANE' $TMP >/dev/null || exit 1
|
||||
|
||||
# An obscured pane is normally redrawn a line at a time when it scrolls, but
|
||||
# that path cannot restore graphical image layers. It must defer to the scene
|
||||
# redraw so the visible part of the image is emitted around the floating pane.
|
||||
IMAGE_PANE=$($TMUX2 new-window -dP -F '#{pane_id}' \
|
||||
"cat '$FIXTURE'; exec sh") || exit 1
|
||||
$TMUX2 set -g pane-border-status top || exit 1
|
||||
$TMUX2 set -pt"$IMAGE_PANE" pane-border-format 'IMAGE STATUS' || exit 1
|
||||
FLOATING_PANE=$($TMUX2 new-pane -dP -F '#{pane_id}' -x 20 -y 6 -X 10 -Y 4 \
|
||||
"printf 'FLOATING PANE'; sleep 10") || exit 1
|
||||
$TMUX2 set -pt"$FLOATING_PANE" pane-border-format 'FLOAT STATUS' || exit 1
|
||||
$TMUX2 select-pane -t"$FLOATING_PANE" || exit 1
|
||||
sleep 1
|
||||
$TMUX pipe-pane || exit 1
|
||||
$TMUX pipe-pane -O "cat >$TMP" || exit 1
|
||||
$TMUX2 send-keys -t"$IMAGE_PANE" -l \
|
||||
"i=0; while [ \$i -lt 5 ]; do printf 'image-scroll-%d\\n' \$i; i=\$((i + 1)); done" || exit 1
|
||||
$TMUX2 send-keys -t"$IMAGE_PANE" Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX pipe-pane || exit 1
|
||||
grep -a 'image-scroll-4' $TMP >/dev/null || exit 1
|
||||
grep -a '"1;1;' $TMP >/dev/null || exit 1
|
||||
$TMUX capture-pane -p >$TMP || exit 1
|
||||
grep 'IMAGE STATUS' $TMP >/dev/null || exit 1
|
||||
grep 'FLOAT STATUS' $TMP >/dev/null || exit 1
|
||||
|
||||
exit 0
|
||||
1
regress/monkey-2.sixel.txt
Normal file
1
regress/monkey-2.sixel.txt
Normal file
File diff suppressed because one or more lines are too long
6
resize.c
6
resize.c
@@ -386,10 +386,12 @@ recalculate_size(struct window *w, int now)
|
||||
* got a resize scheduled, then use the new size; otherwise the old.
|
||||
*/
|
||||
if (w->flags & WINDOW_RESIZE) {
|
||||
if (!now && changed && w->new_sx == sx && w->new_sy == sy)
|
||||
if (!now && changed && w->new_sx == sx && w->new_sy == sy &&
|
||||
w->new_xpixel == xpixel && w->new_ypixel == ypixel)
|
||||
changed = 0;
|
||||
} else {
|
||||
if (!now && changed && w->sx == sx && w->sy == sy)
|
||||
if (!now && changed && w->sx == sx && w->sy == sy &&
|
||||
w->xpixel == xpixel && w->ypixel == ypixel)
|
||||
changed = 0;
|
||||
}
|
||||
|
||||
|
||||
131
screen-redraw.c
131
screen-redraw.c
@@ -99,6 +99,13 @@ enum redraw_span_type {
|
||||
#define REDRAW_ALL 0x7fffffff
|
||||
#define REDRAW_IS_ALL(flags) ((flags) == REDRAW_ALL)
|
||||
|
||||
enum redraw_image_phase {
|
||||
REDRAW_IMAGES_BEFORE,
|
||||
REDRAW_TEXT,
|
||||
REDRAW_IMAGES_AFTER,
|
||||
REDRAW_CHROME
|
||||
};
|
||||
|
||||
/* UTF-8 isolate characters. */
|
||||
#define REDRAW_START_ISOLATE "\342\201\246"
|
||||
#define REDRAW_END_ISOLATE "\342\201\251"
|
||||
@@ -1106,7 +1113,8 @@ redraw_get_scene(struct client *c)
|
||||
/* Draw a pane span. */
|
||||
static void
|
||||
redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
|
||||
struct redraw_span *span, u_int x, u_int y, u_int n)
|
||||
struct redraw_span *span, u_int x, u_int y, u_int n,
|
||||
enum redraw_image_phase phase)
|
||||
{
|
||||
struct redraw_scene *scene = dctx->scene;
|
||||
struct client *c = scene->c;
|
||||
@@ -1124,7 +1132,14 @@ redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
|
||||
|
||||
px = span->data.p.px + (x - span->x);
|
||||
py = span->data.p.py;
|
||||
tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (phase != REDRAW_TEXT)
|
||||
image_draw_line(tty, s, px, py, n, x, y,
|
||||
phase == REDRAW_IMAGES_BEFORE, &style_ctx);
|
||||
else
|
||||
#endif
|
||||
if (phase == REDRAW_TEXT)
|
||||
tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
|
||||
}
|
||||
|
||||
/* Get default border style for spans without a pane. */
|
||||
@@ -1379,7 +1394,7 @@ redraw_draw_menu_span(struct redraw_draw_ctx *dctx,
|
||||
/* Draw a span. */
|
||||
static void
|
||||
redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
|
||||
u_int y)
|
||||
u_int y, enum redraw_image_phase phase)
|
||||
{
|
||||
struct redraw_scene *scene = dctx->scene;
|
||||
struct redraw_span_data *data = &span->data;
|
||||
@@ -1400,10 +1415,15 @@ redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
|
||||
continue;
|
||||
x = rr->px;
|
||||
n = rr->nx;
|
||||
if (type == REDRAW_SPAN_PANE) {
|
||||
if (phase == REDRAW_CHROME)
|
||||
continue;
|
||||
} else if (phase != REDRAW_CHROME)
|
||||
continue;
|
||||
|
||||
switch (span->data.type) {
|
||||
case REDRAW_SPAN_PANE:
|
||||
redraw_draw_pane_span(dctx, span, x, y, n);
|
||||
redraw_draw_pane_span(dctx, span, x, y, n, phase);
|
||||
break;
|
||||
case REDRAW_SPAN_BORDER:
|
||||
case REDRAW_SPAN_EMPTY:
|
||||
@@ -1432,6 +1452,11 @@ redraw_draw_pane_lines(struct redraw_draw_ctx *dctx, struct window_pane *wp,
|
||||
struct redraw_line *line;
|
||||
struct redraw_spans *spans;
|
||||
struct redraw_span *span;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *rr;
|
||||
u_int i;
|
||||
#endif
|
||||
u_int cy;
|
||||
int y, top, bottom;
|
||||
|
||||
@@ -1444,24 +1469,53 @@ redraw_draw_pane_lines(struct redraw_draw_ctx *dctx, struct window_pane *wp,
|
||||
if (bottom > (int)scene->sy)
|
||||
bottom = scene->sy;
|
||||
|
||||
for (y = top; y < bottom; y++) {
|
||||
line = &scene->lines[y];
|
||||
if (dctx->flags & REDRAW_STATUS_TOP)
|
||||
cy = dctx->status_lines + y;
|
||||
else
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (flags & REDRAW_PANE) {
|
||||
for (y = top; y < bottom; y++) {
|
||||
line = &scene->lines[y];
|
||||
cy = y;
|
||||
if (flags & REDRAW_PANE) {
|
||||
if (dctx->flags & REDRAW_STATUS_TOP)
|
||||
cy += dctx->status_lines;
|
||||
spans = &line->spans[REDRAW_SPAN_PANE];
|
||||
TAILQ_FOREACH(span, spans, entry) {
|
||||
if (span->data.p.wp == wp)
|
||||
redraw_draw_span(dctx, span, cy);
|
||||
if (span->data.p.wp != wp)
|
||||
continue;
|
||||
r = tty_check_overlay_range(&scene->c->tty,
|
||||
span->x, cy, span->width);
|
||||
for (i = 0; i < r->used; i++) {
|
||||
rr = &r->ranges[i];
|
||||
if (rr->nx != 0) {
|
||||
image_redraw_start(&scene->c->tty,
|
||||
rr->px, cy, rr->nx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flags & REDRAW_PANE_SCROLLBAR) {
|
||||
spans = &line->spans[REDRAW_SPAN_SCROLLBAR];
|
||||
TAILQ_FOREACH(span, spans, entry) {
|
||||
if (span->data.sb.wp == wp)
|
||||
redraw_draw_span(dctx, span, cy);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (enum redraw_image_phase phase = REDRAW_IMAGES_BEFORE;
|
||||
phase <= REDRAW_CHROME; phase++) {
|
||||
for (y = top; y < bottom; y++) {
|
||||
line = &scene->lines[y];
|
||||
if (dctx->flags & REDRAW_STATUS_TOP)
|
||||
cy = dctx->status_lines + y;
|
||||
else
|
||||
cy = y;
|
||||
if (flags & REDRAW_PANE) {
|
||||
spans = &line->spans[REDRAW_SPAN_PANE];
|
||||
TAILQ_FOREACH(span, spans, entry) {
|
||||
if (span->data.p.wp == wp)
|
||||
redraw_draw_span(dctx, span, cy, phase);
|
||||
}
|
||||
}
|
||||
if (phase == REDRAW_CHROME &&
|
||||
(flags & REDRAW_PANE_SCROLLBAR)) {
|
||||
spans = &line->spans[REDRAW_SPAN_SCROLLBAR];
|
||||
TAILQ_FOREACH(span, spans, entry) {
|
||||
if (span->data.sb.wp == wp)
|
||||
redraw_draw_span(dctx, span, cy, phase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1477,13 +1531,15 @@ redraw_draw_lines(struct redraw_draw_ctx *dctx, int flags)
|
||||
struct redraw_span *span;
|
||||
u_int y, cy, type;
|
||||
|
||||
for (y = 0; y < scene->sy; y++) {
|
||||
line = &scene->lines[y];
|
||||
if (dctx->flags & REDRAW_STATUS_TOP)
|
||||
cy = dctx->status_lines + y;
|
||||
else
|
||||
cy = y;
|
||||
for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
|
||||
for (enum redraw_image_phase phase = REDRAW_IMAGES_BEFORE;
|
||||
phase <= REDRAW_CHROME; phase++) {
|
||||
for (y = 0; y < scene->sy; y++) {
|
||||
line = &scene->lines[y];
|
||||
if (dctx->flags & REDRAW_STATUS_TOP)
|
||||
cy = dctx->status_lines + y;
|
||||
else
|
||||
cy = y;
|
||||
for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
|
||||
if (!REDRAW_IS_ALL(flags)) {
|
||||
switch (type) {
|
||||
case REDRAW_SPAN_PANE:
|
||||
@@ -1520,7 +1576,8 @@ redraw_draw_lines(struct redraw_draw_ctx *dctx, int flags)
|
||||
}
|
||||
spans = &line->spans[type];
|
||||
TAILQ_FOREACH(span, spans, entry)
|
||||
redraw_draw_span(dctx, span, cy);
|
||||
redraw_draw_span(dctx, span, cy, phase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1541,7 +1598,7 @@ redraw_draw_menu_lines(struct redraw_draw_ctx *dctx)
|
||||
else
|
||||
cy = y;
|
||||
TAILQ_FOREACH(span, &line->spans[REDRAW_SPAN_MENU], entry)
|
||||
redraw_draw_span(dctx, span, cy);
|
||||
redraw_draw_span(dctx, span, cy, REDRAW_TEXT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1731,9 +1788,10 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
||||
}
|
||||
|
||||
if (flags & REDRAW_PANE_STATUS) {
|
||||
redraw = 0;
|
||||
redraw = !!(flags & REDRAW_PANE_BORDER);
|
||||
TAILQ_FOREACH(loop, &scene->w->panes, entry) {
|
||||
if (REDRAW_IS_ALL(flags))
|
||||
if (REDRAW_IS_ALL(flags) ||
|
||||
(flags & REDRAW_PANE_BORDER))
|
||||
loop->flags |= PANE_NEWSTATUS;
|
||||
else
|
||||
loop->flags &= ~PANE_NEWSTATUS;
|
||||
@@ -1771,6 +1829,14 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
||||
}
|
||||
}
|
||||
tty_sync_start(tty);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (wp == NULL && (flags & REDRAW_PANE)) {
|
||||
y = 0;
|
||||
if (dctx.flags & REDRAW_STATUS_TOP)
|
||||
y = dctx.status_lines;
|
||||
image_redraw_start(tty, 0, y, scene->sx, scene->sy);
|
||||
}
|
||||
#endif
|
||||
tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL);
|
||||
|
||||
if (wp != NULL)
|
||||
@@ -1817,15 +1883,6 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
||||
tty_reset(tty);
|
||||
tty_sync_end(tty);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (wp != NULL)
|
||||
tty_draw_images(c, wp);
|
||||
else {
|
||||
TAILQ_FOREACH(loop, &scene->w->panes, entry)
|
||||
tty_draw_images(c, loop);
|
||||
}
|
||||
#endif
|
||||
|
||||
log_debug("%s: finished @%u redraw", c->name, scene->w->id);
|
||||
}
|
||||
|
||||
|
||||
269
screen-write.c
269
screen-write.c
@@ -34,6 +34,10 @@ static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
|
||||
static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
|
||||
static void screen_write_collect_flush(struct screen_write_ctx *, int,
|
||||
const char *);
|
||||
#ifdef ENABLE_IMAGES
|
||||
static int screen_write_image_damage(struct screen_write_ctx *, u_int,
|
||||
u_int, u_int, u_int);
|
||||
#endif
|
||||
static int screen_write_overwrite(struct screen_write_ctx *,
|
||||
struct grid_cell *, u_int);
|
||||
static int screen_write_combine(struct screen_write_ctx *,
|
||||
@@ -135,6 +139,8 @@ static int
|
||||
screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
||||
{
|
||||
struct window_pane *wp = ttyctx->arg;
|
||||
u_int sx, sb_w = 0;
|
||||
int xoff;
|
||||
|
||||
if (ttyctx->flags & TTY_CTX_INVISIBLE_PANES) {
|
||||
if (session_has(c->session, wp->window))
|
||||
@@ -168,6 +174,21 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
||||
|
||||
ttyctx->xoff = ttyctx->rxoff = wp->xoff;
|
||||
ttyctx->yoff = ttyctx->ryoff = wp->yoff;
|
||||
ttyctx->flags &= ~TTY_CTX_PANE_FULL_WIDTH;
|
||||
if (~ttyctx->flags & TTY_CTX_WINDOW_BIGGER &&
|
||||
!window_has_floating_panes(wp->window)) {
|
||||
xoff = wp->xoff;
|
||||
sx = wp->sx;
|
||||
if (window_pane_scrollbar_reserve(wp)) {
|
||||
sb_w = wp->scrollbar_style.width +
|
||||
wp->scrollbar_style.pad;
|
||||
sx += sb_w;
|
||||
if (wp->window->sb_pos == PANE_SCROLLBARS_LEFT)
|
||||
xoff -= sb_w;
|
||||
}
|
||||
if (xoff == 0 && sx >= c->tty.sx)
|
||||
ttyctx->flags |= TTY_CTX_PANE_FULL_WIDTH;
|
||||
}
|
||||
|
||||
if (status_at_line(c) == 0)
|
||||
ttyctx->yoff += status_line_size(c);
|
||||
@@ -361,6 +382,23 @@ screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
|
||||
ctx->bg = 8;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Damage temporal images when application input writes text cells. */
|
||||
static int
|
||||
screen_write_image_damage(struct screen_write_ctx *ctx, u_int x, u_int y,
|
||||
u_int sx, u_int sy)
|
||||
{
|
||||
int found;
|
||||
|
||||
if (~ctx->flags & SCREEN_WRITE_INPUT)
|
||||
return (0);
|
||||
found = image_grid_check_area(ctx->s->grid, x,
|
||||
ctx->s->grid->hsize + y, sx, sy);
|
||||
image_grid_damage(ctx->s->grid, x, ctx->s->grid->hsize + y, sx, sy);
|
||||
return (found);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialize writing with a pane. */
|
||||
void
|
||||
screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp,
|
||||
@@ -1285,15 +1323,14 @@ screen_write_clear_dirty(struct window_pane *wp)
|
||||
}
|
||||
}
|
||||
|
||||
/* Redraw all visible cells in a pane. */
|
||||
/* Redraw an obscured pane through the scene to preserve floating panes. */
|
||||
static void
|
||||
screen_write_redraw_pane(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
|
||||
screen_write_redraw_pane(struct screen_write_ctx *ctx)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
u_int yy;
|
||||
|
||||
for (yy = 0; yy < screen_size_y(s); yy++)
|
||||
screen_write_redraw_line(ctx, ttyctx, yy);
|
||||
if (ctx->wp != NULL) {
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
server_redraw_window_borders(ctx->wp->window);
|
||||
}
|
||||
}
|
||||
|
||||
/* VT100 alignment test. */
|
||||
@@ -1308,9 +1345,11 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
|
||||
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||
utf8_set(&gc.data, 'E');
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_free_all(s) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_all(ctx);
|
||||
if (ctx->flags & SCREEN_WRITE_INPUT)
|
||||
image_grid_damage(s->grid, 0, s->grid->hsize,
|
||||
screen_size_x(s), screen_size_y(s));
|
||||
#endif
|
||||
|
||||
for (yy = 0; yy < screen_size_y(s); yy++) {
|
||||
@@ -1334,7 +1373,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
|
||||
return;
|
||||
}
|
||||
|
||||
screen_write_redraw_pane(ctx, &ttyctx);
|
||||
screen_write_redraw_pane(ctx);
|
||||
}
|
||||
|
||||
/* Insert nx characters. */
|
||||
@@ -1355,9 +1394,8 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
||||
@@ -1396,9 +1434,8 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
||||
@@ -1437,9 +1474,8 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
||||
@@ -1472,11 +1508,6 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
||||
if (ny == 0)
|
||||
ny = 1;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#endif
|
||||
|
||||
if (s->cy < s->rupper || s->cy > s->rlower) {
|
||||
if (ny > sy - s->cy)
|
||||
ny = sy - s->cy;
|
||||
@@ -1498,7 +1529,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
screen_write_redraw_pane(ctx, &ttyctx);
|
||||
screen_write_redraw_pane(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1525,7 +1556,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
screen_write_redraw_pane(ctx, &ttyctx);
|
||||
screen_write_redraw_pane(ctx);
|
||||
}
|
||||
|
||||
/* Delete ny lines. */
|
||||
@@ -1540,11 +1571,6 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
||||
if (ny == 0)
|
||||
ny = 1;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#endif
|
||||
|
||||
if (s->cy < s->rupper || s->cy > s->rlower) {
|
||||
if (ny > sy - s->cy)
|
||||
ny = sy - s->cy;
|
||||
@@ -1567,7 +1593,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
screen_write_redraw_pane(ctx, &ttyctx);
|
||||
screen_write_redraw_pane(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1595,7 +1621,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
screen_write_redraw_pane(ctx, &ttyctx);
|
||||
screen_write_redraw_pane(ctx);
|
||||
}
|
||||
|
||||
/* Clear line at cursor. */
|
||||
@@ -1613,9 +1639,8 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
|
||||
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
flags = gl->flags & GRID_LINE_OSC133_FLAGS;
|
||||
@@ -1652,9 +1677,8 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
|
||||
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
|
||||
@@ -1679,9 +1703,8 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
if (s->cx > sx - 1)
|
||||
@@ -1726,6 +1749,9 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
|
||||
struct screen *s = ctx->s;
|
||||
struct tty_ctx ttyctx;
|
||||
u_int ry;
|
||||
#ifdef ENABLE_IMAGES
|
||||
int image_scroll;
|
||||
#endif
|
||||
|
||||
if (s->cy != s->rupper) {
|
||||
if (s->cy > 0)
|
||||
@@ -1733,16 +1759,20 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_free_all(s) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_scroll = image_grid_check_area(s->grid, 0,
|
||||
s->grid->hsize + s->rupper, screen_size_x(s),
|
||||
s->rlower - s->rupper + 1);
|
||||
#endif
|
||||
|
||||
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
|
||||
screen_write_collect_flush(ctx, 0, __func__);
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||
ttyctx.bg = bg;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (image_scroll)
|
||||
ttyctx.flags |= TTY_CTX_IMAGE_SCROLL;
|
||||
#endif
|
||||
|
||||
ry = s->rlower + 1 - s->rupper;
|
||||
if (!screen_write_should_draw_lines(ctx, s->rupper, ry))
|
||||
@@ -1752,7 +1782,7 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
screen_write_redraw_pane(ctx, &ttyctx);
|
||||
screen_write_redraw_pane(ctx);
|
||||
}
|
||||
|
||||
/* Set scroll region. */
|
||||
@@ -1785,9 +1815,6 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
|
||||
struct screen *s = ctx->s;
|
||||
struct grid *gd = s->grid;
|
||||
struct grid_line *gl;
|
||||
#ifdef ENABLE_SIXEL
|
||||
int redraw = 0;
|
||||
#endif
|
||||
u_int rupper = s->rupper, rlower = s->rlower;
|
||||
|
||||
gl = grid_get_line(gd, gd->hsize + s->cy);
|
||||
@@ -1808,13 +1835,10 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (rlower == screen_size_y(s) - 1)
|
||||
redraw = image_scroll_up(s, 1);
|
||||
else
|
||||
redraw = image_check_line(s, rupper, rlower - rupper);
|
||||
if (redraw && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (image_grid_check_area(gd, 0, gd->hsize + rupper,
|
||||
screen_size_x(s), rlower - rupper + 1))
|
||||
ctx->scrolled_images = 1;
|
||||
#endif
|
||||
|
||||
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
|
||||
@@ -1840,9 +1864,10 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
|
||||
ctx->bg = bg;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_scroll_up(s, lines) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (image_grid_check_area(gd, 0, gd->hsize + s->rupper,
|
||||
screen_size_x(s), s->rlower - s->rupper + 1))
|
||||
ctx->scrolled_images = 1;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < lines; i++) {
|
||||
@@ -1869,9 +1894,10 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
|
||||
else if (lines > s->rlower - s->rupper + 1)
|
||||
lines = s->rlower - s->rupper + 1;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_free_all(s) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (image_grid_check_area(gd, 0, gd->hsize + s->rupper,
|
||||
screen_size_x(s), s->rlower - s->rupper + 1))
|
||||
ttyctx.flags |= TTY_CTX_IMAGE_SCROLL;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < lines; i++)
|
||||
@@ -1888,7 +1914,7 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
screen_write_redraw_pane(ctx, &ttyctx);
|
||||
screen_write_redraw_pane(ctx);
|
||||
}
|
||||
|
||||
/* Carriage return (cursor to start of line). */
|
||||
@@ -1910,9 +1936,8 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *ri;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_area(ctx, 0, s->cy, screen_size_x(s), sy - s->cy);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||
@@ -1991,9 +2016,8 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *ri;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_area(ctx, 0, 0, screen_size_x(s), s->cy + 1);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||
@@ -2064,9 +2088,8 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *ri;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_free_all(s) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_all(ctx);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||
@@ -2274,7 +2297,7 @@ screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||
if (ttyctx.flags & TTY_CTX_PANE_OBSCURED && wp != NULL) {
|
||||
screen_write_redraw_pane(ctx, &ttyctx);
|
||||
screen_write_redraw_pane(ctx);
|
||||
return 0;
|
||||
}
|
||||
if (wp != NULL && window_pane_scrollbar_overlay_visible(wp)) {
|
||||
@@ -2291,6 +2314,10 @@ screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
|
||||
ttyctx.orlower -= (wp->yoff + wp->sy - wp->window->sy);
|
||||
ttyctx.n = ctx->scrolled;
|
||||
ttyctx.bg = ctx->bg;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (ctx->scrolled_images)
|
||||
ttyctx.flags |= TTY_CTX_IMAGE_SCROLL;
|
||||
#endif
|
||||
tty_write(tty_cmd_scrollup, &ttyctx);
|
||||
|
||||
if (wp != NULL)
|
||||
@@ -2420,6 +2447,9 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
|
||||
if (!screen_write_collect_flush_scrolled(ctx))
|
||||
goto discard;
|
||||
ctx->scrolled = 0;
|
||||
#ifdef ENABLE_IMAGES
|
||||
ctx->scrolled_images = 0;
|
||||
#endif
|
||||
}
|
||||
ctx->bg = 8;
|
||||
|
||||
@@ -2443,6 +2473,9 @@ discard:
|
||||
}
|
||||
}
|
||||
ctx->scrolled = 0;
|
||||
#ifdef ENABLE_IMAGES
|
||||
ctx->scrolled_images = 0;
|
||||
#endif
|
||||
ctx->bg = 8;
|
||||
}
|
||||
|
||||
@@ -2569,9 +2602,8 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
screen_write_image_damage(ctx, s->cx, s->cy, ci->used, 1);
|
||||
#endif
|
||||
|
||||
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
|
||||
@@ -2661,6 +2693,9 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||
u_int width = ud->width, xx, not_wrap, i, n, vis;
|
||||
int selected, skip = 1, redraw = 0;
|
||||
#ifdef ENABLE_IMAGES
|
||||
int selection_image = 0;
|
||||
#endif
|
||||
int yoff = 0, xoff = 0;
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *ri;
|
||||
@@ -2701,6 +2736,12 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
return;
|
||||
screen_write_initctx(ctx, &ttyctx, 0, 0);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Input semantics decide which image layers this text damages. */
|
||||
if (screen_write_image_damage(ctx, s->cx, s->cy, width, 1))
|
||||
skip = 0;
|
||||
#endif
|
||||
|
||||
/* Handle overwriting of UTF-8 characters. */
|
||||
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
|
||||
if (gl->flags & GRID_LINE_EXTENDED) {
|
||||
@@ -2748,6 +2789,14 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
|
||||
/* Update the selected flag and set the cell. */
|
||||
selected = screen_check_selection(s, s->cx, s->cy);
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Selection highlighting must not overwrite image-covered cells. */
|
||||
if (selected && image_grid_check_area(gd, s->cx,
|
||||
gd->hsize + s->cy, width, 1)) {
|
||||
selected = 0;
|
||||
selection_image = 1;
|
||||
}
|
||||
#endif
|
||||
if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
|
||||
memcpy(&tmp_gc, gc, sizeof tmp_gc);
|
||||
tmp_gc.flags |= GRID_FLAG_SELECTED;
|
||||
@@ -2758,6 +2807,11 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
|
||||
} else if (!skip)
|
||||
grid_view_set_cell(gd, s->cx, s->cy, gc);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (selection_image)
|
||||
skip = 1;
|
||||
else
|
||||
#endif
|
||||
if (selected)
|
||||
skip = 0;
|
||||
|
||||
@@ -2927,6 +2981,9 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
force_wide = 0;
|
||||
|
||||
/* Set the new cell. */
|
||||
#ifdef ENABLE_IMAGES
|
||||
screen_write_image_damage(ctx, cx - n, cy, n, 1);
|
||||
#endif
|
||||
grid_view_set_cell(gd, cx - n, cy, &last);
|
||||
if (force_wide)
|
||||
grid_view_set_padding(gd, cx - 1, cy, last.bg);
|
||||
@@ -3060,63 +3117,21 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
|
||||
tty_write(tty_cmd_rawstring, &ttyctx);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Write a SIXEL image. */
|
||||
void
|
||||
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
|
||||
u_int bg)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct grid *gd = s->grid;
|
||||
struct tty_ctx ttyctx;
|
||||
u_int x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
|
||||
struct sixel_image *new;
|
||||
struct image *im;
|
||||
|
||||
if (screen_size_y(s) == 1)
|
||||
return;
|
||||
|
||||
sixel_size_in_cells(si, &x, &y);
|
||||
if (x > screen_size_x(s) || y > screen_size_y(s) - 1) {
|
||||
if (x > screen_size_x(s) - cx)
|
||||
sx = screen_size_x(s) - cx;
|
||||
else
|
||||
sx = x;
|
||||
if (y > screen_size_y(s) - 1)
|
||||
sy = screen_size_y(s) - 1;
|
||||
else
|
||||
sy = y;
|
||||
new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
|
||||
im = sixel_to_image(si);
|
||||
if (im == NULL) {
|
||||
sixel_free(si);
|
||||
if (new == NULL)
|
||||
return;
|
||||
sixel_size_in_cells(new, &x, &y);
|
||||
si = new;
|
||||
return;
|
||||
}
|
||||
|
||||
sy = screen_size_y(s) - cy;
|
||||
if (sy <= y) {
|
||||
lines = y - sy + 1;
|
||||
if (image_scroll_up(s, lines) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
for (i = 0; i < lines; i++) {
|
||||
grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1,
|
||||
bg);
|
||||
screen_write_collect_scroll(ctx, bg);
|
||||
}
|
||||
ctx->scrolled += lines;
|
||||
if (lines > cy)
|
||||
screen_write_cursormove(ctx, -1, 0, 0);
|
||||
else
|
||||
screen_write_cursormove(ctx, -1, cy - lines, 0);
|
||||
}
|
||||
screen_write_collect_flush(ctx, 0, __func__);
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 0, 0);
|
||||
ttyctx.image = image_store(s, si);
|
||||
|
||||
tty_write(tty_cmd_sixelimage, &ttyctx);
|
||||
|
||||
screen_write_cursormove(ctx, 0, cy + y, 0);
|
||||
image_write_sixel(ctx, im, bg);
|
||||
image_free(image_get_id(im));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
50
screen.c
50
screen.c
@@ -93,11 +93,6 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
|
||||
s->tabs = NULL;
|
||||
s->sel = NULL;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
TAILQ_INIT(&s->images);
|
||||
TAILQ_INIT(&s->saved_images);
|
||||
#endif
|
||||
|
||||
s->write_list = NULL;
|
||||
s->hyperlinks = NULL;
|
||||
|
||||
@@ -131,11 +126,6 @@ screen_reinit(struct screen *s, int check)
|
||||
|
||||
screen_clear_selection(s);
|
||||
screen_free_titles(s);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
image_free_all(s);
|
||||
#endif
|
||||
|
||||
screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0);
|
||||
screen_reset_hyperlinks(s);
|
||||
}
|
||||
@@ -154,10 +144,6 @@ screen_reset_hyperlinks(struct screen *s)
|
||||
void
|
||||
screen_free(struct screen *s)
|
||||
{
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct image *im;
|
||||
#endif
|
||||
|
||||
free(s->sel);
|
||||
free(s->tabs);
|
||||
free(s->path);
|
||||
@@ -174,17 +160,6 @@ screen_free(struct screen *s)
|
||||
hyperlinks_free(s->hyperlinks);
|
||||
screen_free_titles(s);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
/*
|
||||
* Images saved when entering the alternate screen stay linked in the
|
||||
* global list; move them back so they are freed and unlinked here, or
|
||||
* a later eviction would write through this freed screen.
|
||||
*/
|
||||
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
|
||||
TAILQ_FOREACH(im, &s->images, entry)
|
||||
im->list = &s->images;
|
||||
image_free_all(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Reset tabs to default, eight spaces apart. */
|
||||
@@ -377,10 +352,6 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
|
||||
if (sy != screen_size_y(s))
|
||||
screen_resize_y(s, sy, eat_empty, &cy);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
image_free_all(s);
|
||||
#endif
|
||||
|
||||
if (reflow)
|
||||
screen_reflow(s, sx, &cx, &cy, cursor);
|
||||
|
||||
@@ -692,10 +663,6 @@ int
|
||||
screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
{
|
||||
u_int sx, sy;
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct image *im;
|
||||
#endif
|
||||
|
||||
if (SCREEN_IS_ALTERNATE(s))
|
||||
return 0;
|
||||
sx = screen_size_x(s);
|
||||
@@ -709,12 +676,6 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
}
|
||||
memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
TAILQ_CONCAT(&s->saved_images, &s->images, entry);
|
||||
TAILQ_FOREACH(im, &s->saved_images, entry)
|
||||
im->list = &s->saved_images;
|
||||
#endif
|
||||
|
||||
grid_view_clear(s->grid, 0, 0, sx, sy, 8);
|
||||
|
||||
s->saved_flags = s->grid->flags;
|
||||
@@ -728,10 +689,6 @@ int
|
||||
screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
{
|
||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct image *im;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the current size is different, temporarily resize to the old size
|
||||
* before copying back.
|
||||
@@ -774,13 +731,6 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
grid_destroy(s->saved_grid);
|
||||
s->saved_grid = NULL;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
image_free_all(s);
|
||||
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
|
||||
TAILQ_FOREACH(im, &s->images, entry)
|
||||
im->list = &s->images;
|
||||
#endif
|
||||
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
s->cx = screen_size_x(s) - 1;
|
||||
if (s->cy > screen_size_y(s) - 1)
|
||||
|
||||
@@ -2441,18 +2441,19 @@ server_client_check_redraw(struct client *c)
|
||||
|
||||
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
|
||||
return;
|
||||
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
|
||||
log_debug("%s: redraw%s%s%s%s%s", c->name,
|
||||
if (c->flags & (CLIENT_ALLREDRAWFLAGS|CLIENT_REDRAWSCROLLBARS)) {
|
||||
log_debug("%s: redraw%s%s%s%s%s%s", c->name,
|
||||
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
|
||||
(c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
|
||||
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "",
|
||||
(c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "",
|
||||
(c->flags & CLIENT_REDRAWMENU) ? " menu" : "");
|
||||
(c->flags & CLIENT_REDRAWMENU) ? " menu" : "",
|
||||
(c->flags & CLIENT_REDRAWSCROLLBARS) ? " scrollbars" : "");
|
||||
}
|
||||
|
||||
/* Work out if a redraw is actually needed. */
|
||||
needed = 0;
|
||||
if (c->flags & CLIENT_ALLREDRAWFLAGS)
|
||||
if (c->flags & (CLIENT_ALLREDRAWFLAGS|CLIENT_REDRAWSCROLLBARS))
|
||||
needed = 1;
|
||||
else if (server_client_any_pane_redraw(c))
|
||||
needed = 1;
|
||||
@@ -2478,8 +2479,14 @@ server_client_check_redraw(struct client *c)
|
||||
log_debug("redraw timer started");
|
||||
evtimer_add(&ev, &tv);
|
||||
}
|
||||
if (server_client_any_pane_redraw(c))
|
||||
c->flags |= CLIENT_REDRAWWINDOW;
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp->flags & PANE_REDRAW) {
|
||||
c->flags |= CLIENT_REDRAWWINDOW;
|
||||
break;
|
||||
}
|
||||
if (wp->flags & PANE_REDRAWSCROLLBAR)
|
||||
c->flags |= CLIENT_REDRAWSCROLLBARS;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2498,7 +2505,8 @@ server_client_check_redraw(struct client *c)
|
||||
log_debug("%s: redraw pane %%%u", __func__,
|
||||
wp->id);
|
||||
redraw_pane(c, wp);
|
||||
} else if (wp->flags & PANE_REDRAWSCROLLBAR) {
|
||||
} else if ((wp->flags & PANE_REDRAWSCROLLBAR) ||
|
||||
(c->flags & CLIENT_REDRAWSCROLLBARS)) {
|
||||
log_debug("%s: redraw scrollbar %%%u", __func__,
|
||||
wp->id);
|
||||
redraw_pane_scrollbar(c, wp);
|
||||
@@ -2528,7 +2536,8 @@ server_client_check_redraw(struct client *c)
|
||||
* All the redraw flags can now be cleared. Also record how many bytes
|
||||
* were written.
|
||||
*/
|
||||
c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE);
|
||||
c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_REDRAWSCROLLBARS|
|
||||
CLIENT_STATUSFORCE);
|
||||
c->redraw = EVBUFFER_LENGTH(tty->out);
|
||||
log_debug("%s: redraw added %zu bytes", c->name, c->redraw);
|
||||
}
|
||||
|
||||
3
style.c
3
style.c
@@ -30,7 +30,8 @@
|
||||
|
||||
/* Default style. */
|
||||
static struct style style_default = {
|
||||
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 },
|
||||
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0
|
||||
},
|
||||
0,
|
||||
0,
|
||||
|
||||
|
||||
4
tmux.1
4
tmux.1
@@ -5073,6 +5073,10 @@ Ignore function keys from
|
||||
and use the
|
||||
.Nm
|
||||
internal set only.
|
||||
.It kitty
|
||||
Supports Kitty graphics using Unicode placeholders.
|
||||
The terminal must support the Kitty Unicode placeholder extension, not only
|
||||
the basic Kitty graphics protocol.
|
||||
.It margins
|
||||
Supports DECSLRM margins.
|
||||
.It mouse
|
||||
|
||||
171
tmux.h
171
tmux.h
@@ -75,7 +75,12 @@ struct screen_write_cline;
|
||||
struct screen_write_ctx;
|
||||
struct session;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image;
|
||||
struct image_backend;
|
||||
struct image_rect;
|
||||
struct image_line;
|
||||
struct image_store;
|
||||
struct sixel_image;
|
||||
#endif
|
||||
|
||||
@@ -858,7 +863,7 @@ struct colour_palette {
|
||||
struct grid_cell {
|
||||
struct utf8_data data;
|
||||
u_short attr;
|
||||
u_char flags;
|
||||
u_short flags;
|
||||
int fg;
|
||||
int bg;
|
||||
int us;
|
||||
@@ -869,7 +874,7 @@ struct grid_cell {
|
||||
struct grid_extd_entry {
|
||||
utf8_char data;
|
||||
u_short attr;
|
||||
u_char flags;
|
||||
u_short flags;
|
||||
int fg;
|
||||
int bg;
|
||||
int us;
|
||||
@@ -887,7 +892,7 @@ struct grid_cell_entry {
|
||||
u_char data;
|
||||
} data;
|
||||
};
|
||||
u_char flags;
|
||||
u_short flags;
|
||||
} __packed;
|
||||
|
||||
/* OSC 133 data for a grid line. */
|
||||
@@ -911,6 +916,9 @@ struct grid_line {
|
||||
u_int time;
|
||||
struct osc133_data osc133_data;
|
||||
u_short flags;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image_line *images;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Entire grid of cells. */
|
||||
@@ -930,6 +938,9 @@ struct grid {
|
||||
u_int scroll_generation;
|
||||
|
||||
struct grid_line *linedata;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image_store *images;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Virtual cursor in a grid. */
|
||||
@@ -1021,24 +1032,8 @@ struct style {
|
||||
u_int link;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
/* Image. */
|
||||
struct image {
|
||||
struct screen *s;
|
||||
struct sixel_image *data;
|
||||
char *fallback;
|
||||
|
||||
u_int px;
|
||||
u_int py;
|
||||
u_int sx;
|
||||
u_int sy;
|
||||
|
||||
struct images *list;
|
||||
TAILQ_ENTRY (image) entry;
|
||||
|
||||
TAILQ_ENTRY (image) all_entry;
|
||||
};
|
||||
TAILQ_HEAD(images, image);
|
||||
#ifdef ENABLE_IMAGES
|
||||
#define IMAGE_SIZE_LIMIT (64 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
/* Cursor style. */
|
||||
@@ -1097,11 +1092,6 @@ struct screen {
|
||||
bitstr_t *tabs;
|
||||
struct screen_sel *sel;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct images images;
|
||||
struct images saved_images;
|
||||
#endif
|
||||
|
||||
struct screen_write_cline *write_list;
|
||||
|
||||
struct hyperlinks *hyperlinks;
|
||||
@@ -1119,12 +1109,16 @@ struct screen_write_ctx {
|
||||
#define SCREEN_WRITE_SYNC 0x1
|
||||
#define SCREEN_WRITE_OBSCURED 0x2
|
||||
#define SCREEN_WRITE_CHECKED_IF_OBSCURED 0x4
|
||||
#define SCREEN_WRITE_INPUT 0x8
|
||||
|
||||
screen_write_init_ctx_cb init_ctx_cb;
|
||||
void *arg;
|
||||
|
||||
struct screen_write_citem *item;
|
||||
u_int scrolled;
|
||||
#ifdef ENABLE_IMAGES
|
||||
int scrolled_images;
|
||||
#endif
|
||||
u_int bg;
|
||||
};
|
||||
|
||||
@@ -1731,7 +1725,8 @@ struct tty_term {
|
||||
#define TERM_RGBCOLOURS 0x10
|
||||
#define TERM_VT100LIKE 0x20
|
||||
#define TERM_SIXEL 0x40
|
||||
#define TERM_INVALIDMS 0x80
|
||||
#define TERM_KITTY 0x80
|
||||
#define TERM_INVALIDMS 0x100
|
||||
int flags;
|
||||
|
||||
LIST_ENTRY(tty_term) entry;
|
||||
@@ -1833,6 +1828,10 @@ struct tty {
|
||||
|
||||
struct event key_timer;
|
||||
struct tty_key *key_tree;
|
||||
#ifdef ENABLE_IMAGES
|
||||
const struct image_backend *image_backend;
|
||||
void *image_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Terminal command context. */
|
||||
@@ -1854,6 +1853,8 @@ struct tty_ctx {
|
||||
#define TTY_CTX_OVERLAY_SYNC 0x10
|
||||
#define TTY_CTX_CELL_INVALIDATE 0x20
|
||||
#define TTY_CTX_PANE_OBSCURED 0x40
|
||||
#define TTY_CTX_IMAGE_SCROLL 0x80
|
||||
#define TTY_CTX_PANE_FULL_WIDTH 0x100
|
||||
|
||||
union {
|
||||
u_int n;
|
||||
@@ -1869,7 +1870,7 @@ struct tty_ctx {
|
||||
size_t size;
|
||||
} sel;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image *image;
|
||||
#endif
|
||||
};
|
||||
@@ -2287,7 +2288,7 @@ struct client {
|
||||
#define CLIENT_STARTSERVER 0x10000000
|
||||
#define CLIENT_REDRAWMENU 0x20000000
|
||||
#define CLIENT_NOFORK 0x40000000
|
||||
/* 0x80000000ULL unused */
|
||||
#define CLIENT_REDRAWSCROLLBARS 0x80000000ULL
|
||||
#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL
|
||||
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
|
||||
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
|
||||
@@ -2988,10 +2989,6 @@ void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *);
|
||||
void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
|
||||
void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
|
||||
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
|
||||
#ifdef ENABLE_SIXEL
|
||||
void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
|
||||
void tty_draw_images(struct client *, struct window_pane *);
|
||||
#endif
|
||||
void tty_cmd_syncstart(struct tty *, const struct tty_ctx *);
|
||||
void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *);
|
||||
|
||||
@@ -3634,7 +3631,7 @@ void screen_write_setselection(struct screen_write_ctx *, const char *,
|
||||
u_char *, u_int);
|
||||
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
|
||||
int);
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
void screen_write_sixelimage(struct screen_write_ctx *,
|
||||
struct sixel_image *, u_int);
|
||||
#endif
|
||||
@@ -4220,17 +4217,106 @@ void spawn_editor_finish(struct window_pane *);
|
||||
/* regsub.c */
|
||||
char *regsub(const char *, const char *, const char *, int);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* image.c */
|
||||
int image_free_all(struct screen *);
|
||||
struct image *image_store(struct screen *, struct sixel_image *);
|
||||
int image_check_line(struct screen *, u_int, u_int);
|
||||
int image_check_area(struct screen *, u_int, u_int, u_int, u_int);
|
||||
int image_scroll_up(struct screen *, u_int);
|
||||
#define IMAGE_BACKEND_GRAPHICAL 0x1
|
||||
#define IMAGE_BACKEND_SCROLLS 0x2
|
||||
|
||||
struct image *image_create(u_int, u_int, u_int, u_int, u_int, u_int,
|
||||
u_char *);
|
||||
struct image *image_create_view(struct image *, u_int, u_int, u_int,
|
||||
u_int, u_int, u_int, u_int, u_int, u_int, u_int);
|
||||
struct image *image_find(u_int);
|
||||
u_int image_get_id(const struct image *);
|
||||
void image_get_size(const struct image *, u_int *, u_int *);
|
||||
void image_get_canvas_size(const struct image *, u_int *, u_int *);
|
||||
void image_get_size_in_cells(const struct image *, u_int *, u_int *);
|
||||
const u_char *image_get_pixels(const struct image *, size_t *, size_t *);
|
||||
void image_set_no_cursor(struct image *);
|
||||
struct sixel_image *image_get_sixel(const struct image *);
|
||||
void image_set_sixel(struct image *, struct sixel_image *);
|
||||
void image_ref(u_int);
|
||||
void image_free(u_int);
|
||||
void image_write_sixel(struct screen_write_ctx *, struct image *,
|
||||
u_int);
|
||||
void image_write_kitty(struct screen_write_ctx *, struct image *,
|
||||
u_int, u_int, u_int, int32_t);
|
||||
void image_get_pixel_rect(const struct image *, u_int, u_int,
|
||||
u_int, u_int, u_int *, u_int *, u_int *, u_int *);
|
||||
void image_size_in_cells(u_int, u_int, u_int, u_int, u_int *,
|
||||
u_int *);
|
||||
u_char *image_base64_decode(const char *, size_t, size_t, size_t *);
|
||||
u_char *image_png_decode(const u_char *, size_t, size_t, u_int *,
|
||||
u_int *);
|
||||
void image_redraw_area(struct screen_write_ctx *, u_int, u_int,
|
||||
u_int, u_int);
|
||||
void image_redraw_all(struct screen_write_ctx *);
|
||||
void image_redraw_start(struct tty *, u_int, u_int, u_int, u_int);
|
||||
int image_backend_flags(struct tty *);
|
||||
void image_tty_update(struct tty *);
|
||||
void image_tty_geometry_changed(struct tty *);
|
||||
void image_tty_free(struct tty *, int);
|
||||
void image_draw_line(struct tty *, struct screen *, u_int, u_int,
|
||||
u_int, u_int, u_int, int, const struct tty_style_ctx *);
|
||||
void image_draw_after_text(struct tty *, struct screen *, u_int,
|
||||
u_int, u_int, u_int, u_int, const struct tty_style_ctx *);
|
||||
void image_get_fallback_cell(struct tty *, struct image *, u_int,
|
||||
u_int, const struct grid_cell *, struct grid_cell *,
|
||||
const struct tty_style_ctx *);
|
||||
int image_get_fallback_at(struct tty *, struct screen *, u_int,
|
||||
u_int, const struct grid_cell *, struct grid_cell *,
|
||||
const struct tty_style_ctx *);
|
||||
struct image *image_rect_get_image(const struct image_rect *);
|
||||
const struct grid_cell *image_rect_get_cell(
|
||||
const struct image_rect *);
|
||||
void image_rect_get_coords(const struct image_rect *,
|
||||
u_int *, u_int *, u_int *, u_int *, u_int *, u_int *);
|
||||
int32_t image_rect_get_z(const struct image_rect *);
|
||||
void image_clear(struct screen_write_ctx *, u_int);
|
||||
void image_clear_kitty(struct screen_write_ctx *, char, u_int,
|
||||
u_int, int32_t);
|
||||
void image_grid_damage(struct grid *, u_int, u_int, u_int, u_int);
|
||||
void image_grid_free_line(struct grid *, struct grid_line *);
|
||||
void image_grid_free(struct grid *);
|
||||
void image_grid_move_cells(struct grid *, u_int, u_int, u_int,
|
||||
u_int);
|
||||
void image_grid_duplicate_lines(struct grid *, u_int, struct grid *,
|
||||
u_int, u_int);
|
||||
void image_grid_copy_area(struct grid *, u_int, u_int, struct grid *,
|
||||
u_int, u_int, u_int, u_int);
|
||||
int image_grid_line_has_images(const struct grid_line *);
|
||||
int image_grid_check_area(struct grid *, u_int, u_int, u_int,
|
||||
u_int);
|
||||
int image_grid_get_source(struct grid *, u_int, u_int,
|
||||
struct image *, u_int *, u_int *);
|
||||
void image_place_cell_kitty(struct screen_write_ctx *, struct image *,
|
||||
u_int, u_int, u_int, u_int, u_int, u_int, int32_t);
|
||||
#define KITTY_PARSE_ERROR -1
|
||||
#define KITTY_PARSE_OK 0
|
||||
#define KITTY_PARSE_MORE 1
|
||||
#define KITTY_PARSE_MISSING 2
|
||||
struct image *kitty_parse_image(void **, const u_char *, size_t, u_int,
|
||||
u_int, u_int *, u_int *, u_int *, char *, char *, u_int *,
|
||||
int32_t *, int *);
|
||||
int kitty_placeholder_to_image(void *, struct grid *,
|
||||
struct grid_cell *, u_int, u_int, struct image **, u_int *,
|
||||
u_int *, u_int *, u_int *, int32_t *);
|
||||
void kitty_free_state(void *);
|
||||
void kitty_draw_rect(struct tty *,
|
||||
const struct image_rect *, const struct tty_style_ctx *);
|
||||
void kitty_redraw_start(struct tty *, u_int, u_int, u_int, u_int);
|
||||
void kitty_free_output_state(struct tty *, int);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* image-sixel.c */
|
||||
#define SIXEL_COLOUR_REGISTERS 1024
|
||||
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
|
||||
void sixel_draw_rect(struct tty *,
|
||||
const struct image_rect *, const struct tty_style_ctx *);
|
||||
void sixel_redraw_start(struct tty *, u_int, u_int, u_int, u_int);
|
||||
void sixel_free_output(struct tty *, int);
|
||||
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int,
|
||||
u_int);
|
||||
void sixel_free(struct sixel_image *);
|
||||
void sixel_log(struct sixel_image *);
|
||||
void sixel_size_in_cells(struct sixel_image *, u_int *, u_int *);
|
||||
@@ -4239,6 +4325,7 @@ struct sixel_image *sixel_scale(struct sixel_image *, u_int, u_int, u_int,
|
||||
char *sixel_print(struct sixel_image *, struct sixel_image *,
|
||||
size_t *);
|
||||
struct screen *sixel_to_screen(struct sixel_image *);
|
||||
struct image *sixel_to_image(struct sixel_image *);
|
||||
#endif
|
||||
|
||||
/* server-acl.c */
|
||||
|
||||
51
tools/generate-image-diacritics.sh
Executable file
51
tools/generate-image-diacritics.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Generate the Unicode diacritic table used by Kitty image placeholders.
|
||||
#
|
||||
# Input must be UnicodeData.txt from Unicode 6.0.0:
|
||||
# https://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt
|
||||
# SHA-256: 90b45b777346bef027556f9c6cb3ea5d7d745bd60d6762855c08d8a22d34f771
|
||||
#
|
||||
# The selection follows the public Kitty graphics protocol: nonspacing marks
|
||||
# with canonical combining class 230, NSM bidi class, no decomposition, minus
|
||||
# characters which may be combined or normalized in ways unsuitable for a
|
||||
# row or column index.
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "usage: $0 UnicodeData.txt" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
awk -F ';' '
|
||||
BEGIN {
|
||||
omit["0300"] = omit["0301"] = omit["0302"] = 1
|
||||
omit["0303"] = omit["0304"] = omit["0306"] = 1
|
||||
omit["0307"] = omit["0308"] = omit["0309"] = 1
|
||||
omit["030A"] = omit["030B"] = omit["030C"] = 1
|
||||
omit["030F"] = omit["0311"] = omit["0313"] = 1
|
||||
omit["0314"] = omit["0342"] = omit["0653"] = 1
|
||||
omit["0654"] = 1
|
||||
}
|
||||
$3 == "Mn" && $4 == "230" && $5 == "NSM" && $6 == "" && !omit[$1] {
|
||||
point[++count] = $1
|
||||
}
|
||||
END {
|
||||
if (count != 297) {
|
||||
printf "expected 297 diacritics, found %u\n", count > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
print "static const uint32_t kitty_diacritics[] = {"
|
||||
for (i = 1; i <= count; i++) {
|
||||
if ((i - 1) % 8 == 0)
|
||||
printf "\t"
|
||||
printf "0x%s", point[i]
|
||||
if (i != count)
|
||||
printf ","
|
||||
if (i == count || i % 8 == 0)
|
||||
printf "\n"
|
||||
else
|
||||
printf " "
|
||||
}
|
||||
print "};"
|
||||
}
|
||||
' "$1"
|
||||
104
tools/image-edge-test.sh
Normal file
104
tools/image-edge-test.sh
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Compact visual check for image subcell geometry and layering. Run inside a
|
||||
# tmux pane on Kitty or a SIXEL terminal; it needs at least 90 columns/24 rows.
|
||||
|
||||
if [ -z "$TMUX" ]; then
|
||||
echo "Run this script inside tmux." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
columns=$(tput cols 2>/dev/null || echo 0)
|
||||
lines=$(tput lines 2>/dev/null || echo 0)
|
||||
if [ "$columns" -lt 90 ] || [ "$lines" -lt 24 ]; then
|
||||
echo "This test needs at least 90 columns and 24 rows." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
at()
|
||||
{
|
||||
printf '\033[%s;%sH' "$1" "$2"
|
||||
}
|
||||
|
||||
kitty()
|
||||
{
|
||||
printf '\033_G%s\033\\' "$1"
|
||||
}
|
||||
|
||||
kitty_solid()
|
||||
{
|
||||
id=$1
|
||||
rgba=$2
|
||||
payload=$(i=0; while [ "$i" -lt 256 ]; do
|
||||
printf "$rgba"
|
||||
i=$((i + 1))
|
||||
done | base64 | tr -d '\n')
|
||||
kitty "a=t,q=2,f=32,s=16,v=16,i=$id;$payload"
|
||||
}
|
||||
|
||||
sixel_folder()
|
||||
{
|
||||
printf '\033P0;1q"1;1;26;26#0;2;50;50;50#1;2;0;0;0#2;2;49;49;49#3;2;33;33;33#4;2;47;47;47#5;2;44;44;44#6;2;48;48;48#2?O?C!5?S!4?__?_?__$#0?_wW!5KGwo_-#0?~~!8?!10@BB}w$#2!12?a_A?A?A?A??@E-#0?~~!7?KK~~KK!7?~~$#2!9?K!6?K-#0?F^[owowowowowowowowooS^F$#2?G!10?@@!7?G??G-\033\\'
|
||||
}
|
||||
|
||||
feature_info=$(tmux display-message -p \
|
||||
'term=#{client_termname} features=#{client_termfeatures} cells=#{client_cell_width}x#{client_cell_height}')
|
||||
|
||||
printf '\033[2J\033[H\033[?25l'
|
||||
at 1 2
|
||||
printf 'tmux image edge-case check'
|
||||
at 2 2
|
||||
printf '%s' "$feature_info"
|
||||
|
||||
kitty_solid 201 '\377\000\000\377'
|
||||
kitty_solid 202 '\000\377\000\377'
|
||||
kitty_solid 203 '\000\000\377\377'
|
||||
|
||||
# The 26-pixel folder occupies whole grid cells, but its visible right and
|
||||
# bottom edges must remain partial rather than stretching to those boundaries.
|
||||
at 4 2
|
||||
printf '1. partial SIXEL: folder should be the same size as direct output'
|
||||
at 4 72
|
||||
sixel_folder
|
||||
|
||||
# Chawan emits X/Y for its image padding. The red square must begin slightly
|
||||
# below and right of the [img] anchor, never over it.
|
||||
at 8 2
|
||||
printf '2. Kitty X/Y: RED begins below/right of the [img] anchor'
|
||||
at 8 72
|
||||
printf '[img]'
|
||||
at 9 72
|
||||
kitty 'a=p,q=2,C=1,i=201,p=1,x=0,y=0,w=16,h=16,c=3,r=2,X=6,Y=6'
|
||||
|
||||
# Positive Kitty z overlays text and other lower z images; negative z is
|
||||
# below text. The shared red/blue edge makes any wrong order obvious.
|
||||
at 12 2
|
||||
printf '3. Kitty z: BLUE covers RED; GREEN stays behind TEXT'
|
||||
at 12 72
|
||||
printf 'TEXT'
|
||||
at 12 72
|
||||
kitty 'a=p,q=2,C=1,i=202,p=2,x=0,y=0,w=16,h=16,c=4,r=2,z=-1'
|
||||
at 14 72
|
||||
kitty 'a=p,q=2,C=1,i=201,p=3,x=0,y=0,w=16,h=16,c=4,r=2,z=1'
|
||||
at 14 74
|
||||
kitty 'a=p,q=2,C=1,i=203,p=4,x=0,y=0,w=16,h=16,c=4,r=2,z=2'
|
||||
|
||||
# SIXEL is temporal: a later image covers text, while later text destroys the
|
||||
# affected image cells. Both results should match on Kitty and SIXEL output.
|
||||
at 18 2
|
||||
printf '4. SIXEL after text: folder covers FOLDER'
|
||||
at 18 72
|
||||
printf 'FOLDER'
|
||||
at 18 72
|
||||
sixel_folder
|
||||
at 21 2
|
||||
printf '5. SIXEL before text: TEXT WINS, no folder remnant'
|
||||
at 21 72
|
||||
sixel_folder
|
||||
at 21 72
|
||||
printf 'TEXT WINS'
|
||||
|
||||
at 23 2
|
||||
printf 'Take one screenshot, then press Enter to return to the shell.'
|
||||
printf '\033[?25h'
|
||||
IFS= read -r _image_edge_reply
|
||||
109
tools/image-visual-test.sh
Executable file
109
tools/image-visual-test.sh
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/bin/sh
|
||||
|
||||
# One-screen visual check for tmux image input and layering. Run this inside a
|
||||
# tmux pane attached to Kitty, WezTerm, Microsoft Terminal, or a basic terminal.
|
||||
|
||||
if [ -z "$TMUX" ]; then
|
||||
echo "Run this script inside tmux." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
columns=$(tput cols 2>/dev/null || echo 0)
|
||||
lines=$(tput lines 2>/dev/null || echo 0)
|
||||
if [ "$columns" -lt 72 ] || [ "$lines" -lt 23 ]; then
|
||||
echo "This test needs at least 72 columns and 23 rows." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
at()
|
||||
{
|
||||
printf '\033[%s;%sH' "$1" "$2"
|
||||
}
|
||||
|
||||
kitty()
|
||||
{
|
||||
printf '\033_G%s\033\\' "$1"
|
||||
}
|
||||
|
||||
# Transmit a 16x16 solid RGBA texture. A one-pixel texture is unsuitable here
|
||||
# because Kitty filters its transparent texture border into scaled edges.
|
||||
kitty_solid()
|
||||
{
|
||||
id=$1
|
||||
rgba=$2
|
||||
payload=$(i=0; while [ "$i" -lt 256 ]; do
|
||||
printf "$rgba"
|
||||
i=$((i + 1))
|
||||
done | base64 | tr -d '\n')
|
||||
kitty "a=t,q=2,f=32,s=16,v=16,i=$id;$payload"
|
||||
}
|
||||
|
||||
# A small 26x26 transparent SIXEL folder, used by the supplied bptato tests.
|
||||
sixel_folder()
|
||||
{
|
||||
printf '\033P0;1q"1;1;26;26#0;2;50;50;50#1;2;0;0;0#2;2;49;49;49#3;2;33;33;33#4;2;47;47;47#5;2;44;44;44#6;2;48;48;48#2?O?C!5?S!4?__?_?__$#0?_wW!5KGwo_-#0?~~!8?!10@BB}w$#2!12?a_A?A?A?A??@E-#0?~~!7?KK~~KK!7?~~$#2!9?K!6?K-#0?F^[owowowowowowowowooS^F$#2?G!10?@@!7?G??G-\033\\'
|
||||
}
|
||||
|
||||
feature_info=$(tmux display-message -p \
|
||||
'term=#{client_termname} features=#{client_termfeatures} cells=#{client_cell_width}x#{client_cell_height}')
|
||||
|
||||
printf '\033[2J\033[H\033[?25l'
|
||||
at 1 2
|
||||
printf 'tmux image visual check'
|
||||
at 2 2
|
||||
printf '%s' "$feature_info"
|
||||
|
||||
# Red is placed after its text at a positive Kitty z-index, so it must cover
|
||||
# the text. Green is placed below the text at a negative Kitty z-index.
|
||||
kitty_solid 101 '\377\000\000\377'
|
||||
kitty_solid 102 '\000\377\000\377'
|
||||
kitty_solid 103 '\000\000\377\377'
|
||||
kitty_solid 104 '\377\000\000\177'
|
||||
|
||||
at 4 2
|
||||
printf '1. positive Kitty z: RED must hide TEXT UNDER at right'
|
||||
at 4 62
|
||||
printf 'TEXT UNDER'
|
||||
at 4 60
|
||||
kitty 'a=p,q=2,C=1,i=101,p=1,x=1,y=1,w=14,h=14,c=18,r=2,z=1'
|
||||
|
||||
at 7 2
|
||||
printf '2. negative Kitty z: GREEN remains behind TEXT ON TOP'
|
||||
at 7 60
|
||||
kitty 'a=p,q=2,C=1,i=102,p=2,x=1,y=1,w=14,h=14,c=18,r=2,z=-1'
|
||||
at 7 62
|
||||
printf 'TEXT ON TOP'
|
||||
|
||||
at 10 2
|
||||
printf '3. Kitty z overlap: RED left, BLUE on top at right'
|
||||
at 11 55
|
||||
kitty 'a=p,q=2,C=1,i=101,p=3,x=1,y=1,w=14,h=14,c=18,r=3,z=1'
|
||||
at 11 64
|
||||
kitty 'a=p,q=2,C=1,i=103,p=4,x=1,y=1,w=14,h=14,c=9,r=3,z=2'
|
||||
|
||||
at 15 2
|
||||
printf '4. alpha: half-transparent RED, dithered on SIXEL'
|
||||
at 15 60
|
||||
kitty 'a=p,q=2,C=1,i=104,p=5,x=1,y=1,w=14,h=14,c=18,r=2,z=1'
|
||||
|
||||
# SIXEL semantics are temporal: later text destroys a SIXEL image, while a
|
||||
# later SIXEL image appears over existing text.
|
||||
at 18 2
|
||||
printf '5. SIXEL after text: folder covers FOLDER at right'
|
||||
at 18 65
|
||||
printf 'FOLDER'
|
||||
at 18 65
|
||||
sixel_folder
|
||||
|
||||
at 21 2
|
||||
printf '6. SIXEL before text: no folder remains behind TEXT WINS'
|
||||
at 21 65
|
||||
sixel_folder
|
||||
at 21 65
|
||||
printf 'TEXT WINS'
|
||||
|
||||
at 23 2
|
||||
printf 'Take one screenshot, then press Enter to return to the shell.'
|
||||
at 23 66
|
||||
printf '\033[?25h'
|
||||
IFS= read -r _image_visual_reply
|
||||
29
tty-draw.c
29
tty-draw.c
@@ -123,6 +123,10 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
struct grid *gd = s->grid;
|
||||
const struct grid_cell *gcp;
|
||||
struct grid_cell gc, ngc, last;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell image_gc;
|
||||
int image_status;
|
||||
#endif
|
||||
struct grid_line *gl;
|
||||
u_int i, j, last_i, cx, ex, width;
|
||||
u_int cellsize, bg;
|
||||
@@ -247,20 +251,35 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
|
||||
if (px >= ex || i >= ex - px) {
|
||||
/* Outside the area being drawn. */
|
||||
empty = nx - i;
|
||||
gcp = &grid_default_cell;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_status = image_get_fallback_at(tty, s, px + i,
|
||||
py, gcp, &image_gc, style_ctx);
|
||||
if (image_status == 1) {
|
||||
gcp = &image_gc;
|
||||
empty = 0;
|
||||
} else if (image_status == -1)
|
||||
empty = 1;
|
||||
else
|
||||
#endif
|
||||
empty = nx - i;
|
||||
} else {
|
||||
/* Get the current cell. */
|
||||
grid_view_get_cell(gd, px + i, py, &gc);
|
||||
gcp = &gc;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (image_get_fallback_at(tty, s, px + i, py, &gc,
|
||||
&image_gc, style_ctx) == 1)
|
||||
gcp = &image_gc;
|
||||
#endif
|
||||
|
||||
/* Work out empty cells. */
|
||||
empty = tty_draw_line_get_empty(&gc, &last,
|
||||
nx - i);
|
||||
empty = tty_draw_line_get_empty(gcp, &last, nx - i);
|
||||
if (empty != 0)
|
||||
gcp = &gc;
|
||||
;
|
||||
else {
|
||||
/* Update for codeset if needed. */
|
||||
gcp = tty_check_codeset(tty, &gc);
|
||||
gcp = tty_check_codeset(tty, gcp);
|
||||
|
||||
/* And for selection. */
|
||||
if (gcp->flags & GRID_FLAG_SELECTED) {
|
||||
|
||||
@@ -224,9 +224,11 @@ static const struct tty_feature tty_feature_strikethrough = {
|
||||
0
|
||||
};
|
||||
|
||||
#define TTY_FEATURE_SYNC "Sync=\\E[?2026%?%p1%{1}%-%tl%eh%;"
|
||||
|
||||
/* Terminal supports synchronized updates. */
|
||||
static const char *const tty_feature_sync_capabilities[] = {
|
||||
"Sync=\\E[?2026%?%p1%{1}%-%tl%eh%;",
|
||||
TTY_FEATURE_SYNC,
|
||||
NULL
|
||||
};
|
||||
static const struct tty_feature tty_feature_sync = {
|
||||
@@ -357,6 +359,18 @@ static const struct tty_feature tty_feature_sixel = {
|
||||
TERM_SIXEL
|
||||
};
|
||||
|
||||
/* Terminal has Kitty graphics protocol Unicode placeholder capability. */
|
||||
static const char *const tty_feature_kitty_capabilities[] = {
|
||||
"Kty",
|
||||
TTY_FEATURE_SYNC,
|
||||
NULL
|
||||
};
|
||||
static const struct tty_feature tty_feature_kitty = {
|
||||
"kitty",
|
||||
tty_feature_kitty_capabilities,
|
||||
TERM_KITTY
|
||||
};
|
||||
|
||||
/* Terminal supports the OSC 9;4 progress bar. */
|
||||
static const char *const tty_feature_progressbar_capabilities[] = {
|
||||
"Spb=\\E]9;4;%p1%d;%p2%d\\E\\\\",
|
||||
@@ -379,6 +393,7 @@ static const struct tty_feature *const tty_features[] = {
|
||||
&tty_feature_extkeys,
|
||||
&tty_feature_focus,
|
||||
&tty_feature_ignorefkeys,
|
||||
&tty_feature_kitty,
|
||||
&tty_feature_margins,
|
||||
&tty_feature_mouse,
|
||||
&tty_feature_osc7,
|
||||
@@ -609,6 +624,7 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
||||
"extkeys,"
|
||||
"focus,"
|
||||
"hyperlinks,"
|
||||
"sixel,"
|
||||
"usstyle"
|
||||
},
|
||||
{ .name = "ghostty",
|
||||
@@ -622,7 +638,20 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
||||
"osc7,"
|
||||
"sync,"
|
||||
"usstyle,"
|
||||
"progressbar"
|
||||
"progressbar,"
|
||||
"kitty"
|
||||
},
|
||||
{ .name = "kitty",
|
||||
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
|
||||
"ccolour,"
|
||||
"cstyle,"
|
||||
"extkeys,"
|
||||
"focus,"
|
||||
"hyperlinks,"
|
||||
"osc7,"
|
||||
"sync,"
|
||||
"usstyle,"
|
||||
"kitty"
|
||||
},
|
||||
{ .name = "Rio",
|
||||
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
|
||||
|
||||
@@ -728,6 +728,7 @@ tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size)
|
||||
char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0;
|
||||
tty_set_size(tty, tty->sx, tty->sy, char_x, char_y);
|
||||
tty_invalidate(tty);
|
||||
recalculate_sizes();
|
||||
|
||||
tty->flags &= ~TTY_WINSIZEQUERY;
|
||||
*size = end + 1;
|
||||
|
||||
@@ -524,6 +524,7 @@ tty_term_apply_overrides(struct tty_term *term)
|
||||
tty_term_validate(term);
|
||||
}
|
||||
|
||||
/* Mark terminals whose Ms capability does not contain a clipboard argument. */
|
||||
static void
|
||||
tty_term_validate(struct tty_term *term)
|
||||
{
|
||||
|
||||
287
tty.c
287
tty.c
@@ -62,17 +62,15 @@ static void tty_region(struct tty *, u_int, u_int);
|
||||
static void tty_margin_pane(struct tty *, const struct tty_ctx *);
|
||||
static void tty_margin(struct tty *, u_int, u_int);
|
||||
static int tty_large_region(struct tty *, const struct tty_ctx *);
|
||||
static int tty_scroll_images(struct tty *, const struct tty_ctx *, int *);
|
||||
static void tty_redraw_region(struct tty *, const struct tty_ctx *);
|
||||
static void tty_emulate_repeat(struct tty *, enum tty_code_code,
|
||||
enum tty_code_code, u_int);
|
||||
static void tty_draw_pane_line(struct tty *, const struct tty_ctx *, u_int,
|
||||
u_int, u_int, u_int, u_int);
|
||||
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
|
||||
static int tty_check_overlay(struct tty *, u_int, u_int);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
|
||||
struct client *, struct tty_ctx *);
|
||||
#endif
|
||||
|
||||
#define tty_use_margin(tty) \
|
||||
(tty->term->flags & TERM_DECSLRM)
|
||||
#define tty_full_width(tty, ctx) \
|
||||
@@ -163,10 +161,21 @@ tty_resize(struct tty *tty)
|
||||
void
|
||||
tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel)
|
||||
{
|
||||
#ifdef ENABLE_IMAGES
|
||||
int geometry_changed;
|
||||
|
||||
geometry_changed = (tty->xpixel != xpixel || tty->ypixel != ypixel);
|
||||
#endif
|
||||
tty->sx = sx;
|
||||
tty->sy = sy;
|
||||
tty->xpixel = xpixel;
|
||||
tty->ypixel = ypixel;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (geometry_changed) {
|
||||
image_tty_geometry_changed(tty);
|
||||
server_redraw_client(tty->client);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -284,6 +293,10 @@ tty_open(struct tty *tty, char **cause)
|
||||
}
|
||||
tty->flags |= TTY_OPENED;
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_tty_update(tty);
|
||||
#endif
|
||||
|
||||
tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER);
|
||||
|
||||
event_set(&tty->event_in, c->fd, EV_PERSIST|EV_READ,
|
||||
@@ -511,6 +524,9 @@ tty_close(struct tty *tty)
|
||||
tty_stop_tty(tty);
|
||||
|
||||
if (tty->flags & TTY_OPENED) {
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_tty_free(tty, 1);
|
||||
#endif
|
||||
evbuffer_free(tty->in);
|
||||
event_del(&tty->event_in);
|
||||
evbuffer_free(tty->out);
|
||||
@@ -527,6 +543,9 @@ void
|
||||
tty_free(struct tty *tty)
|
||||
{
|
||||
tty_close(tty);
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_tty_free(tty, 0);
|
||||
#endif
|
||||
|
||||
free(tty->r.ranges);
|
||||
}
|
||||
@@ -538,6 +557,9 @@ tty_update_features(struct tty *tty)
|
||||
|
||||
if (tty_apply_features(tty->term))
|
||||
tty_term_apply_overrides(tty->term);
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_tty_update(tty);
|
||||
#endif
|
||||
|
||||
if (tty_use_margin(tty))
|
||||
tty_putcode(tty, TTYC_ENMG);
|
||||
@@ -1071,6 +1093,26 @@ tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx)
|
||||
return (ctx->orlower - ctx->orupper >= ctx->sy / 2);
|
||||
}
|
||||
|
||||
/* Work out whether and how the terminal can scroll image pixels. */
|
||||
static int
|
||||
tty_scroll_images(struct tty *tty, const struct tty_ctx *ctx, int *full_width)
|
||||
{
|
||||
*full_width = 0;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (~ctx->flags & TTY_CTX_IMAGE_SCROLL)
|
||||
return (1);
|
||||
if (image_backend_flags(tty) & IMAGE_BACKEND_SCROLLS)
|
||||
return (1);
|
||||
if (ctx->flags & TTY_CTX_PANE_FULL_WIDTH) {
|
||||
*full_width = 1;
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
#else
|
||||
return (1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Return if BCE is needed but the terminal doesn't have it - it'll need to be
|
||||
* emulated.
|
||||
@@ -1404,11 +1446,34 @@ tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
|
||||
tty_clear_area(tty, ctx, y, ry, x, rx, bg);
|
||||
}
|
||||
|
||||
/* Redraw part of a pane line, including its ordered image layers. */
|
||||
static void
|
||||
tty_draw_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int px,
|
||||
u_int py, u_int nx, u_int atx, u_int aty)
|
||||
{
|
||||
#ifdef ENABLE_IMAGES
|
||||
int has_images;
|
||||
|
||||
has_images = image_grid_check_area(ctx->s->grid, px,
|
||||
ctx->s->grid->hsize + py, nx, 1);
|
||||
if (has_images) {
|
||||
image_redraw_start(tty, atx, aty, nx, 1);
|
||||
image_draw_line(tty, ctx->s, px, py, nx, atx, aty, 1,
|
||||
&ctx->style_ctx);
|
||||
}
|
||||
#endif
|
||||
tty_draw_line(tty, ctx->s, px, py, nx, atx, aty, &ctx->style_ctx);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (has_images)
|
||||
image_draw_line(tty, ctx->s, px, py, nx, atx, aty, 0,
|
||||
&ctx->style_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Redraw a line of a screen at py. */
|
||||
static void
|
||||
tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
u_int nx = ctx->sx, i, x, rx, ry, j;
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *rr;
|
||||
@@ -1421,8 +1486,8 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
|
||||
rr = &r->ranges[j];
|
||||
if (rr->nx == 0)
|
||||
continue;
|
||||
tty_draw_line(tty, s, rr->px - ctx->xoff, py, rr->nx,
|
||||
rr->px, ctx->yoff + py, &ctx->style_ctx);
|
||||
tty_draw_pane_line(tty, ctx, rr->px - ctx->xoff, py,
|
||||
rr->nx, rr->px, ctx->yoff + py);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1432,8 +1497,8 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
|
||||
rr = &r->ranges[j];
|
||||
if (rr->nx == 0)
|
||||
continue;
|
||||
tty_draw_line(tty, s, i + rr->px - x, py, rr->nx,
|
||||
rr->px, ry, &ctx->style_ctx);
|
||||
tty_draw_pane_line(tty, ctx, i + rr->px - x, py,
|
||||
rr->nx, rr->px, ry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1522,60 +1587,6 @@ tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
|
||||
return (c->overlay_check(c, c->overlay_data, px, py, nx));
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
/* Update context for client. */
|
||||
static int
|
||||
tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
||||
{
|
||||
struct window_pane *wp = ttyctx->arg;
|
||||
|
||||
if (c->session->curw->window != wp->window)
|
||||
return (0);
|
||||
if (wp->layout_cell == NULL)
|
||||
return (0);
|
||||
|
||||
if (tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx,
|
||||
&ttyctx->wsy))
|
||||
ttyctx->flags |= TTY_CTX_WINDOW_BIGGER;
|
||||
else
|
||||
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
|
||||
|
||||
ttyctx->yoff = ttyctx->ryoff = wp->yoff;
|
||||
if (status_at_line(c) == 0)
|
||||
ttyctx->yoff += status_line_size(c);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
tty_draw_images(struct client *c, struct window_pane *wp)
|
||||
{
|
||||
struct image *im;
|
||||
struct tty_ctx ttyctx;
|
||||
|
||||
TAILQ_FOREACH(im, &wp->screen->images, entry) {
|
||||
memset(&ttyctx, 0, sizeof ttyctx);
|
||||
|
||||
/* Set the client independent properties. */
|
||||
ttyctx.ocx = im->px;
|
||||
ttyctx.ocy = im->py;
|
||||
|
||||
ttyctx.orlower = wp->screen->rlower;
|
||||
ttyctx.orupper = wp->screen->rupper;
|
||||
|
||||
ttyctx.xoff = ttyctx.rxoff = wp->xoff;
|
||||
ttyctx.sx = wp->sx;
|
||||
ttyctx.sy = wp->sy;
|
||||
|
||||
ttyctx.image = im;
|
||||
ttyctx.arg = wp;
|
||||
ttyctx.set_client_cb = tty_set_client_cb;
|
||||
ttyctx.flags |= TTY_CTX_INVISIBLE_PANES;
|
||||
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
tty_sync_start(struct tty *tty)
|
||||
{
|
||||
@@ -1649,19 +1660,6 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
/* Only write to the incoming tty instead of every client. */
|
||||
static void
|
||||
tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
|
||||
struct client *c, struct tty_ctx *ctx)
|
||||
{
|
||||
if (ctx->set_client_cb == NULL)
|
||||
return;
|
||||
if ((ctx->set_client_cb(ctx, c)) == 1)
|
||||
cmdfn(&c->tty, ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
@@ -1709,9 +1707,19 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
|
||||
void
|
||||
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
#ifdef ENABLE_IMAGES
|
||||
u_int px, py;
|
||||
#endif
|
||||
|
||||
tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
|
||||
|
||||
tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->n, ctx->bg);
|
||||
#ifdef ENABLE_IMAGES
|
||||
px = ctx->xoff + ctx->ocx - ctx->wox;
|
||||
py = ctx->yoff + ctx->ocy - ctx->woy;
|
||||
image_draw_after_text(tty, ctx->s, ctx->ocx, ctx->ocy, ctx->n,
|
||||
px, py, &ctx->style_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1720,7 +1728,8 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
|
||||
struct client *c = tty->client;
|
||||
|
||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
!tty_full_width(tty, ctx) ||
|
||||
(!tty_full_width(tty, ctx) &&
|
||||
(~ctx->flags & TTY_CTX_PANE_FULL_WIDTH)) ||
|
||||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||
!tty_term_has(tty->term, TTYC_IL1) ||
|
||||
@@ -1747,7 +1756,8 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
|
||||
struct client *c = tty->client;
|
||||
|
||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
!tty_full_width(tty, ctx) ||
|
||||
(!tty_full_width(tty, ctx) &&
|
||||
(~ctx->flags & TTY_CTX_PANE_FULL_WIDTH)) ||
|
||||
tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
|
||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||
!tty_term_has(tty->term, TTYC_DL1) ||
|
||||
@@ -1798,12 +1808,15 @@ void
|
||||
tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
struct client *c = tty->client;
|
||||
int full_width;
|
||||
|
||||
if (ctx->ocy != ctx->orupper)
|
||||
return;
|
||||
|
||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
(!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
|
||||
if (!tty_scroll_images(tty, ctx, &full_width) ||
|
||||
(ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
(!full_width && !tty_full_width(tty, ctx) &&
|
||||
!tty_use_margin(tty)) ||
|
||||
tty_fake_bce(tty, &ctx->defaults, 8) ||
|
||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||
(!tty_term_has(tty->term, TTYC_RI) &&
|
||||
@@ -1818,7 +1831,10 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
|
||||
tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
|
||||
|
||||
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
|
||||
tty_margin_pane(tty, ctx);
|
||||
if (full_width)
|
||||
tty_margin_off(tty);
|
||||
else
|
||||
tty_margin_pane(tty, ctx);
|
||||
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
|
||||
|
||||
if (tty_term_has(tty->term, TTYC_RI))
|
||||
@@ -1831,12 +1847,15 @@ void
|
||||
tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
struct client *c = tty->client;
|
||||
int full_width;
|
||||
|
||||
if (ctx->ocy != ctx->orlower)
|
||||
return;
|
||||
|
||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
(!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
|
||||
if (!tty_scroll_images(tty, ctx, &full_width) ||
|
||||
(ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
(!full_width && !tty_full_width(tty, ctx) &&
|
||||
!tty_use_margin(tty)) ||
|
||||
tty_fake_bce(tty, &ctx->defaults, 8) ||
|
||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||
ctx->sx == 1 ||
|
||||
@@ -1849,7 +1868,10 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
|
||||
tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
|
||||
|
||||
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
|
||||
tty_margin_pane(tty, ctx);
|
||||
if (full_width)
|
||||
tty_margin_off(tty);
|
||||
else
|
||||
tty_margin_pane(tty, ctx);
|
||||
|
||||
/*
|
||||
* If we want to wrap a pane while using margins, the cursor needs to
|
||||
@@ -1874,9 +1896,12 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
struct client *c = tty->client;
|
||||
u_int i;
|
||||
int full_width;
|
||||
|
||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
(!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
|
||||
if (!tty_scroll_images(tty, ctx, &full_width) ||
|
||||
(ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
(!full_width && !tty_full_width(tty, ctx) &&
|
||||
!tty_use_margin(tty)) ||
|
||||
tty_fake_bce(tty, &ctx->defaults, 8) ||
|
||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||
ctx->sx == 1 ||
|
||||
@@ -1889,7 +1914,10 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
|
||||
tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
|
||||
|
||||
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
|
||||
tty_margin_pane(tty, ctx);
|
||||
if (full_width)
|
||||
tty_margin_off(tty);
|
||||
else
|
||||
tty_margin_pane(tty, ctx);
|
||||
|
||||
if (ctx->n == 1 || !tty_term_has(tty->term, TTYC_INDN)) {
|
||||
if (!tty_use_margin(tty))
|
||||
@@ -1912,9 +1940,12 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
u_int i;
|
||||
struct client *c = tty->client;
|
||||
int full_width;
|
||||
|
||||
if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
(!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
|
||||
if (!tty_scroll_images(tty, ctx, &full_width) ||
|
||||
(ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
|
||||
(!full_width && !tty_full_width(tty, ctx) &&
|
||||
!tty_use_margin(tty)) ||
|
||||
tty_fake_bce(tty, &ctx->defaults, 8) ||
|
||||
!tty_term_has(tty->term, TTYC_CSR) ||
|
||||
(!tty_term_has(tty->term, TTYC_RI) &&
|
||||
@@ -1929,7 +1960,10 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
|
||||
tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
|
||||
|
||||
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
|
||||
tty_margin_pane(tty, ctx);
|
||||
if (full_width)
|
||||
tty_margin_off(tty);
|
||||
else
|
||||
tty_margin_pane(tty, ctx);
|
||||
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
|
||||
|
||||
if (tty_term_has(tty->term, TTYC_RIN))
|
||||
@@ -2052,6 +2086,16 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
|
||||
if (vis < gcp->data.width) {
|
||||
tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
|
||||
px, py, &ctx->style_ctx);
|
||||
#ifdef ENABLE_IMAGES
|
||||
for (i = 0; i < r->used; i++) {
|
||||
if (r->ranges[i].nx == 0)
|
||||
continue;
|
||||
image_draw_after_text(tty, s,
|
||||
ctx->ocx + r->ranges[i].px - px, ctx->ocy,
|
||||
r->ranges[i].nx, r->ranges[i].px, py,
|
||||
&ctx->style_ctx);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2068,6 +2112,11 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
|
||||
|
||||
tty_cell(tty, ctx->cell, &ctx->style_ctx);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_draw_after_text(tty, s, ctx->ocx, ctx->ocy, gcp->data.width,
|
||||
px, py, &ctx->style_ctx);
|
||||
#endif
|
||||
|
||||
if (ctx->flags & TTY_CTX_CELL_INVALIDATE)
|
||||
tty_invalidate(tty);
|
||||
}
|
||||
@@ -2115,6 +2164,10 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
|
||||
cx = ri->px - ctx->xoff + ctx->wox;
|
||||
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
|
||||
tty_putn(tty, cp + ri->px - px, ri->nx, ri->nx);
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_draw_after_text(tty, ctx->s, cx, ctx->ocy,
|
||||
ri->nx, ri->px, py, &ctx->style_ctx);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2155,58 +2208,6 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
|
||||
tty_invalidate(tty);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
void
|
||||
tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
struct image *im = ctx->image;
|
||||
struct sixel_image *si = im->data;
|
||||
struct sixel_image *new;
|
||||
char *data;
|
||||
size_t size;
|
||||
u_int cx = ctx->ocx, cy = ctx->ocy, sx, sy;
|
||||
u_int i, j, x, y, rx, ry;
|
||||
int fallback = 0;
|
||||
|
||||
if ((~tty->term->flags & TERM_SIXEL) &&
|
||||
!tty_term_has(tty->term, TTYC_SXL))
|
||||
fallback = 1;
|
||||
if (tty->xpixel == 0 || tty->ypixel == 0)
|
||||
fallback = 1;
|
||||
|
||||
sixel_size_in_cells(si, &sx, &sy);
|
||||
log_debug("%s: image is %ux%u", __func__, sx, sy);
|
||||
if (!tty_clamp_area(tty, ctx, cx, cy, sx, sy, &i, &j, &x, &y, &rx, &ry))
|
||||
return;
|
||||
log_debug("%s: clamping to %u,%u-%u,%u", __func__, i, j, rx, ry);
|
||||
|
||||
if (fallback == 1) {
|
||||
data = xstrdup(im->fallback);
|
||||
size = strlen(data);
|
||||
} else {
|
||||
new = sixel_scale(si, tty->xpixel, tty->ypixel, i, j, rx, ry, 0);
|
||||
if (new == NULL)
|
||||
return;
|
||||
|
||||
data = sixel_print(new, si, &size);
|
||||
}
|
||||
if (data != NULL) {
|
||||
log_debug("%s: %zu bytes: %s", __func__, size, data);
|
||||
tty_region_off(tty);
|
||||
tty_margin_off(tty);
|
||||
tty_cursor(tty, x, y);
|
||||
|
||||
tty->flags |= TTY_NOBLOCK;
|
||||
tty_add(tty, data, size);
|
||||
tty_invalidate(tty);
|
||||
free(data);
|
||||
}
|
||||
|
||||
if (fallback == 0)
|
||||
sixel_free(new);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
|
||||
@@ -5363,6 +5363,14 @@ window_copy_write_line(struct window_mode_entry *wme,
|
||||
else
|
||||
content_sx = sx;
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Make the backing line's image layers available before drawing text. */
|
||||
image_grid_free_line(s->grid,
|
||||
&s->grid->linedata[s->grid->hsize + py]);
|
||||
image_grid_copy_area(s->grid, width, s->grid->hsize + py,
|
||||
data->backing->grid, 0, hsize - data->oy + py, content_sx, 1);
|
||||
#endif
|
||||
|
||||
screen_write_cursormove(ctx, 0, py, 0);
|
||||
|
||||
ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
|
||||
@@ -5406,7 +5414,6 @@ window_copy_write_line(struct window_mode_entry *wme,
|
||||
|
||||
window_copy_write_one(wme, ctx, width, py, hsize - data->oy + py,
|
||||
content_sx, &mgc, &cgc, &mkgc, &clgc);
|
||||
|
||||
if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
|
||||
value = options_get_string(oo, "copy-mode-position-format");
|
||||
if (*value != '\0') {
|
||||
|
||||
Reference in New Issue
Block a user