cgui/
x11back.rs

1
2pub use super::*;
3
4/// X11 backend implementation
5impl RenderBack for CWin {
6  fn init(&mut self) {
7    if self.rep.init_flag {
8      return;
9    }
10    // Perform x11 init steps
11    unsafe {
12      match x11_dl::xlib::Xlib::open() {
13        Ok(the_lib) => {
14          // Write the_lib into the uninitialized memory held by xlib_dl_obj
15          ptr::write(&mut (self.rep.xlib_dl_obj), the_lib);
16        }
17        Err(_e) => {
18          println!("[ CGUI ] Fatal Error: we expect to be able to open XLib but cannot. You have uninitialized memory and cannot continue.");
19          return;
20        }
21      }
22      
23      self.rep.x11_disp_ptr = (self.rep.xlib_dl_obj.XOpenDisplay)(null_mut()/*null*/);
24      let screen = (self.rep.xlib_dl_obj.XDefaultScreen)(self.rep.x11_disp_ptr);
25      self.rep.x11_window_ptr = (self.rep.xlib_dl_obj.XCreateSimpleWindow)(
26        self.rep.x11_disp_ptr, (self.rep.xlib_dl_obj.XRootWindow)(self.rep.x11_disp_ptr, screen),
27        0, 0, // x and y
28        self.rep.width as u32, self.rep.height as u32,
29        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*/
30      );
31      let root_win_ptr = (self.rep.xlib_dl_obj.XDefaultRootWindow)(self.rep.x11_disp_ptr);
32      
33      (self.rep.xlib_dl_obj.XSelectInput)(self.rep.x11_disp_ptr, self.rep.x11_window_ptr,
34        x11_dl::xlib::ExposureMask | x11_dl::xlib::KeyPressMask | x11_dl::xlib::PointerMotionMask | x11_dl::xlib::ButtonPressMask | x11_dl::xlib::ButtonReleaseMask | x11_dl::xlib::ButtonReleaseMask
35      );
36      (self.rep.xlib_dl_obj.XMapWindow)(self.rep.x11_disp_ptr, self.rep.x11_window_ptr);
37      
38      let title_ffi_content = CString::new(self.rep.title.clone()).expect("CString::new failed");
39      (self.rep.xlib_dl_obj.XSetWMName)(self.rep.x11_disp_ptr, self.rep.x11_window_ptr, &mut x11_dl::xlib::XTextProperty {
40        value: title_ffi_content.into_raw() as *mut _ as *mut u8,
41        encoding: x11_dl::xlib::XA_STRING,
42        format: 8, // 8, 16, or 32 (char width)
43        nitems: self.rep.title.len() as u64,
44      });
45      
46      self.rep.x11_gc_ptr = (self.rep.xlib_dl_obj.XCreateGC)(self.rep.x11_disp_ptr, root_win_ptr, 0, null_mut() /*null*/);
47      
48    }
49    self.rep.init_flag = true;
50    // Tell app to draw stuff
51    self.call_callbacks("WinShown".to_string());
52    // Do initial draw
53    self.redraw_dirty();
54  }
55  
56  fn redraw_dirty(&mut self) {
57    let x1 = self.rep.dirty_extents[0];
58    let y1 = self.rep.dirty_extents[1];
59    let x2 = self.rep.dirty_extents[2];
60    let y2 = self.rep.dirty_extents[3];
61    self.redraw_box(x1, y1, x2, y2);
62    self.rep.dirty_extents = [self.rep.width, self.rep.height, 0, 0]; // reset to inverted rectangle
63  }
64  
65  fn redraw_box(&mut self, x1: usize, y1: usize, x2: usize, y2: usize) {
66    self.rep.redraw_box(x1, y1, x2, y2);
67  }
68  
69  /// 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.
70  /// 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?)
71  fn event_tick(&self) -> String {
72    unsafe {
73      let mut event: x11_dl::xlib::XEvent = mem::zeroed(); // could also go for mem::uninitialized, but I'd like to be a little safer
74      (self.rep.xlib_dl_obj.XNextEvent)(self.rep.x11_disp_ptr, &mut event);
75      
76      let evt_str: String = match event.get_type() {
77        x11_dl::xlib::Expose => {
78          "WinShown".to_string()
79        }
80        x11_dl::xlib::KeyPress => {
81          match get_alpha_char_from_x11_evt(&self.rep, event) {
82            Some(c) => {
83              format!("KeyPress,{}", c)
84            }
85            None => {
86              "KeyPress,UNKNOWN".to_string()
87            }
88          }
89        }
90        6 => { // idk the constant name
91          format!("MouseMove,{},{}", event.motion.x, event.motion.y)
92        }
93        5 => { // 4 is mouse down
94          format!("MouseUp,{},{}", event.motion.x, event.motion.y)
95        }
96        num => {
97          format!("UNKNOWN: {}", num)
98        }
99      };
100      
101      //let safe_cstr : CString = CString::new(evt_str).unwrap();
102      //let cstr_ptr = safe_cstr.as_ptr();
103      //std::mem::forget(safe_cstr);
104      return evt_str;
105    }
106  }
107  
108  fn event_loop(&mut self) {
109    if ! self.rep.init_flag {
110      self.init();
111    }
112    while ! self.rep.exit_flag {
113      let evt_mess = self.event_tick();
114      self.call_callbacks(evt_mess);
115    }
116  }
117}
118
119fn get_alpha_char_from_x11_evt(self_cwin_rep: &CWinRep, mut event: x11_dl::xlib::XEvent) -> Option<char> {
120  unsafe {
121    let key_pressed_buff = CString::new("buff-buff-buff-buff").expect("CString::new failed");
122    let mut key_sym : x11_dl::xlib::KeySym = mem::zeroed();
123    let c_str_ptr = key_pressed_buff.into_raw();
124    (self_cwin_rep.xlib_dl_obj.XLookupString)(&mut event.key, c_str_ptr, 8 /*len of key_pressed_buff*/, &mut key_sym, null_mut());
125    // We need to re-construct our reference b/c we gave ownership to xlib.
126    let returned_data = CStr::from_ptr(c_str_ptr);
127    if returned_data.to_bytes().len() < 1 {
128      println!("TODO: handle an empty character (I think this happens on arrow keys)");
129      return None;
130    }
131    // Don't shoot me
132    let first_char : char = (returned_data.to_bytes()[0] as u8) as char;
133    return Some(first_char);
134    /*if first_char.is_alphabetic() || first_char.is_numeric() {
135      return Some(first_char);
136    }
137    else {
138      return None;
139    }*/
140  }
141}
142
143impl RenderBack for CWinRep {
144  fn init(&mut self) {
145    if self.init_flag {
146      return;
147    }
148    // Perform x11 init steps
149    unsafe {
150      match x11_dl::xlib::Xlib::open() {
151        Ok(the_lib) => {
152          // Write the_lib into the uninitialized memory held by xlib_dl_obj
153          ptr::write(&mut (self.xlib_dl_obj), the_lib);
154        }
155        Err(_e) => {
156          println!("[ CGUI ] Fatal Error: we expect to be able to open XLib but cannot. You have uninitialized memory and cannot continue.");
157          return;
158        }
159      }
160      
161      self.x11_disp_ptr = (self.xlib_dl_obj.XOpenDisplay)(null_mut()/*null*/);
162      let screen = (self.xlib_dl_obj.XDefaultScreen)(self.x11_disp_ptr);
163      self.x11_window_ptr = (self.xlib_dl_obj.XCreateSimpleWindow)(
164        self.x11_disp_ptr, (self.xlib_dl_obj.XRootWindow)(self.x11_disp_ptr, screen),
165        0, 0, // x and y
166        self.width as u32, self.height as u32,
167        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*/
168      );
169      let root_win_ptr = (self.xlib_dl_obj.XDefaultRootWindow)(self.x11_disp_ptr);
170      
171      (self.xlib_dl_obj.XSelectInput)(self.x11_disp_ptr, self.x11_window_ptr, x11_dl::xlib::ExposureMask | x11_dl::xlib::KeyPressMask);
172      (self.xlib_dl_obj.XMapWindow)(self.x11_disp_ptr, self.x11_window_ptr);
173      
174      let title_ffi_content = CString::new(self.title.clone()).expect("CString::new failed");
175      (self.xlib_dl_obj.XSetWMName)(self.x11_disp_ptr, self.x11_window_ptr, &mut x11_dl::xlib::XTextProperty {
176        value: title_ffi_content.into_raw() as *mut _ as *mut u8,
177        encoding: x11_dl::xlib::XA_STRING,
178        format: 8, // 8, 16, or 32 (char width)
179        nitems: self.title.len() as u64,
180      });
181      
182      self.x11_gc_ptr = (self.xlib_dl_obj.XCreateGC)(self.x11_disp_ptr, root_win_ptr, 0, null_mut() /*null*/);
183      
184    }
185    self.init_flag = true;
186  }
187  
188  fn redraw_dirty(&mut self) {
189    let x1 = self.dirty_extents[0];
190    let y1 = self.dirty_extents[1];
191    let x2 = self.dirty_extents[2];
192    let y2 = self.dirty_extents[3];
193    self.redraw_box(x1, y1, x2, y2);
194    self.dirty_extents = [self.width, self.height, 0, 0]; // reset to inverted rectangle
195  }
196  
197  fn redraw_box(&mut self, x1: usize, y1: usize, x2: usize, y2: usize) {
198    
199    if x1 == x2 || y1 == y2 {
200      // Width or height = 0, no-op
201      return;
202    }
203    if x1 > x2 || y1 > y2 {
204      // Inverted extents, no-op
205      return;
206    }
207    
208    unsafe {
209      // Loop over x1-x2, y1-y2 drawing single pixels. Not very fast.
210      for x in x1..x2 {
211        for y in y1..y2 {
212          let color = self.pixels.get_pixel(x as u32, y as u32).to_rgb().data;
213          let color_int: u64 = // X11 uses R-G-B
214            ((color[0] as u64) << 16) |
215            ((color[1] as u64) << 8) |
216            ((color[2] as u64) << 0)
217            ;
218          //println!("color = {}, {}, {}", color[0], color[1], color[2]);
219          //println!("color_int = {}", color_int);
220          (self.xlib_dl_obj.XSetForeground)(self.x11_disp_ptr, self.x11_gc_ptr, color_int as u64);
221          (self.xlib_dl_obj.XDrawPoint)(self.x11_disp_ptr, self.x11_window_ptr, self.x11_gc_ptr, x as i32, y as i32);
222        }
223      }
224      
225      let pixmap = (self.xlib_dl_obj.XCreatePixmap)(
226        self.x11_disp_ptr, self.x11_window_ptr, self.width as u32, self.height as u32, 8 /*depth*/
227      );
228      (self.xlib_dl_obj.XFreePixmap)(self.x11_disp_ptr, pixmap);
229      
230      
231      (self.xlib_dl_obj.XFlush)(self.x11_disp_ptr);
232    }
233  }
234  
235  /// Apps which call event_tick manually MUST call init() first.
236  /// I refuse to throw a check for self.init every time the user moves their mouse.
237  /// Also apps which explicitly use event_tick must free the c_string returned.
238  // (or do I need to add a str_free-ing function to do the free here?)
239  fn event_tick(&self) -> String {
240    unsafe {
241      let mut event: x11_dl::xlib::XEvent = mem::zeroed(); // could also go for mem::uninitialized, but I'd like to be a little safer
242      (self.xlib_dl_obj.XNextEvent)(self.x11_disp_ptr, &mut event);
243      
244      let evt_str: String = match event.get_type() {
245        x11_dl::xlib::Expose => {
246          "WinShown".to_string()
247        }
248        x11_dl::xlib::KeyPress => {
249          match get_alpha_char_from_x11_evt(self, event) {
250            Some(c) => {
251              format!("KeyPress,{}", c)
252            }
253            None => {
254              "KeyPress,UNKNOWN".to_string()
255            }
256          }
257        }
258        _ => {
259          "UNKNOWN".to_string()
260        }
261      };
262      
263      let safe_cstr : CString = CString::new(evt_str).unwrap();
264      return format!("{}", safe_cstr.to_string_lossy().to_owned() );
265    }
266  }
267  
268  fn event_loop(&mut self) {
269    // We can't do this as a rep
270    println!("Warning: event_loop on a CWinRep is a no-op.");
271  }
272}
273