Commit 2c4240a6 authored by Jeremy Soller's avatar Jeremy Soller
Browse files

First commit

parents
[package]
name = "ransid"
description = "Rust ANSI Driver"
repository = "https://github.com/redox-os/ransid"
version = "0.1.1"
license-file = "LICENSE"
readme = "README.md"
keywords = ["redox", "ansi"]
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
[lib]
name = "ransid"
path = "src/lib.rs"
The MIT License (MIT)
Copyright (c) 2016 Jeremy Soller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# ransid
Rust ANSI Driver - A backend for terminal emulators written in Rust
[![Travis Build Status](https://travis-ci.org/redox-os/ransid.svg?branch=master)](https://travis-ci.org/redox-os/ransid)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![crates.io](http://meritbadge.herokuapp.com/ransid)](https://crates.io/crates/ransid)
use super::{Color, Style};
#[derive(Copy, Clone)]
pub struct Block {
pub c: char,
pub fg: Color,
pub bg: Color,
pub style: Style
}
impl Block {
pub fn new() -> Self {
Block {
c: ' ',
fg: Color::ansi(7),
bg: Color::ansi(0),
style: Style::Normal
}
}
}
/// A color
#[derive(Copy, Clone)]
#[repr(packed)]
pub struct Color {
pub data: u32,
}
impl Color {
/// Create a new color from RGB
pub fn new(r: u8, g: u8, b: u8) -> Self {
Color { data: 0xFF000000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32) }
}
pub fn ansi(value: u8) -> Self {
match value {
0 => Color::new(0x00, 0x00, 0x00),
1 => Color::new(0x80, 0x00, 0x00),
2 => Color::new(0x00, 0x80, 0x00),
3 => Color::new(0x80, 0x80, 0x00),
4 => Color::new(0x00, 0x00, 0x80),
5 => Color::new(0x80, 0x00, 0x80),
6 => Color::new(0x00, 0x80, 0x80),
7 => Color::new(0xc0, 0xc0, 0xc0),
8 => Color::new(0x80, 0x80, 0x80),
9 => Color::new(0xff, 0x00, 0x00),
10 => Color::new(0x00, 0xff, 0x00),
11 => Color::new(0xff, 0xff, 0x00),
12 => Color::new(0x00, 0x00, 0xff),
13 => Color::new(0xff, 0x00, 0xff),
14 => Color::new(0x00, 0xff, 0xff),
15 => Color::new(0xff, 0xff, 0xff),
16 ... 231 => {
let convert = |value: u8| -> u8 {
match value {
0 => 0,
_ => value * 0x28 + 0x28
}
};
let r = convert((value - 16)/36 % 6);
let g = convert((value - 16)/6 % 6);
let b = convert((value - 16) % 6);
Color::new(r, g, b)
},
232 ... 255 => {
let gray = (value - 232) * 10 + 8;
Color::new(gray, gray, gray)
},
_ => Color::new(0, 0, 0)
}
}
}
#![crate_name="console"]
#![crate_type="lib"]
#![feature(alloc)]
#![feature(collections)]
#![no_std]
extern crate alloc;
#[macro_use]
extern crate collections;
use alloc::boxed::Box;
use collections::String;
use collections::Vec;
use core::cmp;
pub use block::Block;
pub use color::Color;
pub use style::Style;
pub mod block;
pub mod color;
pub mod style;
pub struct Console {
pub display: Box<[Block]>,
pub x: usize,
pub y: usize,
pub w: usize,
pub h: usize,
pub foreground: Color,
pub background: Color,
pub style: Style,
pub cursor: bool,
pub redraw: bool,
pub escape: bool,
pub escape_sequence: bool,
pub escape_extra: bool,
pub sequence: Vec<String>,
pub raw_mode: bool,
}
impl Console {
pub fn new(w: usize, h: usize) -> Console {
Console {
display: vec![Block::new(); w * h].into_boxed_slice(),
x: 0,
y: 0,
w: w,
h: h,
foreground: Color::ansi(7),
background: Color::ansi(0),
style: Style::Normal,
cursor: true,
redraw: true,
escape: false,
escape_sequence: false,
escape_extra: false,
sequence: Vec::new(),
raw_mode: false,
}
}
fn block(&self, c: char) -> Block {
Block {
c: c,
fg: self.foreground,
bg: self.background,
style: self.style
}
}
pub fn code(&mut self, c: char) {
if self.escape_sequence {
match c {
'0' ... '9' => {
// Add a number to the sequence list
if let Some(mut value) = self.sequence.last_mut() {
value.push(c);
}
},
';' => {
// Split sequence into list
self.sequence.push(String::new());
},
'm' => {
// Display attributes
let mut value_iter = self.sequence.iter();
while let Some(value_str) = value_iter.next() {
let value = value_str.parse::<u8>().unwrap_or(0);
match value {
0 => {
self.foreground = Color::ansi(7);
self.background = Color::ansi(0);
self.style = Style::Normal;
},
1 => {
self.style = Style::Bold;
},
30 ... 37 => self.foreground = Color::ansi(value - 30),
38 => match value_iter.next().map_or("", |s| &s).parse::<usize>().unwrap_or(0) {
2 => {
//True color
let r = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
let g = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
let b = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
self.foreground = Color::new(r, g, b);
},
5 => {
//256 color
let color_value = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
self.foreground = Color::ansi(color_value);
},
_ => {}
},
40 ... 47 => self.background = Color::ansi(value - 40),
48 => match value_iter.next().map_or("", |s| &s).parse::<usize>().unwrap_or(0) {
2 => {
//True color
let r = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
let g = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
let b = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
self.background = Color::new(r, g, b);
},
5 => {
//256 color
let color_value = value_iter.next().map_or("", |s| &s).parse::<u8>().unwrap_or(0);
self.background = Color::ansi(color_value);
},
_ => {}
},
_ => {},
}
}
self.escape_sequence = false;
},
'A' => {
self.y -= cmp::min(self.y, self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(1));
self.escape_sequence = false;
},
'B' => {
self.y += cmp::min(self.h - 1 - self.y, self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(1));
self.escape_sequence = false;
},
'C' => {
self.x += cmp::min(self.w - 1 - self.x, self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(1));
self.escape_sequence = false;
},
'D' => {
self.x -= cmp::min(self.x, self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(1));
self.escape_sequence = false;
},
'H' | 'f' => {
let row = self.sequence.get(0).map_or("", |p| &p).parse::<isize>().unwrap_or(1);
self.y = cmp::max(0, row - 1) as usize;
let col = self.sequence.get(1).map_or("", |p| &p).parse::<isize>().unwrap_or(1);
self.x = cmp::max(0, col - 1) as usize;
self.escape_sequence = false;
},
'J' => {
match self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(0) {
0 => {
let block = self.block(' ');
for c in self.display[self.y * self.w + self.x ..].iter_mut() {
*c = block;
}
if ! self.raw_mode {
self.redraw = true;
}
},
1 => {
let block = self.block(' ');
/* Should this add one? */
for c in self.display[.. self.y * self.w + self.x + 1].iter_mut() {
*c = block;
}
if ! self.raw_mode {
self.redraw = true;
}
},
2 => {
// Erase all
self.x = 0;
self.y = 0;
let block = self.block(' ');
for c in self.display.iter_mut() {
*c = block;
}
if ! self.raw_mode {
self.redraw = true;
}
},
_ => {}
}
self.escape_sequence = false;
},
'K' => {
match self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(0) {
0 => {
let block = self.block(' ');
for c in self.display[self.y * self.w + self.x .. self.y * self.w + self.w].iter_mut() {
*c = block;
}
if ! self.raw_mode {
self.redraw = true;
}
},
1 => {
let block = self.block(' ');
/* Should this add one? */
for c in self.display[self.y * self.w .. self.y * self.w + self.x + 1].iter_mut() {
*c = block;
}
if ! self.raw_mode {
self.redraw = true;
}
},
2 => {
// Erase all
self.x = 0;
self.y = 0;
let block = self.block(' ');
for c in self.display.iter_mut() {
*c = block;
}
if ! self.raw_mode {
self.redraw = true;
}
},
_ => {}
}
self.escape_sequence = false;
},
/*
@MANSTART{terminal-raw-mode}
INTRODUCTION
Since Redox has no ioctl syscall, it uses escape codes for switching to raw mode.
ENTERING AND EXITING RAW MODE
Entering raw mode is done using CSI-r (^[r). Unsetting raw mode is done by CSI-R (^[R).
RAW MODE
Raw mode means that the stdin must be handled solely by the program itself. It will not automatically be printed nor will it be modified in any way (modulo escape codes).
This means that:
- stdin is not printed.
- newlines are interpreted as carriage returns in stdin.
- stdin is not buffered, meaning that the stream of bytes goes directly to the program, without the user having to press enter.
@MANEND
*/
'r' => {
self.raw_mode = true;
self.escape_sequence = false;
},
'R' => {
self.raw_mode = false;
self.escape_sequence = false;
},
'?' => self.escape_extra = true,
'h' if self.escape_extra => {
match self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(0) {
25 => self.cursor = true,
_ => ()
}
self.escape_sequence = false;
},
'l' if self.escape_extra => {
match self.sequence.get(0).map_or("", |p| &p).parse::<usize>().unwrap_or(0) {
25 => self.cursor = false,
_ => ()
}
self.escape_sequence = false;
},
_ => self.escape_sequence = false,
}
if !self.escape_sequence {
self.sequence.clear();
self.escape = false;
self.escape_extra = false;
}
} else {
match c {
'[' => {
// Control sequence initiator
self.escape_sequence = true;
self.sequence.push(String::new());
},
'c' => {
// Reset
self.x = 0;
self.y = 0;
self.raw_mode = false;
self.foreground = Color::ansi(7);
self.background = Color::ansi(0);
self.style = Style::Normal;
let block = self.block(' ');
for c in self.display.iter_mut() {
*c = block;
}
self.redraw = true;
self.escape = false;
}
_ => self.escape = false,
}
}
}
pub fn character(&mut self, c: char) {
match c {
'\0' => {},
'\x1B' => self.escape = true,
'\n' => {
self.x = 0;
self.y += 1;
if ! self.raw_mode {
self.redraw = true;
}
},
'\t' => self.x = ((self.x / 8) + 1) * 8,
'\r' => self.x = 0,
'\x08' => {
if self.x >= 1 {
self.x -= 1;
if ! self.raw_mode {
self.display[self.y * self.w + self.x] = self.block(' ');
}
}
},
' ' => {
self.display[self.y * self.w + self.x] = self.block(' ');
self.x += 1;
},
_ => {
self.display[self.y * self.w + self.x] = self.block(c);
self.x += 1;
}
}
if self.x >= self.w {
self.x = 0;
self.y += 1;
}
while self.y + 1 > self.h {
for y in 1..self.h {
for x in 0..self.w {
let c = self.display[y * self.w + x];
self.display[(y - 1) * self.w + x] = c;
}
}
let block = self.block(' ');
for x in 0..self.w {
self.display[(self.h - 1) * self.w + x] = block;
}
self.y -= 1;
}
}
pub fn write(&mut self, bytes: &[u8]) {
for byte in bytes.iter() {
let c = *byte as char;
if self.escape {
self.code(c);
} else {
self.character(c);
}
}
}
}
#[derive(Copy, Clone)]
pub enum Style {
Normal,
Bold
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment