Skip to content

Commit 330ed70

Browse files
raphamorimfloens
andauthored
fix: apply kitty X=/Y= sub-cell pixel offset to placements (#1646)
* fix: apply kitty X=/Y= sub-cell pixel offset to placements The kitty graphics protocol lets a placement specify X= and Y= pixel offsets to "display from a different origin within the cell" (see "Controlling displayed image layout" in the spec [1]). The wiring for this was half-finished: KittyPlacement already had cell_x_offset/ cell_y_offset fields and the parser already populated them from X=/Y=, but nothing connected the two. Finish the wiring: thread the offset parser -> PlacementRequest -> KittyPlacement -> render position. Adds parser tests for the offset and the default. [1] https://sw.kovidgoyal.net/kitty/graphics-protocol/ * small updates --------- Co-authored-by: Floens <floens@gmail.com>
1 parent 9a2440b commit 330ed70

4 files changed

Lines changed: 248 additions & 7 deletions

File tree

frontends/rioterm/src/renderer/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,13 @@ impl Renderer {
438438
}
439439
overlays.push(rio_backend::sugarloaf::GraphicOverlay {
440440
image_id: p.image_id,
441-
x: origin_x + p.dest_col as f32 * cell_width,
442-
y: origin_y + screen_row as f32 * cell_height,
441+
// kitty X=/Y= offset, supports sub-cell positioning
442+
x: origin_x
443+
+ p.dest_col as f32 * cell_width
444+
+ p.cell_x_offset as f32,
445+
y: origin_y
446+
+ screen_row as f32 * cell_height
447+
+ p.cell_y_offset as f32,
443448
width: p.pixel_width as f32,
444449
height: p.pixel_height as f32,
445450
z_index: p.z_index,

rio-backend/src/ansi/kitty_graphics_protocol.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ pub struct PlacementRequest {
111111
/// the uppercase `U=1` flag). Currently informational only.
112112
pub unicode_placeholder: u32,
113113
pub cursor_movement: u8, // 0 = move cursor to after image (default), 1 = don't move cursor
114+
/// kitty `X=`/`Y=` pixel offset, supports sub-cell positioning.
115+
pub cell_x_offset: u32,
116+
pub cell_y_offset: u32,
114117
}
115118

116119
#[derive(Debug)]
@@ -631,6 +634,8 @@ pub fn parse(
631634
virtual_placement: cmd.virtual_placement,
632635
unicode_placeholder: cmd.unicode_placeholder,
633636
cursor_movement: cmd.cursor_movement,
637+
cell_x_offset: cmd.cell_x_offset,
638+
cell_y_offset: cmd.cell_y_offset,
634639
})
635640
} else {
636641
None
@@ -659,6 +664,8 @@ pub fn parse(
659664
virtual_placement: cmd.virtual_placement,
660665
unicode_placeholder: cmd.unicode_placeholder,
661666
cursor_movement: cmd.cursor_movement,
667+
cell_x_offset: cmd.cell_x_offset,
668+
cell_y_offset: cmd.cell_y_offset,
662669
};
663670
let response = if cmd.implicit_id {
664671
None
@@ -1553,6 +1560,33 @@ mod tests {
15531560
assert_eq!(placement.z_index, 2);
15541561
}
15551562

1563+
#[test]
1564+
fn test_parse_placement_subcell_offset() {
1565+
// `X=`/`Y=` are the sub-cell pixel offset within the placement's
1566+
// first cell. They must flow through to the PlacementRequest so the
1567+
// renderer can position the image at pixel (not just cell)
1568+
// granularity, for smooth sub-cell scrolling.
1569+
let result = parse_kitty_graphics_protocol("a=p,i=1,X=7,Y=9", "");
1570+
let placement = result
1571+
.expect("parse ok")
1572+
.placement_request
1573+
.expect("placement");
1574+
assert_eq!(placement.cell_x_offset, 7);
1575+
assert_eq!(placement.cell_y_offset, 9);
1576+
}
1577+
1578+
#[test]
1579+
fn test_parse_placement_subcell_offset_defaults_zero() {
1580+
// Omitting `X=`/`Y=` leaves the offset at the cell boundary.
1581+
let result = parse_kitty_graphics_protocol("a=p,i=1", "");
1582+
let placement = result
1583+
.expect("parse ok")
1584+
.placement_request
1585+
.expect("placement");
1586+
assert_eq!(placement.cell_x_offset, 0);
1587+
assert_eq!(placement.cell_y_offset, 0);
1588+
}
1589+
15561590
#[test]
15571591
fn test_parse_delete() {
15581592
let result = parse_kitty_graphics_protocol("a=d,d=i,i=1", "");

rio-backend/src/crosswords/mod.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4416,16 +4416,27 @@ impl<U: EventListener> Crosswords<U> {
44164416
// Absolute row = history_size + screen-relative row
44174417
let dest_row = self.history_size() as i64 + cursor_row as i64;
44184418

4419-
// Compute cell-based size
4419+
// kitty spec the `X=`/`Y=` sub-cell offset must be smaller
4420+
// than the cell size; kitty clamps out-of-range values to the
4421+
// cell box
4422+
let cell_x_offset = (placement.cell_x_offset as usize).min(cell_width - 1);
4423+
let cell_y_offset = (placement.cell_y_offset as usize).min(cell_height - 1);
4424+
4425+
// Compute cell-based size.
4426+
//
4427+
// the trick is the sub-cell offset shifts the image
4428+
// within its first cell, so it can spill into one extra
4429+
// row/column. Include it so cursor movement and row occupation
4430+
// cover the full image.
44204431
let columns = if placement.columns > 0 {
44214432
placement.columns
44224433
} else {
4423-
display_w.div_ceil(cell_width) as u32
4434+
(display_w + cell_x_offset).div_ceil(cell_width) as u32
44244435
};
44254436
let rows = if placement.rows > 0 {
44264437
placement.rows
44274438
} else {
4428-
display_h.div_ceil(cell_height) as u32
4439+
(display_h + cell_y_offset).div_ceil(cell_height) as u32
44294440
};
44304441

44314442
// Create overlay placement.
@@ -4454,8 +4465,8 @@ impl<U: EventListener> Crosswords<U> {
44544465
rows,
44554466
pixel_width: display_w as u32,
44564467
pixel_height: display_h as u32,
4457-
cell_x_offset: 0,
4458-
cell_y_offset: 0,
4468+
cell_x_offset: cell_x_offset as u32,
4469+
cell_y_offset: cell_y_offset as u32,
44594470
z_index: placement.z_index,
44604471
transmit_time,
44614472
};
@@ -6640,6 +6651,8 @@ mod tests {
66406651
virtual_placement: true,
66416652
unicode_placeholder: 0,
66426653
cursor_movement: 0,
6654+
cell_x_offset: 0,
6655+
cell_y_offset: 0,
66436656
};
66446657

66456658
cw.place_graphic(placement);

0 commit comments

Comments
 (0)