text.rs 4.48 KB
Newer Older
Jeremy Soller's avatar
Jeremy Soller committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
extern crate ransid;

use std::collections::{BTreeSet, VecDeque};

use self::ransid::{Console, Event};
use syscall::Result;

use display::Display;

pub struct TextScreen {
    pub console: Console,
    pub display: Display,
    pub changed: BTreeSet<usize>,
    pub input: VecDeque<u8>,
    pub end_of_input: bool,
    pub cooked: VecDeque<u8>,
    pub requested: usize
}

impl TextScreen {
    pub fn new(display: Display) -> TextScreen {
        TextScreen {
            console: Console::new(display.width/8, display.height/16),
            display: display,
            changed: BTreeSet::new(),
            input: VecDeque::new(),
            end_of_input: false,
            cooked: VecDeque::new(),
            requested: 0
        }
    }

    pub fn input(&mut self, buf: &[u8]) -> Result<usize> {
        if self.console.raw_mode {
            for &b in buf.iter() {
                self.input.push_back(b);
            }
        } else {
            for &b in buf.iter() {
                match b {
                    b'\x03' => {
                        self.end_of_input = true;
                        self.write(b"^C\n", true)?;
                    },
                    b'\x08' | b'\x7F' => {
                        if let Some(_c) = self.cooked.pop_back() {
                            self.write(b"\x08", true)?;
                        }
                    },
                    b'\n' | b'\r' => {
                        self.cooked.push_back(b);
                        while let Some(c) = self.cooked.pop_front() {
                            self.input.push_back(c);
                        }
                        self.write(b"\n", true)?;
                    },
                    _ => {
                        self.cooked.push_back(b);
                        self.write(&[b], true)?;
                    }
                }
            }
        }
        Ok(buf.len())
    }

    pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;

        while i < buf.len() && ! self.input.is_empty() {
            buf[i] = self.input.pop_front().unwrap();
            i += 1;
        }

        if i == 0 {
            self.end_of_input = false;
        }

        Ok(i)
    }

    pub fn will_block(&self) -> bool {
        self.input.is_empty() && ! self.end_of_input
    }

    pub fn write(&mut self, buf: &[u8], sync: bool) -> Result<usize> {
        if self.console.cursor && self.console.x < self.console.w && self.console.y < self.console.h {
            let x = self.console.x;
            let y = self.console.y;
            let color = self.console.background;
            self.display.rect(x * 8, y * 16, 8, 16, color.data);
            self.changed.insert(y);
        }

        {
            let display = &mut self.display;
            let changed = &mut self.changed;
            self.console.write(buf, |event| {
                match event {
                    Event::Char { x, y, c, color, bold, .. } => {
                        display.char(x * 8, y * 16, c, color.data, bold, false);
                        changed.insert(y);
                    },
                    Event::Rect { x, y, w, h, color } => {
                        display.rect(x * 8, y * 16, w * 8, h * 16, color.data);
                        for y2 in y..y + h {
                            changed.insert(y2);
                        }
                    },
                    Event::Scroll { rows, color } => {
                        display.scroll(rows * 16, color.data);
                        for y in 0..display.height/16 {
                            changed.insert(y);
                        }
                    }
                }
            });
        }

        if self.console.cursor && self.console.x < self.console.w && self.console.y < self.console.h {
            let x = self.console.x;
            let y = self.console.y;
            let color = self.console.foreground;
            self.display.rect(x * 8, y * 16, 8, 16, color.data);
            self.changed.insert(y);
        }

        if ! self.console.raw_mode && sync {
            self.sync();
        }

        Ok(buf.len())
    }

    pub fn sync(&mut self) {
        let width = self.display.width;
        for change in self.changed.iter() {
            self.display.sync(0, change * 16, width, 16);
        }
        self.changed.clear();
    }

    pub fn redraw(&mut self) {
        let width = self.display.width;
        let height = self.display.height;
        self.display.sync(0, 0, width, height);
        self.changed.clear();
    }
}