/ payloads / libpayload / curses / tinycurses.c
tinycurses.c
   1  /*
   2   *
   3   * Copyright (C) 2007 Uwe Hermann <uwe@hermann-uwe.de>
   4   * Copyright (C) 2008 Ulf Jordan <jordan@chalmers.se>
   5   * Copyright (C) 2008-2009 coresystems GmbH
   6   *
   7   * Redistribution and use in source and binary forms, with or without
   8   * modification, are permitted provided that the following conditions
   9   * are met:
  10   * 1. Redistributions of source code must retain the above copyright
  11   *    notice, this list of conditions and the following disclaimer.
  12   * 2. Redistributions in binary form must reproduce the above copyright
  13   *    notice, this list of conditions and the following disclaimer in the
  14   *    documentation and/or other materials provided with the distribution.
  15   * 3. The name of the author may not be used to endorse or promote products
  16   *    derived from this software without specific prior written permission.
  17   *
  18   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  19   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21   * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  22   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  23   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  24   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  27   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28   * SUCH DAMAGE.
  29   */
  30  
  31  /*
  32   * This is a tiny implementation of the (n)curses library intended to be
  33   * used in embedded/firmware/BIOS code where no libc or operating system
  34   * environment is available and code size is very important.
  35   *
  36   * Design goals:
  37   *  - Small object code.
  38   *  - Self-contained.
  39   *    - Doesn't require a libc (no glibc/uclibc/dietlibc/klibc/newlib).
  40   *    - Works without any other external libraries or header files.
  41   *  - Works without an underlying operating system.
  42   *    - Doesn't use files, signals, syscalls, ttys, library calls, etc.
  43   *  - Doesn't do any dynamic memory allocation (no malloc() and friends).
  44   *     - All data structures are statically allocated.
  45   *  - Supports standard VGA console (80x25) and serial port console.
  46   *     - This includes character output and keyboard input over serial.
  47   *  - Supports beep() through a minimal PC speaker driver.
  48   *
  49   * Limitations:
  50   *  - Only implements a small subset of the (n)curses functions.
  51   *  - Only implements very few sanity checks (for smaller code).
  52   *     - Thus: Don't do obviously stupid things in your code.
  53   *  - Doesn't implement the 'form', 'panel', and 'menu' extensions.
  54   *  - Only implements C bindings (no C++, Ada95, or others).
  55   *  - Doesn't include wide character support.
  56   */
  57  
  58  #include "local.h"
  59  
  60  #undef _XOPEN_SOURCE_EXTENDED
  61  #define _XOPEN_SOURCE_EXTENDED 1
  62  
  63  #define MAX_WINDOWS 3
  64  
  65  /* Statically allocate all structures (no malloc())! */
  66  static WINDOW window_list[MAX_WINDOWS];
  67  static int window_count = 0;
  68  
  69  // struct ldat foo;
  70  static struct ldat ldat_list[MAX_WINDOWS][SCREEN_Y];
  71  static int ldat_count = 0;
  72  
  73  /* One item bigger than SCREEN_X to reserve space for a NUL byte. */
  74  static NCURSES_CH_T linebuf_list[SCREEN_Y * MAX_WINDOWS][SCREEN_X + 1];
  75  static int linebuf_count = 0;
  76  
  77  /* Globals */
  78  int COLORS;		/* Currently unused? */
  79  int COLOR_PAIRS = 255;
  80  WINDOW *stdscr;
  81  WINDOW *curscr;
  82  WINDOW *newscr;
  83  int LINES = 25;
  84  int COLS = 80;
  85  int TABSIZE;
  86  int ESCDELAY;
  87  // char ttytype[];
  88  // cchar_t *_nc_wacs;
  89  SCREEN *SP;
  90  chtype acs_map[128];
  91  
  92  /* See terminfo(5). */
  93  chtype fallback_acs_map[128] =
  94  	{
  95  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
  96  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
  97  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
  98  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
  99  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
 100  	' ',	' ',	' ',	'>',	'<',	'^',	'v',	' ',
 101  	'#',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
 102  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
 103  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
 104  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
 105  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
 106  	' ',	' ',	' ',	' ',	' ',	' ',	' ',	' ',
 107  	'+',	':',	' ',	' ',	' ',	' ',	'\\',   '#',
 108  	'#',	'#',	'+',	'+',	'+',	'+',	'+',	'~',
 109  	'-',	'-',	'-',	'_',	'+',	'+',	'+',	'+',
 110  	'|',	'<',	'>',	'*',	'!',	'f',	'o',	' ',
 111  	};
 112  
 113  #if CONFIG(LP_SERIAL_CONSOLE)
 114  #if CONFIG(LP_SERIAL_ACS_FALLBACK)
 115  chtype serial_acs_map[128];
 116  #else
 117  /* See acsc of vt100. */
 118  chtype serial_acs_map[128] =
 119  	{
 120  	0,	0,	0,	0,	0,	0,	0,	0,
 121  	0,	0,	0,	0,	0,	0,	0,	0,
 122  	0,	0,	0,	0,	0,	0,	0,	0,
 123  	0,	0,	0,	0,	0,	0,	0,	0,
 124  	0,	0,	0,	0,	0,	0,	0,	0,
 125  	0,	0,	0,	0,	0,	0,	0,	0,
 126  	0,	0,	0,	0,	0,	0,	0,	0,
 127  	0,	0,	0,	0,	0,	0,	0,	0,
 128  	0,	0,	0,	0,	0,	0,	0,	0,
 129  	0,	0,	0,	0,	0,	0,	0,	0,
 130  	0,	0,	0,	0,	0,	0,	0,	0,
 131  	0,	0,	0,	0,	0,	0,	0,	0,
 132  	'`',	'a',	0,	0,	0,	0,	'f',	'g',
 133  	0,	0,	'j',	'k',	'l',	'm',	'n',	'o',
 134  	'p',	'q',	'r',	's',	't',	'u',	'v',	'w',
 135  	'x',	'y',	'z',	'{',	'|',	'}',	'~',	0,
 136  	};
 137  #endif
 138  #endif
 139  
 140  #if CONFIG(LP_VIDEO_CONSOLE)
 141  /* See acsc of linux. */
 142  chtype console_acs_map[128] =
 143  	{
 144  	0,	0,	0,	0,	0,	0,	0,	0,
 145  	0,	0,	0,	0,	0,	0,	0,	0,
 146  	0,	0,	0,	0,	0,	0,	0,	0,
 147  	0,	0,	0,	0,	0,	0,	0,	0,
 148  	0,	0,	0,	0,	0,	0,	0,	0,
 149  	0,	0,	0,	'\020', '\021', '\030', '\031',	0,
 150  	'\333',	0,	0,	0,	0,	0,	0,	0,
 151  	0,	0,	0,	0,	0,	0,	0,	0,
 152  	0,	0,	0,	0,	0,	0,	0,	0,
 153  	0,	0,	0,	0,	0,	0,	0,	0,
 154  	0,	0,	0,	0,	0,	0,	0,	0,
 155  	0,	0,	0,	0,	0,	0,	0,	0,
 156  	'\004',	'\261',	0,	0,	0,	0,	'\370',	'\361',
 157  	'\260',	'\316',	'\331',	'\277',	'\332',	'\300',	'\305',	'~',
 158  	'\304',	'\304',	'\304',	'_',	'\303', '\264', '\301',	'\302',
 159  	'\263',	'\363',	'\362',	'\343',	'\330',	'\234',	'\376',	0,
 160  	};
 161  #endif
 162  
 163  // FIXME: Ugly (and insecure!) hack!
 164  char sprintf_tmp[1024];
 165  
 166  int curses_flags = (F_ENABLE_CONSOLE | F_ENABLE_SERIAL);
 167  
 168  /* Return bit mask for clearing color pair number if given ch has color */
 169  #define COLOR_MASK(ch) (~(attr_t)((ch) & A_COLOR ? A_COLOR : 0))
 170  
 171  /* Compute a rendition of the given char correct for the current context. */
 172  static inline NCURSES_CH_T render_char(WINDOW *win, NCURSES_CH_T ch)
 173  {
 174  	/* TODO. */
 175  	return ch;
 176  }
 177  
 178  /* Make render_char() visible while still allowing us to inline it below. */
 179  NCURSES_CH_T _nc_render(WINDOW *win, NCURSES_CH_T ch)
 180  {
 181  	return render_char(win, ch);
 182  }
 183  
 184  /*
 185   * Implementations of most functions marked 'implemented' in include/curses.h:
 186   */
 187  
 188  // int baudrate(void) {}
 189  int beep(void)
 190  {
 191  	/* TODO: Flash the screen if beeping fails? */
 192  #if CONFIG(LP_SPEAKER)
 193  	speaker_tone(1760, 500);	/* 1760 == note A6 */
 194  #endif
 195  	return OK;
 196  }
 197  // bool can_change_color(void) {}
 198  int cbreak(void) { /* TODO */ return 0; }
 199  /* D */ int clearok(WINDOW *win, bool flag) { win->_clear = flag; return OK; }
 200  // int color_content(short color, short *r, short *g, short *b) {}
 201  int curs_set(int on)
 202  {
 203  #if CONFIG(LP_SERIAL_CONSOLE)
 204  	if (curses_flags & F_ENABLE_SERIAL) {
 205  		serial_cursor_enable(on);
 206  	}
 207  #endif
 208  #if CONFIG(LP_VIDEO_CONSOLE)
 209  	if (curses_flags & F_ENABLE_CONSOLE) {
 210  		video_console_cursor_enable(on);
 211  	}
 212  #endif
 213  
 214  	return OK;
 215  }
 216  // int def_prog_mode(void) {}
 217  // int def_shell_mode(void) {}
 218  // int delay_output(int) {}
 219  // void delscreen(SCREEN *) {}
 220  int delwin(WINDOW *win)
 221  {
 222  	/* TODO: Don't try to delete stdscr. */
 223  	/* TODO: Don't delete parent windows before subwindows. */
 224  
 225  	// if (win->_flags & _SUBWIN)
 226  	// 	touchwin(win->_parent);
 227  	// else if (curscr != 0)
 228  	// 	touchwin(curscr);
 229  
 230  	// return _nc_freewin(win);
 231  	return OK;
 232  }
 233  WINDOW *derwin(WINDOW *orig, int num_lines, int num_columns, int begy, int begx)
 234  {
 235  #if 0
 236  	WINDOW *win = NULL;
 237  	int i;
 238  	int flags = _SUBWIN;
 239  
 240  	/* Make sure window fits inside the original one. */
 241  	if (begy < 0 || begx < 0 || orig == 0 || num_lines < 0
 242  	    || num_columns < 0)
 243  		return NULL;
 244  
 245  	if (begy + num_lines > orig->_maxy + 1
 246  	    || begx + num_columns > orig->_maxx + 1)
 247  		return NULL;
 248  
 249  	if (num_lines == 0)
 250  		num_lines = orig->_maxy + 1 - begy;
 251  
 252  	if (num_columns == 0)
 253  		num_columns = orig->_maxx + 1 - begx;
 254  
 255  	if (orig->_flags & _ISPAD)
 256  		flags |= _ISPAD;
 257  
 258  	// FIXME
 259  	if ((win = _nc_makenew(num_lines, num_columns, orig->_begy + begy,
 260  	                        orig->_begx + begx, flags)) == 0)
 261  	     return NULL;
 262  
 263  	win->_pary = begy;
 264  	win->_parx = begx;
 265  	WINDOW_ATTRS(win) = WINDOW_ATTRS(orig);
 266  	win->_nc_bkgd = orig->_nc_bkgd;
 267  
 268  	for (i = 0; i < num_lines; i++)
 269  		win->_line[i].text = &orig->_line[begy++].text[begx];
 270  
 271  	win->_parent = orig;
 272  
 273  	return win;
 274  #else
 275  	return NULL;
 276  #endif
 277  }
 278  int doupdate(void) { /* TODO */ return(0); }
 279  // WINDOW * dupwin (WINDOW *) {}
 280  /* D */ int echo(void) { SP->_echo = TRUE; return OK; }
 281  int endwin(void)
 282  {
 283  	if (!SP)
 284  		return ERR;
 285  
 286  	SP->_endwin = TRUE;
 287  #ifdef NCURSES_MOUSE_VERSION
 288  	SP->_mouse_wrap(SP);
 289  #endif
 290  	// _nc_screen_wrap();
 291  	// _nc_mvcur_wrap();       /* wrap up cursor addressing */
 292  	// return reset_shell_mode();
 293  	return OK;	// FIXME
 294  }
 295  // char erasechar (void) {}
 296  // void filter (void) {}
 297  // int flash(void) {}
 298  int flushinp(void) { /* TODO */ return 0; }
 299  // WINDOW *getwin (FILE *) {}
 300  bool has_colors (void) { return(TRUE); }
 301  // bool has_ic (void) {}
 302  // bool has_il (void) {}
 303  // void idcok (WINDOW *, bool) {}
 304  // int idlok (WINDOW *, bool) {}
 305  void immedok(WINDOW *win, bool flag) { win->_immed = flag; }
 306  /** Note: Must _not_ be called twice! */
 307  WINDOW *initscr(void)
 308  {
 309  	int i;
 310  
 311  	// newterm(name, stdout, stdin);
 312  	// def_prog_mode();
 313  
 314  	for (i = 0; i < 128; i++)
 315  	  acs_map[i] = (chtype) i | A_ALTCHARSET;
 316  #if CONFIG(LP_SERIAL_CONSOLE)
 317  	if (curses_flags & F_ENABLE_SERIAL) {
 318  		serial_clear();
 319  	}
 320  #endif
 321  #if CONFIG(LP_VIDEO_CONSOLE)
 322  	if (curses_flags & F_ENABLE_CONSOLE) {
 323  		/* Clear the screen and kill the cursor */
 324  
 325  		video_console_clear();
 326  		video_console_cursor_enable(0);
 327  	}
 328  #endif
 329  
 330  	// Speaker init?
 331  
 332  	stdscr = newwin(SCREEN_Y, SCREEN_X, 0, 0);
 333  	// TODO: curscr, newscr?
 334  
 335  	werase(stdscr);
 336  
 337  	return stdscr;
 338  }
 339  // int intrflush (WINDOW *,bool) {}
 340  /* D */ bool isendwin(void) { return ((SP == NULL) ? FALSE : SP->_endwin); }
 341  // bool is_linetouched (WINDOW *,int) {}
 342  // bool is_wintouched (WINDOW *) {}
 343  // NCURSES_CONST char * keyname (int) {}
 344  int keypad (WINDOW *win, bool flag) { /* TODO */ return 0; }
 345  // char killchar (void) {}
 346  /* D */ int leaveok(WINDOW *win, bool flag) { win->_leaveok = flag; return OK; }
 347  // char *longname (void) {}
 348  // int meta (WINDOW *,bool) {}
 349  // int mvcur (int,int,int,int) {}
 350  // int mvderwin (WINDOW *, int, int) {}
 351  int mvprintw(int y, int x, const char *fmt, ...)
 352  {
 353  	va_list argp;
 354  	int code;
 355  
 356  	if (move(y, x) == ERR)
 357  		return ERR;
 358  
 359  	va_start(argp, fmt);
 360  	code = vwprintw(stdscr, fmt, argp);
 361  	va_end(argp);
 362  
 363  	return code;
 364  }
 365  // int mvscanw (int,int, NCURSES_CONST char *,...) {}
 366  // int mvwin (WINDOW *,int,int) {}
 367  int mvwprintw(WINDOW *win, int y, int x, const char *fmt, ...)
 368  {
 369  	va_list argp;
 370  	int code;
 371  
 372  	if (wmove(win, y, x) == ERR)
 373  		return ERR;
 374  
 375  	va_start(argp, fmt);
 376  	code = vwprintw(win, fmt, argp);
 377  	va_end(argp);
 378  
 379  	return code;
 380  }
 381  // int mvwscanw (WINDOW *,int,int, NCURSES_CONST char *,...) {}
 382  // int napms (int) {}
 383  // WINDOW *newpad (int,int) {}
 384  // SCREEN *newterm (NCURSES_CONST char *,FILE *,FILE *) {}
 385  WINDOW *newwin(int num_lines, int num_columns, int begy, int begx)
 386  {
 387  	WINDOW *win;
 388  	int i;
 389  
 390  	/* Use next statically allocated window. */
 391  	// TODO: Error handling. Yes. Please.
 392  	// TODO: WINDOWLIST?
 393  
 394  	if (window_count >= MAX_WINDOWS)
 395  		return NULL;
 396  
 397  	win = &window_list[window_count++];
 398  
 399  	// bool is_pad = (flags & _ISPAD);
 400  
 401  	// TODO: Checks.
 402  
 403  	win->_cury = 0;
 404  	win->_curx = 0;
 405  	win->_maxy = num_lines - 1;
 406  	win->_maxx = num_columns - 1;
 407  	win->_begy = begy;
 408  	win->_begx = begx;
 409  	// win->_yoffset = SP->_topstolen;
 410  
 411  	win->_line = ldat_list[ldat_count++];
 412  
 413  	/* FIXME: Is this right? Should the window attributes be normal? */
 414  	win->_color = PAIR_NUMBER(0);
 415  	win->_attrs = A_NORMAL;
 416  
 417  	for (i = 0; i < num_lines; i++)
 418  		win->_line[i].text =
 419  		     (NCURSES_CH_T *)&linebuf_list[linebuf_count++];
 420  
 421  	return win;
 422  }
 423  /* D */ int nl(void) { SP->_nl = TRUE; return OK; }
 424  /* D */ int noecho(void) { SP->_echo = FALSE; return OK; }
 425  /* D */ int nonl(void) { SP->_nl = FALSE; return OK; }
 426  // void noqiflush (void) {}
 427  // int noraw (void) {}
 428  /* D */ int notimeout (WINDOW *win, bool f) { win->_notimeout = f; return OK; }
 429  // int overlay (const WINDOW*,WINDOW *) {}
 430  // int overwrite (const WINDOW*,WINDOW *) {}
 431  // int pair_content (short,short*,short*) {}
 432  // int pechochar (WINDOW *, const chtype) {}
 433  // int pnoutrefresh (WINDOW*,int,int,int,int,int,int) {}
 434  // int prefresh (WINDOW *,int,int,int,int,int,int) {}
 435  int printw(const char *fmt, ...)
 436  {
 437  	va_list argp;
 438  	int code;
 439  
 440  	va_start(argp, fmt);
 441  	code = vwprintw(stdscr, fmt, argp);
 442  	va_end(argp);
 443  
 444  	return code;
 445  }
 446  // int putwin (WINDOW *, FILE *) {}
 447  // void qiflush (void) {}
 448  // int raw (void) {}
 449  // int resetty (void) {}
 450  // int reset_prog_mode (void) {}
 451  // int reset_shell_mode (void) {}
 452  // int ripoffline (int, int (*)(WINDOW *, int)) {}
 453  // int savetty (void) {}
 454  // int scanw (NCURSES_CONST char *,...) {}
 455  // int scr_dump (const char *) {}
 456  // int scr_init (const char *) {}
 457  /* D */ int scrollok(WINDOW *win, bool flag) { win->_scroll = flag; return OK; }
 458  // int scr_restore (const char *) {}
 459  // int scr_set (const char *) {}
 460  // SCREEN *set_term (SCREEN *) {}
 461  // int slk_attroff (const chtype) {}
 462  // int slk_attron (const chtype) {}
 463  // int slk_attrset (const chtype) {}
 464  // attr_t slk_attr (void) {}
 465  // int slk_attr_set (const attr_t,short,void*) {}
 466  // int slk_clear (void) {}
 467  // int slk_color (short) {}
 468  // int slk_init (int) {}
 469  /* D */ char *slk_label(int n)
 470  {
 471  	// TODO: Needed?
 472  	// if (SP == NULL || SP->_slk == NULL || n < 1 || n > SP->_slk->labcnt)
 473  	// 	return NULL;
 474  	return SP->_slk->ent[n - 1].ent_text;
 475  }
 476  // int slk_noutrefresh (void) {}
 477  // int slk_refresh (void) {}
 478  // int slk_restore (void) {}
 479  // int slk_set (int,const char *,int) {}
 480  // int slk_touch (void) {}
 481  
 482  // WINDOW *subpad (WINDOW *, int, int, int, int) {}
 483  WINDOW *subwin(WINDOW *w, int l, int c, int y, int x)
 484  {
 485  	return derwin(w, l, c, y - w->_begy, x - w->_begx);
 486  }
 487  // int syncok (WINDOW *, bool) {}
 488  // chtype termattrs (void) {}
 489  // char *termname (void) {}
 490  // int typeahead (int) {}
 491  int ungetch(int ch) { /* TODO */ return ERR; }
 492  // void use_env (bool) {}
 493  // int vidattr (chtype) {}
 494  // int vidputs (chtype, int (*)(int)) {}
 495  int vwprintw(WINDOW *win, const char *fmt, va_list argp)
 496  {
 497  	vsprintf((char *)&sprintf_tmp, fmt, argp);
 498  
 499  	/* TODO: Error handling? */
 500  	return waddstr(win, (char *)&sprintf_tmp);
 501  }
 502  // int vwscanw (WINDOW *, NCURSES_CONST char *,va_list) {}
 503  int waddch(WINDOW *win, const chtype ch)
 504  {
 505  	int code = ERR;
 506  	// NCURSES_CH_T wch;
 507  	// SetChar2(wch, ch);
 508  
 509  	if (win->_line[win->_cury].firstchar == _NOCHANGE ||
 510  			win->_line[win->_cury].firstchar > win->_curx)
 511  		win->_line[win->_cury].firstchar = win->_curx;
 512  
 513  	win->_line[win->_cury].text[win->_curx].chars[0] =
 514  		((ch) & (chtype)A_CHARTEXT);
 515  
 516  	win->_line[win->_cury].text[win->_curx].attr = WINDOW_ATTRS(win);
 517  	win->_line[win->_cury].text[win->_curx].attr |=
 518  		((ch) & (chtype)A_ATTRIBUTES);
 519  
 520  	if (win->_line[win->_cury].lastchar == _NOCHANGE ||
 521  			win->_line[win->_cury].lastchar < win->_curx)
 522  		win->_line[win->_cury].lastchar = win->_curx;
 523  
 524  	win->_curx++;	// FIXME
 525  
 526  	// if (win && (waddch_nosync(win, wch) != ERR)) {
 527  	// 	_nc_synchook(win);
 528  	// 	code = OK;
 529  	// }
 530  
 531  	return code;
 532  }
 533  // int waddchnstr (WINDOW *,const chtype *,int) {}
 534  int waddnstr(WINDOW *win, const char *astr, int n)
 535  {
 536  	int code = OK;
 537  	const char *str = astr;
 538  
 539  	if (!str)
 540  		return ERR;
 541  
 542  	if (n < 0)
 543  		n = strlen(astr);
 544  
 545  	if (win->_line[win->_cury].firstchar == _NOCHANGE ||
 546  			win->_line[win->_cury].firstchar > win->_curx)
 547  		win->_line[win->_cury].firstchar = win->_curx;
 548  
 549  	while ((n-- > 0) && (*str != '\0')) {
 550  	// while (*str != '\0') {
 551  		win->_line[win->_cury].text[win->_curx].chars[0] = *str++;
 552  		win->_line[win->_cury].text[win->_curx].attr = WINDOW_ATTRS(win)
 553  ;
 554  		win->_curx++;	// FIXME
 555  
 556  		// NCURSES_CH_T ch;
 557  		// SetChar(ch, UChar(*str++), A_NORMAL);
 558  		// if (_nc_waddch_nosync(win, ch) == ERR) {
 559  		// 	code = ERR;
 560  		// 	break;
 561  		// }
 562  	}
 563  
 564  	if (win->_line[win->_cury].lastchar == _NOCHANGE ||
 565  			win->_line[win->_cury].lastchar < win->_curx)
 566  		win->_line[win->_cury].lastchar = win->_curx;
 567  
 568  	return code;
 569  }
 570  int wattr_on(WINDOW *win, attr_t at, void *opts GCC_UNUSED)
 571  {
 572  	if (at & A_COLOR)
 573  		win->_color = PAIR_NUMBER(at);
 574  	// toggle_attr_on(WINDOW_ATTRS(win), at);
 575  	return OK;
 576  }
 577  int wattr_off(WINDOW *win, attr_t at, void *opts GCC_UNUSED)
 578  {
 579  	if (at & A_COLOR)
 580  		win->_color = 0;
 581  	// toggle_attr_off(WINDOW_ATTRS(win), at);
 582  	return 0;
 583  }
 584  // int wbkgd (WINDOW *, chtype) {}
 585  void wbkgdset(WINDOW *win, chtype ch) { /* TODO */ }
 586  
 587  int wborder(WINDOW *win, chtype ls, chtype rs, chtype ts, chtype bs,
 588  		chtype tl, chtype tr, chtype bl, chtype br)
 589  {
 590  	int x, y;
 591  
 592  	if (ls == 0) ls = ACS_VLINE;
 593  	if (rs == 0) rs = ACS_VLINE;
 594  	if (ts == 0) ts = ACS_HLINE;
 595  	if (bs == 0) bs = ACS_HLINE;
 596  	if (tl == 0) tl = ACS_ULCORNER;
 597  	if (tr == 0) tr = ACS_URCORNER;
 598  	if (bl == 0) bl = ACS_LLCORNER;
 599  	if (br == 0) br = ACS_LRCORNER;
 600  
 601  	for(y = 0; y <= win->_maxy; y++) {
 602  
 603  		if (y == 0) {
 604  			 mvwaddch(win, y, 0, tl);
 605  
 606  			for(x = 1; x < win->_maxx; x++)
 607  				mvwaddch(win, y, x, ts);
 608  
 609  			mvwaddch(win, y, win->_maxx, tr);
 610  		}
 611  		else if (y == win->_maxy) {
 612  			mvwaddch(win, y, 0, bl);
 613  
 614  			for(x = 1; x < win->_maxx; x++)
 615  				mvwaddch(win, y, x, bs);
 616  
 617  			mvwaddch(win, y, win->_maxx, br);
 618  		}
 619  		else {
 620  			mvwaddch(win, y, 0, ls);
 621  			mvwaddch(win, y, win->_maxx, rs);
 622  		}
 623  	}
 624  
 625  	return OK;
 626  }
 627  
 628  // int wchgat (WINDOW *, int, attr_t, short, const void *) {}
 629  /* D */ int wclear(WINDOW *win)
 630  {
 631  	if (werase(win) == ERR)
 632  		return ERR;
 633  	win->_clear = TRUE;
 634  	return OK;
 635  }
 636  // int wclrtobot (WINDOW *) {}
 637  int wclrtoeol(WINDOW *win) { /* TODO */ return ERR; }
 638  int wcolor_set(WINDOW *win, short color_pair_number, void *opts)
 639  {
 640  	if (!opts && (color_pair_number >= 0)
 641  	    && (color_pair_number < COLOR_PAIRS)) {
 642  		SET_WINDOW_PAIR(win, color_pair_number);
 643  		if_EXT_COLORS(win->_color = color_pair_number);
 644  		return OK;
 645  	}
 646  	return ERR;
 647  }
 648  // void wcursyncup (WINDOW *) {}
 649  // int wdelch (WINDOW *) {}
 650  // int wechochar (WINDOW *, const chtype) {}
 651  int werase(WINDOW *win)
 652  {
 653  	int x, y;
 654  	for (y = 0; y <= win->_maxy; y++) {
 655  		for (x = 0; x <= win->_maxx; x++) {
 656  			win->_line[y].text[x].chars[0] = ' ';
 657  			win->_line[y].text[x].attr = WINDOW_ATTRS(win);
 658  		}
 659  		// Should we check instead?
 660  		win->_line[y].firstchar = 0;
 661  		win->_line[y].lastchar = win->_maxx;
 662  	}
 663  	return OK;
 664  }
 665  // int wgetnstr (WINDOW *,char *,int) {}
 666  int whline(WINDOW *win, chtype ch, int n)
 667  {
 668  	NCURSES_SIZE_T start, end;
 669  	struct ldat *line = &(win->_line[win->_cury]);
 670  	NCURSES_CH_T wch;
 671  
 672  	start = win->_curx;
 673  	end = start + n - 1;
 674  	if (end > win->_maxx)
 675  		end = win->_maxx;
 676  
 677  	CHANGED_RANGE(line, start, end);
 678  
 679  	//// TODO:
 680  	//// if (ch == 0)
 681  	////     SetChar2(wch, ACS_HLINE);
 682  	//// else
 683  	////     SetChar2(wch, ch);
 684  	// Ugly hack:
 685  	wch.chars[0] = ((ch) & (chtype)A_CHARTEXT);
 686  	wch.attr = ((ch) & (chtype)A_ATTRIBUTES);
 687  	wch = _nc_render(win, wch);
 688  
 689  	while (end >= start) {
 690  		line->text[end] = wch;
 691  		end--;
 692  	}
 693  
 694  	//// _nc_synchook(win);
 695  
 696  	return OK;
 697  }
 698  /* D */ chtype winch(WINDOW *win)
 699  {
 700  	//// TODO
 701  	// return (CharOf(win->_line[win->_cury].text[win->_curx]) |
 702  	//         AttrOf(win->_line[win->_cury].text[win->_curx]));
 703  	return OK;	// FIXME
 704  }
 705  // int winchnstr (WINDOW *, chtype *, int) {}
 706  // int winnstr (WINDOW *, char *, int) {}
 707  // int winsch (WINDOW *, chtype) {}
 708  // int winsdelln (WINDOW *,int) {}
 709  // int winsnstr (WINDOW *, const char *,int) {}
 710  /* D */ int wmove(WINDOW *win, int y, int x)
 711  {
 712  	if (!LEGALYX(win, y, x))
 713  		return ERR;
 714  	win->_curx = (NCURSES_SIZE_T) x;
 715  	win->_cury = (NCURSES_SIZE_T) y;
 716  	win->_flags &= ~_WRAPPED;
 717  	win->_flags |= _HASMOVED;
 718  	return OK;
 719  }
 720  
 721  #define SWAP_RED_BLUE(c) \
 722  	(((c) & 0x4400) >> 2) | ((c) & 0xAA00) | (((c) & 0x1100) << 2)
 723  int wnoutrefresh(WINDOW *win)
 724  {
 725  #if CONFIG(LP_SERIAL_CONSOLE)
 726  	// FIXME.
 727  	int serial_is_bold = 0;
 728  	int serial_is_reverse = 0;
 729  	int serial_is_altcharset = 0;
 730  	int serial_cur_pair = 0;
 731  
 732  	int need_altcharset;
 733  	short fg, bg;
 734  #endif
 735  	int x, y;
 736  	chtype ch;
 737  
 738  #if CONFIG(LP_SERIAL_CONSOLE)
 739  	serial_end_bold();
 740  	serial_end_altcharset();
 741  #endif
 742  
 743  	for (y = 0; y <= win->_maxy; y++) {
 744  
 745  		if (win->_line[y].firstchar == _NOCHANGE)
 746  			continue;
 747  
 748  		/* Position the serial cursor */
 749  
 750  #if CONFIG(LP_SERIAL_CONSOLE)
 751  		if (curses_flags & F_ENABLE_SERIAL)
 752  			serial_set_cursor(win->_begy + y, win->_begx +
 753  					win->_line[y].firstchar);
 754  #endif
 755  
 756  		for (x = win->_line[y].firstchar; x <= win->_line[y].lastchar; x++) {
 757  			attr_t attr = win->_line[y].text[x].attr;
 758  
 759  #if CONFIG(LP_SERIAL_CONSOLE)
 760  			if (curses_flags & F_ENABLE_SERIAL) {
 761  				ch = win->_line[y].text[x].chars[0];
 762  
 763  				if (attr & A_BOLD) {
 764  					if (!serial_is_bold) {
 765  						serial_start_bold();
 766  						serial_is_bold = 1;
 767  					}
 768  				} else {
 769  					if (serial_is_bold) {
 770  						serial_end_bold();
 771  						serial_is_bold = 0;
 772  						/* work around serial.c
 773  						 * shortcoming:
 774  						 */
 775  						serial_is_reverse = 0;
 776  						serial_cur_pair = 0;
 777  					}
 778  				}
 779  
 780  				if (attr & A_REVERSE) {
 781  					if (!serial_is_reverse) {
 782  						serial_start_reverse();
 783  						serial_is_reverse = 1;
 784  					}
 785  				} else {
 786  					if (serial_is_reverse) {
 787  						serial_end_reverse();
 788  						serial_is_reverse = 0;
 789  						/* work around serial.c
 790  						 * shortcoming:
 791  						 */
 792  						serial_is_bold = 0;
 793  						serial_cur_pair = 0;
 794  					}
 795  				}
 796  
 797  				need_altcharset = 0;
 798  				if (attr & A_ALTCHARSET) {
 799  					if (serial_acs_map[ch & 0x7f]) {
 800  						ch = serial_acs_map[ch & 0x7f];
 801  						need_altcharset = 1;
 802  					} else
 803  						ch = fallback_acs_map[ch & 0x7f];
 804  				}
 805  				if (need_altcharset && !serial_is_altcharset) {
 806  					serial_start_altcharset();
 807  					serial_is_altcharset = 1;
 808  				}
 809  				if (!need_altcharset && serial_is_altcharset) {
 810  					serial_end_altcharset();
 811  					serial_is_altcharset = 0;
 812  				}
 813  
 814  				if (serial_cur_pair != PAIR_NUMBER(attr)) {
 815  					pair_content(PAIR_NUMBER(attr),
 816  						     &fg, &bg);
 817  					serial_set_color(fg, bg);
 818  					serial_cur_pair = PAIR_NUMBER(attr);
 819  				}
 820  
 821  				serial_putchar(ch);
 822  
 823  			}
 824  #endif
 825  #if CONFIG(LP_VIDEO_CONSOLE)
 826  			unsigned int c =
 827  				((int)color_pairs[PAIR_NUMBER(attr)]) << 8;
 828  
 829  			c = SWAP_RED_BLUE(c);
 830  
 831  			if (curses_flags & F_ENABLE_CONSOLE) {
 832  				ch = win->_line[y].text[x].chars[0];
 833  
 834  				/* Handle some of the attributes. */
 835  				if (attr & A_BOLD)
 836  					c |= 0x0800;
 837  				if (attr & A_DIM)
 838  					c &= ~0x800;
 839  				if (attr & A_REVERSE) {
 840  					unsigned char tmp = (c >> 8) & 0xf;
 841  					c = (c >> 4) & 0xf00;
 842  					c |= tmp << 12;
 843  				}
 844  				if (attr & A_ALTCHARSET) {
 845  					if (console_acs_map[ch & 0x7f])
 846  						ch = console_acs_map[ch & 0x7f];
 847  					else
 848  						ch = fallback_acs_map[ch & 0x7f];
 849  				}
 850  
 851  				/*
 852  				 * FIXME: Somewhere along the line, the
 853  				 * character value is getting sign-extented.
 854  				 * For now grab just the 8 bit character,
 855  				 * but this will break wide characters!
 856  				 */
 857  				c |= (chtype) (ch & 0xff);
 858  				video_console_putc(win->_begy + y, win->_begx + x, c);
 859  			}
 860  #endif
 861  		}
 862  		win->_line[y].firstchar = _NOCHANGE;
 863  		win->_line[y].lastchar = _NOCHANGE;
 864  	}
 865  
 866  #if CONFIG(LP_SERIAL_CONSOLE)
 867  	if (curses_flags & F_ENABLE_SERIAL)
 868  		serial_set_cursor(win->_begy + win->_cury, win->_begx + win->_curx);
 869  #endif
 870  
 871  #if CONFIG(LP_VIDEO_CONSOLE)
 872  	if (curses_flags & F_ENABLE_CONSOLE)
 873  		video_console_set_cursor(win->_begx + win->_curx, win->_begy + win->_cury);
 874  #endif
 875  
 876  	return OK;
 877  }
 878  int wprintw(WINDOW *win, const char *fmt, ...)
 879  {
 880  	va_list argp;
 881  	int code;
 882  
 883  	va_start(argp, fmt);
 884  	code = vwprintw(win, fmt, argp);
 885  	va_end(argp);
 886  
 887  	return code;
 888  }
 889  
 890  int wredrawln (WINDOW *win, int beg_line, int num_lines)
 891  {
 892  	int i;
 893  
 894  	for (i = beg_line; i < beg_line + num_lines; i++) {
 895  		win->_line[i].firstchar = 0;
 896  		win->_line[i].lastchar = win->_maxx;
 897  	}
 898  
 899  	return OK;
 900  }
 901  
 902  int wrefresh(WINDOW *win)
 903  {
 904  	// FIXME
 905  	return wnoutrefresh(win);
 906  
 907  	// XXX
 908  	int code;
 909  
 910  	if (win == curscr) {
 911  		curscr->_clear = TRUE;
 912  		// code = doupdate();
 913  	} else if ((code = wnoutrefresh(win)) == OK) {
 914  		if (win->_clear)
 915  			newscr->_clear = TRUE;
 916  		// code = doupdate();
 917  		/*
 918  		 * Reset the clearok() flag in case it was set for the special
 919  		 * case in hardscroll.c (if we don't reset it here, we'll get 2
 920  		 * refreshes because the flag is copied from stdscr to newscr).
 921  		 * Resetting the flag shouldn't do any harm, anyway.
 922  		 */
 923  		win->_clear = FALSE;
 924  	}
 925  
 926  	return code;
 927  }
 928  // int wscanw (WINDOW *, NCURSES_CONST char *,...) {}
 929  int wscrl(WINDOW *win, int n)
 930  {
 931  	int x, y;
 932  
 933  	if (!win->_scroll)
 934  		return ERR;
 935  
 936  	if (n == 0)
 937  		return OK;
 938  
 939  	for (y = 0; y <= (win->_maxy - n); y++) {
 940  		win->_line[y].firstchar = win->_line[y + n].firstchar;
 941  		win->_line[y].lastchar = win->_line[y + n].lastchar;
 942  		for (x = 0; x <= win->_maxx; x++) {
 943  			if ((win->_line[y].text[x].chars[0] != win->_line[y + n].text[x].chars[0]) ||
 944  					(win->_line[y].text[x].attr != win->_line[y + n].text[x].attr)) {
 945  				if (win->_line[y].firstchar == _NOCHANGE)
 946  					win->_line[y].firstchar = x;
 947  
 948  				win->_line[y].lastchar = x;
 949  
 950  				win->_line[y].text[x].chars[0] = win->_line[y + n].text[x].chars[0];
 951  				win->_line[y].text[x].attr = win->_line[y + n].text[x].attr;
 952  			}
 953  		}
 954  	}
 955  
 956  	for (y = (win->_maxy+1 - n); y <= win->_maxy; y++) {
 957  		for (x = 0; x <= win->_maxx; x++) {
 958  			if ((win->_line[y].text[x].chars[0] != ' ') ||
 959  					(win->_line[y].text[x].attr != A_NORMAL)) {
 960  				if (win->_line[y].firstchar == _NOCHANGE)
 961  					win->_line[y].firstchar = x;
 962  
 963  				win->_line[y].lastchar = x;
 964  
 965  				win->_line[y].text[x].chars[0] = ' ';
 966  				win->_line[y].text[x].attr = A_NORMAL;
 967  			}
 968  		}
 969  	}
 970  
 971  	// _nc_scroll_window(win, n, win->_regtop, win->_regbottom, win->_nc_bkgd);
 972  	// _nc_synchook(win);
 973  
 974  	return OK;
 975  }
 976  int wsetscrreg(WINDOW *win, int top, int bottom)
 977  {
 978  	if (top >= 0 && top <= win->_maxy && bottom >= 0 &&
 979  	    bottom <= win->_maxy && bottom > top) {
 980  		win->_regtop = (NCURSES_SIZE_T) top;
 981  		win->_regbottom = (NCURSES_SIZE_T) bottom;
 982  		return OK;
 983  	}
 984  	return ERR;
 985  }
 986  // void wsyncdown (WINDOW *) {}
 987  // void wsyncup (WINDOW *) {}
 988  /* D */ void wtimeout(WINDOW *win, int _delay) { win->_delay = _delay; }
 989  /* D */ int wtouchln(WINDOW *win, int y, int n, int changed)
 990  {
 991  	int i;
 992  
 993  	// if ((n < 0) || (y < 0) || (y > win->_maxy))
 994  	//     return ERR;
 995  
 996  	for (i = y; i < y + n; i++) {
 997  		if (i > win->_maxy)
 998  			break;
 999  		win->_line[i].firstchar = changed ? 0 : _NOCHANGE;
1000  		win->_line[i].lastchar = changed ? win->_maxx : _NOCHANGE;
1001  	}
1002  	return OK;
1003  }
1004  // int wvline (WINDOW *,chtype,int) {}
1005  // int tigetflag (NCURSES_CONST char *) {}
1006  // int tigetnum (NCURSES_CONST char *) {}
1007  // char *tigetstr (NCURSES_CONST char *) {}
1008  // int putp (const char *) {}
1009  // #if NCURSES_TPARM_VARARGS
1010  // char *tparm (NCURSES_CONST char *, ...) {}
1011  // #else
1012  // char *tparm (NCURSES_CONST char *, long,long,long,long,long,long,long,long,long) {}
1013  // char *tparm_varargs (NCURSES_CONST char *, ...) {}
1014  // #endif