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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

pub use super::*;

/// X11 backend implementation
impl RenderBack for CWin {
  fn init(&mut self) {
    if self.rep.init_flag {
      return;
    }
    // Perform x11 init steps
    unsafe {
      match x11_dl::xlib::Xlib::open() {
        Ok(the_lib) => {
          // Write the_lib into the uninitialized memory held by xlib_dl_obj
          ptr::write(&mut (self.rep.xlib_dl_obj), the_lib);
        }
        Err(_e) => {
          println!("[ CGUI ] Fatal Error: we expect to be able to open XLib but cannot. You have uninitialized memory and cannot continue.");
          return;
        }
      }
      
      self.rep.x11_disp_ptr = (self.rep.xlib_dl_obj.XOpenDisplay)(null_mut()/*null*/);
      let screen = (self.rep.xlib_dl_obj.XDefaultScreen)(self.rep.x11_disp_ptr);
      self.rep.x11_window_ptr = (self.rep.xlib_dl_obj.XCreateSimpleWindow)(
        self.rep.x11_disp_ptr, (self.rep.xlib_dl_obj.XRootWindow)(self.rep.x11_disp_ptr, screen),
        0, 0, // x and y
        self.rep.width as u32, self.rep.height as u32,
        0 /*border_width*/, (self.rep.xlib_dl_obj.XBlackPixel)(self.rep.x11_disp_ptr, screen) /*border*/, (self.rep.xlib_dl_obj.XWhitePixel)(self.rep.x11_disp_ptr, screen) /*background*/
      );
      let root_win_ptr = (self.rep.xlib_dl_obj.XDefaultRootWindow)(self.rep.x11_disp_ptr);
      
      (self.rep.xlib_dl_obj.XSelectInput)(self.rep.x11_disp_ptr, self.rep.x11_window_ptr, x11_dl::xlib::ExposureMask | x11_dl::xlib::KeyPressMask);
      (self.rep.xlib_dl_obj.XMapWindow)(self.rep.x11_disp_ptr, self.rep.x11_window_ptr);
      
      let title_ffi_content = CString::new(self.rep.title.clone()).expect("CString::new failed");
      (self.rep.xlib_dl_obj.XSetWMName)(self.rep.x11_disp_ptr, self.rep.x11_window_ptr, &mut x11_dl::xlib::XTextProperty {
        value: title_ffi_content.into_raw() as *mut _ as *mut u8,
        encoding: x11_dl::xlib::XA_STRING,
        format: 8, // 8, 16, or 32 (char width)
        nitems: self.rep.title.len() as u64,
      });
      
      self.rep.x11_gc_ptr = (self.rep.xlib_dl_obj.XCreateGC)(self.rep.x11_disp_ptr, root_win_ptr, 0, null_mut() /*null*/);
      
    }
    self.rep.init_flag = true;
  }
  
  fn redraw_dirty(&mut self) {
    let x1 = self.rep.dirty_extents[0];
    let y1 = self.rep.dirty_extents[1];
    let x2 = self.rep.dirty_extents[2];
    let y2 = self.rep.dirty_extents[3];
    self.redraw_box(x1, y1, x2, y2);
    self.rep.dirty_extents = [self.rep.width, self.rep.height, 0, 0]; // reset to inverted rectangle
  }
  
  fn redraw_box(&mut self, x1: usize, y1: usize, x2: usize, y2: usize) {
    self.rep.redraw_box(x1, y1, x2, y2);
  }
  
  /// Apps which call event_tick manually MUST call init() first. I refuse to throw a check for self.init every time the user moves their mouse.
  /// Also apps which explicitly use event_tick must free the c_string returned. (or do I need to add a str_free-ing function to do the free here?)
  fn event_tick(&self) -> String {
    unsafe {
      let mut event: x11_dl::xlib::XEvent = mem::zeroed(); // could also go for mem::uninitialized, but I'd like to be a little safer
      (self.rep.xlib_dl_obj.XNextEvent)(self.rep.x11_disp_ptr, &mut event);
      
      let evt_str: String = match event.get_type() {
        x11_dl::xlib::Expose => {
          "WinShown".to_string()
        }
        x11_dl::xlib::KeyPress => {
          match get_alpha_char_from_x11_evt(&self.rep, event) {
            Some(c) => {
              format!("KeyPress,{}", c)
            }
            None => {
              "KeyPress,UNKNOWN".to_string()
            }
          }
        }
        _ => {
          "UNKNOWN".to_string()
        }
      };
      
      //let safe_cstr : CString = CString::new(evt_str).unwrap();
      //let cstr_ptr = safe_cstr.as_ptr();
      //std::mem::forget(safe_cstr);
      return evt_str;
    }
  }
  
  fn event_loop(&mut self) {
    if ! self.rep.init_flag {
      self.init();
    }
    while ! self.rep.exit_flag {
      let evt_mess = self.event_tick();
      self.call_callbacks(evt_mess);
    }
  }
}

fn get_alpha_char_from_x11_evt(self_cwin_rep: &CWinRep, mut event: x11_dl::xlib::XEvent) -> Option<char> {
  unsafe {
    let key_pressed_buff = CString::new("buff-buff-buff-buff").expect("CString::new failed");
    let mut key_sym : x11_dl::xlib::KeySym = mem::zeroed();
    let c_str_ptr = key_pressed_buff.into_raw();
    (self_cwin_rep.xlib_dl_obj.XLookupString)(&mut event.key, c_str_ptr, 8 /*len of key_pressed_buff*/, &mut key_sym, null_mut());
    // We need to re-construct our reference b/c we gave ownership to xlib.
    let returned_data = CStr::from_ptr(c_str_ptr);
    if returned_data.to_bytes().len() < 1 {
      println!("TODO: handle an empty character (I think this happens on arrow keys)");
      return None;
    }
    // Don't shoot me
    let first_char : char = (returned_data.to_bytes()[0] as u8) as char;
    if first_char.is_alphabetic() || first_char.is_numeric() {
      return Some(first_char);
    }
    else {
      return None;
    }
  }
}

impl RenderBack for CWinRep {
  fn init(&mut self) {
    if self.init_flag {
      return;
    }
    // Perform x11 init steps
    unsafe {
      match x11_dl::xlib::Xlib::open() {
        Ok(the_lib) => {
          // Write the_lib into the uninitialized memory held by xlib_dl_obj
          ptr::write(&mut (self.xlib_dl_obj), the_lib);
        }
        Err(_e) => {
          println!("[ CGUI ] Fatal Error: we expect to be able to open XLib but cannot. You have uninitialized memory and cannot continue.");
          return;
        }
      }
      
      self.x11_disp_ptr = (self.xlib_dl_obj.XOpenDisplay)(null_mut()/*null*/);
      let screen = (self.xlib_dl_obj.XDefaultScreen)(self.x11_disp_ptr);
      self.x11_window_ptr = (self.xlib_dl_obj.XCreateSimpleWindow)(
        self.x11_disp_ptr, (self.xlib_dl_obj.XRootWindow)(self.x11_disp_ptr, screen),
        0, 0, // x and y
        self.width as u32, self.height as u32,
        0 /*border_width*/, (self.xlib_dl_obj.XBlackPixel)(self.x11_disp_ptr, screen) /*border*/, (self.xlib_dl_obj.XWhitePixel)(self.x11_disp_ptr, screen) /*background*/
      );
      let root_win_ptr = (self.xlib_dl_obj.XDefaultRootWindow)(self.x11_disp_ptr);
      
      (self.xlib_dl_obj.XSelectInput)(self.x11_disp_ptr, self.x11_window_ptr, x11_dl::xlib::ExposureMask | x11_dl::xlib::KeyPressMask);
      (self.xlib_dl_obj.XMapWindow)(self.x11_disp_ptr, self.x11_window_ptr);
      
      let title_ffi_content = CString::new(self.title.clone()).expect("CString::new failed");
      (self.xlib_dl_obj.XSetWMName)(self.x11_disp_ptr, self.x11_window_ptr, &mut x11_dl::xlib::XTextProperty {
        value: title_ffi_content.into_raw() as *mut _ as *mut u8,
        encoding: x11_dl::xlib::XA_STRING,
        format: 8, // 8, 16, or 32 (char width)
        nitems: self.title.len() as u64,
      });
      
      self.x11_gc_ptr = (self.xlib_dl_obj.XCreateGC)(self.x11_disp_ptr, root_win_ptr, 0, null_mut() /*null*/);
      
    }
    self.init_flag = true;
  }
  
  fn redraw_dirty(&mut self) {
    let x1 = self.dirty_extents[0];
    let y1 = self.dirty_extents[1];
    let x2 = self.dirty_extents[2];
    let y2 = self.dirty_extents[3];
    self.redraw_box(x1, y1, x2, y2);
    self.dirty_extents = [self.width, self.height, 0, 0]; // reset to inverted rectangle
  }
  
  fn redraw_box(&mut self, x1: usize, y1: usize, x2: usize, y2: usize) {
    
    if x1 == x2 || y1 == y2 {
      // Width or height = 0, no-op
      return;
    }
    if x1 > x2 || y1 > y2 {
      // Inverted extents, no-op
      return;
    }
    
    unsafe {
      // Loop over x1-x2, y1-y2 drawing single pixels. Not very fast.
      for x in x1..x2 {
        for y in y1..y2 {
          let color = self.pixels.get_pixel(x as u32, y as u32).to_rgb().data;
          let color_int: u64 = // X11 uses R-G-B
            ((color[0] as u64) << 16) |
            ((color[1] as u64) << 8) |
            ((color[2] as u64) << 0)
            ;
          //println!("color = {}, {}, {}", color[0], color[1], color[2]);
          //println!("color_int = {}", color_int);
          (self.xlib_dl_obj.XSetForeground)(self.x11_disp_ptr, self.x11_gc_ptr, color_int as u64);
          (self.xlib_dl_obj.XDrawPoint)(self.x11_disp_ptr, self.x11_window_ptr, self.x11_gc_ptr, x as i32, y as i32);
        }
      }
      
      let pixmap = (self.xlib_dl_obj.XCreatePixmap)(
        self.x11_disp_ptr, self.x11_window_ptr, self.width as u32, self.height as u32, 8 /*depth*/
      );
      (self.xlib_dl_obj.XFreePixmap)(self.x11_disp_ptr, pixmap);
      
      
      (self.xlib_dl_obj.XFlush)(self.x11_disp_ptr);
    }
  }
  
  /// Apps which call event_tick manually MUST call init() first.
  /// I refuse to throw a check for self.init every time the user moves their mouse.
  /// Also apps which explicitly use event_tick must free the c_string returned.
  // (or do I need to add a str_free-ing function to do the free here?)
  fn event_tick(&self) -> String {
    unsafe {
      let mut event: x11_dl::xlib::XEvent = mem::zeroed(); // could also go for mem::uninitialized, but I'd like to be a little safer
      (self.xlib_dl_obj.XNextEvent)(self.x11_disp_ptr, &mut event);
      
      let evt_str: String = match event.get_type() {
        x11_dl::xlib::Expose => {
          "WinShown".to_string()
        }
        x11_dl::xlib::KeyPress => {
          match get_alpha_char_from_x11_evt(self, event) {
            Some(c) => {
              format!("KeyPress,{}", c)
            }
            None => {
              "KeyPress,UNKNOWN".to_string()
            }
          }
        }
        _ => {
          "UNKNOWN".to_string()
        }
      };
      
      let safe_cstr : CString = CString::new(evt_str).unwrap();
      return format!("{}", safe_cstr.to_string_lossy().to_owned() );
    }
  }
  
  fn event_loop(&mut self) {
    // We can't do this as a rep
    println!("Warning: event_loop on a CWinRep is a no-op.");
  }
}