1 #![allow(non_camel_case_types, non_snake_case)]
2 use pdcurses::*;
3 use libc::c_int;
4 
5 use std::ffi::{CStr, CString};
6 use std::char::decode_utf16;
7 use std::iter;
8 use std::cmp;
9 
10 pub mod constants;
11 use self::constants::*;
12 
13 use input::Input;
14 
15 #[cfg(any(feature = "win32a", all(not(feature = "win32"), not(feature = "win32a"))))]
16 mod win32a;
17 #[cfg(any(feature = "win32a", all(not(feature = "win32"), not(feature = "win32a"))))]
18 use self::win32a as flavor;
19 
20 #[cfg(feature = "win32")]
21 mod win32;
22 #[cfg(feature = "win32")]
23 use self::win32 as flavor;
24 
25 pub use self::flavor::pre_init;
26 
27 pub fn _attron(w: *mut WINDOW, attributes: chtype) -> i32 {
28     unsafe { wattron(w, attributes) }
29 }
30 
31 pub fn _attroff(w: *mut WINDOW, attributes: chtype) -> i32 {
32     unsafe { wattroff(w, attributes) }
33 }
34 
35 pub fn _attrset(w: *mut WINDOW, attributes: chtype) -> i32 {
36     unsafe { wattrset(w, attributes) }
37 }
38 
39 pub fn _COLORS() -> i32 {
40     unsafe { COLORS }
41 }
42 
43 pub fn _COLOR_PAIRS() -> i32 {
44     unsafe { COLOR_PAIRS }
45 }
46 
47 pub fn _draw_box(w: *mut WINDOW, verch: chtype, horch: chtype) -> i32 {
48     unsafe { _box(w, verch, horch) }
49 }
50 
51 pub fn _getmouse() -> Result<MEVENT, i32> {
52     let mut mevent = MEVENT {
53         id: 0,
54         x: 0,
55         y: 0,
56         z: 0,
57         bstate: 0,
58     };
59     let error = unsafe { nc_getmouse(&mut mevent) };
60     if error == 0 {
61         Ok(mevent)
62     } else {
63         Err(error)
64     }
65 }
66 
67 pub fn _keyname(code: i32) -> Option<String> {
68     let ptr = unsafe { keyname(code) };
69     if ptr.is_null() {
70         None
71     } else {
72         unsafe {
73             // First, get a byte slice of the returned name
74             let bytes = CStr::from_ptr(ptr).to_bytes();
75             // Then assume it's proper UF8 and allocate a String for it.
76             Some(String::from_utf8_unchecked(bytes.to_vec()))
77         }
78     }
79 }
80 
81 pub fn _resize_term(nlines: i32, ncols: i32) -> i32 {
82     unsafe { resize_term(nlines, ncols) }
83 }
84 
85 pub fn _set_blink(enabled: bool) -> i32 {
86     unsafe { PDC_set_blink(enabled as u8) }
87 }
88 
89 pub fn _set_title(title: &str) {
90     let s = CString::new(title).unwrap();
91     unsafe { PDC_set_title(s.as_ptr()) }
92 }
93 
94 /// Converts an integer returned by getch() to an Input value
95 pub fn to_special_keycode(i: i32) -> Option<Input> {
96     // There's two sets of integer constants defined:
97     // - The SPECIAL_KEY_CODES array that contains all codes that are adjacent to each
98     // other for easy indexing into it
99     // - A bunch of scattered constants that need to be checked for
100     // TODO: Unify the constants into a map
101     match i {
102         KEY_RESIZE => Some(Input::KeyResize),
103         KEY_MOUSE => Some(Input::KeyMouse),
104 
105         KEY_NUMPAD_UP => Some(Input::KeyUp),
106         KEY_NUMPAD_DOWN => Some(Input::KeyDown),
107         KEY_NUMPAD_LEFT => Some(Input::KeyLeft),
108         KEY_NUMPAD_RIGHT => Some(Input::KeyRight),
109         KEY_NUMPAD_END => Some(Input::KeyEnd),
110         KEY_NUMPAD_HOME => Some(Input::KeyHome),
111         KEY_NUMPAD_PAGE_UP => Some(Input::KeyPPage),
112         KEY_NUMPAD_PAGE_DOWN => Some(Input::KeyNPage),
113         KEY_NUMPAD_INSERT => Some(Input::KeyIC),
114         KEY_NUMPAD_DELETE => Some(Input::KeyDC),
115         KEY_NUMPAD_ENTER => Some(Input::Character('\n')),
116         KEY_NUMPAD_PLUS => Some(Input::Character('+')),
117         KEY_NUMPAD_MINUS => Some(Input::Character('-')),
118         KEY_NUMPAD_ASTERISK => Some(Input::Character('*')),
119         KEY_NUMPAD_SLASH => Some(Input::Character('/')),
120 
121         _ => {
122             // Since not all special key codes have been added to the SPECIAL_KEY_CODES array,
123             // we need to do some basic math if this input lands into it.
124             let index = if i <= KEY_F15 {
125                 i - KEY_OFFSET // Input that is less than KEY_F15 can be converted directly into an
126                                // an index of the SPECIAL_KEY_CODES array.
127             } else {
128                 i - KEY_OFFSET - 48 // Input past KEY_F15 has to be offset down a bit, since PDCurses
129                                     // has values for 64 function keys
130             };
131             if index < 0 || index as usize >= SPECIAL_KEY_CODES.len() {
132                 // Input is something else. This may require more processing to convert properly into utf8
133                 None
134             } else {
135                 Some(SPECIAL_KEY_CODES[index as usize])
136             }
137         }
138     }
139 }
140 
141 pub fn _wgetch(w: *mut WINDOW) -> Option<Input> {
142     let i = unsafe { wgetch(w) };
143     if i < 0 {
144         None
145     } else {
146         Some(to_special_keycode(i).unwrap_or_else(|| {
147             // Assume that on Windows input is UTF-16
148             // If decoding the single input value fails, it should mean that it is the leading part of a
149             // surrogate pair so calling getch() again should return the trailing part
150 
151             decode_utf16(iter::once(i as u16))
152                 .map(|result| {
153                     result
154                         .map(Input::Character)
155                         .unwrap_or_else(|first_error| {
156                             let trailing = unsafe { wgetch(w) };
157                             let data = [i as u16, trailing as u16];
158                             decode_utf16(data.iter().cloned())
159                                 .map(|result| {
160                                     result.map(Input::Character).unwrap_or_else(
161                                         |second_error| {
162                                             warn!("Decoding input as UTF-16 failed. The two values that could not be decoded were {} and {}.", first_error.unpaired_surrogate(), second_error.unpaired_surrogate());
163                                             Input::Unknown(second_error.unpaired_surrogate() as i32)
164                                         },
165                                     )
166                                 })
167                                 .next()
168                                 .unwrap()
169                         })
170                 })
171                 .next()
172                 .unwrap()
173         }))
174     }
175 }
176 
177 pub fn _ungetch(input: &Input) -> i32 {
178     match *input {
179         Input::Character(c) => {
180             // Need to convert to UTF-16 since Rust chars are UTF-8 while PDCurses deals with UTF-16
181             let mut utf16_buffer = [0; 2];
182             c.encode_utf16(&mut utf16_buffer)
183                 .iter_mut()
184                 .rev()
185                 .map(|x| unsafe { PDC_ungetch(*x as c_int) })
186                 .fold(0, cmp::min)
187         }
188         Input::Unknown(i) => unsafe { PDC_ungetch(i) },
189         Input::KeyResize => unsafe { PDC_ungetch(KEY_RESIZE) },
190         Input::KeyMouse => unsafe { PDC_ungetch(KEY_MOUSE) },
191         specialKeyCode => {
192             for (i, skc) in SPECIAL_KEY_CODES.iter().enumerate() {
193                 if *skc == specialKeyCode {
194                     let result = i as c_int + KEY_OFFSET;
195                     if result <= KEY_F15 {
196                         return unsafe { PDC_ungetch(result) };
197                     } else {
198                         return unsafe { PDC_ungetch(result + 48) };
199                     }
200                 }
201             }
202             panic!("Failed to convert Input back to a c_int");
203         }
204     }
205 }
206 
207 #[cfg(test)]
208 mod tests {
209     use super::*;
210     use input::Input;
211 
212     #[test]
213     fn test_key_dl_to_special_keycode() {
214         assert_eq!(Input::KeyDL, to_special_keycode(KEY_OFFSET + 0x48).unwrap());
215     }
216 
217     #[test]
218     fn test_key_f15_to_input() {
219         assert_eq!(
220             Input::KeyF15,
221             to_special_keycode(KEY_OFFSET + 0x08 + 15).unwrap()
222         );
223     }
224 
225     #[test]
226     fn test_key_up_to_input() {
227         assert_eq!(Input::KeyUp, to_special_keycode(KEY_OFFSET + 3).unwrap());
228     }
229 
230     #[test]
231     fn test_ungetch() {
232         let w = unsafe { initscr() };
233 
234         let chars = [
235             'a', 'b', 'c', 'ä', 'ö', 'å', 'A', 'B', 'C', 'Ä', 'Ö', 'Å', '��', '��',
236             '€', 'ᚠ', 'ᛇ', 'ᚻ', 'þ', 'ð', 'γ', 'λ', 'ώ', 'б', 'е', 'р', 'ვ',
237             'ე', 'პ', 'ხ', 'இ', 'ங', 'க', 'ಬ', 'ಇ', 'ಲ', 'ಸ',
238         ];
239 
240         chars.iter().for_each(|c| {
241             _ungetch(&Input::Character(*c));
242             assert_eq!(_wgetch(w).unwrap(), Input::Character(*c));
243         });
244 
245         SPECIAL_KEY_CODES.iter().for_each(|i| {
246             _ungetch(i);
247             assert_eq!(_wgetch(w).unwrap(), *i);
248         });
249 
250         unsafe {
251             endwin();
252         }
253     }
254 
255 }
256