use std::ptr;
use std::str;
use std::marker::PhantomData;
use std::mem::transmute;
use std::path::Path;
use bindings::ffi::{self, TCOD_bkgnd_flag_t, TCOD_renderer_t, TCOD_font_flags_t, TCOD_alignment_t};
use bindings::{AsNative, FromNative, c_bool, CString};
use colors::Color;
use input::{Key, KeyPressFlags};
pub struct Offscreen {
con: ffi::TCOD_console_t,
}
impl Drop for Offscreen {
fn drop(&mut self) {
unsafe {
ffi::TCOD_console_delete(self.con);
}
}
}
impl Offscreen {
pub fn new(width: i32, height: i32) -> Offscreen {
assert!(width > 0 && height > 0);
unsafe {
Offscreen { con: ffi::TCOD_console_new(width, height) }
}
}
}
unsafe impl Send for Offscreen {}
struct RootId {
id: ffi::TCOD_console_t
}
unsafe impl Sync for RootId {}
static ROOT_ID: RootId = RootId { id: 0 as ffi::TCOD_console_t };
pub struct Root {
_blocker: PhantomData<Root>
}
impl Root {
pub fn initializer<'a>() -> RootInitializer<'a> {
RootInitializer::new()
}
pub fn is_fullscreen(&self) -> bool {
unsafe {
ffi::TCOD_console_is_fullscreen() != 0
}
}
pub fn set_fullscreen(&mut self, fullscreen: bool) {
unsafe {
ffi::TCOD_console_set_fullscreen(fullscreen as u8);
}
}
pub fn is_active(&self) -> bool {
unsafe {
ffi::TCOD_console_is_active() != 0
}
}
pub fn has_focus(&self) -> bool {
unsafe {
ffi::TCOD_console_has_mouse_focus() != 0
}
}
pub fn get_fade(&self) -> u8 {
unsafe {
ffi::TCOD_console_get_fade()
}
}
pub fn get_fading_color(&self) -> Color {
unsafe {
FromNative::from_native(
ffi::TCOD_console_get_fading_color())
}
}
pub fn set_fade(&mut self, fade: u8, fading_color: Color) {
unsafe {
ffi::TCOD_console_set_fade(fade, *fading_color.as_native());
}
}
pub fn wait_for_keypress(&mut self, flush: bool) -> Key {
let tcod_key = unsafe {
ffi::TCOD_console_wait_for_keypress(flush as c_bool)
};
tcod_key.into()
}
pub fn check_for_keypress(&self, status: KeyPressFlags) -> Option<Key> {
let tcod_key = unsafe {
ffi::TCOD_console_check_for_keypress(status.bits() as i32)
};
if tcod_key.vk == ffi::TCOD_keycode_t::TCODK_NONE {
return None;
}
Some(tcod_key.into())
}
pub fn window_closed(&self) -> bool {
unsafe {
ffi::TCOD_console_is_window_closed() != 0
}
}
pub fn flush(&mut self) {
unsafe {
ffi::TCOD_console_flush();
}
}
pub fn set_window_title<T>(&mut self, title: T) where T: AsRef<str> {
unsafe {
let c_title = CString::new(title.as_ref().as_bytes()).unwrap();
ffi::TCOD_console_set_window_title(c_title.as_ptr());
}
}
pub fn render_credits(&self, x : i32, y: i32, alpha: bool) -> bool {
unsafe {
let result = ffi::TCOD_console_credits_render(x, y, alpha as c_bool);
result != 0
}
}
pub fn map_ascii_code_to_font(&mut self,
ascii_code: i32,
font_char_x: i32,
font_char_y: i32) {
unsafe {
ffi::TCOD_console_map_ascii_code_to_font(
ascii_code,
font_char_x,
font_char_y
);
}
}
pub fn map_ascii_codes_to_font(&mut self,
ascii_code: i32,
nb_codes: i32,
font_char_x: i32,
font_char_y: i32) {
unsafe {
ffi::TCOD_console_map_ascii_codes_to_font(
ascii_code,
nb_codes,
font_char_x,
font_char_y
);
}
}
pub fn map_string_to_font(&mut self,
s: &str,
font_char_x: i32,
font_char_y: i32) {
unsafe {
let string = CString::new(s).ok().expect("Could not convert the given \
string to a C string.");
ffi::TCOD_console_map_string_to_font(
string.as_ptr(),
font_char_x,
font_char_y
);
}
}
fn set_custom_font(font_path: &Path,
font_layout: FontLayout,
font_type: FontType,
nb_char_horizontal: i32,
nb_char_vertical: i32) {
unsafe {
let filename = font_path.to_str().expect("Invalid font path");
let path = CString::new(filename).ok().expect("Font path could not be converted \
to a C string");
ffi::TCOD_console_set_custom_font(
path.as_ptr(), (font_layout as i32) | (font_type as i32),
nb_char_horizontal, nb_char_vertical);
}
}
}
pub struct RootInitializer<'a> {
width: i32,
height: i32,
title: Box<AsRef<str> + 'a>,
is_fullscreen: bool,
font_path: Box<AsRef<Path> + 'a>,
font_layout: FontLayout,
font_type: FontType,
font_dimensions: (i32, i32),
console_renderer: Renderer
}
impl<'a> RootInitializer<'a> {
pub fn new() -> RootInitializer<'a> {
RootInitializer {
width: 80,
height: 25,
title: Box::new("Main Window"),
is_fullscreen: false,
font_path: Box::new("terminal.png"),
font_layout: FontLayout::AsciiInCol,
font_type: FontType::Default,
font_dimensions: (0, 0),
console_renderer: Renderer::SDL
}
}
pub fn size(&mut self, width: i32, height: i32) -> &mut RootInitializer<'a> {
self.width = width;
self.height = height;
self
}
pub fn title<T>(&mut self, title: T) -> &mut RootInitializer<'a> where T: AsRef<str> + 'a {
assert!(title.as_ref().is_ascii());
self.title = Box::new(title);
self
}
pub fn fullscreen(&mut self, is_fullscreen: bool) -> &mut RootInitializer<'a> {
self.is_fullscreen = is_fullscreen;
self
}
pub fn font<P>(&mut self, path: P, font_layout: FontLayout) -> &mut RootInitializer<'a> where P: AsRef<Path> + 'a {
self.font_path = Box::new(path);
self.font_layout = font_layout;
self
}
pub fn font_type(&mut self, font_type: FontType) -> &mut RootInitializer<'a> {
self.font_type = font_type;
self
}
pub fn font_dimensions(&mut self, horizontal: i32, vertical: i32) -> &mut RootInitializer<'a> {
self.font_dimensions = (horizontal, vertical);
self
}
pub fn renderer(&mut self, renderer: Renderer) -> &mut RootInitializer<'a> {
self.console_renderer = renderer;
self
}
pub fn init(&self) -> Root {
assert!(self.width > 0 && self.height > 0);
match self.font_dimensions {
(horizontal, vertical) => {
Root::set_custom_font((*self.font_path).as_ref(),
self.font_layout, self.font_type,
horizontal, vertical)
}
}
unsafe {
let c_title = CString::new((*self.title).as_ref().as_bytes()).unwrap();
ffi::TCOD_console_init_root(self.width, self.height,
c_title.as_ptr(),
self.is_fullscreen as c_bool,
self.console_renderer.into());
}
Root { _blocker: PhantomData }
}
}
pub trait TcodString {
fn as_ascii(&self) -> Option<&[u8]>;
}
impl TcodString for str {
fn as_ascii(&self) -> Option<&[u8]> {
match self.is_ascii() {
true => Some(self.as_ref()),
false => None,
}
}
}
impl<'a> TcodString for &'a str {
fn as_ascii(&self) -> Option<&[u8]> {
(*self).as_ascii()
}
}
impl TcodString for String {
fn as_ascii(&self) -> Option<&[u8]> {
AsRef::<str>::as_ref(self).as_ascii()
}
}
impl<'a> TcodString for &'a String {
fn as_ascii(&self) -> Option<&[u8]> {
AsRef::<str>::as_ref(self).as_ascii()
}
}
pub trait AsciiLiteral {}
impl AsciiLiteral for [u8] {}
impl AsciiLiteral for [u8; 0] {}
impl AsciiLiteral for [u8; 1] {}
impl AsciiLiteral for [u8; 2] {}
impl AsciiLiteral for [u8; 3] {}
impl AsciiLiteral for [u8; 4] {}
impl AsciiLiteral for [u8; 5] {}
impl AsciiLiteral for [u8; 6] {}
impl AsciiLiteral for [u8; 7] {}
impl AsciiLiteral for [u8; 8] {}
impl AsciiLiteral for [u8; 9] {}
impl AsciiLiteral for [u8; 10] {}
impl AsciiLiteral for [u8; 11] {}
impl AsciiLiteral for [u8; 12] {}
impl AsciiLiteral for [u8; 13] {}
impl AsciiLiteral for [u8; 14] {}
impl AsciiLiteral for [u8; 15] {}
impl AsciiLiteral for [u8; 16] {}
impl AsciiLiteral for [u8; 17] {}
impl AsciiLiteral for [u8; 18] {}
impl AsciiLiteral for [u8; 19] {}
impl AsciiLiteral for [u8; 20] {}
impl AsciiLiteral for [u8; 21] {}
impl AsciiLiteral for [u8; 22] {}
impl AsciiLiteral for [u8; 23] {}
impl AsciiLiteral for [u8; 24] {}
impl AsciiLiteral for [u8; 25] {}
impl AsciiLiteral for [u8; 26] {}
impl AsciiLiteral for [u8; 27] {}
impl AsciiLiteral for [u8; 28] {}
impl AsciiLiteral for [u8; 29] {}
impl AsciiLiteral for [u8; 30] {}
impl AsciiLiteral for [u8; 31] {}
impl AsciiLiteral for [u8; 32] {}
impl<'a, T> AsciiLiteral for &'a T where T: AsciiLiteral {}
impl<T> TcodString for T where T: AsRef<[u8]> + AsciiLiteral {
fn as_ascii(&self) -> Option<&[u8]> {
Some(self.as_ref())
}
}
#[inline]
fn to_wstring(text: &[u8]) -> Vec<char> {
let mut ret = str::from_utf8(text).unwrap().chars().collect::<Vec<_>>();
ret.push('\0');
ret
}
pub trait Console : AsNative<ffi::TCOD_console_t> {
fn get_alignment(&self) -> TextAlignment {
let alignment = unsafe {
ffi::TCOD_console_get_alignment(*self.as_native())
};
unsafe { transmute(alignment) }
}
fn set_alignment(&mut self, alignment: TextAlignment) {
unsafe {
ffi::TCOD_console_set_alignment(*self.as_native(), alignment.into());
}
}
fn set_key_color(&mut self, color: Color) {
unsafe {
ffi::TCOD_console_set_key_color(*self.as_native(), *color.as_native());
}
}
fn width(&self) -> i32 {
unsafe {
ffi::TCOD_console_get_width(*self.as_native())
}
}
fn height(&self) -> i32 {
unsafe {
ffi::TCOD_console_get_height(*self.as_native())
}
}
fn get_default_background(&mut self) -> Color {
unsafe {
FromNative::from_native(
ffi::TCOD_console_get_default_background(*self.as_native()))
}
}
fn set_default_background(&mut self, color: Color) {
unsafe {
ffi::TCOD_console_set_default_background(*self.as_native(), *color.as_native());
}
}
fn set_default_foreground(&mut self, color: Color) {
unsafe {
ffi::TCOD_console_set_default_foreground(*self.as_native(), *color.as_native());
}
}
fn get_char_background(&self, x: i32, y: i32) -> Color {
unsafe {
FromNative::from_native(
ffi::TCOD_console_get_char_background(*self.as_native(), x, y))
}
}
fn get_char_foreground(&self, x: i32, y: i32) -> Color {
unsafe {
FromNative::from_native(
ffi::TCOD_console_get_char_foreground(*self.as_native(), x, y))
}
}
fn get_background_flag(&self) -> BackgroundFlag {
let flag = unsafe {
ffi::TCOD_console_get_background_flag(*self.as_native())
};
unsafe { transmute(flag) }
}
fn set_background_flag(&mut self, background_flag: BackgroundFlag) {
unsafe {
ffi::TCOD_console_set_background_flag(*self.as_native(),
background_flag.into());
}
}
fn get_char(&self, x: i32, y: i32) -> char {
let ffi_char = unsafe {
ffi::TCOD_console_get_char(*self.as_native(), x, y)
};
assert!(ffi_char >= 0 && ffi_char < 256);
ffi_char as u8 as char
}
fn set_char(&mut self, x: i32, y: i32, c: char) {
assert!(x >= 0 && y >= 0);
unsafe {
ffi::TCOD_console_set_char(*self.as_native(), x, y, c as i32)
}
}
fn set_char_background(&mut self, x: i32, y: i32,
color: Color,
background_flag: BackgroundFlag) {
assert!(x >= 0 && y >= 0);
unsafe {
ffi::TCOD_console_set_char_background(*self.as_native(),
x, y,
*color.as_native(),
background_flag.into())
}
}
fn set_char_foreground(&mut self, x: i32, y: i32, color: Color) {
assert!(x >= 0 && y >= 0);
unsafe {
ffi::TCOD_console_set_char_foreground(*self.as_native(),
x, y,
*color.as_native());
}
}
fn put_char(&mut self,
x: i32, y: i32, glyph: char,
background_flag: BackgroundFlag) {
assert!(x >= 0 && y >= 0);
unsafe {
ffi::TCOD_console_put_char(*self.as_native(),
x, y, glyph as i32,
background_flag.into());
}
}
fn put_char_ex(&mut self,
x: i32, y: i32, glyph: char,
foreground: Color, background: Color) {
assert!(x >= 0 && y >= 0);
unsafe {
ffi::TCOD_console_put_char_ex(*self.as_native(),
x, y, glyph as i32,
*foreground.as_native(),
*background.as_native());
}
}
fn clear(&mut self) {
unsafe {
ffi::TCOD_console_clear(*self.as_native());
}
}
fn print<T>(&mut self, x: i32, y: i32, text: T) where Self: Sized, T: AsRef<[u8]> + TcodString {
assert!(x >= 0 && y >= 0);
if let Some(text) = text.as_ascii() {
let c_text = CString::new(text).unwrap();
unsafe {
ffi::TCOD_console_print(*self.as_native(), x, y, c_text.as_ptr());
}
} else {
let c_text = to_wstring(text.as_ref());
unsafe {
ffi::TCOD_console_print_utf(*self.as_native(), x, y, c_text.as_ptr() as *const i32);
}
}
}
fn print_rect<T>(&mut self,
x: i32, y: i32,
width: i32, height: i32,
text: T) where Self: Sized, T: AsRef<[u8]> + TcodString {
assert!(x >= 0 && y >= 0);
if let Some(text) = text.as_ascii() {
let c_text = CString::new(text).unwrap();
unsafe {
ffi::TCOD_console_print_rect(*self.as_native(), x, y, width, height, c_text.as_ptr());
}
} else {
let c_text = to_wstring(text.as_ref());
unsafe {
ffi::TCOD_console_print_rect_utf(*self.as_native(), x, y, width, height, c_text.as_ptr() as *const i32);
}
}
}
fn print_ex<T>(&mut self,
x: i32, y: i32,
background_flag: BackgroundFlag,
alignment: TextAlignment,
text: T) where Self: Sized, T: AsRef<[u8]> + TcodString {
assert!(x >= 0 && y >= 0);
if let Some(text) = text.as_ascii() {
let c_text = CString::new(text).unwrap();
unsafe {
ffi::TCOD_console_print_ex(*self.as_native(), x, y,
background_flag.into(),
alignment.into(),
c_text.as_ptr());
}
} else {
let c_text = to_wstring(text.as_ref());
unsafe {
ffi::TCOD_console_print_ex_utf(*self.as_native(), x, y,
background_flag.into(),
alignment.into(),
c_text.as_ptr() as *const i32);
}
}
}
fn print_rect_ex<T>(&mut self,
x: i32, y: i32,
width: i32, height: i32,
background_flag: BackgroundFlag,
alignment: TextAlignment,
text: T) where Self: Sized, T: AsRef<[u8]> + TcodString {
assert!(x >= 0 && y >= 0);
if let Some(text) = text.as_ascii() {
let c_text = CString::new(text).unwrap();
unsafe {
ffi::TCOD_console_print_rect_ex(*self.as_native(), x, y, width, height,
background_flag.into(), alignment.into(),
c_text.as_ptr());
}
} else {
let c_text = to_wstring(text.as_ref());
unsafe {
ffi::TCOD_console_print_rect_ex_utf(*self.as_native(), x, y, width, height,
background_flag.into(), alignment.into(),
c_text.as_ptr() as *const i32);
}
}
}
fn get_height_rect<T>(&self,
x: i32, y: i32,
width: i32, height: i32,
text: T) -> i32 where Self: Sized, T: AsRef<[u8]> + TcodString {
assert!(x >= 0 && y >= 0);
if let Some(text) = text.as_ascii() {
let c_text = CString::new(text).unwrap();
unsafe {
ffi::TCOD_console_get_height_rect(*self.as_native(), x, y, width, height,
c_text.as_ptr())
}
} else {
let c_text = to_wstring(text.as_ref());
unsafe {
ffi::TCOD_console_get_height_rect_utf(*self.as_native(), x, y, width, height,
c_text.as_ptr() as *const i32)
}
}
}
fn rect(&mut self,
x: i32, y: i32,
width: i32, height: i32,
clear: bool,
background_flag: BackgroundFlag) {
assert!(x >= 0);
assert!(y >= 0);
assert!(width >= 0);
assert!(height >= 0);
assert!(x + width <= self.width());
assert!(y + height <= self.height());
unsafe {
ffi::TCOD_console_rect(*self.as_native(), x, y, width, height, clear as c_bool, background_flag.into());
}
}
fn horizontal_line(&mut self, x: i32, y: i32, length: i32, background_flag: BackgroundFlag) {
assert!(x >= 0 && y >= 0 && y < self.height());
assert!(length >= 1 && length + x <= self.width());
unsafe {
ffi::TCOD_console_hline(*self.as_native(), x, y, length, background_flag.into());
}
}
fn vertical_line(&mut self, x: i32, y: i32, length: i32, background_flag: BackgroundFlag) {
assert!(x >= 0, y >= 0 && x < self.width());
assert!(length >= 1 && length + y <= self.height());
unsafe {
ffi::TCOD_console_vline(*self.as_native(), x, y, length, background_flag.into());
}
}
fn print_frame<T>(&mut self, x: i32, y: i32, width: i32, height: i32,
clear: bool, background_flag: BackgroundFlag, title: Option<T>) where Self: Sized, T: AsRef<str> {
assert!(x >= 0 && y >= 0 && width >= 0 && height >= 0);
assert!(x + width <= self.width() && y + height <= self.height());
let title = title.map(|s| {
assert!(s.as_ref().is_ascii());
CString::new(s.as_ref().as_bytes()).unwrap()
});
let c_title = title.as_ref().map_or(ptr::null(), |s| s.as_ptr());
unsafe {
ffi::TCOD_console_print_frame(*self.as_native(), x, y, width, height,
clear as c_bool, background_flag.into(),
c_title);
}
}
}
pub fn blit<T, U>(source_console: &T,
(source_x, source_y): (i32, i32),
(source_width, source_height): (i32, i32),
destination_console: &mut U,
(destination_x, destination_y): (i32, i32),
foreground_alpha: f32, background_alpha: f32)
where T: Console,
U: Console {
assert!(source_x >= 0 && source_y >= 0 &&
source_width >= 0 && source_height >= 0);
unsafe {
ffi::TCOD_console_blit(*source_console.as_native(),
source_x, source_y, source_width, source_height,
*destination_console.as_native(),
destination_x, destination_y,
foreground_alpha, background_alpha);
}
}
impl<'a, T: Console + ?Sized> Console for &'a T {}
impl<T: Console + ?Sized> Console for Box<T> {}
impl AsNative<ffi::TCOD_console_t> for Root {
unsafe fn as_native(&self) -> &ffi::TCOD_console_t {
&ROOT_ID.id
}
unsafe fn as_native_mut(&mut self) -> &mut ffi::TCOD_console_t {
unimplemented!();
}
}
impl AsNative<ffi::TCOD_console_t> for Offscreen {
unsafe fn as_native(&self) -> &ffi::TCOD_console_t {
&self.con
}
unsafe fn as_native_mut(&mut self) -> &mut ffi::TCOD_console_t {
&mut self.con
}
}
impl Console for Root {}
impl Console for Offscreen {}
#[repr(u32)]
#[derive(Copy, Clone)]
pub enum TextAlignment {
Left = ffi::TCOD_alignment_t::TCOD_LEFT as u32,
Right = ffi::TCOD_alignment_t::TCOD_RIGHT as u32,
Center = ffi::TCOD_alignment_t::TCOD_CENTER as u32,
}
native_enum_convert!(TextAlignment, TCOD_alignment_t);
#[repr(u32)]
#[derive(Copy, Clone, Debug)]
pub enum BackgroundFlag {
None = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_NONE as u32,
Set = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_SET as u32,
Multiply = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_MULTIPLY as u32,
Lighten = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_LIGHTEN as u32,
Darken = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_DARKEN as u32,
Screen = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_SCREEN as u32,
ColorDodge = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_COLOR_DODGE as u32,
ColorBurn = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_COLOR_BURN as u32,
Add = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_ADD as u32,
AddA = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_ADDA as u32,
Burn = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_BURN as u32,
Overlay = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_OVERLAY as u32,
Alph = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_ALPH as u32,
Default = ffi::TCOD_bkgnd_flag_t::TCOD_BKGND_DEFAULT as u32
}
native_enum_convert!(BackgroundFlag, TCOD_bkgnd_flag_t);
#[repr(u32)]
#[derive(Copy, Clone)]
pub enum Renderer {
GLSL = ffi::TCOD_renderer_t::TCOD_RENDERER_GLSL as u32,
OpenGL = ffi::TCOD_renderer_t::TCOD_RENDERER_OPENGL as u32,
SDL = ffi::TCOD_renderer_t::TCOD_RENDERER_SDL as u32,
}
native_enum_convert!(Renderer, TCOD_renderer_t);
#[repr(u32)]
#[derive(Copy, Clone)]
pub enum FontLayout {
AsciiInCol = ffi::TCOD_font_flags_t::TCOD_FONT_LAYOUT_ASCII_INCOL as u32,
AsciiInRow = ffi::TCOD_font_flags_t::TCOD_FONT_LAYOUT_ASCII_INROW as u32,
Tcod = ffi::TCOD_font_flags_t::TCOD_FONT_LAYOUT_TCOD as u32,
}
native_enum_convert!(FontLayout, TCOD_font_flags_t);
#[repr(u32)]
#[derive(Copy, Clone)]
pub enum FontType {
Default = 0,
Greyscale = ffi::TCOD_font_flags_t::TCOD_FONT_TYPE_GREYSCALE as u32,
}
native_enum_convert!(FontType, TCOD_font_flags_t);
#[cfg(test)]
mod test {
use std::path::Path;
use super::Root;
use super::FontLayout::AsciiInCol;
#[test]
fn test_custom_font_as_static_str() {
Root::initializer().font("terminal.png", AsciiInCol);
}
#[test]
fn test_custom_font_as_path() {
Root::initializer().font(Path::new("terminal.png"), AsciiInCol);
}
#[test]
fn test_custom_font_as_string() {
Root::initializer().font("terminal.png".to_owned(), AsciiInCol);
}
#[test]
fn test_custom_font_as_str() {
let string = "terminal.png".to_owned();
let s: &str = &string;
Root::initializer().font(s, AsciiInCol);
}
}