stevenkhan commited on
Commit
4729538
·
verified ·
1 Parent(s): 671f03a

Upload src/main.rs

Browse files
Files changed (1) hide show
  1. src/main.rs +182 -0
src/main.rs ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use std::sync::Arc;
2
+ use std::time::Instant;
3
+
4
+ use log::info;
5
+ use winit::application::ApplicationHandler;
6
+ use winit::event::WindowEvent;
7
+ use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
8
+ use winit::window::{Window, WindowId};
9
+
10
+ use spectral_core::{Config, Terminal};
11
+ use spectral_font::{Font, FontFamily, FontStyle, GlyphKey};
12
+ use spectral_render::Renderer;
13
+
14
+ struct SpectralApp {
15
+ window: Option<Arc<Window>>,
16
+ renderer: Option<Renderer>,
17
+ terminal: Terminal,
18
+ config: Config,
19
+ font_family: Option<FontFamily>,
20
+ last_render: Instant,
21
+ }
22
+
23
+ impl SpectralApp {
24
+ fn new(config: Config) -> Self {
25
+ let terminal = Terminal::new(config.clone());
26
+ Self {
27
+ window: None,
28
+ renderer: None,
29
+ terminal,
30
+ config,
31
+ font_family: None,
32
+ last_render: Instant::now(),
33
+ }
34
+ }
35
+
36
+ fn init_window(&mut self, event_loop: &ActiveEventLoop) {
37
+ let window_attrs = Window::default_attributes()
38
+ .with_title("Spectral Terminal")
39
+ .with_inner_size(winit::dpi::LogicalSize::new(800, 600));
40
+ let window = Arc::new(
41
+ event_loop.create_window(window_attrs).expect("Failed to create window"),
42
+ );
43
+ let rt = tokio::runtime::Runtime::new().unwrap();
44
+ let renderer = rt.block_on(Renderer::new(
45
+ window.clone(),
46
+ self.terminal.grid.cols as u32,
47
+ self.terminal.grid.rows as u32,
48
+ 2048,
49
+ ));
50
+ self.window = Some(window);
51
+ self.renderer = Some(renderer);
52
+ self.load_fonts();
53
+ }
54
+
55
+ fn load_fonts(&mut self) {
56
+ let font_names = [
57
+ &*self.config.font_family,
58
+ "Iosevka Extended", "Iosevka", "JetBrains Mono", "Fira Code", "monospace",
59
+ ];
60
+ for &name in &font_names {
61
+ if let Some(font_data) = try_load_font(name) {
62
+ let mut family = FontFamily::new(name);
63
+ if let Some(font) = Font::from_bytes(0, font_data, 0, FontStyle::Regular) {
64
+ family.add_font(font);
65
+ self.font_family = Some(family);
66
+ info!("Loaded font: {}", name);
67
+ break;
68
+ }
69
+ }
70
+ }
71
+ if let Some(renderer) = &mut self.renderer {
72
+ if let Some(family) = &self.font_family {
73
+ if let Some(font) = family.get_font(FontStyle::Regular) {
74
+ let px = self.config.font_size;
75
+ for ch in (32u8..=126).map(|b| b as char) {
76
+ let glyph_id = font.lookup_glyph(ch);
77
+ let (metrics, bitmap) = font.rasterize_glyph(glyph_id, px);
78
+ let w = metrics.width as u16;
79
+ let h = metrics.height as u16;
80
+ if w > 0 && h > 0 {
81
+ let key = GlyphKey {
82
+ font_id: font.id, glyph_id,
83
+ size_bucket: (px * 4.0) as u16,
84
+ style_flags: 0,
85
+ };
86
+ let rgba = spectral_font::msdf::r8_to_rgba(&bitmap);
87
+ renderer.glyph_atlas.insert(key, w, h, &rgba);
88
+ }
89
+ }
90
+ renderer.needs_atlas_upload = true;
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ fn handle_input(&mut self, text: &str) {
97
+ let bytes = text.as_bytes();
98
+ self.terminal.feed(bytes);
99
+ self.request_redraw();
100
+ }
101
+
102
+ fn request_redraw(&mut self) {
103
+ if let Some(window) = &self.window {
104
+ window.request_redraw();
105
+ }
106
+ }
107
+
108
+ fn render(&mut self) {
109
+ if let Some(renderer) = &mut self.renderer {
110
+ renderer.update_atlas();
111
+ renderer.update_instances(&self.terminal);
112
+ renderer.render();
113
+ }
114
+ self.last_render = Instant::now();
115
+ }
116
+ }
117
+
118
+ fn try_load_font(name: &str) -> Option<Arc<Vec<u8>>> {
119
+ let paths = [
120
+ format!("/usr/share/fonts/truetype/{}/{}.ttf", name.to_lowercase().replace(' ', ""), name),
121
+ format!("/usr/share/fonts/TTF/{}.ttf", name.replace(' ', "")),
122
+ format!("/usr/share/fonts/truetype/{}.ttf", name.replace(' ', "")),
123
+ format!("/usr/share/fonts/{}.ttf", name),
124
+ format!("/usr/share/fonts/truetype/{}.ttf", name.to_lowercase()),
125
+ format!("/usr/share/fonts/truetype/iosevka/{}-Regular.ttf", name.replace(' ', "")),
126
+ format!("/usr/share/fonts/truetype/iosevka/{}-Regular.ttf", name.replace(" Extended", "")),
127
+ ];
128
+ for path in &paths {
129
+ if let Ok(data) = std::fs::read(path) {
130
+ return Some(Arc::new(data));
131
+ }
132
+ }
133
+ if let Ok(output) = std::process::Command::new("fc-match")
134
+ .args(["-f", "%{file}", name]).output()
135
+ {
136
+ let path = String::from_utf8_lossy(&output.stdout);
137
+ let path = path.trim();
138
+ if !path.is_empty() && path != name {
139
+ if let Ok(data) = std::fs::read(path) {
140
+ return Some(Arc::new(data));
141
+ }
142
+ }
143
+ }
144
+ None
145
+ }
146
+
147
+ impl ApplicationHandler for SpectralApp {
148
+ fn resumed(&mut self, event_loop: &ActiveEventLoop) {
149
+ if self.window.is_none() { self.init_window(event_loop); }
150
+ }
151
+
152
+ fn window_event(&mut self, event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
153
+ match event {
154
+ WindowEvent::CloseRequested => event_loop.exit(),
155
+ WindowEvent::Resized(size) => {
156
+ if let Some(renderer) = &mut self.renderer {
157
+ renderer.resize(size.width, size.height);
158
+ }
159
+ }
160
+ WindowEvent::KeyboardInput { event, .. } => {
161
+ if event.state == winit::event::ElementState::Pressed {
162
+ if let Some(text) = event.text {
163
+ self.handle_input(&text);
164
+ }
165
+ }
166
+ }
167
+ WindowEvent::RedrawRequested => { self.render(); }
168
+ _ => {}
169
+ }
170
+ }
171
+
172
+ fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {}
173
+ }
174
+
175
+ fn main() {
176
+ env_logger::init();
177
+ let event_loop = EventLoop::new().unwrap();
178
+ event_loop.set_control_flow(ControlFlow::Poll);
179
+ let config = Config::default();
180
+ let mut app = SpectralApp::new(config);
181
+ event_loop.run_app(&mut app).unwrap();
182
+ }