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 EnableBracketedPaste,
126 Hide,
127 DisableLineWrap,
128 )?;
129 Window::new(stdout)
130 }
131
132 pub fn keyboard(&mut self) -> io::Result<()> {
134 if let Ok(t) = terminal::supports_keyboard_enhancement() {
135 if !t {
136 return Err(io::Error::new(
137 io::ErrorKind::Unsupported,
138 "Terminal doesn't support the kitty keyboard protocol",
139 ));
140 }
141 if let Some(inline) = &mut self.inline {
142 inline.kitty = true;
143 } else {
144 execute!(
145 self.io(),
146 PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::all())
147 )?;
148 }
149 Ok(())
150 } else {
151 Err(io::Error::new(
152 io::ErrorKind::Unsupported,
153 "Terminal doesn't support the kitty keyboard protocol",
154 ))
155 }
156 }
157
158 pub fn buffer(&self) -> &Buffer {
160 &self.buffers[self.active_buffer]
161 }
162
163 pub fn buffer_mut(&mut self) -> &mut Buffer {
165 &mut self.buffers[self.active_buffer]
166 }
167
168 pub fn swap_buffers(&mut self) {
170 self.active_buffer = 1 - self.active_buffer;
171 self.buffers[self.active_buffer].fill(' ');
172 }
173
174 pub fn size(&self) -> Vec2 {
176 self.buffer().size()
177 }
178
179 pub fn restore(&mut self) -> io::Result<()> {
182 if terminal::supports_keyboard_enhancement().is_ok() {
183 queue!(self.io, PopKeyboardEnhancementFlags)?;
184 }
185 if let Some(inline) = &self.inline {
186 execute!(
187 self.io,
188 DisableMouseCapture,
189 DisableFocusChange,
190 DisableBracketedPaste,
191 PopKeyboardEnhancementFlags,
192 Show,
193 )?;
194 if terminal::size()?.1 != inline.start + 1 {
195 print!(
196 "{}",
197 "\n".repeat(self.buffers[self.active_buffer].size().y as usize)
198 );
199 }
200 disable_raw_mode()?;
201 Ok(())
202 } else {
203 execute!(
204 self.io,
205 PopKeyboardEnhancementFlags,
206 LeaveAlternateScreen,
207 DisableMouseCapture,
208 DisableFocusChange,
209 DisableBracketedPaste,
210 Show,
211 EnableLineWrap,
212 )?;
213 disable_raw_mode()
214 }
215 }
216
217 pub fn render(&mut self) -> io::Result<()> {
219 if self.inline.is_some() {
220 if !self.inline.as_ref().expect("Inline should be some").active {
221 print!("{}", "\n".repeat(self.buffer().size().y as usize));
223
224 enable_raw_mode()?;
225
226 execute!(
227 self.io,
228 EnableMouseCapture,
229 EnableFocusChange,
230 EnableBracketedPaste,
231 DisableLineWrap,
232 Hide,
233 )?;
234 if self.inline.as_ref().expect("Inline should be some").kitty {
235 execute!(
236 self.io,
237 PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::all())
238 )?;
239 }
240 let inline = self.inline.as_mut().expect("Inline should be some");
241 inline.active = true;
242 inline.start = cursor::position()?.1;
243 }
244
245 for (loc, cell) in
246 self.buffers[1 - self.active_buffer].diff(&self.buffers[self.active_buffer])
247 {
248 queue!(
249 self.io,
250 cursor::MoveTo(
251 loc.x,
252 self.inline.as_ref().expect("Inline should be some").start
253 - self.buffers[self.active_buffer].size().y
254 + loc.y
255 ),
256 Print(cell),
257 )?;
258 }
259
260 queue!(
261 self.io,
262 cursor::MoveTo(
263 0,
264 self.inline.as_ref().expect("Inline should be some").start
265 - self.buffers[self.active_buffer].size().y
266 )
267 )?;
268 } else {
269 if self.just_resized {
270 self.just_resized = false;
271 let cell = self.buffers[self.active_buffer].size();
272 for x in 0..cell.x {
273 for y in 0..cell.y {
274 let cell = self.buffers[self.active_buffer]
275 .get((x, y))
276 .expect("Cell should be in bounds");
277 queue!(self.io, cursor::MoveTo(x, y), Print(cell))?;
278 }
279 }
280 }
281
282 for (loc, cell) in
283 self.buffers[1 - self.active_buffer].diff(&self.buffers[self.active_buffer])
284 {
285 queue!(self.io, cursor::MoveTo(loc.x, loc.y), Print(cell))?;
286 }
287 }
288 Ok(())
289 }
290
291 pub fn update(&mut self, poll: Duration) -> io::Result<()> {
293 self.render()?;
295 self.swap_buffers();
296 self.render_cursor()?;
297 self.io.flush()?;
299 self.handle_event(poll)?;
301 Ok(())
302 }
303
304 pub fn render_cursor(&mut self) -> io::Result<()> {
305 if self.cursor_style != self.last_cursor.2
307 || self.cursor != self.last_cursor.1
308 || self.cursor_visible != self.last_cursor.0
309 {
310 if self.cursor_visible {
311 let cursor = self.cursor;
312 let style = self.cursor_style;
313
314 let actual_pos = if let Some(inline) = &self.inline {
316 vec2(
317 cursor.x,
318 inline.start - self.buffers[self.active_buffer].size().y + cursor.y,
319 )
320 } else {
321 cursor
322 };
323
324 queue!(self.io(), MoveTo(actual_pos.x, actual_pos.y), style, Show)?;
325 } else {
326 queue!(self.io(), Hide)?;
327 }
328 }
329 self.last_cursor = (self.cursor_visible, self.cursor, self.cursor_style);
330 Ok(())
331 }
332
333 pub fn handle_event(&mut self, poll: Duration) -> io::Result<()> {
335 self.events = vec![];
336 if event::poll(poll)? {
337 while event::poll(Duration::ZERO)? {
339 let event = event::read()?;
340 match event {
341 Event::Resize(width, height) => {
342 if self.inline.is_none() {
343 self.buffers = [
344 Buffer::new_filled((width, height), ' '),
345 Buffer::new_filled((width, height), ' '),
346 ];
347 self.just_resized = true;
348 }
349 }
350 Event::Mouse(MouseEvent { column, row, .. }) => {
351 self.mouse_pos = vec2(column, row)
352 }
353 _ => {}
354 }
355 self.events.push(event);
356 }
357 }
358 Ok(())
359 }
360
361 pub fn cursor_visible(&self) -> bool {
363 self.cursor_visible
364 }
365
366 pub fn cursor(&self) -> Vec2 {
368 self.cursor
369 }
370
371 pub fn cursor_style(&self) -> SetCursorStyle {
373 self.cursor_style
374 }
375
376 pub fn set_cursor_visible(&mut self, visible: bool) {
378 self.cursor_visible = visible;
379 }
380
381 pub fn set_cursor(&mut self, pos: Vec2) {
383 let size = self.size();
384 self.cursor.x = pos.x.min(size.x.saturating_sub(1));
385 self.cursor.y = pos.y.min(size.y.saturating_sub(1));
386 }
387
388 pub fn set_cursor_style(&mut self, style: SetCursorStyle) {
390 self.cursor_style = style;
391 }
392
393 pub fn move_cursor(&mut self, x: i16, y: i16) {
395 let size = self.size();
396 self.cursor.x = self
397 .cursor
398 .x
399 .saturating_add_signed(x)
400 .min(size.x.saturating_sub(1));
401 self.cursor.y = self
402 .cursor
403 .y
404 .saturating_add_signed(y)
405 .min(size.y.saturating_sub(1));
406 }
407
408 pub fn mouse_pos(&self) -> Vec2 {
409 self.mouse_pos
410 }
411
412 pub fn insert_event(&mut self, event: Event) {
416 match event {
417 Event::Resize(width, height) => {
418 if self.inline.is_none() {
419 self.buffers = [
420 Buffer::new_filled((width, height), ' '),
421 Buffer::new_filled((width, height), ' '),
422 ];
423 self.just_resized = true;
424 }
425 }
426 Event::Mouse(MouseEvent { column, row, .. }) => self.mouse_pos = vec2(column, row),
427 _ => {}
428 }
429
430 self.events.push(event);
431 }
432
433 pub fn clear_events(&mut self) {
436 self.events.clear();
437 }
438
439 pub fn events(&self) -> &Vec<Event> {
441 &self.events
442 }
443
444 pub fn hover<V: Into<Vec2>>(&self, loc: V, size: V) -> io::Result<bool> {
446 let loc = loc.into();
447 let size = size.into();
448 let pos: Vec2 = self.mouse_pos();
449 Ok(pos.x <= loc.x + size.x && pos.x >= loc.x && pos.y <= loc.y + size.y && pos.y >= loc.y)
450 }
451
452 pub fn io(&mut self) -> &mut Stdout {
453 &mut self.io
454 }
455}
456
457#[macro_export]
475macro_rules! event {
476 ($window:expr, $event_type:pat => $($closure:tt)*) => {
477 $window.events().iter().any(|e| {
478 if let $event_type = e {
479 $($closure)*
480 } else {
481 false
482 }
483 })
484 };
485}
486
487pub fn handle_panics() {
489 let original_hook = take_hook();
490 set_hook(Box::new(move |e| {
491 Window::new(io::stdout())
492 .expect("Window should have created for panic")
493 .restore()
494 .expect("Window should have exited for panic");
495 original_hook(e);
496 }))
497}
498
499impl Drop for Window {
500 fn drop(&mut self) {
501 self.restore().expect("Restoration should have succeded");
502 }
503}