libpayload: fix wborder()
[coreboot.git] / payloads / libpayload / curses / tinycurses.c
index 776ff1604285dd2a4757b52258a2eefb2304804a..fb492b6cea73889eed5004c3e7bc7dd74b1d3db9 100644 (file)
@@ -2,6 +2,8 @@
  * This file is part of the libpayload project.
  *
  * Copyright (C) 2007 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2008 Ulf Jordan <jordan@chalmers.se>
+ * Copyright (C) 2008-2009 coresystems GmbH
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -66,21 +68,21 @@ static WINDOW window_list[MAX_WINDOWS];
 static int window_count = 1;
 
 // struct ldat foo;
-static struct ldat ldat_list[3];
+static struct ldat ldat_list[MAX_WINDOWS][SCREEN_Y];
 static int ldat_count = 0;
 
-/* One item bigger than SCREEN_X to reverse place for a NUL byte. */
+/* One item bigger than SCREEN_X to reserve space for a NUL byte. */
 static NCURSES_CH_T linebuf_list[SCREEN_Y * MAX_WINDOWS][SCREEN_X + 1];
 static int linebuf_count = 0;
 
 /* Globals */
 int COLORS;            /* Currently unused? */
-int COLOR_PAIRS;
+int COLOR_PAIRS = 255;
 WINDOW *stdscr;
 WINDOW *curscr;
 WINDOW *newscr;
-int LINES;
-int COLS;
+int LINES = 25;
+int COLS = 80;
 int TABSIZE;
 int ESCDELAY;
 // char ttytype[];
@@ -88,6 +90,77 @@ int ESCDELAY;
 SCREEN *SP;
 chtype acs_map[128];
 
+/* See terminfo(5). */
+chtype fallback_acs_map[128] =
+       {
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    '>',    '<',    '^',    'v',    ' ',
+       '#',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
+       '+',    ':',    ' ',    ' ',    ' ',    ' ',    '\\',   '#',
+       '#',    '#',    '+',    '+',    '+',    '+',    '+',    '~',
+       '-',    '-',    '-',    '_',    '+',    '+',    '+',    '+',
+       '|',    '<',    '>',    '*',    '!',    'f',    'o',    ' ',
+       };
+
+#ifdef CONFIG_SERIAL_CONSOLE
+#ifdef CONFIG_SERIAL_ACS_FALLBACK
+chtype serial_acs_map[128];
+#else
+/* See acsc of vt100. */
+chtype serial_acs_map[128] =
+       {
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       '`',    'a',    0,      0,      0,      0,      'f',    'g',
+       0,      0,      'j',    'k',    'l',    'm',    'n',    'o',
+       'p',    'q',    'r',    's',    't',    'u',    'v',    'w',
+       'x',    'y',    'z',    '{',    '|',    '}',    '~',    0,
+       };
+#endif
+#endif
+
+#ifdef CONFIG_VIDEO_CONSOLE
+/* See acsc of linux. */
+chtype console_acs_map[128] =
+       {
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      '\020', '\021', '\030', '\031', 0,
+       '\333', 0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       0,      0,      0,      0,      0,      0,      0,      0,
+       '\004', '\261', 0,      0,      0,      0,      '\370', '\361',
+       '\260', '\316', '\331', '\277', '\332', '\300', '\305', '~',
+       '\304', '\304', '\304', '_',    '\303', '\264', '\301', '\302',
+       '\263', '\363', '\362', '\343', '\330', '\234', '\376', 0,
+       };
+#endif
+
 // FIXME: Ugly (and insecure!) hack!
 char sprintf_tmp[1024];
 
@@ -118,14 +191,30 @@ NCURSES_CH_T _nc_render(WINDOW *win, NCURSES_CH_T ch)
 int beep(void)
 {
        /* TODO: Flash the screen if beeping fails? */
+#ifdef CONFIG_SPEAKER
        speaker_tone(1760, 500);        /* 1760 == note A6 */
+#endif
        return OK;
 }
 // bool can_change_color(void) {}
 int cbreak(void) { /* TODO */ return 0; }
 /* D */ int clearok(WINDOW *win, bool flag) { win->_clear = flag; return OK; }
 // int color_content(short color, short *r, short *g, short *b) {}
-// int curs_set(int) {}
+int curs_set(int on)
+{
+#ifdef CONFIG_SERIAL_CONSOLE
+       if (curses_flags & F_ENABLE_SERIAL) {
+               serial_cursor_enable(on);
+       }
+#endif
+#ifdef CONFIG_VIDEO_CONSOLE
+       if (curses_flags & F_ENABLE_CONSOLE) {
+               video_console_cursor_enable(on);
+       }
+#endif
+
+       return OK;
+}
 // int def_prog_mode(void) {}
 // int def_shell_mode(void) {}
 // int delay_output(int) {}
@@ -145,6 +234,7 @@ int delwin(WINDOW *win)
 }
 WINDOW *derwin(WINDOW *orig, int num_lines, int num_columns, int begy, int begx)
 {
+#if 0
        WINDOW *win = NULL;
        int i;
        int flags = _SUBWIN;
@@ -168,9 +258,9 @@ WINDOW *derwin(WINDOW *orig, int num_lines, int num_columns, int begy, int begx)
                flags |= _ISPAD;
 
        // FIXME
-       //// if ((win = _nc_makenew(num_lines, num_columns, orig->_begy + begy,
-       ////                        orig->_begx + begx, flags)) == 0)
-       ////     return NULL;
+       if ((win = _nc_makenew(num_lines, num_columns, orig->_begy + begy,
+                               orig->_begx + begx, flags)) == 0)
+            return NULL;
 
        win->_pary = begy;
        win->_parx = begx;
@@ -183,8 +273,11 @@ WINDOW *derwin(WINDOW *orig, int num_lines, int num_columns, int begy, int begx)
        win->_parent = orig;
 
        return win;
+#else
+       return NULL;
+#endif
 }
-int doupdate(void) { /* TODO */ return(*(int *)0); }
+int doupdate(void) { /* TODO */ return(0); }
 // WINDOW * dupwin (WINDOW *) {}
 /* D */ int echo(void) { SP->_echo = TRUE; return OK; }
 int endwin(void)
@@ -202,9 +295,9 @@ int endwin(void)
 // char erasechar (void) {}
 // void filter (void) {}
 // int flash(void) {}
-// int flushinp (void) {}
+int flushinp(void) { /* TODO */ return 0; }
 // WINDOW *getwin (FILE *) {}
-bool has_colors (void) { /* TODO */ return(*(bool *)0); }
+bool has_colors (void) { return(TRUE); }
 // bool has_ic (void) {}
 // bool has_il (void) {}
 // void idcok (WINDOW *, bool) {}
@@ -213,28 +306,33 @@ void immedok(WINDOW *win, bool flag) { win->_immed = flag; }
 /** Note: Must _not_ be called twice! */
 WINDOW *initscr(void)
 {
-       int x, y;
+       int i;
 
        // newterm(name, stdout, stdin);
        // def_prog_mode();
 
+       for (i = 0; i < 128; i++)
+         acs_map[i] = (chtype) i | A_ALTCHARSET;
+#ifdef CONFIG_SERIAL_CONSOLE
+       if (curses_flags & F_ENABLE_SERIAL) {
+               serial_clear();
+       }
+#endif
+#ifdef CONFIG_VIDEO_CONSOLE
        if (curses_flags & F_ENABLE_CONSOLE) {
-               /* Clear the screen and kill the cursor. */
-               vga_clear();
-               vga_cursor_enable(0);
+               /* Clear the screen and kill the cursor */
+
+               video_console_clear();
+               video_console_cursor_enable(0);
        }
+#endif
 
        // Speaker init?
 
-       stdscr = newwin(SCREEN_Y, SCREEN_X + 1, 0, 0);
+       stdscr = newwin(SCREEN_Y, SCREEN_X, 0, 0);
        // TODO: curscr, newscr?
 
-       for (y = 0; y < stdscr->_maxy; y++) {
-               for (x = 0; x < stdscr->_maxx; x++) {
-                       stdscr->_line[y].text[x].chars[0] = ' ';
-                       stdscr->_line[y].text[x].attr = A_NORMAL;
-               }
-       }
+       werase(stdscr);
 
        return stdscr;
 }
@@ -305,7 +403,7 @@ WINDOW *newwin(int num_lines, int num_columns, int begy, int begx)
        win->_begx = begx;
        // win->_yoffset = SP->_topstolen;
 
-       win->_line = &ldat_list[ldat_count++];
+       win->_line = ldat_list[ldat_count++];
 
        /* FIXME: Is this right? Should the window attributes be normal? */
        win->_color = PAIR_NUMBER(0);
@@ -318,12 +416,11 @@ WINDOW *newwin(int num_lines, int num_columns, int begy, int begx)
        return win;
 }
 /* D */ int nl(void) { SP->_nl = TRUE; return OK; }
-int nocbreak(void) { /* TODO */ return(*(int *)0); }
 /* D */ int noecho(void) { SP->_echo = FALSE; return OK; }
 /* D */ int nonl(void) { SP->_nl = FALSE; return OK; }
 // void noqiflush (void) {}
 // int noraw (void) {}
-// int notimeout (WINDOW *,bool) {}
+/* D */ int notimeout (WINDOW *win, bool f) { win->_notimeout = f; return OK; }
 // int overlay (const WINDOW*,WINDOW *) {}
 // int overwrite (const WINDOW*,WINDOW *) {}
 // int pair_content (short,short*,short*) {}
@@ -398,19 +495,27 @@ int vwprintw(WINDOW *win, const char *fmt, va_list argp)
        return waddstr(win, (char *)&sprintf_tmp);
 }
 // int vwscanw (WINDOW *, NCURSES_CONST char *,va_list) {}
-// int waddch (WINDOW *, const chtype) {}
 int waddch(WINDOW *win, const chtype ch)
 {
        int code = ERR;
        // NCURSES_CH_T wch;
        // SetChar2(wch, ch);
 
-       win->_line[win->_cury].text[win->_curx].chars[0] = ch;
+       if (win->_line[win->_cury].firstchar == _NOCHANGE ||
+                       win->_line[win->_cury].firstchar > win->_curx)
+               win->_line[win->_cury].firstchar = win->_curx;
 
-       /* Use the window attributes - perhaps we also pull attributes from
-          the ch itself, I don't know */
+       win->_line[win->_cury].text[win->_curx].chars[0] =
+               ((ch) & (chtype)A_CHARTEXT);
 
        win->_line[win->_cury].text[win->_curx].attr = WINDOW_ATTRS(win);
+       win->_line[win->_cury].text[win->_curx].attr |=
+               ((ch) & (chtype)A_ATTRIBUTES);
+
+       if (win->_line[win->_cury].lastchar == _NOCHANGE ||
+                       win->_line[win->_cury].lastchar < win->_curx)
+               win->_line[win->_cury].lastchar = win->_curx;
+
        win->_curx++;   // FIXME
 
        // if (win && (waddch_nosync(win, wch) != ERR)) {
@@ -432,6 +537,10 @@ int waddnstr(WINDOW *win, const char *astr, int n)
        if (n < 0)
                n = strlen(astr);
 
+       if (win->_line[win->_cury].firstchar == _NOCHANGE ||
+                       win->_line[win->_cury].firstchar > win->_curx)
+               win->_line[win->_cury].firstchar = win->_curx;
+
        while ((n-- > 0) && (*str != '\0')) {
        // while (*str != '\0') {
                win->_line[win->_cury].text[win->_curx].chars[0] = *str++;
@@ -446,6 +555,11 @@ int waddnstr(WINDOW *win, const char *astr, int n)
                //      break;
                // }
        }
+
+       if (win->_line[win->_cury].lastchar == _NOCHANGE ||
+                       win->_line[win->_cury].lastchar < win->_curx)
+               win->_line[win->_cury].lastchar = win->_curx;
+
        return code;
 }
 int wattr_on(WINDOW *win, attr_t at, void *opts GCC_UNUSED)
@@ -464,7 +578,48 @@ int wattr_off(WINDOW *win, attr_t at, void *opts GCC_UNUSED)
 }
 // int wbkgd (WINDOW *, chtype) {}
 void wbkgdset(WINDOW *win, chtype ch) { /* TODO */ }
-// int wborder (WINDOW *,chtype,chtype,chtype,chtype,chtype,chtype,chtype,chtype) {}
+
+int wborder(WINDOW *win, chtype ls, chtype rs, chtype ts, chtype bs,
+               chtype tl, chtype tr, chtype bl, chtype br)
+{
+       int x, y;
+
+       if (ls == 0) ls = ACS_VLINE;
+       if (rs == 0) rs = ACS_VLINE;
+       if (ts == 0) ts = ACS_HLINE;
+       if (bs == 0) bs = ACS_HLINE;
+       if (tl == 0) tl = ACS_ULCORNER;
+       if (tr == 0) tr = ACS_URCORNER;
+       if (bl == 0) bl = ACS_LLCORNER;
+       if (br == 0) br = ACS_LRCORNER;
+
+       for(y = 0; y <= win->_maxy; y++) {
+
+               if (y == 0) {
+                        mvwaddch(win, y, 0, tl);
+
+                       for(x = 1; x < win->_maxx; x++)
+                               mvwaddch(win, y, x, ts);
+
+                       mvwaddch(win, y, win->_maxx, tr);
+               }
+               else if (y == win->_maxy) {
+                       mvwaddch(win, y, 0, bl);
+
+                       for(x = 1; x < win->_maxx; x++)
+                               mvwaddch(win, y, x, bs);
+
+                       mvwaddch(win, y, win->_maxx, br);
+               }
+               else {
+                       mvwaddch(win, y, 0, ls);
+                       mvwaddch(win, y, win->_maxx, rs);
+               }
+       }
+
+       return OK;
+}
+
 // int wchgat (WINDOW *, int, attr_t, short, const void *) {}
 /* D */ int wclear(WINDOW *win)
 {
@@ -474,7 +629,7 @@ void wbkgdset(WINDOW *win, chtype ch) { /* TODO */ }
        return OK;
 }
 // int wclrtobot (WINDOW *) {}
-int wclrtoeol(WINDOW *win) { /* TODO */ return(*(int *)0); }
+int wclrtoeol(WINDOW *win) { /* TODO */ return ERR; }
 int wcolor_set(WINDOW *win, short color_pair_number, void *opts)
 {
        if (!opts && (color_pair_number >= 0)
@@ -491,11 +646,14 @@ int wcolor_set(WINDOW *win, short color_pair_number, void *opts)
 int werase(WINDOW *win)
 {
        int x, y;
-       for (y = 0; y < win->_maxy; y++) {
-               for (x = 0; x < win->_maxx; x++) {
+       for (y = 0; y <= win->_maxy; y++) {
+               for (x = 0; x <= win->_maxx; x++) {
                        win->_line[y].text[x].chars[0] = ' ';
                        win->_line[y].text[x].attr = WINDOW_ATTRS(win);
                }
+               // Should we check instead?
+               win->_line[y].firstchar = 0;
+               win->_line[y].lastchar = win->_maxx;
        }
        return OK;
 }
@@ -554,20 +712,119 @@ int whline(WINDOW *win, chtype ch, int n)
        win->_flags |= _HASMOVED;
        return OK;
 }
+
+#define SWAP_RED_BLUE(c) \
+       (((c) & 0x4400) >> 2) | ((c) & 0xAA00) | (((c) & 0x1100) << 2)
 int wnoutrefresh(WINDOW *win)
 {
+#ifdef CONFIG_SERIAL_CONSOLE
        // FIXME.
+       int serial_is_bold = 0;
+       int serial_is_reverse = 0;
+       int serial_is_altcharset = 0;
+       int serial_cur_pair = 0;
+
+       int need_altcharset;
+       short fg, bg;
+#endif
        int x, y;
+       chtype ch;
+
+#ifdef CONFIG_SERIAL_CONSOLE
+       serial_end_bold();
+       serial_end_altcharset();
+#endif
+
+       for (y = 0; y <= win->_maxy; y++) {
+
+               if (win->_line[y].firstchar == _NOCHANGE)
+                       continue;
+
+               /* Position the serial cursor */
+
+#ifdef CONFIG_SERIAL_CONSOLE
+               if (curses_flags & F_ENABLE_SERIAL)
+                       serial_set_cursor(win->_begy + y, win->_begx +
+                                       win->_line[y].firstchar);
+#endif
+
+               for (x = win->_line[y].firstchar; x <= win->_line[y].lastchar; x++) {
+                       attr_t attr = win->_line[y].text[x].attr;
+
+#ifdef CONFIG_SERIAL_CONSOLE
+                       if (curses_flags & F_ENABLE_SERIAL) {
+                               ch = win->_line[y].text[x].chars[0];
+
+                               if (attr & A_BOLD) {
+                                       if (!serial_is_bold) {
+                                               serial_start_bold();
+                                               serial_is_bold = 1;
+                                       }
+                               } else {
+                                       if (serial_is_bold) {
+                                               serial_end_bold();
+                                               serial_is_bold = 0;
+                                               /* work around serial.c
+                                                * shortcoming:
+                                                */
+                                               serial_is_reverse = 0;
+                                               serial_cur_pair = 0;
+                                       }
+                               }
 
-       for (y = 0; y < win->_maxy; y++) {
-               for (x = 0; x < win->_maxx; x++) {
-                       if (curses_flags & F_ENABLE_SERIAL)
-                               serial_putchar(win->_line[y].text[x].chars[0]);
+                               if (attr & A_REVERSE) {
+                                       if (!serial_is_reverse) {
+                                               serial_start_reverse();
+                                               serial_is_reverse = 1;
+                                       }
+                               } else {
+                                       if (serial_is_reverse) {
+                                               serial_end_reverse();
+                                               serial_is_reverse = 0;
+                                               /* work around serial.c
+                                                * shortcoming:
+                                                */
+                                               serial_is_bold = 0;
+                                               serial_cur_pair = 0;
+                                       }
+                               }
+
+                               need_altcharset = 0;
+                               if (attr & A_ALTCHARSET) {
+                                       if (serial_acs_map[ch & 0x7f]) {
+                                               ch = serial_acs_map[ch & 0x7f];
+                                               need_altcharset = 1;
+                                       } else
+                                               ch = fallback_acs_map[ch & 0x7f];
+                               }
+                               if (need_altcharset && !serial_is_altcharset) {
+                                       serial_start_altcharset();
+                                       serial_is_altcharset = 1;
+                               }
+                               if (!need_altcharset && serial_is_altcharset) {
+                                       serial_end_altcharset();
+                                       serial_is_altcharset = 0;
+                               }
+
+                               if (serial_cur_pair != PAIR_NUMBER(attr)) {
+                                       pair_content(PAIR_NUMBER(attr),
+                                                    &fg, &bg);
+                                       serial_set_color(fg, bg);
+                                       serial_cur_pair = PAIR_NUMBER(attr);
+                               }
+
+                               serial_putchar(ch);
+
+                       }
+#endif
+#ifdef CONFIG_VIDEO_CONSOLE
+                       unsigned int c =
+                               ((int)color_pairs[PAIR_NUMBER(attr)]) << 8;
+
+                       c = SWAP_RED_BLUE(c);
 
                        if (curses_flags & F_ENABLE_CONSOLE) {
-                               attr_t attr = win->_line[y].text[x].attr;
-                               unsigned int c =
-                                 ((int)color_pairs[PAIR_NUMBER(attr)]) << 8;
+                               ch = win->_line[y].text[x].chars[0];
 
                                /* Handle some of the attributes. */
                                if (attr & A_BOLD)
@@ -579,13 +836,38 @@ int wnoutrefresh(WINDOW *win)
                                        c = (c >> 4) & 0xf00;
                                        c |= tmp << 12;
                                }
+                               if (attr & A_ALTCHARSET) {
+                                       if (console_acs_map[ch & 0x7f])
+                                               ch = console_acs_map[ch & 0x7f];
+                                       else
+                                               ch = fallback_acs_map[ch & 0x7f];
+                               }
 
-                               c |= win->_line[y].text[x].chars[0];
-                               vga_putc(y, x, c);
+                               /*
+                                * FIXME: Somewhere along the line, the
+                                * character value is getting sign-extented.
+                                * For now grab just the 8 bit character,
+                                * but this will break wide characters!
+                                */
+                               c |= (chtype) (ch & 0xff);
+                               video_console_putc(win->_begy + y, win->_begx + x, c);
                        }
+#endif
                }
+               win->_line[y].firstchar = _NOCHANGE;
+               win->_line[y].lastchar = _NOCHANGE;
        }
 
+#ifdef CONFIG_SERIAL_CONSOLE
+       if (curses_flags & F_ENABLE_SERIAL)
+               serial_set_cursor(win->_begy + win->_cury, win->_begx + win->_curx);
+#endif
+
+#ifdef CONFIG_VIDEO_CONSOLE
+       if (curses_flags & F_ENABLE_CONSOLE)
+               video_console_set_cursor(win->_begx + win->_curx, win->_begy + win->_cury);
+#endif
+
        return OK;
 }
 int wprintw(WINDOW *win, const char *fmt, ...)
@@ -599,7 +881,19 @@ int wprintw(WINDOW *win, const char *fmt, ...)
 
        return code;
 }
-// int wredrawln (WINDOW *,int,int) {}
+
+int wredrawln (WINDOW *win, int beg_line, int num_lines)
+{
+       int i;
+
+       for (i = beg_line; i < beg_line + num_lines; i++) {
+               win->_line[i].firstchar = 0;
+               win->_line[i].lastchar = win->_maxx;
+       }
+
+       return OK;
+}
+
 int wrefresh(WINDOW *win)
 {
        // FIXME
@@ -629,13 +923,49 @@ int wrefresh(WINDOW *win)
 // int wscanw (WINDOW *, NCURSES_CONST char *,...) {}
 int wscrl(WINDOW *win, int n)
 {
+       int x, y;
+
        if (!win->_scroll)
                return ERR;
 
-       if (n != 0) {
-               // _nc_scroll_window(win, n, win->_regtop, win->_regbottom, win->_nc_bkgd);
-               // _nc_synchook(win);
+       if (n == 0)
+               return OK;
+
+       for (y = 0; y <= (win->_maxy - n); y++) {
+               win->_line[y].firstchar = win->_line[y + n].firstchar;
+               win->_line[y].lastchar = win->_line[y + n].lastchar;
+               for (x = 0; x <= win->_maxx; x++) {
+                       if ((win->_line[y].text[x].chars[0] != win->_line[y + n].text[x].chars[0]) ||
+                                       (win->_line[y].text[x].attr != win->_line[y + n].text[x].attr)) {
+                               if (win->_line[y].firstchar == _NOCHANGE)
+                                       win->_line[y].firstchar = x;
+
+                               win->_line[y].lastchar = x;
+
+                               win->_line[y].text[x].chars[0] = win->_line[y + n].text[x].chars[0];
+                               win->_line[y].text[x].attr = win->_line[y + n].text[x].attr;
+                       }
+               }
        }
+
+       for (y = (win->_maxy+1 - n); y <= win->_maxy; y++) {
+               for (x = 0; x <= win->_maxx; x++) {
+                       if ((win->_line[y].text[x].chars[0] != ' ') ||
+                                       (win->_line[y].text[x].attr != A_NORMAL)) {
+                               if (win->_line[y].firstchar == _NOCHANGE)
+                                       win->_line[y].firstchar = x;
+
+                               win->_line[y].lastchar = x;
+
+                               win->_line[y].text[x].chars[0] = ' ';
+                               win->_line[y].text[x].attr = A_NORMAL;
+                       }
+               }
+       }
+
+       // _nc_scroll_window(win, n, win->_regtop, win->_regbottom, win->_nc_bkgd);
+       // _nc_synchook(win);
+
        return OK;
 }
 int wsetscrreg(WINDOW *win, int top, int bottom)
@@ -650,7 +980,7 @@ int wsetscrreg(WINDOW *win, int top, int bottom)
 }
 // void wsyncdown (WINDOW *) {}
 // void wsyncup (WINDOW *) {}
-// void wtimeout (WINDOW *,int) {}
+/* D */ void wtimeout(WINDOW *win, int _delay) { win->_delay = _delay; }
 /* D */ int wtouchln(WINDOW *win, int y, int n, int changed)
 {
        int i;