1#![feature(decl_macro)]
2
3use std::path::Path;
4use bmp;
5use log;
6use easycurses;
7use pancurses;
8use png;
9use easycurses::EasyCurses;
10use lazy_static::lazy_static;
11use mat::Mat;
12
13pub use pancurses::COLOR_PAIR as color_pair;
14
15type PancursesResult = i32;
16
17#[allow(unused_macros)]
18macro_rules! debug {
19 ($e:expr) => { log::debug!("{}: {:?}", stringify!($e), $e); }
20}
21
22lazy_static!{
23 pub static ref INIT_COLORS_DEFAULT : [[u8; 3]; 8] = {
26 use easycurses::Color;
27 let mut colors = [[0,0,0]; 8];
28 colors[Color::Black as usize ] = [ 25, 25, 25 ];
29 colors[Color::Red as usize ] = [ 238, 0, 0 ];
30 colors[Color::Green as usize ] = [ 0, 163, 0 ];
31 colors[Color::Blue as usize ] = [ 67, 67, 255 ];
32 colors[Color::Yellow as usize ] = [ 137, 137, 0 ];
33 colors[Color::Magenta as usize ] = [ 181, 0, 181 ];
34 colors[Color::Cyan as usize ] = [ 0, 147, 147 ];
35 colors[Color::White as usize ] = [ 230, 230, 230 ];
36 colors
37 };
38}
39
40pub macro pancurses_ok {
42 ($e:expr) => {{
43 let result = $e;
44 if result != $crate::pancurses::OK {
45 $crate::log::error!("{}:l{}: pancurses error: {:?}", file!(), line!(),
46 result);
47 panic!()
48 }
49 }}
50}
51
52pub macro pancurses_warn_ok {
54 ($e:expr) => {{
55 let result = $e;
56 if result != $crate::pancurses::OK {
57 $crate::log::warn!("{}:l{}: pancurses error: {:?}", file!(), line!(),
58 result);
59 }
60 }}
61}
62
63pub macro pancurses_err {
65 ($e:expr) => {{
66 let result = $e;
67 if result != $crate::pancurses::ERR {
68 $crate::log::error!("{}:l{}: pancurses expected error, got: {:?}",
69 file!(), line!(), result);
70 panic!()
71 }
72 }}
73}
74
75pub macro pancurses_warn_err {
77 ($e:expr) => {{
78 let result = $e;
79 if result != $crate::pancurses::ERR {
80 $crate::log::warn!("{}:l{}: pancurses expected error, got: {:?}",
81 file!(), line!(), result);
82 }
83 }}
84}
85
86#[cfg(not(target_os = "windows"))]
87pub const ATTRIBUTE_MASK : pancurses::chtype = pancurses::A_ATTRIBUTES;
88#[cfg(target_os = "windows")]
89pub const ATTRIBUTE_MASK : pancurses::chtype =
90 !0x0 << pancurses::PDC_CHARTEXT_BITS;
91
92#[derive(Debug)]
94pub struct Curses {
95 pub easycurses : EasyCurses
96}
97
98#[inline]
102pub const fn color_pair_id (fg : easycurses::Color, bg : easycurses::Color)
103 -> i16
104{
105 1 + 8 * fg as i16 + bg as i16
106}
107
108#[inline]
112pub fn color_pair_attr (fg : easycurses::Color, bg : easycurses::Color)
113 -> pancurses::chtype
114{
115 color_pair (color_pair_id (fg, bg) as pancurses::chtype)
116}
117
118#[inline]
120pub fn chtype_character (ch : pancurses::chtype) -> char {
121 ch.to_le_bytes()[0] as char
122}
123
124#[inline]
126pub fn chtype_color_pair (ch : pancurses::chtype) -> i16 {
127 ch.to_le_bytes()[1] as i16
128}
129
130#[inline]
136pub fn color_pair_colors (color_pair : i16)
137 -> Option <(easycurses::Color, easycurses::Color)>
138{
139 if color_pair == 0 {
140 Some ((easycurses::Color::White, easycurses::Color::Black))
141 } else if color_pair <= 64 {
142 let bg = (color_pair-1) & 0b0000_0111;
143 let fg = ((color_pair-1) & 0b0011_1000) >> 3;
144 Some ((
145 color_from_primitive (fg).unwrap(),
146 color_from_primitive (bg).unwrap()
147 ))
148 } else {
149 None
150 }
151}
152
153#[inline]
156pub const fn chtype_attrs (ch : pancurses::chtype) -> pancurses::chtype {
157 ATTRIBUTE_MASK & ch
158}
159
160pub fn color_from_primitive (color : i16)
161 -> Option <easycurses::Color>
162{
163 use easycurses::Color;
164 let color = match color {
165 0 => Color::Black,
166 1 => Color::Red,
167 2 => Color::Green,
168 3 => Color::Yellow,
169 4 => Color::Blue,
170 5 => Color::Magenta,
171 6 => Color::Cyan,
172 7 => Color::White,
173 _ => return None
174 };
175 Some (color)
176}
177
178pub fn color_complement (color : easycurses::Color)
179 -> easycurses::Color
180{
181 use easycurses::Color;
182 match color {
183 Color::Black => Color::White,
184 Color::White => Color::Black,
185 Color::Red => Color::Cyan,
186 Color::Green => Color::Magenta,
187 Color::Blue => Color::Yellow,
188 Color::Cyan => Color::Red,
189 Color::Magenta => Color::Green,
190 Color::Yellow => Color::Blue
191 }
192}
193
194pub fn rgba_to_color (rgba : [u8; 4]) -> Option <easycurses::Color> {
200 use easycurses::Color;
201 let color = match rgba {
202 [ _, _, _, 0] => return None,
203 [ 0, 0, 0, _] => Color::Black,
204 [255, 255, 255, _] => Color::White,
205 [255, 0, 0, _] => Color::Red,
206 [ 0, 255, 0, _] => Color::Green,
207 [ 0, 0, 255, _] => Color::Blue,
208 [ 0, 255, 255, _] => Color::Cyan,
209 [255, 0, 255, _] => Color::Magenta,
210 [255, 255, 0, _] => Color::Yellow,
211 _ => return None
212 };
213 Some (color)
214}
215
216#[inline]
219pub fn rgba_to_color_pair (rgba : [u8; 4]) -> Option <pancurses::ColorPair> {
220 rgba_to_color (rgba).map (
221 |bg| pancurses::ColorPair (color_pair_id (color_complement (bg), bg) as u8))
222}
223
224#[inline]
233pub fn rgba_to_color_halftone (rgba : [u8; 4]) -> Option <pancurses::chtype> {
234 use easycurses::Color;
235 let fg = Color::Black;
236 let (bg, ch) = if let Some (bg) = rgba_to_color (rgba) {
237 (bg, b' ' as pancurses::chtype)
238 } else {
239 let bg = match rgba {
240 [ _, _, _, 0] => return None,
241 [ 0, 0, 0, _] => Color::Black,
242 [128, 128, 128, _] => Color::White,
243 [128, 0, 0, _] => Color::Red,
244 [ 0, 128, 0, _] => Color::Green,
245 [ 0, 0, 128, _] => Color::Blue,
246 [ 0, 128, 128, _] => Color::Cyan,
247 [128, 0, 128, _] => Color::Magenta,
248 [128, 128, 0, _] => Color::Yellow,
249 _ => return None
250 };
251 (bg, pancurses::ACS_CKBOARD())
252 };
253 Some (ch | color_pair_attr (fg, bg))
254}
255
256#[deprecated]
261pub fn image_text_load <P : AsRef <Path>> (path : P) -> Mat <char> {
262 use std::io::Read;
263 let mut buf = String::new();
264 let _ = std::fs::File::open (path).unwrap().read_to_string (&mut buf).unwrap();
265 buf.pop(); #[allow(deprecated)]
267 image_text_load_str (&buf)
268}
269
270#[deprecated]
278pub fn image_text_load_str (chars : &str) -> Mat <char> {
279 let lines = chars.lines();
280 let rows = lines.clone().count();
281 let cols = lines.clone().map (|line| line.len()).max().unwrap();
282 let mut vec = Vec::new();
283 for line in lines {
284 let len = line.chars().count();
285 vec.extend (line.chars());
286 vec.extend (std::iter::repeat (' ').take (cols - len));
287 }
288 Mat::from_vec ((rows, cols).into(), vec).unwrap()
289}
290
291pub fn image_ascii_load <P : AsRef <Path>> (path : P) -> Mat <u8> {
293 use std::io::Read;
294 let mut buf = Vec::new();
295 let _ = std::fs::File::open (path).unwrap().read_to_end (&mut buf).unwrap();
296 buf.pop(); image_ascii_load_bytes (buf.as_slice())
298}
299
300pub fn image_ascii_load_bytes (chars : &[u8]) -> Mat <u8> {
305 let split = chars.split (|ch| ch == &b'\n');
306 let rows = split.clone().count();
307 let cols = split.clone().map (|line| line.len()).max().unwrap();
308 let mut vec = Vec::new();
309 for line in split {
310 let len = line.len();
311 vec.extend (line);
312 vec.extend (std::iter::repeat (b' ').take (cols - len));
313 }
314 Mat::from_vec ((rows, cols).into(), vec).unwrap()
315}
316
317#[inline]
319pub fn image_color_load_bmp24 <P : AsRef <Path>>
320 (path : P) -> Mat <easycurses::Color>
321{
322 let image = bmp::open (path).unwrap();
323 image_color_load_bmp24_helper (image)
324}
325
326#[inline]
328pub fn image_color_load_bmp24_bytes (mut bytes : &[u8])
329 -> Mat <easycurses::Color>
330{
331 let image = bmp::from_reader (&mut bytes).unwrap();
332 image_color_load_bmp24_helper (image)
333}
334
335#[inline]
338pub fn image_color_load_png <P : AsRef <Path>>
339 (path : P) -> Mat <Option <easycurses::Color>>
340{
341 let file = std::fs::File::open (path).unwrap();
342 let decoder = png::Decoder::new (file);
343 image_color_load_png_helper (decoder)
344}
345
346#[inline]
349pub fn image_color_load_png_bytes (bytes : &[u8])
350 -> Mat <Option <easycurses::Color>>
351{
352 let decoder = png::Decoder::new (bytes);
353 image_color_load_png_helper (decoder)
354}
355
356#[inline]
359pub fn image_color_bg_load_bmp24 <P : AsRef <Path>>
360 (path : P) -> Mat <pancurses::ColorPair>
361{
362 let image = bmp::open (path).unwrap();
363 image_color_bg_load_bmp24_helper (image)
364}
365
366#[inline]
369pub fn image_color_bg_load_bmp24_bytes (mut bytes : &[u8])
370 -> Mat <pancurses::ColorPair>
371{
372 let image = bmp::from_reader (&mut bytes).unwrap();
373 image_color_bg_load_bmp24_helper (image)
374}
375
376pub fn image_color_pair_load_bmp24 <P : AsRef <Path>>
380 (fg : P, bg : P) -> Mat <pancurses::ColorPair>
381{
382 let fg = bmp::open (fg).unwrap();
383 let bg = bmp::open (bg).unwrap();
384 image_color_pair_load_bmp24_helper (fg, bg)
385}
386
387pub fn image_color_pair_load_bmp24_bytes (mut fg : &[u8], mut bg : &[u8])
391 -> Mat <pancurses::ColorPair>
392{
393 let fg = bmp::from_reader (&mut fg).unwrap();
394 let bg = bmp::from_reader (&mut bg).unwrap();
395 image_color_pair_load_bmp24_helper (fg, bg)
396}
397
398pub fn image_color_halftone_load_bmp24 <P : AsRef <Path>>
400 (path : P) -> Mat <pancurses::chtype>
401{
402 let image = bmp::open (path).unwrap();
403 image_color_halftone_load_bmp24_helper (image)
404}
405
406pub fn image_color_halftone_load_bmp24_bytes (mut bytes : &[u8])
408 -> Mat <pancurses::chtype>
409{
410 let image = bmp::from_reader (&mut bytes).unwrap();
411 image_color_halftone_load_bmp24_helper (image)
412}
413
414pub fn image_chtype (colors : &Mat <pancurses::ColorPair>, chars : &Mat <u8>)
416 -> Mat <pancurses::chtype>
417{
418 let dimensions = colors.dimensions();
419 assert_eq!(dimensions, chars.dimensions());
420 let vec = colors.elements().zip (chars.elements()).map (|(color, ch)|
421 color_pair (color.0 as pancurses::chtype) |
422 *ch as pancurses::chtype
423 ).collect::<Vec <pancurses::chtype>>();
424 Mat::from_vec (dimensions, vec).unwrap()
425}
426
427pub fn image_chtype_text (
429 colors : &Mat <pancurses::ColorPair>,
430 chars : &Mat <char>
431) -> Mat <pancurses::chtype> {
432 let dimensions = colors.dimensions();
433 assert_eq!(dimensions, chars.dimensions());
434 let vec = colors.elements().zip (chars.elements()).map (|(color, ch)|
435 color_pair (color.0 as pancurses::chtype) |
436 *ch as pancurses::chtype
437 ).collect::<Vec <pancurses::chtype>>();
438 Mat::from_vec (dimensions, vec).unwrap()
439}
440
441fn image_color_load_bmp24_helper (image : bmp::Image)
446 -> Mat <easycurses::Color>
447{
448 let cols = image.get_width() as usize;
449 let rows = image.get_height() as usize;
450 let vec = image.coordinates().map (|(x, y)|{
451 let pixel = image.get_pixel (x, y);
452 rgba_to_color ([pixel.r, pixel.g, pixel.b, 255]).unwrap()
453 }).collect::<Vec <easycurses::Color>>();
454 Mat::from_vec ((rows, cols).into(), vec).unwrap()
455}
456
457fn image_color_bg_load_bmp24_helper (image : bmp::Image)
458 -> Mat <pancurses::ColorPair>
459{
460 let cols = image.get_width() as usize;
461 let rows = image.get_height() as usize;
462 let vec = image.coordinates().map (|(x, y)|{
463 let pixel = image.get_pixel (x, y);
464 rgba_to_color_pair ([pixel.r, pixel.g, pixel.b, 255]).unwrap()
465 }).collect::<Vec <pancurses::ColorPair>>();
466 Mat::from_vec ((rows, cols).into(), vec).unwrap()
467}
468
469fn image_color_pair_load_bmp24_helper (fg : bmp::Image, bg : bmp::Image)
470 -> Mat <pancurses::ColorPair>
471{
472 let rows = fg.get_height() as usize;
473 let cols = fg.get_width() as usize;
474 assert_eq!(rows, bg.get_height() as usize);
475 assert_eq!(cols, bg.get_width() as usize);
476 let vec = fg.coordinates().map (|(x, y)|{
477 let fg = {
478 let pixel = fg.get_pixel (x, y);
479 rgba_to_color ([pixel.r, pixel.g, pixel.b, 255]).unwrap()
480 };
481 let bg = {
482 let pixel = bg.get_pixel (x, y);
483 rgba_to_color ([pixel.r, pixel.g, pixel.b, 255]).unwrap()
484 };
485 pancurses::ColorPair (color_pair_id (fg, bg) as u8)
486 }).collect::<Vec <pancurses::ColorPair>>();
487 Mat::from_vec ((rows, cols).into(), vec).unwrap()
488}
489
490fn image_color_halftone_load_bmp24_helper (image : bmp::Image)
491 -> Mat <pancurses::chtype>
492{
493 let cols = image.get_width() as usize;
494 let rows = image.get_height() as usize;
495 let vec = image.coordinates().map (|(x, y)|{
496 let pixel = image.get_pixel (x, y);
497 rgba_to_color_halftone ([pixel.r, pixel.g, pixel.b, 255]).unwrap()
498 }).collect::<Vec <pancurses::chtype>>();
499 Mat::from_vec ((rows, cols).into(), vec).unwrap()
500}
501
502fn image_color_load_png_helper <R : std::io::Read> (decoder : png::Decoder <R>)
503 -> Mat <Option <easycurses::Color>>
504{
505 let mut reader = decoder.read_info().unwrap();
506 let info = reader.info();
507 match info.color_type {
508 png::ColorType::Rgba => {}
509 _ => {
510 log::error!("invalid color type: {:?}", info.color_type);
511 panic!()
512 }
513 }
514 match info.bit_depth {
515 png::BitDepth::Eight => {}
516 _ => {
517 log::error!("invalid bit depth: {:?}", info.bit_depth);
518 panic!()
519 }
520 }
521 let mut bytes = vec![0; reader.output_buffer_size()];
522 let rows = info.height as usize;
523 let cols = info.width as usize;
524 reader.next_frame (&mut bytes).unwrap();
525 let mut vec = Vec::with_capacity (rows * cols);
526 for i in 0..rows {
527 for j in 0..cols {
528 let offset = i * 4 * cols + j * 4;
529 let pixel = &bytes[offset..offset+4];
530 let color = rgba_to_color ([pixel[0], pixel[1], pixel[2], pixel[3]]);
531 vec.push (color);
532 }
533 }
534 Mat::from_vec ((rows, cols).into(), vec).unwrap()
535}
536
537impl Curses {
538 pub fn new (color_init : Option <&[[u8; 3]; 8]>) -> Self {
547 log::trace!("new...");
548 use easycurses::{CursorVisibility, InputMode};
549 let easycurses = EasyCurses::initialize_system().unwrap();
550 easycurses.win.keypad (true); let mut curses = Curses { easycurses };
552 curses.set_cursor_visibility (CursorVisibility::Invisible); curses.set_echo (false); curses.set_input_mode (InputMode::Character); if let Some (colors) = color_init {
556 if !pancurses::can_change_color() {
557 log::warn!("curses color init: pancurses backend reports terminal can \
558 not change color");
559 } else {
560 let init_color = |color_name| {
561 const fn byte_to_milli_color (byte : u8) -> i16 {
562 ((byte as f64 / 255.0) * 1000.0) as i16
563 }
564 let color = colors[color_name as usize];
565 pancurses_ok!(pancurses::init_color (
566 color_name as i16,
567 byte_to_milli_color (color[0]),
568 byte_to_milli_color (color[1]),
569 byte_to_milli_color (color[2])
570 ));
571 };
572 init_color (easycurses::Color::Black);
573 init_color (easycurses::Color::Red);
574 init_color (easycurses::Color::Green);
575 init_color (easycurses::Color::Blue);
576 init_color (easycurses::Color::Yellow);
577 init_color (easycurses::Color::Magenta);
578 init_color (easycurses::Color::Cyan);
579 init_color (easycurses::Color::White);
580 }
581 }
582 log::trace!("...new");
583 curses
584 }
585
586 #[inline]
588 pub fn win (&self) -> &pancurses::Window {
589 &self.easycurses.win
590 }
591
592 #[inline]
594 pub fn getch_wait (&mut self) -> Option <easycurses::Input> {
595 use easycurses::{InputMode, TimeoutMode};
596 self.set_input_mode (InputMode::Character);
597 self.set_input_timeout (TimeoutMode::Never);
598 self.get_input()
599 }
600
601 #[inline]
603 pub fn getch_timeout (&mut self, timeout : u32)
604 -> Option <easycurses::Input>
605 {
606 debug_assert!(timeout <= std::i32::MAX as u32);
607 use easycurses::{InputMode, TimeoutMode};
608 self.set_input_mode (InputMode::Character);
609 self.set_input_timeout (TimeoutMode::WaitUpTo (timeout as i32));
610 self.get_input()
611 }
612
613 #[inline]
615 pub fn getch_nowait (&mut self) -> Option <easycurses::Input> {
616 use easycurses::{InputMode, TimeoutMode};
617 self.set_input_mode (InputMode::Character);
618 self.set_input_timeout (TimeoutMode::Immediate);
619 self.get_input()
620 }
621
622 #[inline]
624 pub fn getline_wait (&mut self) -> Option <easycurses::Input> {
625 use easycurses::{InputMode, TimeoutMode};
626 self.set_input_mode (InputMode::Cooked);
627 self.set_input_timeout (TimeoutMode::Never);
628 self.get_input()
629 }
630
631 #[inline]
633 pub fn getline_nowait (&mut self) -> Option <easycurses::Input> {
634 use easycurses::{InputMode, TimeoutMode};
635 self.set_input_mode (InputMode::Cooked);
636 self.set_input_timeout (TimeoutMode::Immediate);
637 self.get_input()
638 }
639
640 #[inline]
641 pub fn rows (&self) -> i32 {
642 self.win().get_max_y()
643 }
644
645 #[inline]
646 pub fn columns (&self) -> i32 {
647 self.win().get_max_x()
648 }
649
650 #[inline]
651 pub fn dimensions_rc (&self) -> (i32, i32) {
652 self.easycurses.get_row_col_count()
653 }
654
655 #[inline]
656 pub fn center_col (&self) -> i32 {
657 let (_, cols) = self.get_row_col_count();
658 cols / 2
659 }
660
661 #[inline]
662 pub fn center_row (&self) -> i32 {
663 let (rows, _) = self.get_row_col_count();
664 rows / 2
665 }
666
667 #[inline]
668 pub fn center_rc (&self) -> (i32, i32) {
669 let (rows, cols) = self.get_row_col_count();
670 (rows / 2, cols / 2)
671 }
672
673 pub fn print_centered (&mut self,
675 string : &str,
676 row_offset : Option <i32>,
677 col_offset : Option <i32>
678 ) -> (i32, i32) {
679 let (longest, count) = string.lines().fold ((0,0),
680 |(longest, count), next| {
681 let next_len = next.len();
682 (usize::max (longest, next_len), count+1)
683 }
684 );
685 let half_longest = longest as i32 / 2;
686 let half_count = count as i32 / 2;
687 let (center_row, center_col) = {
688 let (row, col) = self.center_rc();
689 (row + row_offset.unwrap_or (0), col + col_offset.unwrap_or (0))
690 };
691 for (i, line) in string.lines().enumerate() {
692 let at_row = center_row - half_count + i as i32;
693 let at_col = center_col - half_longest;
694 let _ = self.move_rc (at_row, at_col);
697 let _ = self.print (line);
700 }
701 (center_row - half_count, center_col - half_longest)
702 }
703
704 #[inline]
707 #[must_use]
708 pub fn draw_border_default (&mut self) -> PancursesResult {
709 self.win().draw_box (pancurses::ACS_VLINE(), pancurses::ACS_HLINE())
710 }
711
712 #[must_use]
717 pub fn draw_box (&mut self,
718 ch : pancurses::chtype,
719 rc_min : (i32, i32),
720 rc_max : (i32, i32)
721 ) -> PancursesResult {
722 let mut out = pancurses::OK;
723 let mut result_ok = |result| if out == pancurses::OK {
725 out = result;
726 };
727 let (min_row, min_col) =
729 (i32::min (rc_min.0, rc_max.0), i32::min (rc_min.1, rc_max.1));
730 let (max_row, max_col) =
731 (i32::max (rc_min.0, rc_max.0), i32::max (rc_min.1, rc_max.1));
732 let rows = max_row - min_row + 1;
733 let cols = max_col - min_col + 1;
734 let _ = self.move_rc (min_row, min_col);
735 result_ok (self.win().hline (ch, cols));
736 let _ = self.move_rc (min_row, min_col);
737 result_ok (self.win().vline (ch, rows));
738 let _ = self.move_rc (max_row, min_col);
739 result_ok (self.win().hline (ch, cols));
740 let _ = self.move_rc (min_row, max_col);
741 result_ok (self.win().vline (ch, rows));
742 out
743 }
744
745 #[must_use]
750 pub fn draw_border (&mut self,
751 left : pancurses::chtype,
752 right : pancurses::chtype,
753 top : pancurses::chtype,
754 bottom : pancurses::chtype,
755 top_left : pancurses::chtype,
756 top_right : pancurses::chtype,
757 bottom_left : pancurses::chtype,
758 bottom_right : pancurses::chtype,
759 rc_min : (i32, i32),
760 rc_max : (i32, i32),
761 thickness_top : u32,
762 thickness_bottom : u32,
763 thickness_left : u32,
764 thickness_right : u32
765 ) -> PancursesResult {
766 let mut out = pancurses::OK;
767 let mut result_ok = |result| if out == pancurses::OK {
769 out = result;
770 };
771 let (thickness_top, thickness_bottom, thickness_left, thickness_right) = (
772 thickness_top as i32,
773 thickness_bottom as i32,
774 thickness_left as i32,
775 thickness_right as i32);
776 let (min_row, min_col) =
778 (i32::min (rc_min.0, rc_max.0), i32::min (rc_min.1, rc_max.1));
779 let (max_row, max_col) =
780 (i32::max (rc_min.0, rc_max.0), i32::max (rc_min.1, rc_max.1));
781 let rows = max_row - min_row + 1;
782 let cols = max_col - min_col + 1;
783 let total_width = thickness_left + thickness_right;
784 let total_height = thickness_top + thickness_bottom;
785 for i in 0..thickness_top {
786 let _ = self.move_rc (min_row + i, min_col);
787 result_ok (self.win().hline (top_left, thickness_left));
788 let _ = self.move_rc (min_row + i, min_col + thickness_left);
789 result_ok (self.win().hline (top, cols - total_width));
790 let _ = self.move_rc (min_row + i, min_col + cols - thickness_right);
791 result_ok (self.win().hline (top_right, thickness_right));
792 }
793 for i in 0..thickness_bottom {
794 let _ = self.move_rc (max_row - i, min_col);
795 result_ok (self.win().hline (bottom_left, thickness_left));
796 let _ = self.move_rc (max_row - i, min_col + thickness_left);
797 result_ok (self.win().hline (bottom, cols - total_width));
798 let _ = self.move_rc (max_row - i, min_col + cols - thickness_right);
799 result_ok (self.win().hline (bottom_right, thickness_right));
800 }
801 for i in 0..thickness_left {
802 let _ = self.move_rc (min_row + thickness_top, min_col + i);
803 result_ok (self.win().vline (left, rows - total_height));
804 }
805 for i in 0..thickness_right {
806 let _ = self.move_rc (min_row + thickness_top, max_col - i);
807 result_ok (self.win().vline (right, rows - total_height));
808 }
809 out
810 }
811
812 pub fn draw_rect (&mut self,
814 border : pancurses::chtype,
815 fill : pancurses::chtype,
816 rc_min : (i32, i32),
817 rc_max : (i32, i32),
818 thickness_h : u32, thickness_v : u32 ) {
821 let mut out = pancurses::OK;
822 let mut result_ok = |result| if out == pancurses::OK {
824 out = result;
825 };
826 let (min_row, min_col) =
828 (i32::min (rc_min.0, rc_max.0), i32::min (rc_min.1, rc_max.1));
829 let (max_row, max_col) =
830 (i32::max (rc_min.0, rc_max.0), i32::max (rc_min.1, rc_max.1));
831 let rows = max_row - min_row + 1;
832 let cols = max_col - min_col + 1;
833 for i in 0..rows {
834 let _ = self.move_rc (min_row + i, min_col);
835 result_ok (self.win().hline (fill, cols));
836 }
837 for i in 0..thickness_h as i32 {
838 let _ = self.move_rc (min_row + i, min_col);
839 result_ok (self.win().hline (border, cols));
840 let _ = self.move_rc (max_row - i, min_col);
841 result_ok (self.win().hline (border, cols));
842 }
843 for i in 0..thickness_v as i32 {
844 let _ = self.move_rc (min_row, min_col + i);
845 result_ok (self.win().vline (border, rows));
846 let _ = self.move_rc (min_row, max_col - i);
847 result_ok (self.win().vline (border, rows));
848 }
849 }
850
851 pub fn draw_image_color_pair (&mut self,
853 at_rc : (i32, i32), image : &Mat <pancurses::ColorPair>
854 ) {
855 let mut row = 0;
856 while row < image.height() {
857 let mut col = 0;
858 loop {
859 let color = *image.get ((row, col).into()).unwrap();
860 let start = col as i32;
861 col += 1;
862 while col < image.width()-1 {
863 if color == *image.get ((row, col).into()).unwrap() {
864 col += 1;
865 } else {
866 break
867 }
868 }
869 let _ = self.win().mvchgat (
871 at_rc.0 + row as i32,
872 at_rc.1 + start as i32,
873 col as i32 - start,
874 0x0, color.0 as i16);
875 if col == image.width() {
876 break
877 }
878 }
879 row += 1;
880 }
881 }
882
883 pub fn draw_image_chtype (&mut self,
885 at_rc : (i32, i32), image : &Mat <pancurses::chtype>
886 ) {
887 let dimensions = image.dimensions();
888 for row in 0..dimensions.rows {
889 for col in 0..dimensions.columns {
890 let ch = *image.get ((row, col).into()).unwrap();
891 let at_row = at_rc.0 + row as i32;
892 let at_col = at_rc.1 + col as i32;
893 let _ = self.win().mvaddch (at_row, at_col, ch);
895 }
896 }
897 }
898
899 pub fn log_info (&self) {
901 log::info!("log info...");
902 log::info!(" cursor(row,col): {:?}", self.get_cursor_rc());
903 log::info!(" dimensions(row,col): {:?}", self.dimensions_rc());
904 log::info!(" is color terminal: {}", self.is_color_terminal());
905 log::info!(" can change color: {}", pancurses::can_change_color());
906 log::info!(" maximum color pairs: {}", pancurses::COLOR_PAIRS());
907 log::info!(" maximum colors: {}", pancurses::COLORS());
908 log::info!(" colors:");
909 for i in 0..pancurses::COLORS() as i16 {
910 let color_string = if let Some (color) = color_from_primitive (i) {
911 format!("{:?}", color)
912 } else {
913 i.to_string()
914 };
915 let rgb = pancurses::color_content (i);
916 log::info!(" {}: {:?}", color_string, rgb);
917 }
918 log::info!("...log info");
919 }
920}
921
922impl Default for Curses {
923 fn default() -> Self {
925 Curses::new (None)
926 }
927}
928
929impl std::ops::Deref for Curses {
930 type Target = EasyCurses;
931 fn deref (&self) -> &EasyCurses {
932 &self.easycurses
933 }
934}
935
936impl std::ops::DerefMut for Curses {
937 fn deref_mut (&mut self) -> &mut EasyCurses {
938 &mut self.easycurses
939 }
940}
941
942#[cfg(test)]
943mod tests {
944 use super::*;
945 #[test]
946 fn test_color_from_primitive() {
947 for i in 0..8 {
948 assert_eq!(i, color_from_primitive (i).unwrap() as i16);
949 }
950 }
951}