1pub use crate::prelude::*;
2
3use crossterm::{
4 cursor::{self, Hide, MoveTo, Show},
5 event, execute, queue,
6 terminal::{self, *},
7 tty::IsTty,
8};
9use std::{
10 io::{self, Stdout, Write},
11 panic::{set_hook, take_hook},
12 time::Duration,
13};
14
15#[derive(Default)]
16pub struct Inline {
17 active: bool,
18 kitty: bool,
19 start: u16,
20}
21
22impl AsMut<Buffer> for Window {
23 fn as_mut(&mut self) -> &mut Buffer {
24 self.buffer_mut()
25 }
26}
27
28pub struct Window {
42 io: io::Stdout,
43 buffers: [Buffer; 2],
44 active_buffer: usize,
45 events: Vec<Event>,
46
47 last_cursor: (bool, Vec2, SetCursorStyle),
48
49 cursor_visible: bool,
50 cursor: Vec2,
51 cursor_style: SetCursorStyle,
52
53 mouse_pos: Vec2,
55 inline: Option<Inline>,
57 just_resized: bool,
59}
60
61impl Default for Window {
62 fn default() -> Self {
63 Self::init().expect("Init should have succeeded")
64 }
65}
66
67impl Window {
68 pub fn new(io: io::Stdout) -> io::Result<Self> {
71 Ok(Self {
72 io,
73 buffers: [
74 Buffer::new_filled(size()?, ' '),
75 Buffer::new_filled(size()?, ' '),
76 ],
77 active_buffer: 0,
78 events: vec![],
79 last_cursor: (false, vec2(0, 0), SetCursorStyle::SteadyBlock),
80 cursor_visible: false,
81 cursor_style: SetCursorStyle::SteadyBlock,
82 cursor: vec2(0, 0),
83 mouse_pos: vec2(0, 0),
84 inline: None,
85 just_resized: false,
86 })
87 }
88
89 pub fn new_inline(io: io::Stdout, height: u16) -> io::Result<Self> {
91 let size = vec2(size()?.0, height);
92 Ok(Self {
93 io,
94 buffers: [Buffer::new_filled(size, ' '), Buffer::new_filled(size, ' ')],
95 active_buffer: 0,
96 events: vec![],
97 last_cursor: (false, vec2(0, 0), SetCursorStyle::SteadyBlock),
98 cursor_visible: false,
99 cursor_style: SetCursorStyle::SteadyBlock,
100 cursor: vec2(0, 0),
101 mouse_pos: vec2(0, 0),
102 inline: Some(Inline::default()),
103 just_resized: false,
104 })
105 }
106
107 pub fn init_inline(height: u16) -> io::Result<Self> {
110 let stdout = io::stdout();
111 assert!(stdout.is_tty());
112 Window::new_inline(stdout, height)
113 }
114
115 pub fn init() -> io::Result<Self> {
117 enable_raw_mode()?;
118 let mut stdout = io::stdout();
119 assert!(stdout.is_tty());
120 execute!(
121 stdout,
122 EnterAlternateScreen,
123 EnableMouseCapture,
124 EnableFocusChange,
125 Hide,
126 DisableLineWrap,
127 )?;
128 Window::new(stdout)
129 }
130
131 pub fn keyboard(&mut self) -> io::Result<()> {
133 if let Ok(t) = terminal::supports_keyboard_enhancement() {
134 if !t {
135 return Err(io::Error::new(
136 io::ErrorKind::Unsupported,
137 "Terminal doesn't support the kitty keyboard protocol",
138 ));
139 }
140 if let Some(inline) = &mut self.inline {
141 inline.kitty = true;
142 } else {
143 execute!(
144 self.io(),
145 PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::all())
146 )?;
147 }
148 Ok(())
149 } else {
150 Err(io::Error::new(
151 io::ErrorKind::Unsupported,
152 "Terminal doesn't support the kitty keyboard protocol",
153 ))
154 }
155 }
156
157 pub fn buffer(&self) -> &Buffer {
159 &self.buffers[self.active_buffer]
160 }
161
162 pub fn buffer_mut(&mut self) -> &mut Buffer {
164 &mut self.buffers[self.active_buffer]
165 }
166
167 pub fn swap_buffers(&mut self) {
169 self.active_buffer = 1 - self.active_buffer;
170 self.buffers[self.active_buffer].fill(' ');
171 }
172
173 pub fn size(&self) -> Vec2 {
175 self.buffer().size()
176 }
177
178 pub fn restore(&mut self) -> io::Result<()> {
181 if terminal::supports_keyboard_enhancement().is_ok() {
182 queue!(self.io, PopKeyboardEnhancementFlags)?;
183 }
184 if let Some(inline) = &self.inline {
185 execute!(
186 self.io,
187 DisableMouseCapture,
188 DisableFocusChange,
189 PopKeyboardEnhancementFlags,
190 Show,
191 )?;
192 if terminal::size()?.1 != inline.start + 1 {
193 print!(
194 "{}",
195 "\n".repeat(self.buffers[self.active_buffer].size().y as usize)
196 );
197 }
198 disable_raw_mode()?;
199 Ok(())
200 } else {
201 execute!(
202 self.io,
203 PopKeyboardEnhancementFlags,
204 LeaveAlternateScreen,
205 DisableMouseCapture,
206 DisableFocusChange,
207 Show,
208 EnableLineWrap,
209 )?;
210 disable_raw_mode()
211 }
212 }
213
214 pub fn render(&mut self) -> io::Result<()> {
216 if self.inline.is_some() {
217 if !self.inline.as_ref().expect("Inline should be some").active {
218 print!("{}", "\n".repeat(self.buffer().size().y as usize));
220
221 enable_raw_mode()?;
222
223 execute!(
224 self.io,
225 EnableMouseCapture,
226 EnableFocusChange,
227 DisableLineWrap,
228 Hide,
229 )?;
230 if self.inline.as_ref().expect("Inline should be some").kitty {
231 execute!(
232 self.io,
233 PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::all())
234 )?;
235 }
236 let inline = self.inline.as_mut().expect("Inline should be some");
237 inline.active = true;
238 inline.start = cursor::position()?.1;
239 }
240
241 for (loc, cell) in
242 self.buffers[1 - self.active_buffer].diff(&self.buffers[self.active_buffer])
243 {
244 queue!(
245 self.io,
246 cursor::MoveTo(
247 loc.x,
248 self.inline.as_ref().expect("Inline should be some").start
249 - self.buffers[self.active_buffer].size().y
250 + loc.y
251 ),
252 Print(cell),
253 )?;
254 }
255
256 queue!(
257 self.io,
258 cursor::MoveTo(
259 0,
260 self.inline.as_ref().expect("Inline should be some").start
261 - self.buffers[self.active_buffer].size().y
262 )
263 )?;
264 } else {
265 if self.just_resized {
266 self.just_resized = false;
267 let cell = self.buffers[self.active_buffer].size();
268 for x in 0..cell.x {
269 for y in 0..cell.y {
270 let cell = self.buffers[self.active_buffer]
271 .get((x, y))
272 .expect("Cell should be in bounds");
273 queue!(self.io, cursor::MoveTo(x, y), Print(cell))?;
274 }
275 }
276 }
277
278 for (loc, cell) in
279 self.buffers[1 - self.active_buffer].diff(&self.buffers[self.active_buffer])
280 {
281 queue!(self.io, cursor::MoveTo(loc.x, loc.y), Print(cell))?;
282 }
283 }
284 Ok(())
285 }
286
287 pub fn update(&mut self, poll: Duration) -> io::Result<()> {
289 self.render()?;
291 self.swap_buffers();
292 self.render_cursor()?;
293 self.io.flush()?;
295 self.handle_event(poll)?;
297 Ok(())
298 }
299
300 pub fn render_cursor(&mut self) -> io::Result<()> {
301 let cursor_pos = cursor::position()?;
303 if self.cursor_style != self.last_cursor.2
304 || self.cursor != cursor_pos.into()
305 || self.cursor != self.last_cursor.1
306 || self.cursor_visible != self.last_cursor.0
307 {
308 if self.cursor_visible {
309 let cursor = self.cursor;
310 let style = self.cursor_style;
311
312 let actual_pos = if let Some(inline) = &self.inline {
314 vec2(
315 cursor.x,
316 inline.start - self.buffers[self.active_buffer].size().y + cursor.y,
317 )
318 } else {
319 cursor
320 };
321
322 queue!(self.io(), MoveTo(actual_pos.x, actual_pos.y), style, Show)?;
323 } else {
324 queue!(self.io(), Hide)?;
325 }
326 }
327 self.last_cursor = (self.cursor_visible, self.cursor, self.cursor_style);
328 Ok(())
329 }
330
331 pub fn handle_event(&mut self, poll: Duration) -> io::Result<()> {
333 self.events = vec![];
334 if event::poll(poll)? {
335 while event::poll(Duration::ZERO)? {
337 let event = event::read()?;
338 match event {
339 Event::Resize(width, height) => {
340 if self.inline.is_none() {
341 self.buffers = [
342 Buffer::new_filled((width, height), ' '),
343 Buffer::new_filled((width, height), ' '),
344 ];
345 self.just_resized = true;
346 }
347 }
348 Event::Mouse(MouseEvent { column, row, .. }) => {
349 self.mouse_pos = vec2(column, row)
350 }
351 _ => {}
352 }
353 self.events.push(event);
354 }
355 }
356 Ok(())
357 }
358
359 pub fn cursor_visible(&self) -> bool {
361 self.cursor_visible
362 }
363
364 pub fn cursor(&self) -> Vec2 {
366 self.cursor
367 }
368
369 pub fn cursor_style(&self) -> SetCursorStyle {
371 self.cursor_style
372 }
373
374 pub fn set_cursor_visible(&mut self, visible: bool) {
376 self.cursor_visible = visible;
377 }
378
379 pub fn set_cursor(&mut self, pos: Vec2) {
381 let size = self.size();
382 self.cursor.x = pos.x.min(size.x.saturating_sub(1));
383 self.cursor.y = pos.y.min(size.y.saturating_sub(1));
384 }
385
386 pub fn set_cursor_style(&mut self, style: SetCursorStyle) {
388 self.cursor_style = style;
389 }
390
391 pub fn move_cursor(&mut self, x: i16, y: i16) {
393 let size = self.size();
394 self.cursor.x = self
395 .cursor
396 .x
397 .saturating_add_signed(x)
398 .min(size.x.saturating_sub(1));
399 self.cursor.y = self
400 .cursor
401 .y
402 .saturating_add_signed(y)
403 .min(size.y.saturating_sub(1));
404 }
405
406 pub fn mouse_pos(&self) -> Vec2 {
407 self.mouse_pos
408 }
409
410 pub fn events(&self) -> &Vec<Event> {
412 &self.events
413 }
414
415 pub fn hover<V: Into<Vec2>>(&self, loc: V, size: V) -> io::Result<bool> {
417 let loc = loc.into();
418 let size = size.into();
419 let pos: Vec2 = self.mouse_pos();
420 Ok(pos.x <= loc.x + size.x && pos.x >= loc.x && pos.y <= loc.y + size.y && pos.y >= loc.y)
421 }
422
423 pub fn io(&mut self) -> &mut Stdout {
424 &mut self.io
425 }
426}
427
428#[macro_export]
446macro_rules! event {
447 ($window:expr, $event_type:pat => $($closure:tt)*) => {
448 $window.events().iter().any(|e| {
449 if let $event_type = e {
450 $($closure)*
451 } else {
452 false
453 }
454 })
455 };
456}
457
458pub fn handle_panics() {
460 let original_hook = take_hook();
461 set_hook(Box::new(move |e| {
462 Window::new(io::stdout())
463 .expect("Window should have created for panic")
464 .restore()
465 .expect("Window should have exited for panic");
466 original_hook(e);
467 }))
468}
469
470impl Drop for Window {
471 fn drop(&mut self) {
472 self.restore().expect("Restoration should have succeded");
473 }
474}