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