use crossterm::{Attribute, ClearType, Color, Colored, Terminal, TerminalCursor}; use glk; use glk::garglk::zcolor; use glk::gidispatch; use glk::helpers::stream::Style; use glk::types::{strid_t, style, winid_t, winmethod, wintype}; use glk::{latin1, unicode}; use crate::window_stream::WindowStreamFuncs; /** A Glk window. */ pub struct Window { /** Glk window type. */ pub wtype: wintype, /** Glk rock for window. */ pub rock: u32, /** GI dispatch rock for window. */ pub girock: gidispatch::rock, /** Associated window stream. */ pub stream: strid_t, /** Associated echo stream. */ pub echo_stream: strid_t, /** Current colors from Glk style. */ pub style: (Color, Color), /** Crossterm terminal. */ pub terminal: Terminal, /** Cursor positioning. */ pub cursor: TerminalCursor, } /** Hardcoded foreground and background per Glk style. * We don't pay attention to glk_stylehint_set at the moment. */ fn colors_from_style(styl: style) -> (Color, Color) { match styl { style::Normal => (Color::Grey, Color::Reset), style::Emphasized => (Color::White, Color::Reset), style::Preformatted => (Color::Yellow, Color::Reset), style::Header => (Color::DarkMagenta, Color::Reset), style::Subheader => (Color::Magenta, Color::Reset), style::Alert => (Color::Red, Color::Reset), style::Note => (Color::DarkGrey, Color::Reset), style::BlockQuote => (Color::DarkYellow, Color::Reset), style::Input => (Color::Reset, Color::Reset), style::User1 => (Color::Blue, Color::Reset), style::User2 => (Color::Cyan, Color::Reset), _ => (Color::Reset, Color::Reset), } } impl Window { pub fn new(wtype: wintype, rock: u32) -> Self { let mut ret = Self { wtype, rock, girock: gidispatch::DUMMY_ROCK, stream: 0 as strid_t, echo_stream: 0 as strid_t, style: (Color::Reset, Color::Reset), terminal: Terminal::new(), cursor: TerminalCursor::new(), }; ret.set_style(Style::Glk(style::Normal)); ret } /* Window handling. */ pub fn get_size(&mut self) -> (u32, u32) { let (cols, rows) = self.terminal.size().unwrap(); (u32::from(cols), u32::from(rows)) } pub fn set_arrangement(&mut self, _method: winmethod, _size: u32, _keywin: winid_t) { /* Nothing to do, with a single window there is no possibility of arrangement. */ } pub fn get_arrangement(&mut self) -> (u32, u32, winid_t) { (0, 0, 0 as winid_t) } pub fn get_rock(&mut self) -> u32 { self.rock } pub fn get_type(&mut self) -> wintype { self.wtype } pub fn get_parent(&mut self) -> winid_t { 0 as winid_t } pub fn get_sibling(&mut self) -> winid_t { 0 as winid_t } pub fn clear(&mut self) { self.terminal.clear(ClearType::All).unwrap(); } pub fn move_cursor(&mut self, xpos: u32, ypos: u32) { // both Glk and crossterm positions are (0,0) based. // check positions (65535 would panic too because internally in crossterm 1 will be added) if xpos < 65535 && ypos < 65535 { self.cursor.goto(xpos as u16, ypos as u16).unwrap() } } pub fn get_stream(&self) -> strid_t { self.stream } pub fn set_echo_stream(&mut self, str: strid_t) { self.echo_stream = str; } pub fn get_echo_stream(&self) -> strid_t { self.echo_stream } pub fn get_di_rock(&self) -> gidispatch::rock { self.girock } } impl WindowStreamFuncs for Window { /* Window stream handling. */ fn put_buffer(&mut self, buf: &[u8]) -> strid_t { if buf.len() == 1 { print!("{}", latin1::to_char(buf[0])); } else { print!("{}", latin1::to_string(buf)); } self.echo_stream } fn put_buffer_uni(&mut self, buf: &[u32]) -> strid_t { if buf.len() == 1 { print!("{}", unicode::to_char(buf[0])); } else { print!("{}", unicode::to_string(buf)); } self.echo_stream } fn set_style(&mut self, styl: Style) -> strid_t { match styl { /* Glk style. */ Style::Glk(styl) => { self.style = colors_from_style(styl); print!("{}{}", Colored::Fg(self.style.0), Colored::Bg(self.style.1)); } /* GarGlk color. */ Style::Zcolor(fg, bg) => { print!( "{}{}", Colored::Fg(if let Some((r, g, b)) = fg.to_rgb() { Color::Rgb { r, g, b } } else { match fg { zcolor::Current => self.style.0, zcolor::Default => Color::Reset, _ => Color::Reset, } }), Colored::Bg(if let Some((r, g, b)) = bg.to_rgb() { Color::Rgb { r, g, b } } else { match bg { zcolor::Current => self.style.1, zcolor::Default => Color::Reset, _ => Color::Reset, } }) ); } /* GarGlk reverse-video. */ Style::Reverse(val) => { print!( "{}", if val != 0 { Attribute::Reverse } else { Attribute::NoInverse } ); } } self.echo_stream } }