1use std::{
11 io::{ self, Write, Read },
12 cmp::{ max, min },
13 collections::VecDeque,
14};
15
16use termios::{ Termios, TCSANOW, ECHO, ICANON, tcsetattr };
17
18pub fn getch() -> u8 {
29 let stdin = 0;
31 let termios = Termios::from_fd(stdin).unwrap();
32 let mut new_termios = termios;
33 new_termios.c_lflag &= !(ICANON | ECHO);
34 tcsetattr(stdin, TCSANOW, &new_termios).unwrap();
35 let stdout = io::stdout();
36 let reader = io::stdin();
37 let mut buffer = [0; 1];
38 stdout.lock().flush().unwrap();
39 reader.lock().read_exact(&mut buffer).unwrap();
40 tcsetattr(stdin, TCSANOW, &termios).unwrap();
41 buffer[0]
42}
43
44pub fn test_chars() {
53 loop {
54 println!("{:?}", getch());
55 }
56}
57
58pub struct InputList {
60 ilist: VecDeque<String>,
61 maxlen: usize,
62}
63
64impl InputList {
65 pub fn new(maxlen: usize) -> Self {
75 Self{
76 ilist: VecDeque::new(),
77 maxlen
78 }
79 }
80
81 fn trim(&mut self){
82 while self.ilist.len() > self.maxlen {
83 self.ilist.pop_front();
84 }
85 }
86
87 pub fn add(&mut self, string: &str) {
103 self.ilist.push_back(string.to_string());
104 self.trim();
105 }
106
107 pub fn get_index(&self, mut index: i32) -> Option<&String> {
127 if !self.ilist.is_empty(){
128 index %= self.ilist.len() as i32;
129 }
130 if index < 0{
131 index += self.maxlen as i32;
132 }
133 self.ilist.get(index as usize)
134 }
135}
136
137#[derive(Debug, Copy, Clone, PartialEq)]
151pub enum PrintChar {
152 Copy,
153 Substitute(char),
154 None,
155}
156
157pub fn input_field(ilist: &mut InputList, pc: PrintChar, newline: bool) -> String {
158 fn charvec_to_string(vec: &[char]) -> String {
159 let mut string = String::new();
160 for &ch in vec {
161 string.push(ch);
162 }
163 string
164 }
165 fn typed_char(
166 ch: u8,
167 buff: &mut Vec<char>,
168 gstate: &mut u8,
169 hstate: &mut u8,
170 pos: &mut usize,
171 pc: PrintChar
172 ){
173 let ch = ch as char;
174 buff.insert(*pos, ch);
175 if pc != PrintChar::None {
176 if *pos != buff.len() - 1 {
177 for item in buff.iter().skip(*pos) {
178 put_char(*item, pc);
179 }
180 go_back(*pos, buff.len() - 1, pc);
181 } else {
182 put_char(ch, pc);
183 }
184 }
185 *hstate = 0;
186 *gstate = 0;
187 *pos += 1;
188 }
189 fn delete_all(buff: &mut Vec<char>, pc: PrintChar) -> usize {
190 if pc == PrintChar::None {
191 buff.clear();
192 return 0;
193 }
194 let len = buff.len();
195 go_back(0, len, pc);
196 buff.clear();
197 len
198 }
199 fn feed_into_buffer(buff: &mut Vec<char>, string: &str) {
200 for ch in string.chars() {
201 buff.push(ch);
202 }
203 }
204 fn write_all(buff: &[char], pc: PrintChar) {
205 for item in buff.iter() {
206 put_char(*item, pc);
207 }
208 }
209 fn scroll_action(
210 res: &mut Vec<char>,
211 pos: &mut usize,
212 ilist: &InputList,
213 his_index: i32,
214 pc: PrintChar
215 ){
216 let val = ilist.get_index(his_index);
217 if let Some(valv) = val {
218 let old_len = delete_all(res, pc);
219 feed_into_buffer(res, valv);
220 *pos = res.len();
221 if pc == PrintChar::None { return; }
222 write_all(res, pc);
223 let diff = old_len as i32 - res.len() as i32;
224 if diff <= 0 { return; }
225 for _ in 0..diff {
226 print!(" ");
227 }
228 for _ in 0..diff {
229 print!("{}", 8 as char);
230 }
231 }
232 }
233 fn delete(res: &mut Vec<char>, pos: &mut usize, gstate: &mut u8, pc: PrintChar) {
234 if res.is_empty() { return; }
235 if *pos >= res.len() - 1 { return; }
236 res.remove(*pos);
237 for item in res.iter().skip(*pos) {
238 put_char(*item, pc);
239 }
240 if pc != PrintChar::None { print!(" "); }
241 go_back(*pos, res.len() + 1, pc);
242 *gstate = 0;
243 }
244 fn end(res: &mut [char], pos: &mut usize, hoen_state: &mut u8, pc: PrintChar) {
245 if pc != PrintChar::None {
246 for _ in *pos..res.len() {
247 print!("\x1B[1C");
248 }
249 }
250 *hoen_state = 0;
251 *pos = res.len();
252 }
253 fn put_char(ch: char, pc: PrintChar) {
254 match pc {
255 PrintChar::Copy => print!("{}", ch),
256 PrintChar::Substitute(sch) => print!("{}", sch),
257 PrintChar::None => {},
258 };
259 }
260 fn go_back(start: usize, end: usize, pc: PrintChar) {
261 if pc == PrintChar::None { return; }
262 for _ in start..end {
263 print!("{}", 8 as char);
264 }
265 }
266
267 let mut res = Vec::new();
268 let mut gstate: u8 = 0;
269 let mut hoen_state: u8 = 0;
270 let mut pos = 0;
271 let mut his_index: i32 = 0;
272
273 flush().expect("term_basics_linux: Error: stdout flush failed.");
274 loop {
275 let mut x = getch();
276 if x == 8 { x = 127; } match x {
278 10 => { if newline {
280 println!();
281 }
282 break;
283 }
284 127 => { if res.is_empty() { continue; }
286 if pos == 0 { continue; }
287 res.remove(pos - 1);
288 if pc != PrintChar::None {
289 print!("{}", 8 as char);
290 for item in res.iter().skip(pos - 1){
292 put_char(*item, pc);
293 }
294 print!(" ");
295 go_back(pos - 1, res.len() + 1, pc);
296 }
297 pos -= 1;
298 gstate = 0;
299 }
300 27 => { gstate = 1;
302 hoen_state = 1;
303 }
304 91 => { if gstate == 1 { gstate = 2; }
306 if hoen_state == 1 { hoen_state = 2; }
307 if gstate == 2 || hoen_state == 2 { continue; }
308 typed_char(91, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc);
309 }
310 65 => { if gstate == 2 {
312 gstate = 0;
313 his_index += 1;
314 scroll_action(&mut res, &mut pos, ilist, his_index, pc);
315 }
316 else { typed_char(65, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc); }
317 }
318 66 => { if gstate == 2 {
320 gstate = 0;
321 his_index -= 1;
322 scroll_action(&mut res, &mut pos, ilist, his_index, pc);
323 }
324 else { typed_char(66, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc); }
325 }
326 72 => { if hoen_state != 2 {
328 typed_char(72, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc);
329 continue;
330 }
331 go_back(0, pos, pc);
332 pos = 0;
333 hoen_state = 0;
334 }
335 51 => {
336 if gstate != 2 {
337 typed_char(51, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc);
338 }
339 else {
340 gstate = 3;
341 }
342 }
343 52 => { if hoen_state == 2 { hoen_state = 3; }
345 else { typed_char(52, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc); }
346 }
347 70 =>{ if hoen_state == 2 {
349 end(&mut res, &mut pos, &mut hoen_state, pc);
350 }
351 else{
352 typed_char(70, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc);
353 }
354 }
355 126 => { if hoen_state == 3 { end(&mut res, &mut pos, &mut hoen_state, pc);
358 }
359 else if gstate >= 2{ delete(&mut res, &mut pos, &mut gstate, pc);
361 }
362 else {
363 typed_char(126, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc);
364 }
365 }
366 80 => { if gstate != 2 {
368 typed_char(80, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc);
369 continue;
370 }
371 delete(&mut res, &mut pos, &mut gstate, pc);
372 }
373 67 => { if gstate == 2 {
375 let old_pos = pos;
376 pos = min(pos + 1, res.len());
377 gstate = 0;
378 if pc == PrintChar::None { continue; }
379 if pos > old_pos { print!("\x1B[1C"); }
381 }
382 else { typed_char(67, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc); }
383 }
384 68 => { if gstate == 2 {
386 let old_pos = pos;
387 pos = max(pos as i32 - 1, 0_i32) as usize;
388 gstate = 0;
389 if pc == PrintChar::None { continue; }
390 if pos < old_pos { print!("{}", 8 as char); }
392 }
393 else { typed_char(68, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc); }
394 }
395 x => { typed_char(x, &mut res, &mut gstate, &mut hoen_state, &mut pos, pc); }
396 }
397 }
398 let string = charvec_to_string(&res);
399 ilist.add(&string);
400 string
401}
402
403pub fn string_to_bool(string: &str) -> bool {
404 matches!(string, "y" | "ye" | "yes" | "ok" | "+" | "t" | "tr" | "tru" | "true")
405}
406
407pub fn string_to_value<T: std::str::FromStr>(string: &str) -> Option<T> {
421 let res = string.parse::<T>();
422 if res.is_err() { return Option::None; }
423 res.ok()
424}
425
426pub fn flush() -> io::Result<()> {
440 std::io::stdout().flush()
441}
442
443pub fn input_field_simple(newline: bool) -> String {
458 input_field(&mut InputList::new(0), PrintChar::Copy, newline)
459}
460
461pub fn input_field_scrollable(ilist: &mut InputList, newline: bool) -> String {
462 input_field(ilist, PrintChar::Copy, newline)
463}
464
465#[cfg(test)]
466mod tests {
467 #[test]
468 fn it_works() {
469 assert_eq!(2 + 2, 4);
470 }
471
472 #[test]
473 fn string_to_int0(){
474 let t: Option<u32> = super::string_to_value(&String::from("12981398"));
475 assert_eq!(t, Option::Some(12981398));
476 }
477
478 #[test]
479 fn string_to_int1(){
480 let t: Option<i32> = super::string_to_value(&String::from("-1234"));
481 assert_eq!(t, Option::Some(-1234));
482 }
483
484 #[test]
485 fn string_to_int2(){
486 let t: Option<u8> = super::string_to_value(&String::from("70000"));
487 assert_eq!(t, Option::None);
488 }
489
490 #[test]
491 fn string_to_int3(){
492 let t: Option<i32> = super::string_to_value(&String::from("23ohno23"));
493 assert_eq!(t, Option::None);
494 }
495
496 #[test]
497 fn string_to_float0(){
498 let t: Option<f32> = super::string_to_value(&String::from("34.5"));
499 assert_eq!(t, Option::Some(34.5));
500 }
501
502 #[test]
503 fn string_to_float1(){
504 let t: Option<f64> = super::string_to_value(&String::from("-0.00000000000001"));
505 assert_eq!(t, Option::Some(-0.00000000000001));
506 }
507
508 #[test]
509 fn string_to_bool0(){
510 assert!(super::string_to_bool(&String::from("yes")));
511 }
512
513 #[test]
514 fn string_to_bool1(){
515 let t: Option<bool> = super::string_to_value(&String::from("true"));
516 assert_eq!(t, Option::Some(true));
517 }
518
519 #[test]
520 fn string_to_bool2(){
521 let t: Option<bool> = super::string_to_value(&String::from("false"));
522 assert_eq!(t, Option::Some(false));
523 }
524}
525