1 /**************************************************************************** 2 * Copyright (c) 1998-2006,2008 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29 /* 30 ** lib_addch.c 31 ** 32 ** The routine waddch(). 33 ** 34 */ 35 36 #include <curses.priv.h> 37 #include <ctype.h> 38 39 MODULE_ID("$Id: lib_addch.c,v 1.113 2008/08/16 19:20:04 tom Exp $") 40 41 static const NCURSES_CH_T blankchar = NewChar(BLANK_TEXT); 42 43 /* 44 * Ugly microtweaking alert. Everything from here to end of module is 45 * likely to be speed-critical -- profiling data sure says it is! 46 * Most of the important screen-painting functions are shells around 47 * waddch(). So we make every effort to reduce function-call overhead 48 * by inlining stuff, even at the cost of making wrapped copies for 49 * export. Also we supply some internal versions that don't call the 50 * window sync hook, for use by string-put functions. 51 */ 52 53 /* Return bit mask for clearing color pair number if given ch has color */ 54 #define COLOR_MASK(ch) (~(attr_t)((ch) & A_COLOR ? A_COLOR : 0)) 55 56 static NCURSES_INLINE NCURSES_CH_T 57 render_char(WINDOW *win, NCURSES_CH_T ch) 58 /* compute a rendition of the given char correct for the current context */ 59 { 60 attr_t a = WINDOW_ATTRS(win); 61 int pair = GetPair(ch); 62 63 if (ISBLANK(ch) 64 && AttrOf(ch) == A_NORMAL 65 && pair == 0) { 66 /* color/pair in attrs has precedence over bkgrnd */ 67 ch = win->_nc_bkgd; 68 SetAttr(ch, a | AttrOf(win->_nc_bkgd)); 69 if ((pair = GET_WINDOW_PAIR(win)) == 0) 70 pair = GetPair(win->_nc_bkgd); 71 SetPair(ch, pair); 72 } else { 73 /* color in attrs has precedence over bkgrnd */ 74 a |= AttrOf(win->_nc_bkgd) & COLOR_MASK(a); 75 /* color in ch has precedence */ 76 if (pair == 0) { 77 if ((pair = GET_WINDOW_PAIR(win)) == 0) 78 pair = GetPair(win->_nc_bkgd); 79 } 80 #if 0 81 if (pair > 255) { 82 NCURSES_CH_T fixme = ch; 83 SetPair(fixme, pair); 84 } 85 #endif 86 AddAttr(ch, (a & COLOR_MASK(AttrOf(ch)))); 87 SetPair(ch, pair); 88 } 89 90 TR(TRACE_VIRTPUT, 91 ("render_char bkg %s (%d), attrs %s (%d) -> ch %s (%d)", 92 _tracech_t2(1, CHREF(win->_nc_bkgd)), 93 GetPair(win->_nc_bkgd), 94 _traceattr(WINDOW_ATTRS(win)), 95 GET_WINDOW_PAIR(win), 96 _tracech_t2(3, CHREF(ch)), 97 GetPair(ch))); 98 99 return (ch); 100 } 101 102 NCURSES_EXPORT(NCURSES_CH_T) 103 _nc_render(WINDOW *win, NCURSES_CH_T ch) 104 /* make render_char() visible while still allowing us to inline it below */ 105 { 106 return render_char(win, ch); 107 } 108 109 /* check if position is legal; if not, return error */ 110 #ifndef NDEBUG /* treat this like an assertion */ 111 #define CHECK_POSITION(win, x, y) \ 112 if (y > win->_maxy \ 113 || x > win->_maxx \ 114 || y < 0 \ 115 || x < 0) { \ 116 TR(TRACE_VIRTPUT, ("Alert! Win=%p _curx = %d, _cury = %d " \ 117 "(_maxx = %d, _maxy = %d)", win, x, y, \ 118 win->_maxx, win->_maxy)); \ 119 return(ERR); \ 120 } 121 #else 122 #define CHECK_POSITION(win, x, y) /* nothing */ 123 #endif 124 125 static bool 126 newline_forces_scroll(WINDOW *win, NCURSES_SIZE_T * ypos) 127 { 128 bool result = FALSE; 129 130 if (*ypos >= win->_regtop && *ypos == win->_regbottom) { 131 *ypos = win->_regbottom; 132 result = TRUE; 133 } else { 134 *ypos += 1; 135 } 136 return result; 137 } 138 139 /* 140 * The _WRAPPED flag is useful only for telling an application that we've just 141 * wrapped the cursor. We don't do anything with this flag except set it when 142 * wrapping, and clear it whenever we move the cursor. If we try to wrap at 143 * the lower-right corner of a window, we cannot move the cursor (since that 144 * wouldn't be legal). So we return an error (which is what SVr4 does). 145 * Unlike SVr4, we can successfully add a character to the lower-right corner 146 * (Solaris 2.6 does this also, however). 147 */ 148 static int 149 wrap_to_next_line(WINDOW *win) 150 { 151 win->_flags |= _WRAPPED; 152 if (newline_forces_scroll(win, &(win->_cury))) { 153 win->_curx = win->_maxx; 154 if (!win->_scroll) 155 return (ERR); 156 scroll(win); 157 } 158 win->_curx = 0; 159 return (OK); 160 } 161 162 #if USE_WIDEC_SUPPORT 163 static int waddch_literal(WINDOW *, NCURSES_CH_T); 164 /* 165 * Fill the given number of cells with blanks using the current background 166 * rendition. This saves/restores the current x-position. 167 */ 168 static void 169 fill_cells(WINDOW *win, int count) 170 { 171 NCURSES_CH_T blank = blankchar; 172 int save_x = win->_curx; 173 int save_y = win->_cury; 174 175 while (count-- > 0) { 176 if (waddch_literal(win, blank) == ERR) 177 break; 178 } 179 win->_curx = save_x; 180 win->_cury = save_y; 181 } 182 #endif 183 184 /* 185 * Build up the bytes for a multibyte character, returning the length when 186 * complete (a positive number), -1 for error and -2 for incomplete. 187 */ 188 #if USE_WIDEC_SUPPORT 189 NCURSES_EXPORT(int) 190 _nc_build_wch(WINDOW *win, ARG_CH_T ch) 191 { 192 char *buffer = WINDOW_EXT(win, addch_work); 193 int len; 194 int x = win->_curx; 195 int y = win->_cury; 196 mbstate_t state; 197 wchar_t result; 198 199 if ((WINDOW_EXT(win, addch_used) != 0) && 200 (WINDOW_EXT(win, addch_x) != x || 201 WINDOW_EXT(win, addch_y) != y)) { 202 /* discard the incomplete multibyte character */ 203 WINDOW_EXT(win, addch_used) = 0; 204 TR(TRACE_VIRTPUT, 205 ("Alert discarded multibyte on move (%d,%d) -> (%d,%d)", 206 WINDOW_EXT(win, addch_y), WINDOW_EXT(win, addch_x), 207 y, x)); 208 } 209 WINDOW_EXT(win, addch_x) = x; 210 WINDOW_EXT(win, addch_y) = y; 211 212 init_mb(state); 213 buffer[WINDOW_EXT(win, addch_used)] = (char) CharOf(CHDEREF(ch)); 214 WINDOW_EXT(win, addch_used) += 1; 215 buffer[WINDOW_EXT(win, addch_used)] = '\0'; 216 if ((len = mbrtowc(&result, 217 buffer, 218 WINDOW_EXT(win, addch_used), &state)) > 0) { 219 attr_t attrs = AttrOf(CHDEREF(ch)); 220 if_EXT_COLORS(int pair = GetPair(CHDEREF(ch))); 221 SetChar(CHDEREF(ch), result, attrs); 222 if_EXT_COLORS(SetPair(CHDEREF(ch), pair)); 223 WINDOW_EXT(win, addch_used) = 0; 224 } else if (len == -1) { 225 /* 226 * An error occurred. We could either discard everything, 227 * or assume that the error was in the previous input. 228 * Try the latter. 229 */ 230 TR(TRACE_VIRTPUT, ("Alert! mbrtowc returns error")); 231 /* handle this with unctrl() */ 232 WINDOW_EXT(win, addch_used) = 0; 233 } 234 return len; 235 } 236 #endif /* USE_WIDEC_SUPPORT */ 237 238 static 239 #if !USE_WIDEC_SUPPORT /* cannot be inline if it is recursive */ 240 NCURSES_INLINE 241 #endif 242 int 243 waddch_literal(WINDOW *win, NCURSES_CH_T ch) 244 { 245 int x; 246 int y; 247 struct ldat *line; 248 249 x = win->_curx; 250 y = win->_cury; 251 252 CHECK_POSITION(win, x, y); 253 254 ch = render_char(win, ch); 255 256 line = win->_line + y; 257 258 CHANGED_CELL(line, x); 259 260 /* 261 * Build up multibyte characters until we have a wide-character. 262 */ 263 if_WIDEC({ 264 if (WINDOW_EXT(win, addch_used) != 0 || !Charable(ch)) { 265 int len = _nc_build_wch(win, CHREF(ch)); 266 267 if (len >= -1) { 268 /* handle EILSEQ */ 269 if (is8bits(CharOf(ch))) { 270 const char *s = unctrl((chtype) CharOf(ch)); 271 if (s[1] != 0) { 272 return waddstr(win, s); 273 } 274 } 275 if (len == -1) 276 return waddch(win, ' '); 277 } else { 278 return OK; 279 } 280 } 281 }); 282 283 /* 284 * Non-spacing characters are added to the current cell. 285 * 286 * Spacing characters that are wider than one column require some display 287 * adjustments. 288 */ 289 if_WIDEC({ 290 int len = wcwidth(CharOf(ch)); 291 int i; 292 int j; 293 wchar_t *chars; 294 295 if (len == 0) { /* non-spacing */ 296 if ((x > 0 && y >= 0) 297 || (win->_maxx >= 0 && win->_cury >= 1)) { 298 if (x > 0 && y >= 0) 299 chars = (win->_line[y].text[x - 1].chars); 300 else 301 chars = (win->_line[y - 1].text[win->_maxx].chars); 302 for (i = 0; i < CCHARW_MAX; ++i) { 303 if (chars[i] == 0) { 304 TR(TRACE_VIRTPUT, 305 ("added non-spacing %d: %x", 306 x, (int) CharOf(ch))); 307 chars[i] = CharOf(ch); 308 break; 309 } 310 } 311 } 312 goto testwrapping; 313 } else if (len > 1) { /* multi-column characters */ 314 /* 315 * Check if the character will fit on the current line. If it does 316 * not fit, fill in the remainder of the line with blanks. and 317 * move to the next line. 318 */ 319 if (len > win->_maxx + 1) { 320 TR(TRACE_VIRTPUT, ("character will not fit")); 321 return ERR; 322 } else if (x + len > win->_maxx + 1) { 323 int count = win->_maxx + 1 - x; 324 TR(TRACE_VIRTPUT, ("fill %d remaining cells", count)); 325 fill_cells(win, count); 326 if (wrap_to_next_line(win) == ERR) 327 return ERR; 328 x = win->_curx; 329 y = win->_cury; 330 } 331 /* 332 * Check for cells which are orphaned by adding this character, set 333 * those to blanks. 334 * 335 * FIXME: this actually could fill j-i cells, more complicated to 336 * setup though. 337 */ 338 for (i = 0; i < len; ++i) { 339 if (isWidecBase(win->_line[y].text[x + i])) { 340 break; 341 } else if (isWidecExt(win->_line[y].text[x + i])) { 342 for (j = i; x + j <= win->_maxx; ++j) { 343 if (!isWidecExt(win->_line[y].text[x + j])) { 344 TR(TRACE_VIRTPUT, ("fill %d orphan cells", j)); 345 fill_cells(win, j); 346 break; 347 } 348 } 349 break; 350 } 351 } 352 /* 353 * Finally, add the cells for this character. 354 */ 355 for (i = 0; i < len; ++i) { 356 NCURSES_CH_T value = ch; 357 SetWidecExt(value, i); 358 TR(TRACE_VIRTPUT, ("multicolumn %d:%d (%d,%d)", 359 i + 1, len, 360 win->_begy + y, win->_begx + x)); 361 line->text[x] = value; 362 CHANGED_CELL(line, x); 363 ++x; 364 } 365 goto testwrapping; 366 } 367 }); 368 369 /* 370 * Single-column characters. 371 */ 372 line->text[x++] = ch; 373 /* 374 * This label is used only for wide-characters. 375 */ 376 if_WIDEC( 377 testwrapping: 378 ); 379 380 TR(TRACE_VIRTPUT, ("cell (%ld, %ld..%d) = %s", 381 (long) win->_cury, (long) win->_curx, x - 1, 382 _tracech_t(CHREF(ch)))); 383 384 if (x > win->_maxx) { 385 return wrap_to_next_line(win); 386 } 387 win->_curx = x; 388 return OK; 389 } 390 391 static NCURSES_INLINE int 392 waddch_nosync(WINDOW *win, const NCURSES_CH_T ch) 393 /* the workhorse function -- add a character to the given window */ 394 { 395 NCURSES_SIZE_T x, y; 396 chtype t = CharOf(ch); 397 const char *s = unctrl(t); 398 399 /* 400 * If we are using the alternate character set, forget about locale. 401 * Otherwise, if unctrl() returns a single-character or the locale 402 * claims the code is printable, treat it that way. 403 */ 404 if ((AttrOf(ch) & A_ALTCHARSET) 405 || ( 406 #if USE_WIDEC_SUPPORT 407 (SP != 0 && SP->_legacy_coding) && 408 #endif 409 s[1] == 0 410 ) 411 || ( 412 isprint(t) 413 #if USE_WIDEC_SUPPORT 414 || ((SP == 0 || !SP->_legacy_coding) && 415 (WINDOW_EXT(win, addch_used) 416 || !_nc_is_charable(CharOf(ch)))) 417 #endif 418 )) 419 return waddch_literal(win, ch); 420 421 /* 422 * Handle carriage control and other codes that are not printable, or are 423 * known to expand to more than one character according to unctrl(). 424 */ 425 x = win->_curx; 426 y = win->_cury; 427 428 switch (t) { 429 case '\t': 430 x += (TABSIZE - (x % TABSIZE)); 431 432 /* 433 * Space-fill the tab on the bottom line so that we'll get the 434 * "correct" cursor position. 435 */ 436 if ((!win->_scroll && (y == win->_regbottom)) 437 || (x <= win->_maxx)) { 438 NCURSES_CH_T blank = blankchar; 439 AddAttr(blank, AttrOf(ch)); 440 while (win->_curx < x) { 441 if (waddch_literal(win, blank) == ERR) 442 return (ERR); 443 } 444 break; 445 } else { 446 wclrtoeol(win); 447 win->_flags |= _WRAPPED; 448 if (newline_forces_scroll(win, &y)) { 449 x = win->_maxx; 450 if (win->_scroll) { 451 scroll(win); 452 x = 0; 453 } 454 } else { 455 x = 0; 456 } 457 } 458 break; 459 case '\n': 460 wclrtoeol(win); 461 if (newline_forces_scroll(win, &y)) { 462 if (win->_scroll) 463 scroll(win); 464 else 465 return (ERR); 466 } 467 /* FALLTHRU */ 468 case '\r': 469 x = 0; 470 win->_flags &= ~_WRAPPED; 471 break; 472 case '\b': 473 if (x == 0) 474 return (OK); 475 x--; 476 win->_flags &= ~_WRAPPED; 477 break; 478 default: 479 while (*s) { 480 NCURSES_CH_T sch; 481 SetChar(sch, *s++, AttrOf(ch)); 482 if_EXT_COLORS(SetPair(sch, GetPair(ch))); 483 if (waddch_literal(win, sch) == ERR) 484 return ERR; 485 } 486 return (OK); 487 } 488 489 win->_curx = x; 490 win->_cury = y; 491 492 return (OK); 493 } 494 495 NCURSES_EXPORT(int) 496 _nc_waddch_nosync(WINDOW *win, const NCURSES_CH_T c) 497 /* export copy of waddch_nosync() so the string-put functions can use it */ 498 { 499 return (waddch_nosync(win, c)); 500 } 501 502 /* 503 * The versions below call _nc_synchook(). We wanted to avoid this in the 504 * version exported for string puts; they'll call _nc_synchook once at end 505 * of run. 506 */ 507 508 /* These are actual entry points */ 509 510 NCURSES_EXPORT(int) 511 waddch(WINDOW *win, const chtype ch) 512 { 513 int code = ERR; 514 NCURSES_CH_T wch; 515 SetChar2(wch, ch); 516 517 TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("waddch(%p, %s)"), win, 518 _tracechtype(ch))); 519 520 if (win && (waddch_nosync(win, wch) != ERR)) { 521 _nc_synchook(win); 522 code = OK; 523 } 524 525 TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code)); 526 return (code); 527 } 528 529 NCURSES_EXPORT(int) 530 wechochar(WINDOW *win, const chtype ch) 531 { 532 int code = ERR; 533 NCURSES_CH_T wch; 534 SetChar2(wch, ch); 535 536 TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), win, 537 _tracechtype(ch))); 538 539 if (win && (waddch_nosync(win, wch) != ERR)) { 540 bool save_immed = win->_immed; 541 win->_immed = TRUE; 542 _nc_synchook(win); 543 win->_immed = save_immed; 544 code = OK; 545 } 546 TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code)); 547 return (code); 548 } 549