Render with winit/softbuffer
This commit is contained in:
parent
518796396d
commit
2fbec2db8f
3 changed files with 1627 additions and 64 deletions
1431
Cargo.lock
generated
1431
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -7,3 +7,10 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
alacritty_terminal = "0.19"
|
alacritty_terminal = "0.19"
|
||||||
|
softbuffer = "0.4"
|
||||||
|
tiny-skia = "0.11"
|
||||||
|
winit = "0.29"
|
||||||
|
|
||||||
|
[dependencies.cosmic-text]
|
||||||
|
git = "https://github.com/pop-os/cosmic-text.git"
|
||||||
|
branch = "refactor"
|
||||||
|
|
|
||||||
253
src/main.rs
253
src/main.rs
|
|
@ -1,13 +1,24 @@
|
||||||
use alacritty_terminal::{
|
use alacritty_terminal::{
|
||||||
config::{Config, PtyConfig},
|
config::{Config, PtyConfig},
|
||||||
event::{Event, EventListener, Notify, WindowSize},
|
event::{Event as TermEvent, EventListener, Notify, WindowSize},
|
||||||
event_loop::{EventLoop, Notifier},
|
event_loop::{EventLoop as PtyEventLoop, Notifier},
|
||||||
grid::Dimensions,
|
grid::Dimensions,
|
||||||
index::{Column, Line, Point},
|
index::{Column, Line, Point},
|
||||||
sync::FairMutex,
|
sync::FairMutex,
|
||||||
|
term::cell::Flags,
|
||||||
tty, Term,
|
tty, Term,
|
||||||
};
|
};
|
||||||
use std::sync::{mpsc, Arc};
|
use cosmic_text::{
|
||||||
|
Attrs, AttrsList, Buffer, BufferLine, Family, FontSystem, Metrics, Shaping, Style, SwashCache,
|
||||||
|
Weight,
|
||||||
|
};
|
||||||
|
use std::{num::NonZeroU32, rc::Rc, slice, sync::Arc};
|
||||||
|
use tiny_skia::{Color, Paint, PixmapMut, Rect, Transform};
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event as WinitEvent, KeyEvent, Modifiers, WindowEvent},
|
||||||
|
event_loop::{ControlFlow, EventLoopBuilder, EventLoopProxy},
|
||||||
|
window::WindowBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
struct TermSize {
|
struct TermSize {
|
||||||
rows: usize,
|
rows: usize,
|
||||||
|
|
@ -29,21 +40,35 @@ impl Dimensions for TermSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct EventProxy(mpsc::Sender<Event>);
|
struct EventProxy(EventLoopProxy<TermEvent>);
|
||||||
|
|
||||||
impl EventListener for EventProxy {
|
impl EventListener for EventProxy {
|
||||||
fn send_event(&self, event: Event) {
|
fn send_event(&self, event: TermEvent) {
|
||||||
let _ = self.0.send(event);
|
let _ = self.0.send_event(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let mut font_system = FontSystem::new();
|
||||||
|
let mut swash_cache = SwashCache::new();
|
||||||
|
let metrics = Metrics::new(14.0, 20.0);
|
||||||
|
let mut buffer = Buffer::new_empty(metrics);
|
||||||
|
let mut buffer = buffer.borrow_with(&mut font_system);
|
||||||
|
|
||||||
|
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 config = Config::default();
|
let config = Config::default();
|
||||||
let dimensions = TermSize { rows: 24, cols: 80 };
|
let dimensions = TermSize { rows: 24, cols: 80 };
|
||||||
let (event_tx, event_rx) = mpsc::channel();
|
let event_proxy = EventProxy(event_loop_proxy);
|
||||||
let event_proxy = EventProxy(event_tx);
|
let term = Arc::new(FairMutex::new(Term::new(
|
||||||
let term = Term::new(&config, &dimensions, event_proxy.clone());
|
&config,
|
||||||
let term = Arc::new(FairMutex::new(term));
|
&dimensions,
|
||||||
|
event_proxy.clone(),
|
||||||
|
)));
|
||||||
|
|
||||||
let pty_config = PtyConfig::default();
|
let pty_config = PtyConfig::default();
|
||||||
let window_size = WindowSize {
|
let window_size = WindowSize {
|
||||||
|
|
@ -55,43 +80,187 @@ fn main() {
|
||||||
let window_id = 0;
|
let window_id = 0;
|
||||||
let pty = tty::new(&pty_config, window_size, window_id).unwrap();
|
let pty = tty::new(&pty_config, window_size, window_id).unwrap();
|
||||||
|
|
||||||
let event_loop = EventLoop::new(term.clone(), event_proxy, pty, pty_config.hold, false);
|
let pty_event_loop = PtyEventLoop::new(term.clone(), event_proxy, pty, pty_config.hold, false);
|
||||||
let notifier = Notifier(event_loop.channel());
|
let notifier = Notifier(pty_event_loop.channel());
|
||||||
let join_handle = event_loop.spawn();
|
let pty_join_handle = pty_event_loop.spawn();
|
||||||
|
|
||||||
for i in 0..dimensions.rows {
|
for i in 0..dimensions.rows {
|
||||||
notifier.notify(format!("echo {}\r", i).into_bytes());
|
notifier.notify(format!("echo {}\r", i).into_bytes());
|
||||||
}
|
}
|
||||||
notifier.notify(&b"exit 0\r"[..]);
|
|
||||||
|
|
||||||
loop {
|
let context = softbuffer::Context::new(window.clone()).unwrap();
|
||||||
let event = match event_rx.recv() {
|
let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap();
|
||||||
Ok(ok) => ok,
|
let mut modifiers = Modifiers::default();
|
||||||
Err(err) => {
|
event_loop
|
||||||
eprintln!("failed to recv event: {}", err);
|
.run(move |event, elwt| {
|
||||||
break;
|
elwt.set_control_flow(ControlFlow::Wait);
|
||||||
|
|
||||||
|
match event {
|
||||||
|
WinitEvent::WindowEvent {
|
||||||
|
window_id,
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
} if window_id == window.id() => {
|
||||||
|
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();
|
||||||
|
pixmap.fill(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);
|
||||||
|
// Shape until scroll, ensures scroll is clamped
|
||||||
|
buffer.shape_until_scroll(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,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
buffer.set_redraw(false);
|
||||||
|
|
||||||
|
surface_buffer.present().unwrap();
|
||||||
|
}
|
||||||
|
WinitEvent::WindowEvent {
|
||||||
|
event:
|
||||||
|
WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
KeyEvent {
|
||||||
|
text: Some(text),
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => {
|
||||||
|
println!("{:?}", text);
|
||||||
|
notifier.notify(text.as_bytes().to_vec());
|
||||||
|
}
|
||||||
|
WinitEvent::WindowEvent {
|
||||||
|
event: WindowEvent::ModifiersChanged(new_modifiers),
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => {
|
||||||
|
modifiers = new_modifiers;
|
||||||
|
}
|
||||||
|
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::PtyWrite(text) => notifier.notify(text.into_bytes()),
|
||||||
|
TermEvent::Exit => elwt.exit(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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);
|
||||||
|
for indexed in term.lock().grid().display_iter() {
|
||||||
|
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?
|
||||||
|
|
||||||
|
let start = text.len();
|
||||||
|
text.push(indexed.cell.c);
|
||||||
|
let end = text.len();
|
||||||
|
|
||||||
|
let mut attrs = default_attrs;
|
||||||
|
println!("{:?}", indexed.cell.fg);
|
||||||
|
//TODO: fg and bg
|
||||||
|
//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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
println!("{:?}", event);
|
.unwrap();
|
||||||
match event {
|
|
||||||
Event::PtyWrite(text) => notifier.notify(text.into_bytes()),
|
|
||||||
Event::Exit => break,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut last_point = Point::new(Line(0), Column(0));
|
//TODO: hangs after event loop exit pty_join_handle.join().unwrap();
|
||||||
let mut text = String::new();
|
|
||||||
for indexed in term.lock().grid().display_iter() {
|
|
||||||
if indexed.point.line != last_point.line {
|
|
||||||
println!("{:2}: {:?}", last_point.line.0, text);
|
|
||||||
text.clear();
|
|
||||||
}
|
|
||||||
//TODO: use indexed.point.column?
|
|
||||||
text.push(indexed.cell.c);
|
|
||||||
last_point = indexed.point;
|
|
||||||
}
|
|
||||||
println!("{:2}: {:?}", last_point.line.0, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
join_handle.join().unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue