1use std::cmp::{max, min};
26use std::sync::atomic::{AtomicUsize, Ordering};
27use std::sync::mpsc::{channel, Receiver, Sender};
28use std::sync::Arc;
29use std::thread;
30use std::time::Duration;
31
32use crate::attr::Attr;
33use crate::canvas::Canvas;
34use crate::cell::Cell;
35use crate::draw::Draw;
36use crate::error::TuikitError;
37use crate::event::Event;
38use crate::input::{KeyBoard, KeyboardHandler};
39use crate::key::Key;
40use crate::output::Command;
41use crate::output::Output;
42use crate::raw::{get_tty, IntoRawMode};
43use crate::screen::Screen;
44use crate::spinlock::SpinLock;
45use crate::sys::signal::{initialize_signals, notify_on_sigwinch, unregister_sigwinch};
46use crate::Result;
47
48const MIN_HEIGHT: usize = 1;
49const WAIT_TIMEOUT: Duration = Duration::from_millis(300);
50const POLLING_TIMEOUT: Duration = Duration::from_millis(10);
51
52#[derive(Debug, Copy, Clone)]
53pub enum TermHeight {
54 Fixed(usize),
55 Percent(usize),
56}
57
58pub struct Term<UserEvent: Send + 'static = ()> {
59 components_to_stop: Arc<AtomicUsize>,
60 keyboard_handler: SpinLock<Option<KeyboardHandler>>,
61 resize_signal_id: Arc<AtomicUsize>,
62 term_lock: SpinLock<TermLock>,
63 event_rx: SpinLock<Receiver<Event<UserEvent>>>,
64 event_tx: Arc<SpinLock<Sender<Event<UserEvent>>>>,
65 raw_mouse: bool, }
67
68pub struct TermOptions {
69 max_height: TermHeight,
70 min_height: TermHeight,
71 height: TermHeight,
72 clear_on_exit: bool,
73 clear_on_start: bool,
74 mouse_enabled: bool,
75 raw_mouse: bool,
76 hold: bool, disable_alternate_screen: bool,
78}
79
80impl Default for TermOptions {
81 fn default() -> Self {
82 Self {
83 max_height: TermHeight::Percent(100),
84 min_height: TermHeight::Fixed(3),
85 height: TermHeight::Percent(100),
86 clear_on_exit: true,
87 clear_on_start: true,
88 mouse_enabled: false,
89 raw_mouse: false,
90 hold: false,
91 disable_alternate_screen: false,
92 }
93 }
94}
95
96impl TermOptions {
98 pub fn max_height(mut self, max_height: TermHeight) -> Self {
99 self.max_height = max_height;
100 self
101 }
102
103 pub fn min_height(mut self, min_height: TermHeight) -> Self {
104 self.min_height = min_height;
105 self
106 }
107 pub fn height(mut self, height: TermHeight) -> Self {
108 self.height = height;
109 self
110 }
111 pub fn clear_on_exit(mut self, clear: bool) -> Self {
112 self.clear_on_exit = clear;
113 self
114 }
115 pub fn clear_on_start(mut self, clear: bool) -> Self {
116 self.clear_on_start = clear;
117 self
118 }
119 pub fn mouse_enabled(mut self, enabled: bool) -> Self {
120 self.mouse_enabled = enabled;
121 self
122 }
123 pub fn raw_mouse(mut self, enabled: bool) -> Self {
124 self.raw_mouse = enabled;
125 self
126 }
127 pub fn hold(mut self, hold: bool) -> Self {
128 self.hold = hold;
129 self
130 }
131 pub fn disable_alternate_screen(mut self, disable_alternate_screen: bool) -> Self {
132 self.disable_alternate_screen = disable_alternate_screen;
133 self
134 }
135}
136
137impl<UserEvent: Send + 'static> Term<UserEvent> {
138 pub fn with_height(height: TermHeight) -> Result<Term<UserEvent>> {
152 Term::with_options(TermOptions::default().height(height))
153 }
154
155 pub fn new() -> Result<Term<UserEvent>> {
164 Term::with_options(TermOptions::default())
165 }
166
167 pub fn with_options(options: TermOptions) -> Result<Term<UserEvent>> {
175 initialize_signals();
176
177 let (event_tx, event_rx) = channel();
178 let raw_mouse = options.raw_mouse;
179 let ret = Term {
180 components_to_stop: Arc::new(AtomicUsize::new(0)),
181 keyboard_handler: SpinLock::new(None),
182 resize_signal_id: Arc::new(AtomicUsize::new(0)),
183 term_lock: SpinLock::new(TermLock::with_options(&options)),
184 event_tx: Arc::new(SpinLock::new(event_tx)),
185 event_rx: SpinLock::new(event_rx),
186 raw_mouse,
187 };
188 if options.hold {
189 Ok(ret)
190 } else {
191 ret.restart().map(|_| ret)
192 }
193 }
194
195 fn ensure_not_stopped(&self) -> Result<()> {
196 if self.components_to_stop.load(Ordering::SeqCst) == 2 {
197 Ok(())
198 } else {
199 Err(TuikitError::TerminalNotStarted)
200 }
201 }
202
203 fn get_cursor_pos(
204 &self,
205 keyboard: &mut KeyBoard,
206 output: &mut Output,
207 ) -> Result<(usize, usize)> {
208 output.ask_for_cpr();
209
210 if let Ok(key) = keyboard.next_key_timeout(WAIT_TIMEOUT) {
211 if let Key::CursorPos(row, col) = key {
212 return Ok((row as usize, col as usize));
213 }
214 }
215
216 Ok((0, 0))
217 }
218
219 pub fn restart(&self) -> Result<()> {
221 let mut termlock = self.term_lock.lock();
222 if self.components_to_stop.load(Ordering::SeqCst) == 2 {
223 return Ok(());
224 }
225
226 let ttyout = get_tty()?.into_raw_mode()?;
227 let mut output = Output::new(Box::new(ttyout))?;
228 let mut keyboard = KeyBoard::new_with_tty().raw_mouse(self.raw_mouse);
229 self.keyboard_handler
230 .lock()
231 .replace(keyboard.get_interrupt_handler());
232 let cursor_pos = self.get_cursor_pos(&mut keyboard, &mut output)?;
233 termlock.restart(output, cursor_pos)?;
234
235 self.start_key_listener(keyboard);
237 self.start_size_change_listener();
238
239 while self.components_to_stop.load(Ordering::SeqCst) < 2 {
241 debug!(
242 "restart: components: {}",
243 self.components_to_stop.load(Ordering::SeqCst)
244 );
245 thread::sleep(POLLING_TIMEOUT);
246 }
247
248 let event_tx = self.event_tx.lock();
249 let _ = event_tx.send(Event::Restarted);
250
251 Ok(())
252 }
253
254 pub fn pause(&self) -> Result<()> {
260 self.pause_internal(false)
261 }
262
263 fn pause_internal(&self, exiting: bool) -> Result<()> {
264 debug!("pause");
265 let mut termlock = self.term_lock.lock();
266
267 if self.components_to_stop.load(Ordering::SeqCst) == 0 {
268 return Ok(());
269 }
270
271 self.keyboard_handler.lock().take().map(|h| h.interrupt());
274 unregister_sigwinch(self.resize_signal_id.load(Ordering::Relaxed)).map(|tx| tx.send(()));
275
276 termlock.pause(exiting)?;
277
278 while self.components_to_stop.load(Ordering::SeqCst) > 0 {
280 debug!(
281 "pause: components: {}",
282 self.components_to_stop.load(Ordering::SeqCst)
283 );
284 thread::sleep(POLLING_TIMEOUT);
285 }
286
287 Ok(())
288 }
289
290 fn start_key_listener(&self, mut keyboard: KeyBoard) {
291 let event_tx_clone = self.event_tx.clone();
292 let components_to_stop = self.components_to_stop.clone();
293 thread::spawn(move || {
294 components_to_stop.fetch_add(1, Ordering::SeqCst);
295 debug!("key listener start");
296 loop {
297 let next_key = keyboard.next_key();
298 trace!("next key: {:?}", next_key);
299 match next_key {
300 Ok(key) => {
301 let event_tx = event_tx_clone.lock();
302 let _ = event_tx.send(Event::Key(key));
303 }
304 Err(TuikitError::Interrupted) => break,
305 _ => {} }
307 }
308 components_to_stop.fetch_sub(1, Ordering::SeqCst);
309 debug!("key listener stop");
310 });
311 }
312
313 fn start_size_change_listener(&self) {
314 let event_tx_clone = self.event_tx.clone();
315 let resize_signal_id = self.resize_signal_id.clone();
316 let components_to_stop = self.components_to_stop.clone();
317
318 thread::spawn(move || {
319 let (id, sigwinch_rx) = notify_on_sigwinch();
320 resize_signal_id.store(id, Ordering::Relaxed);
321
322 components_to_stop.fetch_add(1, Ordering::SeqCst);
323 debug!("size change listener started");
324 loop {
325 if let Ok(_) = sigwinch_rx.recv() {
326 let event_tx = event_tx_clone.lock();
327 let _ = event_tx.send(Event::Resize {
328 width: 0,
329 height: 0,
330 });
331 } else {
332 break;
333 }
334 }
335 components_to_stop.fetch_sub(1, Ordering::SeqCst);
336 debug!("size change listener stop");
337 });
338 }
339
340 fn filter_event(&self, event: Event<UserEvent>) -> Event<UserEvent> {
341 match event {
342 Event::Resize { .. } => {
343 {
344 let mut termlock = self.term_lock.lock();
345 let _ = termlock.on_resize();
346 }
347 let (width, height) = self.term_size().unwrap_or((0, 0));
348 Event::Resize { width, height }
349 }
350 Event::Key(Key::MousePress(button, row, col)) => {
351 let cursor_row = self.term_lock.lock().get_term_start_row() as u16;
353 if row < cursor_row {
354 Event::__Nonexhaustive
355 } else {
356 Event::Key(Key::MousePress(button, row - cursor_row, col))
357 }
358 }
359 Event::Key(Key::MouseRelease(row, col)) => {
360 let cursor_row = self.term_lock.lock().get_term_start_row() as u16;
362 if row < cursor_row {
363 Event::__Nonexhaustive
364 } else {
365 Event::Key(Key::MouseRelease(row - cursor_row, col))
366 }
367 }
368 Event::Key(Key::MouseHold(row, col)) => {
369 let cursor_row = self.term_lock.lock().get_term_start_row() as u16;
371 if row < cursor_row {
372 Event::__Nonexhaustive
373 } else {
374 Event::Key(Key::MouseHold(row - cursor_row, col))
375 }
376 }
377 Event::Key(Key::SingleClick(button, row, col)) => {
378 let cursor_row = self.term_lock.lock().get_term_start_row() as u16;
379 if row < cursor_row {
380 Event::__Nonexhaustive
381 } else {
382 Event::Key(Key::SingleClick(button, row - cursor_row, col))
383 }
384 }
385 Event::Key(Key::DoubleClick(button, row, col)) => {
386 let cursor_row = self.term_lock.lock().get_term_start_row() as u16;
387 if row < cursor_row {
388 Event::__Nonexhaustive
389 } else {
390 Event::Key(Key::DoubleClick(button, row - cursor_row, col))
391 }
392 }
393 Event::Key(Key::WheelUp(row, col, num)) => {
394 let cursor_row = self.term_lock.lock().get_term_start_row() as u16;
395 if row < cursor_row {
396 Event::__Nonexhaustive
397 } else {
398 Event::Key(Key::WheelUp(row - cursor_row, col, num))
399 }
400 }
401 Event::Key(Key::WheelDown(row, col, num)) => {
402 let cursor_row = self.term_lock.lock().get_term_start_row() as u16;
403 if row < cursor_row {
404 Event::__Nonexhaustive
405 } else {
406 Event::Key(Key::WheelDown(row - cursor_row, col, num))
407 }
408 }
409 ev => ev,
410 }
411 }
412
413 pub fn peek_event(&self, timeout: Duration) -> Result<Event<UserEvent>> {
415 let event_rx = self.event_rx.lock();
416 event_rx
417 .recv_timeout(timeout)
418 .map(|ev| self.filter_event(ev))
419 .map_err(|_| TuikitError::Timeout(timeout))
420 }
421
422 pub fn poll_event(&self) -> Result<Event<UserEvent>> {
424 let event_rx = self.event_rx.lock();
425 event_rx
426 .recv()
427 .map(|ev| self.filter_event(ev))
428 .map_err(|err| TuikitError::ChannelReceiveError(err))
429 }
430
431 pub fn send_event(&self, event: Event<UserEvent>) -> Result<()> {
433 let event_tx = self.event_tx.lock();
434 event_tx
435 .send(event)
436 .map_err(|err| TuikitError::SendEventError(err.to_string()))
437 }
438
439 pub fn present(&self) -> Result<()> {
441 self.ensure_not_stopped()?;
442 let mut termlock = self.term_lock.lock();
443 termlock.present()
444 }
445
446 pub fn term_size(&self) -> Result<(usize, usize)> {
448 self.ensure_not_stopped()?;
449 let termlock = self.term_lock.lock();
450 Ok(termlock.term_size()?)
451 }
452
453 pub fn clear(&self) -> Result<()> {
455 self.ensure_not_stopped()?;
456 let mut termlock = self.term_lock.lock();
457 termlock.clear()
458 }
459
460 pub fn put_cell(&self, row: usize, col: usize, cell: Cell) -> Result<usize> {
462 self.ensure_not_stopped()?;
463 let mut termlock = self.term_lock.lock();
464 termlock.put_cell(row, col, cell)
465 }
466
467 pub fn print(&self, row: usize, col: usize, content: &str) -> Result<usize> {
469 self.print_with_attr(row, col, content, Attr::default())
470 }
471
472 pub fn print_with_attr(
474 &self,
475 row: usize,
476 col: usize,
477 content: &str,
478 attr: impl Into<Attr>,
479 ) -> Result<usize> {
480 self.ensure_not_stopped()?;
481 let mut termlock = self.term_lock.lock();
482 termlock.print_with_attr(row, col, content, attr)
483 }
484
485 pub fn set_cursor(&self, row: usize, col: usize) -> Result<()> {
487 self.ensure_not_stopped()?;
488 let mut termlock = self.term_lock.lock();
489 termlock.set_cursor(row, col)
490 }
491
492 pub fn show_cursor(&self, show: bool) -> Result<()> {
494 self.ensure_not_stopped()?;
495 let mut termlock = self.term_lock.lock();
496 termlock.show_cursor(show)
497 }
498
499 pub fn enable_mouse_support(&self) -> Result<()> {
501 self.ensure_not_stopped()?;
502 let mut termlock = self.term_lock.lock();
503 termlock.enable_mouse_support()
504 }
505
506 pub fn disable_mouse_support(&self) -> Result<()> {
508 self.ensure_not_stopped()?;
509 let mut termlock = self.term_lock.lock();
510 termlock.disable_mouse_support()
511 }
512
513 pub fn clear_on_exit(&self, clear: bool) -> Result<()> {
515 self.ensure_not_stopped()?;
516 let mut termlock = self.term_lock.lock();
517 termlock.clear_on_exit(clear);
518 Ok(())
519 }
520
521 pub fn draw(&self, draw: &dyn Draw) -> Result<()> {
522 let mut canvas = TermCanvas { term: &self };
523 draw.draw(&mut canvas)
524 .map_err(|err| TuikitError::DrawError(err))
525 }
526
527 pub fn draw_mut(&self, draw: &mut dyn Draw) -> Result<()> {
528 let mut canvas = TermCanvas { term: &self };
529 draw.draw_mut(&mut canvas)
530 .map_err(|err| TuikitError::DrawError(err))
531 }
532}
533
534impl<'a, UserEvent: Send + 'static> Drop for Term<UserEvent> {
535 fn drop(&mut self) {
536 let _ = self.pause_internal(true);
537 }
538}
539
540pub struct TermCanvas<'a, UserEvent: Send + 'static> {
541 term: &'a Term<UserEvent>,
542}
543
544impl<'a, UserEvent: Send + 'static> Canvas for TermCanvas<'a, UserEvent> {
545 fn size(&self) -> Result<(usize, usize)> {
546 self.term.term_size()
547 }
548
549 fn clear(&mut self) -> Result<()> {
550 self.term.clear()
551 }
552
553 fn put_cell(&mut self, row: usize, col: usize, cell: Cell) -> Result<usize> {
554 self.term.put_cell(row, col, cell)
555 }
556
557 fn print_with_attr(
558 &mut self,
559 row: usize,
560 col: usize,
561 content: &str,
562 attr: Attr,
563 ) -> Result<usize> {
564 self.term.print_with_attr(row, col, content, attr)
565 }
566
567 fn set_cursor(&mut self, row: usize, col: usize) -> Result<()> {
568 self.term.set_cursor(row, col)
569 }
570
571 fn show_cursor(&mut self, show: bool) -> Result<()> {
572 self.term.show_cursor(show)
573 }
574}
575
576struct TermLock {
577 prefer_height: TermHeight,
578 max_height: TermHeight,
579 min_height: TermHeight,
580 bottom_intact: bool,
582 clear_on_exit: bool,
583 clear_on_start: bool,
584 mouse_enabled: bool,
585 alternate_screen: bool,
586 disable_alternate_screen: bool,
587 cursor_row: usize,
588 screen_height: usize,
589 screen_width: usize,
590 screen: Screen,
591 output: Option<Output>,
592}
593
594impl Default for TermLock {
595 fn default() -> Self {
596 Self {
597 prefer_height: TermHeight::Percent(100),
598 max_height: TermHeight::Percent(100),
599 min_height: TermHeight::Fixed(3),
600 bottom_intact: false,
601 alternate_screen: false,
602 disable_alternate_screen: false,
603 cursor_row: 0,
604 screen_height: 0,
605 screen_width: 0,
606 screen: Screen::new(0, 0),
607 output: None,
608 clear_on_exit: true,
609 clear_on_start: true,
610 mouse_enabled: false,
611 }
612 }
613}
614
615impl TermLock {
616 pub fn with_options(options: &TermOptions) -> Self {
617 let mut term = TermLock::default();
618 term.prefer_height = options.height;
619 term.max_height = options.max_height;
620 term.min_height = options.min_height;
621 term.clear_on_exit = options.clear_on_exit;
622 term.clear_on_start = options.clear_on_start;
623 term.screen.clear_on_start(options.clear_on_start);
624 term.disable_alternate_screen = options.disable_alternate_screen;
625 term.mouse_enabled = options.mouse_enabled;
626 term
627 }
628
629 pub fn present(&mut self) -> Result<()> {
631 let output = self
632 .output
633 .as_mut()
634 .ok_or(TuikitError::TerminalNotStarted)?;
635 let mut commands = self.screen.present();
636
637 let cursor_row = self.cursor_row;
638 for cmd in commands.iter_mut() {
640 if let Command::CursorGoto { row, col } = *cmd {
641 *cmd = Command::CursorGoto {
642 row: row + cursor_row,
643 col,
644 }
645 }
646 }
647
648 for cmd in commands.into_iter() {
649 output.execute(cmd);
650 }
651 output.flush();
652 Ok(())
653 }
654
655 pub fn on_resize(&mut self) -> Result<()> {
657 let output = self
658 .output
659 .as_mut()
660 .ok_or(TuikitError::TerminalNotStarted)?;
661 let (screen_width, screen_height) = output
662 .terminal_size()
663 .expect("term:restart get terminal size failed");
664 self.screen_height = screen_height;
665 self.screen_width = screen_width;
666
667 let width = screen_width;
668 let height = Self::calc_preferred_height(
669 &self.min_height,
670 &self.max_height,
671 &self.prefer_height,
672 screen_height,
673 );
674
675 if self.cursor_row + height >= screen_height {
677 self.bottom_intact = true;
678 }
679
680 if self.bottom_intact {
681 self.cursor_row = screen_height - height;
682 }
683
684 let _ = output.cursor_goto(self.cursor_row, 0);
686 if self.clear_on_start {
687 let _ = output.erase_down();
688 }
689
690 self.screen.resize(width, height);
692 Ok(())
693 }
694
695 fn calc_height(height_spec: &TermHeight, actual_height: usize) -> usize {
696 match *height_spec {
697 TermHeight::Fixed(h) => h,
698 TermHeight::Percent(p) => actual_height * min(p, 100) / 100,
699 }
700 }
701
702 fn calc_preferred_height(
703 min_height: &TermHeight,
704 max_height: &TermHeight,
705 prefer_height: &TermHeight,
706 height: usize,
707 ) -> usize {
708 let max_height = Self::calc_height(max_height, height);
709 let min_height = Self::calc_height(min_height, height);
710 let prefer_height = Self::calc_height(prefer_height, height);
711
712 let max_height = max(min(max_height, height), MIN_HEIGHT);
714 let min_height = max(min(min_height, height), MIN_HEIGHT);
715 max(min(prefer_height, max_height), min_height)
716 }
717
718 fn pause(&mut self, exiting: bool) -> Result<()> {
720 self.disable_mouse()?;
721 self.output.take().map(|mut output| {
722 output.show_cursor();
723 if self.clear_on_exit || !exiting {
724 if !self.disable_alternate_screen {
726 output.quit_alternate_screen();
727 } else {
728 output.cursor_goto(self.cursor_row, 0);
729 output.erase_down();
730 }
731 } else {
732 output.cursor_goto(self.cursor_row + self.screen.height(), 0);
733 if self.bottom_intact {
734 output.write("\n");
735 }
736 }
737 output.flush();
738 });
739 Ok(())
740 }
741
742 fn ensure_height(&mut self, cursor_pos: (usize, usize)) -> Result<()> {
746 let output = self
747 .output
748 .as_mut()
749 .ok_or(TuikitError::TerminalNotStarted)?;
750
751 let (screen_width, screen_height) = output
754 .terminal_size()
755 .expect("termlock:ensure_height get terminal size failed");
756 let height_to_be = Self::calc_preferred_height(
757 &self.min_height,
758 &self.max_height,
759 &self.prefer_height,
760 screen_height,
761 );
762
763 self.alternate_screen = false;
764 let (mut cursor_row, cursor_col) = cursor_pos;
765 if height_to_be >= screen_height {
766 self.alternate_screen = true;
768 self.bottom_intact = false;
769 self.cursor_row = 0;
770 if !self.disable_alternate_screen {
771 output.enter_alternate_screen();
772 }
773 } else {
774 if cursor_col > 0 {
778 output.write("\n");
779 cursor_row += 1;
780 }
781
782 if (cursor_row + height_to_be) <= screen_height {
783 self.bottom_intact = false;
784 self.cursor_row = cursor_row;
785 } else {
786 for _ in 0..(height_to_be - 1) {
787 output.write("\n");
788 }
789 self.bottom_intact = true;
790 self.cursor_row = min(cursor_row, screen_height - height_to_be);
791 }
792 }
793
794 output.cursor_goto(self.cursor_row, 0);
795 output.flush();
796 self.screen_height = screen_height;
797 self.screen_width = screen_width;
798 Ok(())
799 }
800
801 pub fn get_term_start_row(&self) -> usize {
803 self.cursor_row
804 }
805
806 pub fn restart(&mut self, output: Output, cursor_pos: (usize, usize)) -> Result<()> {
808 self.output.replace(output);
810 self.ensure_height(cursor_pos)?;
811 self.on_resize()?;
812 if self.mouse_enabled {
813 self.enable_mouse()?;
814 }
815 Ok(())
816 }
817
818 pub fn term_size(&self) -> Result<(usize, usize)> {
820 self.screen.size()
821 }
822
823 pub fn clear(&mut self) -> Result<()> {
825 self.screen.clear()
826 }
827
828 pub fn put_cell(&mut self, row: usize, col: usize, cell: Cell) -> Result<usize> {
830 self.screen.put_cell(row, col, cell)
831 }
832
833 pub fn print_with_attr(
835 &mut self,
836 row: usize,
837 col: usize,
838 content: &str,
839 attr: impl Into<Attr>,
840 ) -> Result<usize> {
841 self.screen.print_with_attr(row, col, content, attr.into())
842 }
843
844 pub fn set_cursor(&mut self, row: usize, col: usize) -> Result<()> {
846 self.screen.set_cursor(row, col)
847 }
848
849 pub fn show_cursor(&mut self, show: bool) -> Result<()> {
851 self.screen.show_cursor(show)
852 }
853
854 pub fn enable_mouse_support(&mut self) -> Result<()> {
856 self.mouse_enabled = true;
857 self.enable_mouse()
858 }
859
860 pub fn disable_mouse_support(&mut self) -> Result<()> {
862 self.mouse_enabled = false;
863 self.disable_mouse()
864 }
865
866 pub fn clear_on_exit(&mut self, clear: bool) {
867 self.clear_on_exit = clear;
868 }
869
870 fn enable_mouse(&mut self) -> Result<()> {
872 let output = self
873 .output
874 .as_mut()
875 .ok_or(TuikitError::TerminalNotStarted)?;
876 output.enable_mouse_support();
877 Ok(())
878 }
879
880 fn disable_mouse(&mut self) -> Result<()> {
882 let output = self
883 .output
884 .as_mut()
885 .ok_or(TuikitError::TerminalNotStarted)?;
886 output.disable_mouse_support();
887 Ok(())
888 }
889}
890
891impl Drop for TermLock {
892 fn drop(&mut self) {
893 let _ = self.pause(true);
894 }
895}