2023-12-17 11:53:26 -07:00
|
|
|
use alacritty_terminal::{
|
2023-12-17 22:51:50 -07:00
|
|
|
ansi::{Color, NamedColor},
|
2023-12-17 11:53:26 -07:00
|
|
|
config::{Config, PtyConfig},
|
2023-12-17 22:51:50 -07:00
|
|
|
event::{Event as TermEvent, EventListener, Notify, OnResize, WindowSize},
|
2023-12-17 17:49:39 -07:00
|
|
|
event_loop::{EventLoop as PtyEventLoop, Notifier},
|
2023-12-17 11:53:26 -07:00
|
|
|
grid::Dimensions,
|
|
|
|
|
index::{Column, Line, Point},
|
|
|
|
|
sync::FairMutex,
|
2023-12-17 22:51:50 -07:00
|
|
|
term::{
|
|
|
|
|
cell::Flags,
|
|
|
|
|
color::{Colors, Rgb},
|
|
|
|
|
},
|
2023-12-17 11:53:26 -07:00
|
|
|
tty, Term,
|
|
|
|
|
};
|
2023-12-17 17:49:39 -07:00
|
|
|
use cosmic_text::{
|
|
|
|
|
Attrs, AttrsList, Buffer, BufferLine, Family, FontSystem, Metrics, Shaping, Style, SwashCache,
|
2023-12-18 09:41:00 -07:00
|
|
|
SwashContent, Weight, Wrap,
|
2023-12-17 17:49:39 -07:00
|
|
|
};
|
2023-12-18 09:41:00 -07:00
|
|
|
use std::{mem, num::NonZeroU32, rc::Rc, slice, sync::Arc, time::Instant};
|
|
|
|
|
use tiny_skia::{ColorU8, Paint, PixmapMut, PixmapPaint, PixmapRef, Rect, Transform};
|
2023-12-17 17:49:39 -07:00
|
|
|
use winit::{
|
|
|
|
|
event::{ElementState, Event as WinitEvent, KeyEvent, Modifiers, WindowEvent},
|
|
|
|
|
event_loop::{ControlFlow, EventLoopBuilder, EventLoopProxy},
|
2023-12-18 09:54:23 -07:00
|
|
|
keyboard::{Key, ModifiersState, NamedKey},
|
2023-12-17 17:49:39 -07:00
|
|
|
window::WindowBuilder,
|
|
|
|
|
};
|
2023-12-17 11:53:26 -07:00
|
|
|
|
2023-12-17 22:51:50 -07:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
|
struct Size {
|
|
|
|
|
width: f32,
|
|
|
|
|
height: f32,
|
|
|
|
|
cell_width: f32,
|
|
|
|
|
cell_height: f32,
|
2023-12-17 11:53:26 -07:00
|
|
|
}
|
|
|
|
|
|
2023-12-17 22:51:50 -07:00
|
|
|
impl Dimensions for Size {
|
2023-12-17 11:53:26 -07:00
|
|
|
fn total_lines(&self) -> usize {
|
2023-12-17 22:51:50 -07:00
|
|
|
self.screen_lines()
|
2023-12-17 11:53:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn screen_lines(&self) -> usize {
|
2023-12-17 22:51:50 -07:00
|
|
|
(self.height / self.cell_height).floor() as usize
|
2023-12-17 11:53:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn columns(&self) -> usize {
|
2023-12-17 22:51:50 -07:00
|
|
|
(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,
|
|
|
|
|
}
|
2023-12-17 11:53:26 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2023-12-17 17:49:39 -07:00
|
|
|
struct EventProxy(EventLoopProxy<TermEvent>);
|
2023-12-17 11:53:26 -07:00
|
|
|
|
|
|
|
|
impl EventListener for EventProxy {
|
2023-12-17 17:49:39 -07:00
|
|
|
fn send_event(&self, event: TermEvent) {
|
|
|
|
|
let _ = self.0.send_event(event);
|
2023-12-17 11:53:26 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-17 22:51:50 -07:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-17 11:53:26 -07:00
|
|
|
fn main() {
|
2023-12-17 17:49:39 -07:00
|
|
|
let mut font_system = FontSystem::new();
|
|
|
|
|
let mut swash_cache = SwashCache::new();
|
|
|
|
|
let metrics = Metrics::new(14.0, 20.0);
|
2023-12-17 22:51:50 -07:00
|
|
|
//TODO: set color to default fg
|
|
|
|
|
let default_attrs = Attrs::new().family(Family::Monospace);
|
2023-12-17 17:49:39 -07:00
|
|
|
let mut buffer = Buffer::new_empty(metrics);
|
2023-12-17 22:51:50 -07:00
|
|
|
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();
|
2023-12-17 22:54:14 -07:00
|
|
|
(layout[0].w, metrics.line_height)
|
2023-12-17 22:51:50 -07:00
|
|
|
};
|
|
|
|
|
println!("{}, {}", cell_width, cell_height);
|
2023-12-17 17:49:39 -07:00
|
|
|
|
|
|
|
|
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());
|
2023-12-17 22:51:50 -07:00
|
|
|
let (width, height) = {
|
|
|
|
|
let inner_size = window.inner_size();
|
|
|
|
|
(inner_size.width as f32, inner_size.height as f32)
|
|
|
|
|
};
|
2023-12-17 17:49:39 -07:00
|
|
|
|
2023-12-17 11:53:26 -07:00
|
|
|
let config = Config::default();
|
2023-12-17 22:51:50 -07:00
|
|
|
let mut dimensions = Size {
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
cell_width,
|
|
|
|
|
cell_height,
|
|
|
|
|
};
|
2023-12-17 17:49:39 -07:00
|
|
|
let event_proxy = EventProxy(event_loop_proxy);
|
|
|
|
|
let term = Arc::new(FairMutex::new(Term::new(
|
|
|
|
|
&config,
|
|
|
|
|
&dimensions,
|
|
|
|
|
event_proxy.clone(),
|
|
|
|
|
)));
|
2023-12-17 22:51:50 -07:00
|
|
|
let colors = colors();
|
2023-12-17 11:53:26 -07:00
|
|
|
|
|
|
|
|
let pty_config = PtyConfig::default();
|
|
|
|
|
let window_id = 0;
|
2023-12-17 22:51:50 -07:00
|
|
|
let pty = tty::new(&pty_config, dimensions.into(), window_id).unwrap();
|
2023-12-17 11:53:26 -07:00
|
|
|
|
2023-12-17 17:49:39 -07:00
|
|
|
let pty_event_loop = PtyEventLoop::new(term.clone(), event_proxy, pty, pty_config.hold, false);
|
2023-12-17 22:51:50 -07:00
|
|
|
let mut notifier = Notifier(pty_event_loop.channel());
|
2023-12-17 17:49:39 -07:00
|
|
|
let pty_join_handle = pty_event_loop.spawn();
|
2023-12-17 11:53:26 -07:00
|
|
|
|
2023-12-17 17:49:39 -07:00
|
|
|
let context = softbuffer::Context::new(window.clone()).unwrap();
|
|
|
|
|
let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap();
|
|
|
|
|
let mut modifiers = Modifiers::default();
|
|
|
|
|
event_loop
|
|
|
|
|
.run(move |event, elwt| {
|
|
|
|
|
elwt.set_control_flow(ControlFlow::Wait);
|
|
|
|
|
|
|
|
|
|
match event {
|
|
|
|
|
WinitEvent::WindowEvent {
|
|
|
|
|
window_id,
|
|
|
|
|
event: WindowEvent::RedrawRequested,
|
|
|
|
|
} if window_id == window.id() => {
|
2023-12-18 09:41:00 -07:00
|
|
|
let instant = Instant::now();
|
|
|
|
|
|
2023-12-17 17:49:39 -07:00
|
|
|
let (width, height) = {
|
|
|
|
|
let size = window.inner_size();
|
|
|
|
|
(size.width, size.height)
|
|
|
|
|
};
|
|
|
|
|
surface
|
|
|
|
|
.resize(
|
|
|
|
|
NonZeroU32::new(width).unwrap(),
|
|
|
|
|
NonZeroU32::new(height).unwrap(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let mut surface_buffer = surface.buffer_mut().unwrap();
|
|
|
|
|
let surface_buffer_u8 = unsafe {
|
|
|
|
|
slice::from_raw_parts_mut(
|
|
|
|
|
surface_buffer.as_mut_ptr() as *mut u8,
|
|
|
|
|
surface_buffer.len() * 4,
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let mut pixmap =
|
|
|
|
|
PixmapMut::from_bytes(surface_buffer_u8, width, height).unwrap();
|
2023-12-17 22:51:50 -07:00
|
|
|
pixmap.fill(tiny_skia::Color::from_rgba8(0, 0, 0, 0xFF));
|
2023-12-17 17:49:39 -07:00
|
|
|
|
|
|
|
|
// Set scroll to view scroll
|
|
|
|
|
//TODO buffer.set_scroll(*scroll);
|
|
|
|
|
// Set size, will relayout and shape until scroll if changed
|
2023-12-17 22:51:50 -07:00
|
|
|
buffer.set_size(&mut font_system, width as f32, height as f32);
|
2023-12-17 17:49:39 -07:00
|
|
|
// Shape until scroll, ensures scroll is clamped
|
2023-12-17 22:51:50 -07:00
|
|
|
buffer.shape_until_scroll(&mut font_system, true);
|
2023-12-17 17:49:39 -07:00
|
|
|
// Update scroll after buffer clamps it
|
|
|
|
|
//TODO *scroll = buffer.scroll();
|
|
|
|
|
|
|
|
|
|
let mut paint = Paint::default();
|
|
|
|
|
paint.anti_alias = false;
|
2023-12-18 09:41:00 -07:00
|
|
|
let pixmap_paint = PixmapPaint::default();
|
2023-12-17 17:49:39 -07:00
|
|
|
let transform = Transform::identity();
|
2023-12-17 22:51:50 -07:00
|
|
|
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);
|
2023-12-18 09:41:00 -07:00
|
|
|
if background_color.0 != 0xFF000000 {
|
|
|
|
|
//TODO: Have to swap RGB for BGR
|
|
|
|
|
paint.set_color_rgba8(
|
|
|
|
|
background_color.b(),
|
|
|
|
|
background_color.g(),
|
|
|
|
|
background_color.r(),
|
|
|
|
|
background_color.a(),
|
|
|
|
|
);
|
|
|
|
|
pixmap.fill_rect(
|
|
|
|
|
Rect::from_xywh(
|
|
|
|
|
glyph.x,
|
|
|
|
|
run.line_top,
|
|
|
|
|
glyph.w,
|
|
|
|
|
metrics.line_height,
|
|
|
|
|
)
|
|
|
|
|
.unwrap(),
|
|
|
|
|
&paint,
|
|
|
|
|
transform,
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-12-17 22:51:50 -07:00
|
|
|
|
2023-12-18 09:41:00 -07:00
|
|
|
match swash_cache.get_image(&mut font_system, physical_glyph.cache_key)
|
|
|
|
|
{
|
|
|
|
|
Some(image) if !image.data.is_empty() => {
|
|
|
|
|
let mut data = Vec::with_capacity(
|
|
|
|
|
(image.placement.width * image.placement.height) as usize,
|
2023-12-17 22:51:50 -07:00
|
|
|
);
|
2023-12-18 09:41:00 -07:00
|
|
|
match image.content {
|
|
|
|
|
SwashContent::Mask => {
|
|
|
|
|
let mut i = 0;
|
|
|
|
|
while i < image.data.len() {
|
|
|
|
|
//TODO: Have to swap RGB for BGR
|
|
|
|
|
data.push(
|
|
|
|
|
ColorU8::from_rgba(
|
|
|
|
|
glyph_color.b(),
|
|
|
|
|
glyph_color.g(),
|
|
|
|
|
glyph_color.r(),
|
|
|
|
|
image.data[i],
|
|
|
|
|
)
|
|
|
|
|
.premultiply(),
|
|
|
|
|
);
|
|
|
|
|
i += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SwashContent::Color => {
|
|
|
|
|
let mut i = 0;
|
|
|
|
|
while i < image.data.len() {
|
|
|
|
|
//TODO: Have to swap RGB for BGR
|
|
|
|
|
data.push(
|
|
|
|
|
ColorU8::from_rgba(
|
|
|
|
|
image.data[i + 2],
|
|
|
|
|
image.data[i + 1],
|
|
|
|
|
image.data[i],
|
|
|
|
|
image.data[i + 3],
|
|
|
|
|
)
|
|
|
|
|
.premultiply(),
|
|
|
|
|
);
|
|
|
|
|
i += 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SwashContent::SubpixelMask => {
|
|
|
|
|
todo!("TODO: SubpixelMask");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let glyph_pixmap = PixmapRef::from_bytes(
|
|
|
|
|
unsafe {
|
|
|
|
|
slice::from_raw_parts(
|
|
|
|
|
data.as_ptr() as *const u8,
|
|
|
|
|
data.len() * 4,
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
image.placement.width,
|
|
|
|
|
image.placement.height,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
pixmap.draw_pixmap(
|
|
|
|
|
physical_glyph.x + image.placement.left,
|
|
|
|
|
run.line_y as i32 + physical_glyph.y - image.placement.top,
|
|
|
|
|
glyph_pixmap,
|
|
|
|
|
&pixmap_paint,
|
|
|
|
|
transform,
|
|
|
|
|
None,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
2023-12-17 22:51:50 -07:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-17 17:49:39 -07:00
|
|
|
buffer.set_redraw(false);
|
|
|
|
|
|
|
|
|
|
surface_buffer.present().unwrap();
|
2023-12-18 09:41:00 -07:00
|
|
|
|
|
|
|
|
println!("draw {:?}", instant.elapsed());
|
2023-12-17 17:49:39 -07:00
|
|
|
}
|
|
|
|
|
WinitEvent::WindowEvent {
|
|
|
|
|
event:
|
|
|
|
|
WindowEvent::KeyboardInput {
|
|
|
|
|
event:
|
|
|
|
|
KeyEvent {
|
2023-12-18 09:54:23 -07:00
|
|
|
logical_key,
|
|
|
|
|
text: text_opt,
|
2023-12-17 17:49:39 -07:00
|
|
|
state: ElementState::Pressed,
|
|
|
|
|
..
|
|
|
|
|
},
|
|
|
|
|
..
|
|
|
|
|
},
|
|
|
|
|
window_id,
|
|
|
|
|
} if window_id == window.id() => {
|
2023-12-18 09:54:23 -07:00
|
|
|
println!("{:?} {:?}", modifiers, text_opt);
|
2023-12-17 22:51:50 -07:00
|
|
|
match (
|
|
|
|
|
modifiers.state().contains(ModifiersState::SUPER),
|
|
|
|
|
modifiers.state().contains(ModifiersState::CONTROL),
|
|
|
|
|
modifiers.state().contains(ModifiersState::ALT),
|
|
|
|
|
) {
|
|
|
|
|
(true, _, _) => {} // Ignore super
|
2023-12-18 09:54:23 -07:00
|
|
|
(false, true, _) => match text_opt {
|
|
|
|
|
Some(text) if text.len() == 1 => {
|
|
|
|
|
// Control keys
|
2023-12-17 22:51:50 -07:00
|
|
|
let c = text.chars().next().unwrap_or_default();
|
|
|
|
|
if c >= 'a' && c <= 'z' {
|
|
|
|
|
notifier.notify(vec![(c as u8) - b'a' + 1]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-18 09:54:23 -07:00
|
|
|
_ => {}
|
|
|
|
|
},
|
|
|
|
|
(false, false, true) => match text_opt {
|
|
|
|
|
Some(text) => {
|
|
|
|
|
// Alt keys
|
|
|
|
|
//TODO: handle alt keys with no text
|
|
|
|
|
let mut bytes = text.as_bytes().to_vec();
|
|
|
|
|
bytes.insert(0, b'\x1B');
|
|
|
|
|
notifier.notify(bytes);
|
|
|
|
|
}
|
|
|
|
|
None => {}
|
|
|
|
|
},
|
|
|
|
|
(false, false, false) => match text_opt {
|
|
|
|
|
Some(text) => {
|
|
|
|
|
notifier.notify(text.as_bytes().to_vec());
|
|
|
|
|
}
|
|
|
|
|
None => match logical_key {
|
|
|
|
|
Key::Named(NamedKey::ArrowUp) => {
|
|
|
|
|
notifier.notify(b"\x1B[A".as_slice());
|
|
|
|
|
}
|
|
|
|
|
Key::Named(NamedKey::ArrowDown) => {
|
|
|
|
|
notifier.notify(b"\x1B[B".as_slice());
|
|
|
|
|
}
|
|
|
|
|
Key::Named(NamedKey::ArrowRight) => {
|
|
|
|
|
notifier.notify(b"\x1B[C".as_slice());
|
|
|
|
|
}
|
|
|
|
|
Key::Named(NamedKey::ArrowLeft) => {
|
|
|
|
|
notifier.notify(b"\x1B[D".as_slice());
|
|
|
|
|
}
|
|
|
|
|
Key::Named(NamedKey::End) => {
|
|
|
|
|
notifier.notify(b"\x1B[F".as_slice());
|
|
|
|
|
}
|
|
|
|
|
Key::Named(NamedKey::Home) => {
|
|
|
|
|
notifier.notify(b"\x1B[H".as_slice());
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
},
|
|
|
|
|
},
|
2023-12-17 22:51:50 -07:00
|
|
|
}
|
2023-12-17 17:49:39 -07:00
|
|
|
}
|
|
|
|
|
WinitEvent::WindowEvent {
|
|
|
|
|
event: WindowEvent::ModifiersChanged(new_modifiers),
|
|
|
|
|
window_id,
|
|
|
|
|
} if window_id == window.id() => {
|
|
|
|
|
modifiers = new_modifiers;
|
|
|
|
|
}
|
2023-12-17 22:51:50 -07:00
|
|
|
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);
|
2023-12-18 09:41:00 -07:00
|
|
|
window.request_redraw();
|
2023-12-17 22:51:50 -07:00
|
|
|
}
|
2023-12-17 17:49:39 -07:00
|
|
|
WinitEvent::WindowEvent {
|
|
|
|
|
event: WindowEvent::CloseRequested,
|
|
|
|
|
window_id,
|
|
|
|
|
} if window_id == window.id() => {
|
|
|
|
|
term.lock().exit();
|
|
|
|
|
}
|
|
|
|
|
WinitEvent::UserEvent(user_event) => {
|
|
|
|
|
println!("{:?}", user_event);
|
|
|
|
|
match user_event {
|
|
|
|
|
TermEvent::Exit => elwt.exit(),
|
2023-12-18 11:53:56 -07:00
|
|
|
TermEvent::PtyWrite(text) => notifier.notify(text.into_bytes()),
|
|
|
|
|
TermEvent::Title(title) => {
|
|
|
|
|
window.set_title(&title);
|
|
|
|
|
}
|
2023-12-17 17:49:39 -07:00
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-18 09:41:00 -07:00
|
|
|
let instant = Instant::now();
|
|
|
|
|
|
2023-12-17 17:49:39 -07:00
|
|
|
//TODO: is redraw needed after all events?
|
|
|
|
|
//TODO: use LineDamageBounds
|
|
|
|
|
{
|
|
|
|
|
let mut last_point = Point::new(Line(0), Column(0));
|
|
|
|
|
let mut text = String::new();
|
|
|
|
|
let mut attrs_list = AttrsList::new(default_attrs);
|
2023-12-18 09:41:00 -07:00
|
|
|
let term_guard = term.lock();
|
|
|
|
|
let grid = term_guard.grid();
|
|
|
|
|
for indexed in grid.display_iter() {
|
2023-12-17 17:49:39 -07:00
|
|
|
if indexed.point.line != last_point.line {
|
|
|
|
|
let line_i = last_point.line.0 as usize;
|
|
|
|
|
while line_i >= buffer.lines.len() {
|
|
|
|
|
buffer.lines.push(BufferLine::new(
|
|
|
|
|
"",
|
|
|
|
|
AttrsList::new(default_attrs),
|
|
|
|
|
Shaping::Advanced,
|
|
|
|
|
));
|
|
|
|
|
buffer.set_redraw(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if buffer.lines[line_i].set_text(text.clone(), attrs_list.clone()) {
|
|
|
|
|
buffer.set_redraw(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
text.clear();
|
|
|
|
|
attrs_list.clear_spans();
|
|
|
|
|
}
|
|
|
|
|
//TODO: use indexed.point.column?
|
|
|
|
|
|
2023-12-18 11:53:56 -07:00
|
|
|
//TODO: skip leading spacer?
|
|
|
|
|
if indexed.cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
|
|
|
|
|
// Skip wide spacers (cells after wide characters)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-17 17:49:39 -07:00
|
|
|
let start = text.len();
|
|
|
|
|
text.push(indexed.cell.c);
|
2023-12-18 11:53:56 -07:00
|
|
|
if let Some(zerowidth) = indexed.cell.zerowidth() {
|
|
|
|
|
for &c in zerowidth {
|
|
|
|
|
text.push(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-17 17:49:39 -07:00
|
|
|
let end = text.len();
|
|
|
|
|
|
2023-12-17 22:51:50 -07:00
|
|
|
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)
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-17 17:49:39 -07:00
|
|
|
let mut attrs = default_attrs;
|
2023-12-18 09:41:00 -07:00
|
|
|
let mut fg = convert_color(indexed.cell.fg);
|
|
|
|
|
let mut bg = convert_color(indexed.cell.bg);
|
|
|
|
|
//TODO: better handling of cursor
|
|
|
|
|
if indexed.point == grid.cursor.point {
|
|
|
|
|
mem::swap(&mut fg, &mut bg);
|
|
|
|
|
}
|
|
|
|
|
attrs = attrs.color(fg);
|
2023-12-17 22:51:50 -07:00
|
|
|
// Use metadata as background color
|
2023-12-18 09:41:00 -07:00
|
|
|
attrs = attrs.metadata(bg.0 as usize);
|
2023-12-17 17:49:39 -07:00
|
|
|
//TODO: more flags
|
|
|
|
|
if indexed.cell.flags.contains(Flags::BOLD) {
|
|
|
|
|
attrs = attrs.weight(Weight::BOLD);
|
|
|
|
|
}
|
|
|
|
|
if indexed.cell.flags.contains(Flags::ITALIC) {
|
|
|
|
|
attrs = attrs.style(Style::Italic);
|
|
|
|
|
}
|
|
|
|
|
if attrs != attrs_list.defaults() {
|
|
|
|
|
attrs_list.add_span(start..end, attrs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_point = indexed.point;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//TODO: do not repeat!
|
|
|
|
|
let line_i = last_point.line.0 as usize;
|
|
|
|
|
while line_i >= buffer.lines.len() {
|
|
|
|
|
buffer.lines.push(BufferLine::new(
|
|
|
|
|
"",
|
|
|
|
|
AttrsList::new(default_attrs),
|
|
|
|
|
Shaping::Advanced,
|
|
|
|
|
));
|
|
|
|
|
buffer.set_redraw(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if buffer.lines[line_i].set_text(text, attrs_list) {
|
|
|
|
|
buffer.set_redraw(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if buffer.redraw() {
|
|
|
|
|
window.request_redraw();
|
|
|
|
|
}
|
2023-12-18 09:41:00 -07:00
|
|
|
|
|
|
|
|
println!("buffer update {:?}", instant.elapsed());
|
2023-12-17 17:49:39 -07:00
|
|
|
}
|
|
|
|
|
_ => {}
|
2023-12-17 11:53:26 -07:00
|
|
|
}
|
2023-12-17 17:49:39 -07:00
|
|
|
})
|
|
|
|
|
.unwrap();
|
2023-12-17 11:53:26 -07:00
|
|
|
|
2023-12-17 17:49:39 -07:00
|
|
|
//TODO: hangs after event loop exit pty_join_handle.join().unwrap();
|
2023-12-17 11:53:26 -07:00
|
|
|
}
|