Implement colors and resizing
This commit is contained in:
parent
2fbec2db8f
commit
bc5c7f3a48
1 changed files with 223 additions and 46 deletions
269
src/main.rs
269
src/main.rs
|
|
@ -1,41 +1,60 @@
|
|||
use alacritty_terminal::{
|
||||
ansi::{Color, NamedColor},
|
||||
config::{Config, PtyConfig},
|
||||
event::{Event as TermEvent, EventListener, Notify, WindowSize},
|
||||
event::{Event as TermEvent, EventListener, Notify, OnResize, WindowSize},
|
||||
event_loop::{EventLoop as PtyEventLoop, Notifier},
|
||||
grid::Dimensions,
|
||||
index::{Column, Line, Point},
|
||||
sync::FairMutex,
|
||||
term::cell::Flags,
|
||||
term::{
|
||||
cell::Flags,
|
||||
color::{Colors, Rgb},
|
||||
},
|
||||
tty, Term,
|
||||
};
|
||||
use cosmic_text::{
|
||||
Attrs, AttrsList, Buffer, BufferLine, Family, FontSystem, Metrics, Shaping, Style, SwashCache,
|
||||
Weight,
|
||||
Weight, Wrap,
|
||||
};
|
||||
use std::{num::NonZeroU32, rc::Rc, slice, sync::Arc};
|
||||
use tiny_skia::{Color, Paint, PixmapMut, Rect, Transform};
|
||||
use tiny_skia::{Paint, PixmapMut, Rect, Transform};
|
||||
use winit::{
|
||||
event::{ElementState, Event as WinitEvent, KeyEvent, Modifiers, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoopBuilder, EventLoopProxy},
|
||||
keyboard::ModifiersState,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
struct TermSize {
|
||||
rows: usize,
|
||||
cols: usize,
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Size {
|
||||
width: f32,
|
||||
height: f32,
|
||||
cell_width: f32,
|
||||
cell_height: f32,
|
||||
}
|
||||
|
||||
impl Dimensions for TermSize {
|
||||
impl Dimensions for Size {
|
||||
fn total_lines(&self) -> usize {
|
||||
self.rows
|
||||
self.screen_lines()
|
||||
}
|
||||
|
||||
fn screen_lines(&self) -> usize {
|
||||
self.rows
|
||||
(self.height / self.cell_height).floor() as usize
|
||||
}
|
||||
|
||||
fn columns(&self) -> usize {
|
||||
self.cols
|
||||
(self.width / self.cell_width).floor() as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Size> for WindowSize {
|
||||
fn from(size: Size) -> Self {
|
||||
Self {
|
||||
num_lines: size.screen_lines() as u16,
|
||||
num_cols: size.columns() as u16,
|
||||
cell_width: size.cell_width as u16,
|
||||
cell_height: size.cell_height as u16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,46 +67,119 @@ impl EventListener for EventProxy {
|
|||
}
|
||||
}
|
||||
|
||||
fn colors() -> Colors {
|
||||
let mut colors = Colors::default();
|
||||
|
||||
// These colors come from `ransid`: https://gitlab.redox-os.org/redox-os/ransid/-/blob/master/src/color.rs
|
||||
let encode_rgb = |r: u8, g: u8, b: u8| -> Rgb { Rgb { r, g, b } };
|
||||
for value in 0..=255 {
|
||||
let color = match value {
|
||||
0 => encode_rgb(0x00, 0x00, 0x00),
|
||||
1 => encode_rgb(0x80, 0x00, 0x00),
|
||||
2 => encode_rgb(0x00, 0x80, 0x00),
|
||||
3 => encode_rgb(0x80, 0x80, 0x00),
|
||||
4 => encode_rgb(0x00, 0x00, 0x80),
|
||||
5 => encode_rgb(0x80, 0x00, 0x80),
|
||||
6 => encode_rgb(0x00, 0x80, 0x80),
|
||||
7 => encode_rgb(0xc0, 0xc0, 0xc0),
|
||||
8 => encode_rgb(0x80, 0x80, 0x80),
|
||||
9 => encode_rgb(0xff, 0x00, 0x00),
|
||||
10 => encode_rgb(0x00, 0xff, 0x00),
|
||||
11 => encode_rgb(0xff, 0xff, 0x00),
|
||||
12 => encode_rgb(0x00, 0x00, 0xff),
|
||||
13 => encode_rgb(0xff, 0x00, 0xff),
|
||||
14 => encode_rgb(0x00, 0xff, 0xff),
|
||||
15 => encode_rgb(0xff, 0xff, 0xff),
|
||||
16..=231 => {
|
||||
let convert = |value: u8| -> u8 {
|
||||
match value {
|
||||
0 => 0,
|
||||
_ => value * 0x28 + 0x28,
|
||||
}
|
||||
};
|
||||
|
||||
let r = convert((value - 16) / 36 % 6);
|
||||
let g = convert((value - 16) / 6 % 6);
|
||||
let b = convert((value - 16) % 6);
|
||||
encode_rgb(r, g, b)
|
||||
}
|
||||
232..=255 => {
|
||||
let gray = (value - 232) * 10 + 8;
|
||||
encode_rgb(gray, gray, gray)
|
||||
}
|
||||
};
|
||||
colors[value as usize] = Some(color);
|
||||
}
|
||||
|
||||
// Set special colors
|
||||
colors[NamedColor::Foreground] = colors[NamedColor::White];
|
||||
colors[NamedColor::Background] = colors[NamedColor::Black];
|
||||
/*TODO
|
||||
colors[NamedColor::Cursor] = colors[NamedColor::];
|
||||
colors[NamedColor::DimBlack] = colors[NamedColor::];
|
||||
colors[NamedColor::DimRed] = colors[NamedColor::];
|
||||
colors[NamedColor::DimGreen] = colors[NamedColor::];
|
||||
colors[NamedColor::DimYellow] = colors[NamedColor::];
|
||||
colors[NamedColor::DimBlue] = colors[NamedColor::];
|
||||
colors[NamedColor::DimMagenta] = colors[NamedColor::];
|
||||
colors[NamedColor::DimCyan] = colors[NamedColor::];
|
||||
colors[NamedColor::DimWhite] = colors[NamedColor::];
|
||||
*/
|
||||
colors[NamedColor::BrightForeground] = colors[NamedColor::BrightWhite];
|
||||
//TODO colors[NamedColor::DimForeground] = colors[NamedColor::];
|
||||
|
||||
colors
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut font_system = FontSystem::new();
|
||||
let mut swash_cache = SwashCache::new();
|
||||
let metrics = Metrics::new(14.0, 20.0);
|
||||
//TODO: set color to default fg
|
||||
let default_attrs = Attrs::new().family(Family::Monospace);
|
||||
let mut buffer = Buffer::new_empty(metrics);
|
||||
let mut buffer = buffer.borrow_with(&mut font_system);
|
||||
buffer.set_wrap(&mut font_system, Wrap::None);
|
||||
let (cell_width, cell_height) = {
|
||||
// Use size of space to determine cell size
|
||||
buffer.set_text(&mut font_system, " ", default_attrs, Shaping::Advanced);
|
||||
let layout = buffer.line_layout(&mut font_system, 0).unwrap();
|
||||
(layout[0].w, layout[0].max_ascent + layout[0].max_descent)
|
||||
};
|
||||
println!("{}, {}", cell_width, cell_height);
|
||||
|
||||
let event_loop = EventLoopBuilder::<TermEvent>::with_user_event()
|
||||
.build()
|
||||
.unwrap();
|
||||
let event_loop_proxy = event_loop.create_proxy();
|
||||
let window = Rc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
||||
let (width, height) = {
|
||||
let inner_size = window.inner_size();
|
||||
(inner_size.width as f32, inner_size.height as f32)
|
||||
};
|
||||
|
||||
let config = Config::default();
|
||||
let dimensions = TermSize { rows: 24, cols: 80 };
|
||||
let mut dimensions = Size {
|
||||
width,
|
||||
height,
|
||||
cell_width,
|
||||
cell_height,
|
||||
};
|
||||
let event_proxy = EventProxy(event_loop_proxy);
|
||||
let term = Arc::new(FairMutex::new(Term::new(
|
||||
&config,
|
||||
&dimensions,
|
||||
event_proxy.clone(),
|
||||
)));
|
||||
let colors = colors();
|
||||
|
||||
let pty_config = PtyConfig::default();
|
||||
let window_size = WindowSize {
|
||||
num_lines: dimensions.rows as u16,
|
||||
num_cols: dimensions.cols as u16,
|
||||
cell_width: 8, /*TODO*/
|
||||
cell_height: 16, /*TODO*/
|
||||
};
|
||||
let window_id = 0;
|
||||
let pty = tty::new(&pty_config, window_size, window_id).unwrap();
|
||||
let pty = tty::new(&pty_config, dimensions.into(), window_id).unwrap();
|
||||
|
||||
let pty_event_loop = PtyEventLoop::new(term.clone(), event_proxy, pty, pty_config.hold, false);
|
||||
let notifier = Notifier(pty_event_loop.channel());
|
||||
let mut notifier = Notifier(pty_event_loop.channel());
|
||||
let pty_join_handle = pty_event_loop.spawn();
|
||||
|
||||
for i in 0..dimensions.rows {
|
||||
notifier.notify(format!("echo {}\r", i).into_bytes());
|
||||
}
|
||||
|
||||
let context = softbuffer::Context::new(window.clone()).unwrap();
|
||||
let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap();
|
||||
let mut modifiers = Modifiers::default();
|
||||
|
|
@ -120,33 +212,65 @@ fn main() {
|
|||
};
|
||||
let mut pixmap =
|
||||
PixmapMut::from_bytes(surface_buffer_u8, width, height).unwrap();
|
||||
pixmap.fill(Color::from_rgba8(0, 0, 0, 0xFF));
|
||||
pixmap.fill(tiny_skia::Color::from_rgba8(0, 0, 0, 0xFF));
|
||||
|
||||
// Set scroll to view scroll
|
||||
//TODO buffer.set_scroll(*scroll);
|
||||
// Set size, will relayout and shape until scroll if changed
|
||||
buffer.set_size(width as f32, height as f32);
|
||||
buffer.set_size(&mut font_system, width as f32, height as f32);
|
||||
// Shape until scroll, ensures scroll is clamped
|
||||
buffer.shape_until_scroll(true);
|
||||
buffer.shape_until_scroll(&mut font_system, true);
|
||||
// Update scroll after buffer clamps it
|
||||
//TODO *scroll = buffer.scroll();
|
||||
|
||||
let mut paint = Paint::default();
|
||||
paint.anti_alias = false;
|
||||
let transform = Transform::identity();
|
||||
buffer.draw(
|
||||
&mut swash_cache,
|
||||
cosmic_text::Color::rgb(0xFF, 0xFF, 0xFF),
|
||||
|x, y, w, h, color| {
|
||||
paint.set_color_rgba8(color.r(), color.g(), color.b(), color.a());
|
||||
pixmap.fill_rect(
|
||||
Rect::from_xywh(x as f32, y as f32, w as f32, h as f32).unwrap(),
|
||||
&paint,
|
||||
transform,
|
||||
None,
|
||||
let mut f = |x: f32, y: f32, w: f32, h: f32, color: cosmic_text::Color| {
|
||||
// Have to swap RGB for BGR
|
||||
paint.set_color_rgba8(color.b(), color.g(), color.r(), color.a());
|
||||
pixmap.fill_rect(
|
||||
Rect::from_xywh(x, y, w, h).unwrap(),
|
||||
&paint,
|
||||
transform,
|
||||
None,
|
||||
);
|
||||
};
|
||||
for run in buffer.layout_runs() {
|
||||
for glyph in run.glyphs.iter() {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
|
||||
let glyph_color = match glyph.color_opt {
|
||||
Some(some) => some,
|
||||
None => cosmic_text::Color::rgb(0xFF, 0xFF, 0xFF),
|
||||
};
|
||||
|
||||
let background_color = cosmic_text::Color(glyph.metadata as u32);
|
||||
|
||||
f(
|
||||
glyph.x,
|
||||
run.line_top,
|
||||
glyph.w,
|
||||
metrics.line_height,
|
||||
background_color,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
swash_cache.with_pixels(
|
||||
&mut font_system,
|
||||
physical_glyph.cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(
|
||||
(physical_glyph.x + x) as f32,
|
||||
run.line_y + (physical_glyph.y + y) as f32,
|
||||
1.0,
|
||||
1.0,
|
||||
color,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
buffer.set_redraw(false);
|
||||
|
||||
surface_buffer.present().unwrap();
|
||||
|
|
@ -164,8 +288,32 @@ fn main() {
|
|||
},
|
||||
window_id,
|
||||
} if window_id == window.id() => {
|
||||
println!("{:?}", text);
|
||||
notifier.notify(text.as_bytes().to_vec());
|
||||
println!("{:?} {:?}", modifiers, text);
|
||||
match (
|
||||
modifiers.state().contains(ModifiersState::SUPER),
|
||||
modifiers.state().contains(ModifiersState::CONTROL),
|
||||
modifiers.state().contains(ModifiersState::ALT),
|
||||
) {
|
||||
(true, _, _) => {} // Ignore super
|
||||
(false, true, _) => {
|
||||
// Control keys
|
||||
if text.len() == 1 {
|
||||
let c = text.chars().next().unwrap_or_default();
|
||||
if c >= 'a' && c <= 'z' {
|
||||
notifier.notify(vec![(c as u8) - b'a' + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
(false, false, true) => {
|
||||
// Alt keys
|
||||
let mut bytes = text.as_bytes().to_vec();
|
||||
bytes.insert(0, b'\x1B');
|
||||
notifier.notify(bytes);
|
||||
}
|
||||
(false, false, false) => {
|
||||
notifier.notify(text.as_bytes().to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
WinitEvent::WindowEvent {
|
||||
event: WindowEvent::ModifiersChanged(new_modifiers),
|
||||
|
|
@ -173,6 +321,15 @@ fn main() {
|
|||
} if window_id == window.id() => {
|
||||
modifiers = new_modifiers;
|
||||
}
|
||||
WinitEvent::WindowEvent {
|
||||
event: WindowEvent::Resized(physical_size),
|
||||
window_id,
|
||||
} if window_id == window.id() => {
|
||||
dimensions.width = physical_size.width as f32;
|
||||
dimensions.height = physical_size.height as f32;
|
||||
notifier.on_resize(dimensions.into());
|
||||
term.lock().resize(dimensions);
|
||||
}
|
||||
WinitEvent::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
|
|
@ -190,8 +347,6 @@ fn main() {
|
|||
//TODO: is redraw needed after all events?
|
||||
//TODO: use LineDamageBounds
|
||||
{
|
||||
//TODO: set color to default fg
|
||||
let default_attrs = Attrs::new().family(Family::Monospace);
|
||||
let mut last_point = Point::new(Line(0), Column(0));
|
||||
let mut text = String::new();
|
||||
let mut attrs_list = AttrsList::new(default_attrs);
|
||||
|
|
@ -220,9 +375,31 @@ fn main() {
|
|||
text.push(indexed.cell.c);
|
||||
let end = text.len();
|
||||
|
||||
let convert_color = |color| {
|
||||
let rgb = match color {
|
||||
Color::Named(named_color) => match colors[named_color] {
|
||||
Some(rgb) => rgb,
|
||||
None => {
|
||||
eprintln!("missing named color {:?}", named_color);
|
||||
Rgb::default()
|
||||
}
|
||||
},
|
||||
Color::Spec(rgb) => rgb,
|
||||
Color::Indexed(index) => match colors[index as usize] {
|
||||
Some(rgb) => rgb,
|
||||
None => {
|
||||
eprintln!("missing indexed color {}", index);
|
||||
Rgb::default()
|
||||
}
|
||||
},
|
||||
};
|
||||
cosmic_text::Color::rgb(rgb.r, rgb.g, rgb.b)
|
||||
};
|
||||
|
||||
let mut attrs = default_attrs;
|
||||
println!("{:?}", indexed.cell.fg);
|
||||
//TODO: fg and bg
|
||||
attrs = attrs.color(convert_color(indexed.cell.fg));
|
||||
// Use metadata as background color
|
||||
attrs = attrs.metadata(convert_color(indexed.cell.bg).0 as usize);
|
||||
//TODO: more flags
|
||||
if indexed.cell.flags.contains(Flags::BOLD) {
|
||||
attrs = attrs.weight(Weight::BOLD);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue