/* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "output.h" #include "tty.h" #include "terminal.h" #include "prompt.h" #include "line.h" #include "misc.h" #include "xmalloc.h" #include #include #include #define min(a, b) ((a) < (b) ? (a) : (b)) /* position dans la ligne de saisie */ struct position_struct { int row; int col; }; static struct position_struct start_pos; /* position du debut de la saisie */ static struct position_struct cursor_pos; /* position du curseur */ /* Tampon */ #define OUTBUF_INITIAL_SIZE 1024 /* tampon pour les sorties */ static char *outbuf; static size_t outbuf_size; static size_t outbuf_pos; /* Agrandit le tampon si necessaire pour obtenir incr cases libres * supplementaires. * Si le tampon est non vide, la derniere case est toujours libre. */ static void grow_outbuf(size_t incr) { size_t min_size; size_t new_size; if (incr == 0) return; min_size = outbuf_pos + incr + 1; if (min_size <= outbuf_size) return; new_size = outbuf_size ? : OUTBUF_INITIAL_SIZE; while (min_size > new_size) new_size *= 2; outbuf = xrealloc(outbuf, new_size); outbuf_size = new_size; } static void outbuf_init() { outbuf = NULL; outbuf_size = 0; outbuf_pos = 0; } static void outbuf_putc(char c) { grow_outbuf(1); outbuf[outbuf_pos++] = c; } static void outbuf_puts(const char *s) { size_t len; len = strlen(s); grow_outbuf(len); memcpy(&outbuf[outbuf_pos], s, len); outbuf_pos += len; } static void outbuf_flush() { if (outbuf_pos == 0) /* en particulier si outbuf == NULL ou outbuf_size == 0 */ return; outbuf[outbuf_pos] = '\0'; /* il reste toujours au moins une case libre */ fputs(outbuf, rl_outstream); fflush(rl_outstream); outbuf_pos = 0; } static void free_outbuf() { free(outbuf); } /* Fonctions elementaires d'affichage */ static void put_bs() { outbuf_putc('\b'); cursor_pos.col--; } static void put_lf() { outbuf_putc('\n'); cursor_pos.row++; } static void put_cr() { outbuf_putc('\r'); cursor_pos.col = 0; } static void put_print(char c) { outbuf_putc(c); cursor_pos.col++; } static void put_cursor_up_seq(int n) { outbuf_puts(cursor_up_seq(n)); cursor_pos.row -= n; } static void put_cursor_down_seq(int n) { outbuf_puts(cursor_down_seq(n)); cursor_pos.row += n; } static void put_cursor_forward_seq(int n) { outbuf_puts(cursor_forward_seq(n)); cursor_pos.col += n; } static void put_cursor_backward_seq(int n) { outbuf_puts(cursor_backward_seq(n)); cursor_pos.col -= n; } static void wrap(char c) { outbuf_putc(c); cursor_pos.row++; put_cr(); } /* Fonctions evoluees d'affichage */ static void print_prompt() { if (!local_prompt) return; if (local_prompt_prefix) outbuf_puts(local_prompt_prefix); outbuf_puts(local_prompt); cursor_pos.row = (prompt_visible_length - 1) / screenwidth; cursor_pos.col = (prompt_visible_length - 1) % screenwidth + 1; } static void print_tab() { int newcol; if (cursor_pos.col == screenwidth) wrap(' '); newcol = (cursor_pos.col | 7) + 1; do put_print(' '); while (cursor_pos.col != screenwidth && cursor_pos.col != newcol); } static void print_linefeed() { if (cursor_pos.col == screenwidth) wrap('\n'); outbuf_puts(erase_end_of_line_seq); put_lf(); put_cr(); } static void print_print(char c) { if (cursor_pos.col == screenwidth) wrap(c); put_print(c); } static void print_char(char c) { if (c == '\t') print_tab(); else if (c == '\n') print_linefeed(); else if (ctrl_char(c) || c == RUBOUT) { print_print('^'); print_print(ctrl_char(c) ? unctrl(c) : '?'); } else print_print(c); } static void print_string(const char *s) { while (*s) print_char(*s++); } static void erase(int n) { while (n) { print_char(' '); n--; } } static void wrap_if_needed() { if (cursor_pos.col == screenwidth) wrap(' '); } static void move_cursor_to(struct position_struct pos) { if (cursor_pos.row < pos.row) put_cursor_down_seq(pos.row - cursor_pos.row); else if (cursor_pos.row > pos.row) put_cursor_up_seq(cursor_pos.row - pos.row); if (cursor_pos.col < pos.col) put_cursor_forward_seq(pos.col - cursor_pos.col); else if (cursor_pos.col > pos.col) { if (pos.col == 0) put_cr(); else if (pos.col == cursor_pos.col - 1) put_bs(); else put_cursor_backward_seq(cursor_pos.col - pos.col); } } static void move_cursor_to_origin() { if (cursor_pos.row > 0) put_cursor_up_seq(cursor_pos.row); put_cr(); } /* Fonctions sur les positions */ static void pos_wrap(struct position_struct *pos) { pos->row++; pos->col = 0; } static void pos_move_forward_tab(struct position_struct *pos) { if (pos->col == screenwidth) pos_wrap(pos); pos->col = min((pos->col | 7) + 1, screenwidth); } static void pos_move_forward_linefeed(struct position_struct *pos) { if (pos->col == screenwidth) pos_wrap(pos); pos->row++; pos->col = 0; } static void pos_move_forward_print(struct position_struct *pos) { if (pos->col == screenwidth) pos_wrap(pos); pos->col++; } static void pos_move_forward_char(struct position_struct *pos, char c) { if (c == '\t') pos_move_forward_tab(pos); else if (c == '\n') pos_move_forward_linefeed(pos); else if (ctrl_char(c) || c == RUBOUT) { pos_move_forward_print(pos); pos_move_forward_print(pos); } else pos_move_forward_print(pos); } static void pos_move_forward_string(struct position_struct *pos, const char *s) { while (*s) pos_move_forward_char(pos, *s++); } static void pos_move_forward_region(struct position_struct *pos, const char *start, const char *end) { while (start < end) pos_move_forward_char(pos, *start++); } static void pos_wrap_if_needed(struct position_struct *pos) { if (pos->col == screenwidth) pos_wrap(pos); } static int pos_coincide(struct position_struct pos1, struct position_struct pos2) { pos_wrap_if_needed(&pos1); pos_wrap_if_needed(&pos2); return pos1.row == pos2.row && pos1.col == pos2.col; } /* Commandes d'affichage */ static void print_line() { struct position_struct pos; print_prompt(); print_string(line); wrap_if_needed(); pos = start_pos; pos_move_forward_region(&pos, line, line + point); pos_wrap_if_needed(&pos); move_cursor_to(pos); } static void move_cursor_to_point(int to_point) { int from_point; struct position_struct pos; if (to_point >= point) { from_point = point; pos = cursor_pos; } else { from_point = 0; pos = start_pos; } pos_move_forward_region(&pos, line + from_point, line + to_point); pos_wrap_if_needed(&pos); move_cursor_to(pos); } /* Deplace le curseur au point start et positionne pos a la position du point * end. */ static void select_region(int start, int end, struct position_struct *pos) { if (start != point) move_cursor_to_point(start); *pos = cursor_pos; if (end != start) pos_move_forward_region(pos, line + start, line + end); } /* Reecrit la ligne a partir du point start a la position du curseur. oldpos * est la position ou la ligne a partir du point start etait precedemment * ecrite. Le curseur est en suite deplace a la position pos. */ static void reprint(int start, struct position_struct oldpos, struct position_struct pos) { char c; while ((c = line[start]) && !pos_coincide(cursor_pos, oldpos)) { print_char(c); pos_move_forward_char(&oldpos, c); start++; } wrap_if_needed(); pos_wrap_if_needed(&oldpos); while (cursor_pos.row < oldpos.row) { outbuf_puts(erase_end_of_line_seq); put_lf(); put_cr(); } if (cursor_pos.row == oldpos.row) while (cursor_pos.col < oldpos.col) print_print(' '); pos_wrap_if_needed(&pos); move_cursor_to(pos); } void begin_output(const char *prompt) { set_prompt(prompt); outbuf_init(); print_prompt(); if (tty_echo) wrap_if_needed(); outbuf_flush(); start_pos = cursor_pos; } void output_replace_region_with_char(int start, int end, char c) { struct position_struct pos; if (!tty_echo) return; select_region(start, end, &pos); print_char(c); reprint(end, pos, cursor_pos); outbuf_flush(); } void output_replace_region_with_text(int start, int end, const char *text) { struct position_struct pos; if (!tty_echo) return; select_region(start, end, &pos); print_string(text); reprint(end, pos, cursor_pos); outbuf_flush(); } void output_erase_region(int start, int end) { struct position_struct pos1, pos2; if (!tty_echo) return; select_region(start, end, &pos1); pos2 = cursor_pos; erase(end - start); reprint(end, pos1, pos2); outbuf_flush(); } void output_delete_region(int start, int end) { struct position_struct pos; if (!tty_echo) return; select_region(start, end, &pos); reprint(end, pos, cursor_pos); outbuf_flush(); } void output_move_cursor(int newpoint) { if (!tty_echo) return; move_cursor_to_point(newpoint); outbuf_flush(); } void output_clear_line_and_reprint() { if (tty_echo) move_cursor_to_origin(); outbuf_puts(erase_end_of_line_seq); outbuf_flush(); if (!tty_echo) return; print_line(); outbuf_flush(); } void output_clear_screen_and_reprint() { outbuf_puts(cursor_home_seq); outbuf_puts(erase_screen_seq); outbuf_flush(); if (!tty_echo) return; print_line(); outbuf_flush(); } void output_new_line() { struct position_struct pos; if (!tty_echo) return; pos = cursor_pos; pos_move_forward_string(&pos, line + point); pos_wrap_if_needed(&pos); pos.col = cursor_pos.col; /* mouvement uniquement vertical */ move_cursor_to(pos); outbuf_puts("\r\n"); outbuf_flush(); } void end_output() { free_outbuf(); free_prompt(); } void ding() { if (!tty_echo) return; ring_bell(); }