Skip to content

Commit bd8d938

Browse files
committed
Fix TTY remapped default background fills
1 parent a561077 commit bd8d938

7 files changed

Lines changed: 152 additions & 5 deletions

File tree

neomacs-display-protocol/src/glyph_matrix.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,21 @@ pub struct BackgroundItem {
550550
pub color: Color,
551551
}
552552

553+
/// A rectangular fill painted with a realized face.
554+
///
555+
/// This represents redisplay-owned blank cells: areas such as the body text
556+
/// region of a window whose background comes from buffer-local face remapping.
557+
/// It is intentionally face-based instead of color-only so TTY backends can
558+
/// preserve terminal-default foreground/background semantics.
559+
#[derive(Clone, Debug)]
560+
pub struct FaceFillItem {
561+
pub window_id: DisplayWindowId,
562+
pub row_role: GlyphRowRole,
563+
pub clip_rect: Option<Rect>,
564+
pub bounds: Rect,
565+
pub face_id: u32,
566+
}
567+
553568
/// A window border/divider rectangle.
554569
#[derive(Clone, Debug)]
555570
pub struct BorderItem {
@@ -666,6 +681,8 @@ pub struct FrameDisplayState {
666681
pub transition_hints: Vec<WindowTransitionHint>,
667682
/// Window background rectangles.
668683
pub backgrounds: Vec<BackgroundItem>,
684+
/// Face-backed rectangular fills for redisplay-owned blank cells.
685+
pub face_fills: Vec<FaceFillItem>,
669686
/// Window border/divider rectangles.
670687
pub borders: Vec<BorderItem>,
671688
/// Cursor entries.
@@ -820,6 +837,7 @@ impl FrameDisplayState {
820837
window_infos: Vec::new(),
821838
transition_hints: Vec::new(),
822839
backgrounds: Vec::new(),
840+
face_fills: Vec::new(),
823841
borders: Vec::new(),
824842
cursors: Vec::new(),
825843
cursor_effects_by_window: HashMap::new(),
@@ -1145,6 +1163,30 @@ impl FrameDisplayState {
11451163
color: bg.color,
11461164
});
11471165
}
1166+
for fill in &self.face_fills {
1167+
let face_data = self.resolve_face_for_materialize(fill.face_id);
1168+
push(FrameGlyph::Stretch {
1169+
window_id: fill.window_id,
1170+
row_role: fill.row_role,
1171+
clip_rect: fill.clip_rect,
1172+
slot_id: DisplaySlotId::from_pixels(
1173+
fill.window_id,
1174+
fill.bounds.x,
1175+
fill.bounds.y,
1176+
self.char_width,
1177+
self.char_height,
1178+
),
1179+
bidi_level: 0,
1180+
x: fill.bounds.x,
1181+
y: fill.bounds.y,
1182+
width: fill.bounds.width,
1183+
height: fill.bounds.height,
1184+
bg: face_data.bg,
1185+
face_id: fill.face_id,
1186+
stipple_id: 0,
1187+
stipple_fg: None,
1188+
});
1189+
}
11481190

11491191
// --- Materialize grid content -> pixel-positioned Char/Stretch glyphs ---
11501192
for frame_row in &self.frame_chrome_rows {

neomacs-display-protocol/src/tty_rif.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ impl TtyRif {
361361
};
362362
}
363363

364+
for fill in &state.face_fills {
365+
self.rasterize_face_fill(origin_col, origin_row, state, fill);
366+
}
367+
364368
for frame_row in &state.frame_chrome_rows {
365369
let char_w = state.char_width.max(1.0);
366370
let win_col = origin_col + (frame_row.pixel_bounds.x / char_w) as usize;
@@ -687,6 +691,35 @@ impl TtyRif {
687691
std::mem::take(&mut self.output)
688692
}
689693

694+
fn rasterize_face_fill(
695+
&mut self,
696+
origin_col: usize,
697+
origin_row: usize,
698+
state: &FrameDisplayState,
699+
fill: &FaceFillItem,
700+
) {
701+
let char_w = state.char_width.max(1.0);
702+
let char_h = state.char_height.max(1.0);
703+
let start_col = origin_col + (fill.bounds.x / char_w).round().max(0.0) as usize;
704+
let start_row = origin_row + (fill.bounds.y / char_h).round().max(0.0) as usize;
705+
let width_cols = (fill.bounds.width / char_w).ceil().max(0.0) as usize;
706+
let height_rows = (fill.bounds.height / char_h).ceil().max(0.0) as usize;
707+
if width_cols == 0 || height_rows == 0 {
708+
return;
709+
}
710+
711+
let attrs = self.resolve_attrs(fill.face_id);
712+
let max_row = start_row
713+
.saturating_add(height_rows)
714+
.min(self.desired.height);
715+
let max_col = start_col.saturating_add(width_cols).min(self.desired.width);
716+
for row in start_row..max_row {
717+
for col in start_col..max_col {
718+
self.desired.set(row, col, ' ', attrs, false);
719+
}
720+
}
721+
}
722+
690723
fn rasterize_glyph_row(
691724
&mut self,
692725
screen_col_start: usize,

neomacs-display-protocol/src/tty_rif_test.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::*;
22
use crate::face::{Face, FaceAttributes, UnderlineStyle};
33
use crate::frame_glyphs::{CursorStyle, DisplaySlotId, GlyphRowRole, PhysCursor};
44
use crate::glyph_matrix::{
5-
FrameDisplayState, Glyph, GlyphArea, GlyphMatrix, GlyphRow, WindowMatrixEntry,
5+
FaceFillItem, FrameDisplayState, Glyph, GlyphArea, GlyphMatrix, GlyphRow, WindowMatrixEntry,
66
};
77
use crate::types::{Color, DisplayFrameId, DisplayWindowId, Rect};
88
use std::collections::HashMap;
@@ -304,6 +304,48 @@ fn rasterize_respects_matrix_position() {
304304
assert_eq!(rif.desired.cells[0].ch, ' ');
305305
}
306306

307+
#[test]
308+
fn rasterize_face_fill_paints_blank_cells_before_glyphs() {
309+
let mut state = FrameDisplayState::new(8, 2, 1.0, 1.0);
310+
state.background = Color::from_pixel(0x000000);
311+
let mut fill_face = Face::new(7);
312+
fill_face.background = Color::from_pixel(0x112233);
313+
let mut glyph_face = Face::new(8);
314+
glyph_face.background = Color::from_pixel(0x445566);
315+
state.faces.insert(7, fill_face);
316+
state.faces.insert(8, glyph_face);
317+
state.face_fills.push(FaceFillItem {
318+
window_id: DisplayWindowId::new(1),
319+
row_role: GlyphRowRole::Text,
320+
clip_rect: Some(Rect::new(0.0, 0.0, 8.0, 2.0)),
321+
bounds: Rect::new(0.0, 0.0, 8.0, 1.0),
322+
face_id: 7,
323+
});
324+
325+
let mut matrix = GlyphMatrix::new(1, 6);
326+
let mut row = GlyphRow::new(GlyphRowRole::Text);
327+
row.glyphs[GlyphArea::Text as usize].push(Glyph::char('X', 8, 0));
328+
matrix.rows[0] = row;
329+
state.window_matrices.push(WindowMatrixEntry {
330+
window_id: 1,
331+
matrix,
332+
pixel_bounds: Rect::new(0.0, 0.0, 8.0, 1.0),
333+
text_pixel_bounds: Rect::new(2.0, 0.0, 6.0, 1.0),
334+
selected: true,
335+
});
336+
337+
let mut rif = TtyRif::new(8, 2);
338+
rif.rasterize(&state);
339+
340+
assert_eq!(rif.desired.cells[0].ch, ' ');
341+
assert_eq!(rif.desired.cells[0].attrs.bg, Some((0x11, 0x22, 0x33)));
342+
assert_eq!(rif.desired.cells[1].attrs.bg, Some((0x11, 0x22, 0x33)));
343+
assert_eq!(rif.desired.cells[2].ch, 'X');
344+
assert_eq!(rif.desired.cells[2].attrs.bg, Some((0x44, 0x55, 0x66)));
345+
assert_eq!(rif.desired.cells[7].attrs.bg, Some((0x11, 0x22, 0x33)));
346+
assert_eq!(rif.desired.cells[8].attrs.bg, Some((0, 0, 0)));
347+
}
348+
307349
#[test]
308350
fn rasterize_uses_grid_rows_not_pixel_row_metrics() {
309351
let mut state = FrameDisplayState::new(12, 5, 1.0, 1.0);

neomacs-layout-engine/src/display_buffer_source_render_plan.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ use crate::font_metrics::FontMetricsService;
2727
use crate::neovm_bridge::{FaceResolver, LayoutBufferView, ResolvedFace, RustBufferAccess};
2828
use crate::types::WindowParams;
2929
use crate::window_output::render_window_chrome_rows;
30-
use neomacs_display_protocol::types::{Color, Rect};
30+
use neomacs_display_protocol::frame_glyphs::GlyphRowRole;
31+
use neomacs_display_protocol::glyph_matrix::FaceFillItem;
32+
use neomacs_display_protocol::types::{Color, DisplayWindowId, Rect};
3133
use neovm_core::buffer::BufferId;
3234
use neovm_core::window::{FrameId, WindowId};
3335

@@ -424,6 +426,22 @@ impl BufferSourceOutputSetup {
424426
}
425427

426428
let (mut output, evaluator) = output.into_parts();
429+
if !default_face.face().use_default_background && geometry.text_height > 0.0 {
430+
let face_id = face_ids.allocate();
431+
output.install_resolved_face(face_id, default_face.face(), None);
432+
output.builder().add_output_face_fill(FaceFillItem {
433+
window_id: DisplayWindowId::new(params.window_id),
434+
row_role: GlyphRowRole::Text,
435+
clip_rect: Some(params.bounds),
436+
bounds: Rect::new(
437+
params.bounds.x,
438+
geometry.text_y,
439+
params.bounds.width,
440+
geometry.text_height,
441+
),
442+
face_id,
443+
});
444+
}
427445
let mut render_services =
428446
ChromeRowRenderServices::new(font_metrics, face_resolver, &mut face_ids);
429447
let mut output_emitter = output_emitter;

neomacs-layout-engine/src/display_output_builder.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,10 @@ impl DisplayOutputBuilder {
365365
});
366366
}
367367

368+
pub(crate) fn add_output_face_fill(&mut self, item: FaceFillItem) {
369+
self.install_output_frame_artifact(OutputFrameArtifactInstallRequest::FaceFill(item));
370+
}
371+
368372
pub(crate) fn add_output_border(
369373
&mut self,
370374
window_id: i64,

neomacs-layout-engine/src/display_output_frame_state.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ use neomacs_display_protocol::frame_glyphs::{
1010
PhysCursor, WindowEffectHint, WindowInfo, WindowTransitionHint,
1111
};
1212
use neomacs_display_protocol::glyph_matrix::{
13-
BackgroundItem, BorderItem, CursorItem, FrameDisplayState, ImageItem, ScrollBarItem, VideoItem,
14-
XwidgetItem,
13+
BackgroundItem, BorderItem, CursorItem, FaceFillItem, FrameDisplayState, ImageItem,
14+
ScrollBarItem, VideoItem, XwidgetItem,
1515
};
1616
use neomacs_display_protocol::types::{Color, DisplayFrameId, DisplayWindowId};
1717
use std::collections::HashMap;
1818

1919
pub(crate) struct OutputFrameBuildState {
2020
backgrounds: Vec<BackgroundItem>,
21+
face_fills: Vec<FaceFillItem>,
2122
borders: Vec<BorderItem>,
2223
cursors: Vec<CursorItem>,
2324
images: Vec<ImageItem>,
@@ -48,6 +49,7 @@ impl OutputFrameBuildState {
4849
pub(crate) fn new() -> Self {
4950
Self {
5051
backgrounds: Vec::new(),
52+
face_fills: Vec::new(),
5153
borders: Vec::new(),
5254
cursors: Vec::new(),
5355
images: Vec::new(),
@@ -87,6 +89,7 @@ impl OutputFrameBuildState {
8789

8890
pub(crate) fn reset(&mut self) {
8991
self.backgrounds.clear();
92+
self.face_fills.clear();
9093
self.borders.clear();
9194
self.cursors.clear();
9295
self.images.clear();
@@ -146,6 +149,9 @@ impl OutputFrameBuildState {
146149
OutputFrameArtifactInstallRequest::Background { bounds, color } => {
147150
self.backgrounds.push(BackgroundItem { bounds, color });
148151
}
152+
OutputFrameArtifactInstallRequest::FaceFill(item) => {
153+
self.face_fills.push(item);
154+
}
149155
OutputFrameArtifactInstallRequest::Border {
150156
window_id,
151157
x,
@@ -284,6 +290,7 @@ impl OutputFrameBuildState {
284290

285291
pub(crate) fn install_into(self, state: &mut FrameDisplayState) {
286292
state.backgrounds = self.backgrounds;
293+
state.face_fills = self.face_fills;
287294
state.borders = self.borders;
288295
state.cursors = self.cursors;
289296
state.images = self.images;

neomacs-layout-engine/src/display_output_install_request.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use neomacs_display_protocol::frame_glyphs::{
66
CursorStyle, DisplaySlotId, GlyphRowRole, PhysCursor, WindowEffectHint, WindowInfo,
77
WindowTransitionHint,
88
};
9-
use neomacs_display_protocol::glyph_matrix::{CursorItem, ScrollBarItem};
9+
use neomacs_display_protocol::glyph_matrix::{CursorItem, FaceFillItem, ScrollBarItem};
1010
use neomacs_display_protocol::types::{
1111
Color, DisplayFrameId, DisplayWindowId, ImageId, Rect, VideoId, XwidgetId,
1212
};
@@ -197,6 +197,7 @@ pub(crate) enum OutputFrameArtifactInstallRequest {
197197
bounds: Rect,
198198
color: Color,
199199
},
200+
FaceFill(FaceFillItem),
200201
Border {
201202
window_id: DisplayWindowId,
202203
x: f32,

0 commit comments

Comments
 (0)