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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207

// 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);
}