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