ransid/
lib.rs

1extern crate vte;
2#[macro_use]
3extern crate log;
4
5use std::{char, cmp, str};
6
7pub use color::Color;
8
9pub mod color;
10
11pub enum Event<'a> {
12    Char {
13        x: usize,
14        y: usize,
15        c: char,
16        bold: bool,
17        italic: bool,
18        underlined: bool,
19        strikethrough: bool,
20        color: Color
21    },
22    Input {
23        data: &'a [u8]
24    },
25    Rect {
26        x: usize,
27        y: usize,
28        w: usize,
29        h: usize,
30        color: Color
31    },
32    ScreenBuffer {
33        alternate: bool,
34        clear: bool,
35    },
36    Move {
37        from_x: usize,
38        from_y: usize,
39        to_x: usize,
40        to_y: usize,
41        w: usize,
42        h: usize,
43    },
44    Resize {
45        w: usize,
46        h: usize,
47    },
48    Title {
49        title: String
50    }
51}
52
53pub struct State {
54    pub x: usize,
55    pub y: usize,
56    pub save_x: usize,
57    pub save_y: usize,
58    pub w: usize,
59    pub h: usize,
60    pub top_margin: usize,
61    pub bottom_margin: usize,
62    pub g0: char,
63    pub g1: char,
64    pub foreground: Color,
65    pub background: Color,
66    pub foreground_default: Color,
67    pub background_default: Color,
68    pub bold: bool,
69    pub inverted: bool,
70    pub italic: bool,
71    pub underlined: bool,
72    pub strikethrough: bool,
73    pub cursor: bool,
74    pub redraw: bool,
75    pub origin: bool,
76    pub autowrap: bool,
77    pub mouse_vt200: bool,
78    pub mouse_btn: bool,
79    pub mouse_sgr: bool,
80    pub mouse_rxvt: bool,
81}
82
83impl State {
84    pub fn new(w: usize, h: usize) -> State {
85        State {
86            x: 0,
87            y: 0,
88            save_x: 0,
89            save_y: 0,
90            w: w,
91            h: h,
92            top_margin: 0,
93            bottom_margin: cmp::max(0, h as isize - 1) as usize,
94            g0: 'B',
95            g1: '0',
96            foreground: Color::Ansi(7),
97            background: Color::Ansi(0),
98            foreground_default: Color::Ansi(7),
99            background_default: Color::Ansi(0),
100            bold: false,
101            inverted: false,
102            italic: false,
103            underlined: false,
104            strikethrough: false,
105            cursor: true,
106            redraw: true,
107            origin: false,
108            autowrap: true,
109            mouse_vt200: false,
110            mouse_btn: false,
111            mouse_sgr: false,
112            mouse_rxvt: false,
113        }
114    }
115
116    fn block<F: FnMut(Event)>(&self, c: char, callback: &mut F) {
117        callback(Event::Rect {
118            x: self.x,
119            y: self.y,
120            w: 1,
121            h: 1,
122            color: if self.inverted { self.foreground } else { self.background }
123        });
124        callback(Event::Char {
125            x: self.x,
126            y: self.y,
127            c: c,
128            bold: self.bold,
129            italic: self.italic,
130            underlined: self.underlined,
131            strikethrough: self.strikethrough,
132            color: if self.inverted { self.background } else { self.foreground }
133        });
134    }
135
136    fn scroll<F: FnMut(Event)>(&self, rows: usize, callback: &mut F) {
137        //TODO: Use min and max to ensure correct behavior
138        callback(Event::Move {
139            from_x: 0,
140            from_y: self.top_margin + rows,
141            to_x: 0,
142            to_y: self.top_margin,
143            w: self.w,
144            h: (self.bottom_margin + 1) - rows,
145        });
146        callback(Event::Rect {
147            x: 0,
148            y: (self.bottom_margin + 1) - rows,
149            w: self.w,
150            h: rows,
151            color: self.background,
152        });
153    }
154
155    fn reverse_scroll<F: FnMut(Event)>(&self, rows: usize, callback: &mut F) {
156        //TODO: Use min and max to ensure correct behavior
157        callback(Event::Move {
158            from_x: 0,
159            from_y: self.top_margin,
160            to_x: 0,
161            to_y: self.top_margin + rows,
162            w: self.w,
163            h: (self.bottom_margin + 1) - rows,
164        });
165        callback(Event::Rect {
166            x: 0,
167            y: self.top_margin,
168            w: self.w,
169            h: rows,
170            color: self.background,
171        });
172    }
173
174    fn fix_cursor<F: FnMut(Event)>(&mut self, callback: &mut F) {
175        let w = self.w;
176        let h = cmp::min(self.h, self.bottom_margin + 1);
177
178        if self.x >= w {
179            if self.autowrap {
180                self.x = 0;
181                self.y += 1;
182            } else {
183                self.x = w.checked_sub(1).unwrap_or(0);
184            }
185        }
186
187        if self.y + 1 > h {
188            let rows = self.y + 1 - h;
189            self.scroll(rows, callback);
190            self.y = self.y.checked_sub(rows).unwrap_or(0);
191        }
192    }
193
194    pub fn print<F: FnMut(Event)>(&mut self, c: char, callback: &mut F) {
195        self.fix_cursor(callback);
196        self.block(c, callback);
197        self.x += 1;
198    }
199
200    pub fn execute<F: FnMut(Event)>(&mut self, c: char, _callback: &mut F) {
201        // Fix for vt100 wrapping behavior: http://invisible-island.net/xterm/xterm.faq.html#vt100_wrapping
202        //let xenl = self.x + 1 >= self.w;
203        let xenl = false;
204
205        match c {
206            //'\x07' => {}, // FIXME: Add bell
207            '\x08' => { // Backspace
208                self.x = cmp::max(0, self.x as i64 - 1) as usize;
209            },
210            '\x09' => if ! xenl { // Tab
211                self.x = cmp::max(0, cmp::min(self.w as i64 - 1, ((self.x as i64 / 8) + 1) * 8)) as usize;
212            },
213            '\x0A' => if ! xenl { // Newline
214                self.x = 0;
215                self.y += 1;
216            },
217            '\x0D' => if ! xenl { // Carriage Return
218                self.x = 0;
219            },
220            _ => {
221                warn!("Unknown execute {:?}", c);
222            }
223        }
224    }
225
226    pub fn csi<F: FnMut(Event)>(&mut self, c: char, params: &[i64], _intermediates: &[u8], callback: &mut F) {
227        match c {
228            'A' => {
229                let param = params.get(0).map(|v| *v).unwrap_or(1);
230                if self.y < self.top_margin {
231                    self.y = cmp::max(0, self.y as i64 - cmp::max(1, param)) as usize;
232                } else {
233                    self.y = cmp::max(self.top_margin as i64, self.y as i64 - cmp::max(1, param)) as usize;
234                }
235            },
236            'B' => {
237                let param = params.get(0).map(|v| *v).unwrap_or(1);
238                if self.y > self.bottom_margin {
239                    self.y = cmp::max(0, cmp::min(self.h as i64 - 1, self.y as i64 + cmp::max(1, param))) as usize;
240                } else {
241                    self.y = cmp::max(0, cmp::min(self.bottom_margin as i64, self.y as i64 + cmp::max(1, param))) as usize;
242                }
243            },
244            'C' => {
245                let param = params.get(0).map(|v| *v).unwrap_or(1);
246                self.x = cmp::max(0, cmp::min(self.w as i64 - 1, self.x as i64 + cmp::max(1, param))) as usize;
247            },
248            'D' => {
249                let param = params.get(0).map(|v| *v).unwrap_or(1);
250                self.x = cmp::max(0, self.x as i64 - cmp::max(1, param)) as usize;
251            },
252            'E' => {
253                let param = params.get(0).map(|v| *v).unwrap_or(1);
254                self.x = 0;
255                self.y += cmp::min(self.h.checked_sub(self.y + 1).unwrap_or(0), cmp::max(1, param) as usize);
256            },
257            'F' => {
258                let param = params.get(0).map(|v| *v).unwrap_or(1);
259                self.x = 0;
260                self.y -= cmp::min(self.y, cmp::max(1, param) as usize);
261            },
262            'G' => {
263                let param = params.get(0).map(|v| *v).unwrap_or(1);
264                let col = cmp::max(1, param);
265                self.x = cmp::max(0, cmp::min(self.w as i64 - 1, col - 1)) as usize;
266            },
267            'H' | 'f' => {
268                {
269                    let param = params.get(0).map(|v| *v).unwrap_or(1);
270                    let row = cmp::max(1, param);
271
272                    let (top, bottom) = if self.origin {
273                        (self.top_margin, self.bottom_margin + 1)
274                    } else {
275                        (0, self.h)
276                    };
277
278                    self.y = cmp::max(0, cmp::min(bottom as i64 - 1, row + top as i64 - 1)) as usize;
279                }
280
281                {
282                    let param = params.get(1).map(|v| *v).unwrap_or(1);
283                    let col = cmp::max(1, param);
284                    self.x = cmp::max(0, cmp::min(self.w as i64 - 1, col - 1)) as usize;
285                }
286            },
287            'J' => {
288                self.fix_cursor(callback);
289
290                let param = params.get(0).map(|v| *v).unwrap_or(0);
291                match param {
292                    0 => {
293                        // Clear current row from cursor
294                        callback(Event::Rect {
295                            x: self.x,
296                            y: self.y,
297                            w: self.w - self.x,
298                            h: 1,
299                            color: self.background
300                        });
301
302                        // Clear following rows
303                        callback(Event::Rect {
304                            x: 0,
305                            y: self.y,
306                            w: self.w,
307                            h: self.h - self.y,
308                            color: self.background
309                        });
310                    },
311                    1 => {
312                        // Clear previous rows
313                        callback(Event::Rect {
314                            x: 0,
315                            y: 0,
316                            w: self.w,
317                            h: self.y,
318                            color: self.background
319                        });
320
321                        // Clear current row to cursor
322                        callback(Event::Rect {
323                            x: 0,
324                            y: self.y,
325                            w: self.x,
326                            h: 1,
327                            color: self.background
328                        });
329                    },
330                    2 => {
331                        // Erase all
332                        self.x = 0;
333                        self.y = 0;
334
335                        // Clear all rows
336                        callback(Event::Rect {
337                            x: 0,
338                            y: 0,
339                            w: self.w,
340                            h: self.h,
341                            color: self.background
342                        });
343                    },
344                    _ => {
345                        warn!("Unknown CSI {:?} param {:?}", c, param);
346                    }
347                }
348            },
349            'K' => {
350                self.fix_cursor(callback);
351
352                let param = params.get(0).map(|v| *v).unwrap_or(0);
353                match param {
354                    0 => {
355                        // Clear current row from cursor
356                        callback(Event::Rect {
357                            x: self.x,
358                            y: self.y,
359                            w: self.w - self.x,
360                            h: 1,
361                            color: self.background
362                        });
363                    },
364                    1 => {
365                        // Clear current row to cursor
366                        callback(Event::Rect {
367                            x: 0,
368                            y: self.y,
369                            w: self.x,
370                            h: 1,
371                            color: self.background
372                        });
373                    },
374                    2 => {
375                        // Erase row
376                        callback(Event::Rect {
377                            x: 0,
378                            y: self.y,
379                            w: self.w,
380                            h: 1,
381                            color: self.background
382                        });
383                    },
384                    _ => {
385                        warn!("Unknown CSI {:?} param {:?}", c, param);
386                    }
387                }
388            },
389            'P' => {
390                let param = params.get(0).map(|v| *v).unwrap_or(1);
391                let cols = cmp::max(0, cmp::min(self.w as i64 - self.x as i64 - 1, param)) as usize;
392                //TODO: Use min and max to ensure correct behavior
393                callback(Event::Move {
394                    from_x: self.x + cols,
395                    from_y: self.y,
396                    to_x: self.x,
397                    to_y: self.y,
398                    w: self.w - (self.x + cols),
399                    h: 1,
400                });
401                callback(Event::Rect {
402                    x: self.w - cols,
403                    y: self.y,
404                    w: cols,
405                    h: 1,
406                    color: self.background,
407                });
408            },
409            'S' => {
410                let param = params.get(0).map(|v| *v).unwrap_or(1);;
411                self.scroll(cmp::max(0, param) as usize, callback);
412            },
413            'T' => {
414                let param = params.get(0).map(|v| *v).unwrap_or(1);
415                self.reverse_scroll(cmp::max(0, param) as usize, callback);
416            },
417            'c' => {
418                let report = format!("\x1B[?6c");
419                callback(Event::Input {
420                    data: &report.into_bytes()
421                });
422            },
423            'd' => {
424                let param = params.get(0).map(|v| *v).unwrap_or(1);
425                self.y = cmp::max(0, cmp::min(self.h as i64 - 1, param - 1)) as usize;
426            },
427            'h' => {
428                //TODO: Check intermediate
429                let param = params.get(0).map(|v| *v).unwrap_or(0);
430                match param {
431                    3 => {
432                        self.x = 0;
433                        self.y = 0;
434                        self.top_margin = 0;
435                        self.bottom_margin = cmp::max(0, self.h as isize - 1) as usize;
436
437                        self.w = 132;
438                        //Resize screen
439                        callback(Event::Resize {
440                            w: self.w,
441                            h: self.h
442                        });
443
444                        // Clear screen
445                        callback(Event::Rect {
446                            x: 0,
447                            y: 0,
448                            w: self.w,
449                            h: self.h,
450                            color: self.background
451                        });
452                    },
453                    6 => {
454                        self.origin = true;
455                        self.x = 0;
456                        self.y = self.top_margin;
457                    },
458                    7 => self.autowrap = true,
459                    25 => self.cursor = true,
460                    47 => callback(Event::ScreenBuffer {
461                        alternate: true,
462                        clear: false,
463                    }),
464                    1000 => self.mouse_vt200 = true,
465                    1002 => self.mouse_btn = true,
466                    1006 => self.mouse_sgr = true,
467                    1015 => self.mouse_rxvt = true,
468                    1047 => callback(Event::ScreenBuffer {
469                        alternate: true,
470                        clear: false,
471                    }),
472                    1048 => {
473                        self.save_x = self.x;
474                        self.save_y = self.y;
475                    },
476                    1049 => {
477                        self.save_x = self.x;
478                        self.save_y = self.y;
479
480                        callback(Event::ScreenBuffer {
481                            alternate: true,
482                            clear: true,
483                        });
484                    },
485                    unknown => {
486                        warn!("Unknown CSI {:?} param {:?}", c, unknown);
487                    }
488                }
489            },
490            'l' => {
491                //TODO: Check intermediate
492                let param = params.get(0).map(|v| *v).unwrap_or(0);
493                match param {
494                    3 => {
495                        self.x = 0;
496                        self.y = 0;
497                        self.top_margin = 0;
498                        self.bottom_margin = cmp::max(0, self.h as isize - 1) as usize;
499
500                        self.w = 80;
501                        //Resize screen
502                        callback(Event::Resize {
503                            w: self.w,
504                            h: self.h
505                        });
506
507                        // Clear screen
508                        callback(Event::Rect {
509                            x: 0,
510                            y: 0,
511                            w: self.w,
512                            h: self.h,
513                            color: self.background
514                        });
515                    },
516                    6 => {
517                        self.origin = false;
518                        self.x = 0;
519                        self. y = 0;
520                    },
521                    7 => self.autowrap = false,
522                    25 => self.cursor = false,
523                    47 => callback(Event::ScreenBuffer {
524                        alternate: false,
525                        clear: false,
526                    }),
527                    1000 => self.mouse_vt200 = false,
528                    1002 => self.mouse_btn = false,
529                    1006 => self.mouse_sgr = false,
530                    1015 => self.mouse_rxvt = false,
531                    1047 => callback(Event::ScreenBuffer {
532                        alternate: false,
533                        clear: true
534                    }),
535                    1048 => {
536                        self.x = self.save_x;
537                        self.y = self.save_y;
538                    },
539                    1049 => {
540                        self.x = self.save_x;
541                        self.y = self.save_y;
542
543                        callback(Event::ScreenBuffer {
544                            alternate: false,
545                            clear: false,
546                        });
547                    }
548                    unknown => {
549                        warn!("Unknown CSI {:?} param {:?}", c, unknown);
550                    }
551                }
552            },
553            'm' => {
554                // Display attributes
555                let mut value_iter = if params.len() == 0 {
556                    [0].iter()
557                } else {
558                    params.iter()
559                };
560                while let Some(value) = value_iter.next() {
561                    match *value {
562                        0 => {
563                            self.foreground = self.foreground_default;
564                            self.background = self.background_default;
565                            self.bold = false;
566                            self.italic = false;
567                            self.underlined = false;
568                            self.strikethrough = false;
569                            self.inverted = false;
570                        },
571                        1 => {
572                            self.bold = true;
573                        },
574                        3 => {
575                            self.italic = true;
576                        },
577                        4 => {
578                            self.underlined = true;
579                        },
580                        7 => {
581                            self.inverted = true;
582                        },
583                        9 => {
584                            self.strikethrough = true;
585                        }
586                        21 => {
587                            self.bold = false;
588                        },
589                        23 => {
590                            self.italic = false;
591                        },
592                        24 => {
593                            self.underlined = false;
594                        },
595                        27 => {
596                            self.inverted = false;
597                        },
598                        30 ... 37 => self.foreground = Color::Ansi(*value as u8 - 30),
599                        38 => match value_iter.next().map(|v| *v).unwrap_or(0) {
600                            2 => {
601                                //True color
602                                let r = value_iter.next().map(|v| *v).unwrap_or(0);
603                                let g = value_iter.next().map(|v| *v).unwrap_or(0);
604                                let b = value_iter.next().map(|v| *v).unwrap_or(0);
605                                self.foreground = Color::TrueColor(r as u8, g as u8, b as u8);
606                            },
607                            5 => {
608                                //256 color
609                                let color_value = value_iter.next().map(|v| *v).unwrap_or(0);
610                                self.foreground = Color::Ansi(color_value as u8);
611                            },
612                            _ => {}
613                        },
614                        39 => {
615                            self.foreground = self.foreground_default;
616                        },
617                        40 ... 47 => self.background = Color::Ansi(*value as u8 - 40),
618                        48 => match value_iter.next().map(|v| *v).unwrap_or(0) {
619                            2 => {
620                                //True color
621                                let r = value_iter.next().map(|v| *v).unwrap_or(0);
622                                let g = value_iter.next().map(|v| *v).unwrap_or(0);
623                                let b = value_iter.next().map(|v| *v).unwrap_or(0);
624                                self.background = Color::TrueColor(r as u8, g as u8, b as u8);
625                            },
626                            5 => {
627                                //256 color
628                                let color_value = value_iter.next().map(|v| *v).unwrap_or(0);
629                                self.background = Color::Ansi(color_value as u8);
630                            },
631                            _ => {}
632                        },
633                        49 => {
634                            self.background = self.background_default;
635                        },
636                        _ => {
637                            warn!("Unknown CSI {:?} param {:?}", c, value);
638                        },
639                    }
640                }
641            },
642            'n' => {
643                let param = params.get(0).map(|v| *v).unwrap_or(0);
644                match param {
645                    6 => {
646                        let report = format!("\x1B[{};{}R", self.y + 1, self.x + 1);
647                        callback(Event::Input {
648                            data: &report.into_bytes()
649                        });
650                    },
651                    _ => {
652                        warn!("Unknown CSI {:?} param {:?}", c, param);
653                    }
654                }
655            },
656            'r' => {
657                let top = params.get(0).map(|v| *v).unwrap_or(1);
658                let bottom = params.get(1).map(|v| *v).unwrap_or(self.h as i64);
659                self.top_margin = cmp::max(0, cmp::min(self.h as isize - 1, top as isize - 1)) as usize;
660                self.bottom_margin = cmp::max(self.top_margin as isize, cmp::min(self.h as isize - 1, bottom as isize - 1)) as usize;
661            },
662            's' => {
663                self.save_x = self.x;
664                self.save_y = self.y;
665            },
666            'u' => {
667                self.x = self.save_x;
668                self.y = self.save_y;
669            },
670            '@' => {
671                let param = params.get(0).map(|v| *v).unwrap_or(1);
672                let cols = cmp::max(0, cmp::min(self.w as i64 - self.x as i64 - 1, param)) as usize;
673                //TODO: Use min and max to ensure correct behavior
674                callback(Event::Move {
675                    from_x: self.x,
676                    from_y: self.y,
677                    to_x: self.x + cols,
678                    to_y: self.y,
679                    w: self.w - (self.x + cols),
680                    h: 1,
681                });
682                callback(Event::Rect {
683                    x: self.x,
684                    y: self.y,
685                    w: cols,
686                    h: 1,
687                    color: self.background,
688                });
689            },
690            _ => {
691                warn!("Unknown CSI {:?}", c);
692            }
693        }
694    }
695
696    pub fn esc<F: FnMut(Event)>(&mut self, c: char, _params: &[i64], intermediates: &[u8], callback: &mut F) {
697        match c {
698            'D' => {
699                self.y += 1;
700            },
701            'E' => {
702                self.x = 0;
703                self.y += 1;
704            },
705            'M' => {
706                while self.y <= 0 {
707                    self.reverse_scroll(1, callback);
708                    self.y += 1;
709                }
710                self.y -= 1;
711            },
712            '7' => {
713                // Save
714                self.save_x = self.x;
715                self.save_y = self.y;
716            },
717            '8' => {
718                match intermediates.get(0).map(|v| *v as char) {
719                    Some('#') => {
720                        // Test pattern
721                        for x in (self.w/2).checked_sub(30).unwrap_or(10)..(self.w/2).checked_add(30).unwrap_or(70) {
722                            self.x = x;
723
724                            self.y = 8;
725                            self.block('E', callback);
726
727                            self.y = 15;
728                            self.block('E', callback);
729                        }
730
731                        for y in 9..15 {
732                            self.y = y;
733
734                            self.x = (self.w/2).checked_sub(30).unwrap_or(10);
735                            self.block('E', callback);
736
737                            self.x = (self.w/2).checked_add(29).unwrap_or(69);
738                            self.block('E', callback);
739                        }
740
741                        self.x = 0;
742                        self.y = 0;
743                    },
744                    Some(inter) => {
745                        warn!("Unknown ESC {:?} intermediate {:?}", c, inter);
746                    },
747                    None => {
748                        // Restore
749                        self.x = self.save_x;
750                        self.y = self.save_y;
751                    }
752                }
753            },
754            'c' => {
755                // Reset
756                self.x = 0;
757                self.y = 0;
758                self.save_x = 0;
759                self.save_y = 0;
760                self.top_margin = 0;
761                self.bottom_margin = cmp::max(0, self.h as isize - 1) as usize;
762                self.cursor = true;
763                self.g0 = 'B';
764                self.g1 = '0';
765                self.foreground = self.foreground_default;
766                self.background = self.background_default;
767                self.bold = false;
768                self.inverted = false;
769                self.italic = false;
770                self.underlined = false;
771                self.strikethrough = false;
772
773                // Clear screen
774                callback(Event::Rect {
775                    x: 0,
776                    y: 0,
777                    w: self.w,
778                    h: self.h,
779                    color: self.background
780                });
781
782                self.redraw = true;
783            },
784            _ => {
785                warn!("Unknown ESC {:?}", c);
786            }
787        }
788    }
789
790    pub fn osc<F: FnMut(Event)>(&mut self, params: &[&[u8]], callback: &mut F) {
791        match params.get(0).map(|s| s.get(0).map(|b| *b).unwrap_or(0)).unwrap_or(0) as char {
792            '0' | '1' | '2' => if let Some(bytes) = params.get(1) {
793                if let Ok(string) = str::from_utf8(bytes) {
794                    callback(Event::Title {
795                        title: string.to_string()
796                    });
797                } else {
798                    warn!("Invalid UTF-8 {:?}", bytes);
799                }
800            } else {
801                warn!("Unknown OSC {:?}", params);
802            },
803            _ => {
804                warn!("Unknown OSC {:?}", params);
805            }
806        }
807    }
808}
809
810pub struct Performer<'a, F: FnMut(Event) + 'a> {
811    state: &'a mut State,
812    callback: &'a mut F,
813}
814
815impl<'a, F: FnMut(Event)> vte::Perform for Performer<'a, F> {
816    fn print(&mut self, c: char) {
817        //println!("[print] {:?} at {}, {}", c, self.state.x, self.state.y);
818        self.state.print(c, self.callback);
819    }
820
821    fn execute(&mut self, byte: u8) {
822        //println!("[execute] {:02x} at {}, {}", byte, self.state.x, self.state.y);
823        self.state.execute(byte as char, self.callback);
824    }
825
826    fn hook(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool) {
827        //println!("[hook] params={:?}, intermediates={:?}, ignore={:?}", params, intermediates, ignore);
828    }
829
830    fn put(&mut self, _byte: u8) {
831        //println!("[put] {:02x}", byte);
832    }
833
834    fn unhook(&mut self) {
835        //println!("[unhook]");
836    }
837
838    fn osc_dispatch(&mut self, params: &[&[u8]]) {
839        //println!("[osc] params={:?}", params);
840        self.state.osc(params, self.callback);
841    }
842
843    fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], _ignore: bool, c: char) {
844        //println!("[csi] params={:?}, intermediates={:?}, ignore={:?}, char={:?} at {}, {}", params, intermediates, ignore, c, self.state.x, self.state.y);
845        self.state.csi(c, params, intermediates, self.callback);
846    }
847
848    fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], _ignore: bool, byte: u8) {
849        //println!("[esc] params={:?}, intermediates={:?}, ignore={:?}, byte={:02x} at {}, {}", params, intermediates, ignore, byte, self.state.x, self.state.y);
850        self.state.esc(byte as char, params, intermediates, self.callback);
851    }
852}
853
854pub struct Console {
855    pub parser: vte::Parser,
856    pub state: State,
857}
858
859impl Console {
860    pub fn new(w: usize, h: usize) -> Console {
861        Console {
862            parser: vte::Parser::new(),
863            state: State::new(w, h),
864        }
865    }
866
867    pub fn resize(&mut self, w: usize, h: usize) {
868        let state = &mut self.state;
869
870        state.top_margin = cmp::max(0, cmp::min(h as isize - 1, state.top_margin as isize)) as usize;
871        state.bottom_margin = cmp::max(state.top_margin as isize, cmp::min(h as isize - 1, state.bottom_margin as isize + h as isize - state.h as isize)) as usize;
872
873        state.w = w;
874        state.h = h;
875    }
876
877    pub fn write<F: FnMut(Event)>(&mut self, bytes: &[u8], mut callback: F) {
878        for byte in bytes.iter() {
879            self.parser.advance(&mut Performer {
880                state: &mut self.state,
881                callback: &mut callback,
882            }, *byte);
883        };
884    }
885}