rust-cgui 0.1.15

A low-level cross-platform GUI library
Documentation

// External ffi bindings will be written in ext.rs
pub mod ext;

//#[macro_use]
//extern crate lazy_static;

#[macro_use]
extern crate cfg_if;

cfg_if! {
  if #[cfg(windows)] {
    extern crate winapi;
    use winapi::shared::windef::HWND;
    use winapi::um::winuser::*;
    use winapi::um::libloaderapi::GetModuleHandleW;
    use winapi::ctypes::c_int;
    use std::os::windows::ffi::OsStrExt;
    use std::os::raw::c_char;
    use std::io::Error;
    use std::ffi::{CStr, OsStr, CString};
    use std::iter::once;
    
    mod winback;
    pub use self::winback::*;
    // pub use winback; // cannot re-import
    
  }
  else if #[cfg(unix)] {
    extern crate x11_dl;
    use std::ffi::{CStr, CString};
    use libc::c_char; // Caution - windows has an os:: c_char we collide with
    
    mod x11back;
    pub use self::x11back::*;
    // pub use x11back; // Cannot re-import
    
  }
}

// Libraries both unix and win use

extern crate image;
#[allow(unused_imports)]
use image::{ImageBuffer, Rgb, Pixel};

extern crate libc;

use std::mem;
use std::ptr::null_mut;
use std::ptr;

// Library data structures

/// Write-dominant data structure which holds details about the window
/// which is pushed to various backends.
pub struct CWinRep {
  pub title: String,
  pub width: usize,
  pub height: usize,
  pub pixels: ImageBuffer<Rgb<u8>, Vec<u8>>,
  init_flag: bool,
  pub exit_flag: bool,
  
  // TODO add mutator method
  pub dirty_extents: [usize; 4], // [x_leftmost, y_topmost, x_rightmost, y_botmost]
  
  // Implementation-specific pointers, hooks, whatever we need to remember to talk back to the backend.
  
  #[cfg(unix)]
  x11_disp_ptr: *mut x11_dl::xlib::Display,
  #[cfg(unix)]
  x11_window_ptr: std::os::raw::c_ulong,
  #[cfg(unix)]
  x11_gc_ptr: x11_dl::xlib::GC,
  #[cfg(unix)]
  xlib_dl_obj: x11_dl::xlib::Xlib,
  
  #[cfg(windows)]
  win32_win_ptr: winback::Window,
}

pub struct CWin {
  // platform-agnostic representaition of the window state
  pub rep: CWinRep,
  event_callbacks: Vec<Box<extern fn(*mut CWinRep, String /* event string */)>>,
  event_raw_callbacks: Vec<Box<extern fn(*mut CWinRep, *const c_char /* event string */)>>,
}


// Implementation details for the structures

impl CWin {
  pub fn new() -> CWin {
    return CWin::new_detail("Untitled Window", 800, 600);
  }
  pub fn new_detail<S: Into<String>>(title: S, width: u32, height: u32) -> CWin {
    let title = title.into();
    return CWin {
      rep: CWinRep {
        title: title,
        width: width as usize,
        height: height as usize,
        pixels: ImageBuffer::new(width, height),
        init_flag: false,
        exit_flag: false,
        dirty_extents: [width as usize, height as usize, 0, 0], // begin with an inverted rectangle ("leftmost x" is all the way to the right)
        
        // Impl specific stuff (mostly null the fields)
        
        #[cfg(unix)]
        x11_window_ptr: unsafe { mem::uninitialized() },
        #[cfg(unix)]
        x11_gc_ptr: unsafe { mem::uninitialized() },
        #[cfg(unix)]
        x11_disp_ptr: unsafe { mem::uninitialized() },
        #[cfg(unix)]
        xlib_dl_obj: unsafe { mem::uninitialized() },
        
        #[cfg(windows)]
        win32_win_ptr: unsafe { mem::uninitialized() },
        
      },
      event_callbacks: vec![],
      event_raw_callbacks: vec![],
    };
  }
  pub fn to_ffi_ptr(self) -> *mut CWin {
    let boxed = Box::new(self);
    return Box::into_raw(boxed);
  }
  
  pub fn add_callback(&mut self, function: extern fn(*mut CWinRep, String)) {
    // that box::new means we take ownership of the function pointer
    self.event_callbacks.push(Box::new(function));
  }
  
  pub fn add_callback_raw(&mut self, function: extern fn(*mut CWinRep, *const c_char /* event string */)) {
    // that box::new means we take ownership of the function pointer
    self.event_raw_callbacks.push(Box::new(function));
  }
  
  pub fn call_callbacks(&mut self, event_string: String) {
    let cwinrep : *mut CWinRep = &mut self.rep;
    self.event_callbacks.iter_mut().for_each(|c| {
      (c)(cwinrep, event_string.clone());
    });
    // this external Cstr will be free-ed by rust after it is passed
    // to each raw_callback.
    let safe_cstr : CString = CString::new(event_string).unwrap();
    let cstr_ptr = safe_cstr.as_ptr();
    self.call_raw_callbacks(cstr_ptr);
  }
  
  fn call_raw_callbacks(&mut self, event_string: *const c_char) {
    let cwinrep : *mut CWinRep = &mut self.rep;
    self.event_raw_callbacks.iter_mut().for_each(|c| {
      (c)(cwinrep, event_string.clone());
    });
  }
  
  /// Writes a pixel to our buffer. Does not push to window manager until redraw() or redraw_dirty() is called.
  pub fn write_px(&mut self, x: u32, y: u32, rgb: [u8; 3]) {
    self.rep.write_px(x, y, rgb);
  }
  
}

impl CWinRep {
  /// Writes a pixel to our buffer. Does not push to window manager until redraw() or redraw_dirty() is called.
  pub fn write_px(&mut self, x: u32, y: u32, rgb: [u8; 3]) {
    let x = x as usize;
    let y = y as usize;
    // If this pixel is outside any of our dirty extents, expand them
    if x < self.dirty_extents[0] {
      self.dirty_extents[0] = x;
    }
    if x > self.dirty_extents[2] {
      self.dirty_extents[2] = x+1;
    }
    if y < self.dirty_extents[1] {
      self.dirty_extents[1] = y;
    }
    if y > self.dirty_extents[3] {
      self.dirty_extents[3] = y+1;
    }
    
    // Now write pixel data to our buffer
    self.pixels.get_pixel_mut(x as u32, y as u32).data = rgb;
  }
  
  /**/
  
}

/// Backend renderers are implemented in other source files (winback.rs, x11back.rs)
/// and contain implementations of RenderBack for CWin.
/// At compile time there must only be one implementation visible, which will be baked into the shared lib.
/// No support for dynamically changing system backends is planned (eg win32 -> x11 with an x11 server on windows).
pub trait RenderBack {
  fn init(&mut self);
  fn redraw_dirty(&mut self);
  fn redraw_box(&mut self, x1: usize, y1: usize, x2: usize, y2: usize);
  fn event_tick(&self) -> String;
  fn event_loop(&mut self);
}