1166124Srafan/**************************************************************************** 2262629Sdelphij * Copyright (c) 2004-2010,2011 Free Software Foundation, Inc. * 3166124Srafan * * 4166124Srafan * Permission is hereby granted, free of charge, to any person obtaining a * 5166124Srafan * copy of this software and associated documentation files (the * 6166124Srafan * "Software"), to deal in the Software without restriction, including * 7166124Srafan * without limitation the rights to use, copy, modify, merge, publish, * 8166124Srafan * distribute, distribute with modifications, sublicense, and/or sell * 9166124Srafan * copies of the Software, and to permit persons to whom the Software is * 10166124Srafan * furnished to do so, subject to the following conditions: * 11166124Srafan * * 12166124Srafan * The above copyright notice and this permission notice shall be included * 13166124Srafan * in all copies or substantial portions of the Software. * 14166124Srafan * * 15166124Srafan * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16166124Srafan * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17166124Srafan * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18166124Srafan * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19166124Srafan * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20166124Srafan * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21166124Srafan * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22166124Srafan * * 23166124Srafan * Except as contained in this notice, the name(s) of the above copyright * 24166124Srafan * holders shall not be used in advertising or otherwise to promote the * 25166124Srafan * sale, use or other dealings in this Software without prior written * 26166124Srafan * authorization. * 27166124Srafan ****************************************************************************/ 28166124Srafan 29166124Srafan/* 30166124Srafan** lib_add_wch.c 31166124Srafan** 32166124Srafan** The routine wadd_wch(). 33166124Srafan** 34166124Srafan*/ 35166124Srafan 36166124Srafan#include <curses.priv.h> 37166124Srafan 38262629Sdelphij#if HAVE_WCTYPE_H 39262629Sdelphij#include <wctype.h> 40262629Sdelphij#endif 41166124Srafan 42262629SdelphijMODULE_ID("$Id: lib_add_wch.c,v 1.12 2011/03/22 09:31:15 Petr.Pavlu Exp $") 43262629Sdelphij 44262629Sdelphij/* clone/adapt lib_addch.c */ 45262629Sdelphijstatic const cchar_t blankchar = NewChar(BLANK_TEXT); 46262629Sdelphij 47262629Sdelphij/* 48262629Sdelphij * Ugly microtweaking alert. Everything from here to end of module is 49262629Sdelphij * likely to be speed-critical -- profiling data sure says it is! 50262629Sdelphij * Most of the important screen-painting functions are shells around 51262629Sdelphij * wadd_wch(). So we make every effort to reduce function-call overhead 52262629Sdelphij * by inlining stuff, even at the cost of making wrapped copies for 53262629Sdelphij * export. Also we supply some internal versions that don't call the 54262629Sdelphij * window sync hook, for use by string-put functions. 55262629Sdelphij */ 56262629Sdelphij 57262629Sdelphij/* Return bit mask for clearing color pair number if given ch has color */ 58262629Sdelphij#define COLOR_MASK(ch) (~(attr_t)((ch) & A_COLOR ? A_COLOR : 0)) 59262629Sdelphij 60262629Sdelphijstatic NCURSES_INLINE cchar_t 61262629Sdelphijrender_char(WINDOW *win, cchar_t ch) 62262629Sdelphij/* compute a rendition of the given char correct for the current context */ 63166124Srafan{ 64262629Sdelphij attr_t a = WINDOW_ATTRS(win); 65262629Sdelphij int pair = GetPair(ch); 66166124Srafan 67262629Sdelphij if (ISBLANK(ch) 68262629Sdelphij && AttrOf(ch) == A_NORMAL 69262629Sdelphij && pair == 0) { 70262629Sdelphij /* color/pair in attrs has precedence over bkgrnd */ 71262629Sdelphij ch = win->_nc_bkgd; 72262629Sdelphij SetAttr(ch, a | AttrOf(win->_nc_bkgd)); 73262629Sdelphij if ((pair = GET_WINDOW_PAIR(win)) == 0) 74262629Sdelphij pair = GetPair(win->_nc_bkgd); 75262629Sdelphij SetPair(ch, pair); 76262629Sdelphij } else { 77262629Sdelphij /* color in attrs has precedence over bkgrnd */ 78262629Sdelphij a |= AttrOf(win->_nc_bkgd) & COLOR_MASK(a); 79262629Sdelphij /* color in ch has precedence */ 80262629Sdelphij if (pair == 0) { 81262629Sdelphij if ((pair = GET_WINDOW_PAIR(win)) == 0) 82262629Sdelphij pair = GetPair(win->_nc_bkgd); 83262629Sdelphij } 84262629Sdelphij AddAttr(ch, (a & COLOR_MASK(AttrOf(ch)))); 85262629Sdelphij SetPair(ch, pair); 86262629Sdelphij } 87166124Srafan 88262629Sdelphij TR(TRACE_VIRTPUT, 89262629Sdelphij ("render_char bkg %s (%d), attrs %s (%d) -> ch %s (%d)", 90262629Sdelphij _tracech_t2(1, CHREF(win->_nc_bkgd)), 91262629Sdelphij GetPair(win->_nc_bkgd), 92262629Sdelphij _traceattr(WINDOW_ATTRS(win)), 93262629Sdelphij GET_WINDOW_PAIR(win), 94262629Sdelphij _tracech_t2(3, CHREF(ch)), 95262629Sdelphij GetPair(ch))); 96166124Srafan 97262629Sdelphij return (ch); 98262629Sdelphij} 99262629Sdelphij 100262629Sdelphij/* check if position is legal; if not, return error */ 101262629Sdelphij#ifndef NDEBUG /* treat this like an assertion */ 102262629Sdelphij#define CHECK_POSITION(win, x, y) \ 103262629Sdelphij if (y > win->_maxy \ 104262629Sdelphij || x > win->_maxx \ 105262629Sdelphij || y < 0 \ 106262629Sdelphij || x < 0) { \ 107262629Sdelphij TR(TRACE_VIRTPUT, ("Alert! Win=%p _curx = %d, _cury = %d " \ 108262629Sdelphij "(_maxx = %d, _maxy = %d)", win, x, y, \ 109262629Sdelphij win->_maxx, win->_maxy)); \ 110262629Sdelphij return(ERR); \ 111262629Sdelphij } 112262629Sdelphij#else 113262629Sdelphij#define CHECK_POSITION(win, x, y) /* nothing */ 114262629Sdelphij#endif 115262629Sdelphij 116262629Sdelphijstatic bool 117262629Sdelphijnewline_forces_scroll(WINDOW *win, NCURSES_SIZE_T * ypos) 118262629Sdelphij{ 119262629Sdelphij bool result = FALSE; 120262629Sdelphij 121262629Sdelphij if (*ypos >= win->_regtop && *ypos == win->_regbottom) { 122262629Sdelphij *ypos = win->_regbottom; 123262629Sdelphij result = TRUE; 124262629Sdelphij } else { 125262629Sdelphij *ypos = (NCURSES_SIZE_T) (*ypos + 1); 126262629Sdelphij } 127262629Sdelphij return result; 128262629Sdelphij} 129262629Sdelphij 130262629Sdelphij/* 131262629Sdelphij * The _WRAPPED flag is useful only for telling an application that we've just 132262629Sdelphij * wrapped the cursor. We don't do anything with this flag except set it when 133262629Sdelphij * wrapping, and clear it whenever we move the cursor. If we try to wrap at 134262629Sdelphij * the lower-right corner of a window, we cannot move the cursor (since that 135262629Sdelphij * wouldn't be legal). So we return an error (which is what SVr4 does). 136262629Sdelphij * Unlike SVr4, we can successfully add a character to the lower-right corner 137262629Sdelphij * (Solaris 2.6 does this also, however). 138262629Sdelphij */ 139262629Sdelphijstatic int 140262629Sdelphijwrap_to_next_line(WINDOW *win) 141262629Sdelphij{ 142262629Sdelphij win->_flags |= _WRAPPED; 143262629Sdelphij if (newline_forces_scroll(win, &(win->_cury))) { 144262629Sdelphij win->_curx = win->_maxx; 145262629Sdelphij if (!win->_scroll) 146262629Sdelphij return (ERR); 147262629Sdelphij scroll(win); 148262629Sdelphij } 149262629Sdelphij win->_curx = 0; 150262629Sdelphij return (OK); 151262629Sdelphij} 152262629Sdelphij 153262629Sdelphijstatic int wadd_wch_literal(WINDOW *, cchar_t); 154262629Sdelphij/* 155262629Sdelphij * Fill the given number of cells with blanks using the current background 156262629Sdelphij * rendition. This saves/restores the current x-position. 157262629Sdelphij */ 158262629Sdelphijstatic void 159262629Sdelphijfill_cells(WINDOW *win, int count) 160262629Sdelphij{ 161262629Sdelphij cchar_t blank = blankchar; 162262629Sdelphij int save_x = win->_curx; 163262629Sdelphij int save_y = win->_cury; 164262629Sdelphij 165262629Sdelphij while (count-- > 0) { 166262629Sdelphij if (wadd_wch_literal(win, blank) == ERR) 167262629Sdelphij break; 168262629Sdelphij } 169262629Sdelphij win->_curx = (NCURSES_SIZE_T) save_x; 170262629Sdelphij win->_cury = (NCURSES_SIZE_T) save_y; 171262629Sdelphij} 172262629Sdelphij 173262629Sdelphijstatic int 174262629Sdelphijwadd_wch_literal(WINDOW *win, cchar_t ch) 175262629Sdelphij{ 176262629Sdelphij int x; 177262629Sdelphij int y; 178262629Sdelphij struct ldat *line; 179262629Sdelphij 180262629Sdelphij x = win->_curx; 181262629Sdelphij y = win->_cury; 182262629Sdelphij 183262629Sdelphij CHECK_POSITION(win, x, y); 184262629Sdelphij 185262629Sdelphij ch = render_char(win, ch); 186262629Sdelphij 187262629Sdelphij line = win->_line + y; 188262629Sdelphij 189262629Sdelphij CHANGED_CELL(line, x); 190262629Sdelphij 191262629Sdelphij /* 192262629Sdelphij * Non-spacing characters are added to the current cell. 193262629Sdelphij * 194262629Sdelphij * Spacing characters that are wider than one column require some display 195262629Sdelphij * adjustments. 196262629Sdelphij */ 197262629Sdelphij { 198262629Sdelphij int len = wcwidth(CharOf(ch)); 199262629Sdelphij int i; 200262629Sdelphij int j; 201262629Sdelphij wchar_t *chars; 202262629Sdelphij 203262629Sdelphij if (len == 0) { /* non-spacing */ 204262629Sdelphij if ((x > 0 && y >= 0) 205262629Sdelphij || (win->_maxx >= 0 && win->_cury >= 1)) { 206262629Sdelphij if (x > 0 && y >= 0) 207262629Sdelphij chars = (win->_line[y].text[x - 1].chars); 208262629Sdelphij else 209262629Sdelphij chars = (win->_line[y - 1].text[win->_maxx].chars); 210262629Sdelphij for (i = 0; i < CCHARW_MAX; ++i) { 211262629Sdelphij if (chars[i] == 0) { 212262629Sdelphij TR(TRACE_VIRTPUT, 213262629Sdelphij ("added non-spacing %d: %x", 214262629Sdelphij x, (int) CharOf(ch))); 215262629Sdelphij chars[i] = CharOf(ch); 216262629Sdelphij break; 217262629Sdelphij } 218262629Sdelphij } 219166124Srafan } 220262629Sdelphij goto testwrapping; 221262629Sdelphij } else if (len > 1) { /* multi-column characters */ 222262629Sdelphij /* 223262629Sdelphij * Check if the character will fit on the current line. If it does 224262629Sdelphij * not fit, fill in the remainder of the line with blanks. and 225262629Sdelphij * move to the next line. 226262629Sdelphij */ 227262629Sdelphij if (len > win->_maxx + 1) { 228262629Sdelphij TR(TRACE_VIRTPUT, ("character will not fit")); 229262629Sdelphij return ERR; 230262629Sdelphij } else if (x + len > win->_maxx + 1) { 231262629Sdelphij int count = win->_maxx + 1 - x; 232262629Sdelphij TR(TRACE_VIRTPUT, ("fill %d remaining cells", count)); 233262629Sdelphij fill_cells(win, count); 234262629Sdelphij if (wrap_to_next_line(win) == ERR) 235262629Sdelphij return ERR; 236262629Sdelphij x = win->_curx; 237262629Sdelphij y = win->_cury; 238262629Sdelphij line = win->_line + y; 239262629Sdelphij } 240262629Sdelphij /* 241262629Sdelphij * Check for cells which are orphaned by adding this character, set 242262629Sdelphij * those to blanks. 243262629Sdelphij * 244262629Sdelphij * FIXME: this actually could fill j-i cells, more complicated to 245262629Sdelphij * setup though. 246262629Sdelphij */ 247262629Sdelphij for (i = 0; i < len; ++i) { 248262629Sdelphij if (isWidecBase(win->_line[y].text[x + i])) { 249166124Srafan break; 250262629Sdelphij } else if (isWidecExt(win->_line[y].text[x + i])) { 251262629Sdelphij for (j = i; x + j <= win->_maxx; ++j) { 252262629Sdelphij if (!isWidecExt(win->_line[y].text[x + j])) { 253262629Sdelphij TR(TRACE_VIRTPUT, ("fill %d orphan cells", j)); 254262629Sdelphij fill_cells(win, j); 255262629Sdelphij break; 256262629Sdelphij } 257262629Sdelphij } 258262629Sdelphij break; 259166124Srafan } 260166124Srafan } 261262629Sdelphij /* 262262629Sdelphij * Finally, add the cells for this character. 263262629Sdelphij */ 264262629Sdelphij for (i = 0; i < len; ++i) { 265262629Sdelphij cchar_t value = ch; 266262629Sdelphij SetWidecExt(value, i); 267262629Sdelphij TR(TRACE_VIRTPUT, ("multicolumn %d:%d (%d,%d)", 268262629Sdelphij i + 1, len, 269262629Sdelphij win->_begy + y, win->_begx + x)); 270262629Sdelphij line->text[x] = value; 271262629Sdelphij CHANGED_CELL(line, x); 272262629Sdelphij ++x; 273262629Sdelphij } 274262629Sdelphij goto testwrapping; 275166124Srafan } 276166124Srafan } 277166124Srafan 278262629Sdelphij /* 279262629Sdelphij * Single-column characters. 280262629Sdelphij */ 281262629Sdelphij line->text[x++] = ch; 282262629Sdelphij /* 283262629Sdelphij * This label is used only for wide-characters. 284262629Sdelphij */ 285262629Sdelphij testwrapping: 286262629Sdelphij 287262629Sdelphij TR(TRACE_VIRTPUT, ("cell (%ld, %ld..%d) = %s", 288262629Sdelphij (long) win->_cury, (long) win->_curx, x - 1, 289262629Sdelphij _tracech_t(CHREF(ch)))); 290262629Sdelphij 291262629Sdelphij if (x > win->_maxx) { 292262629Sdelphij return wrap_to_next_line(win); 293262629Sdelphij } 294262629Sdelphij win->_curx = (NCURSES_SIZE_T) x; 295262629Sdelphij return OK; 296166124Srafan} 297166124Srafan 298262629Sdelphijstatic NCURSES_INLINE int 299262629Sdelphijwadd_wch_nosync(WINDOW *win, cchar_t ch) 300262629Sdelphij/* the workhorse function -- add a character to the given window */ 301166124Srafan{ 302262629Sdelphij NCURSES_SIZE_T x, y; 303262629Sdelphij wchar_t *s; 304262629Sdelphij int tabsize = 8; 305262629Sdelphij#if USE_REENTRANT 306262629Sdelphij SCREEN *sp = _nc_screen_of(win); 307262629Sdelphij#endif 308166124Srafan 309262629Sdelphij /* 310262629Sdelphij * If we are using the alternate character set, forget about locale. 311262629Sdelphij * Otherwise, if the locale claims the code is printable, treat it that 312262629Sdelphij * way. 313262629Sdelphij */ 314262629Sdelphij if ((AttrOf(ch) & A_ALTCHARSET) 315262629Sdelphij || iswprint((wint_t) CharOf(ch))) 316262629Sdelphij return wadd_wch_literal(win, ch); 317166124Srafan 318262629Sdelphij /* 319262629Sdelphij * Handle carriage control and other codes that are not printable, or are 320262629Sdelphij * known to expand to more than one character according to unctrl(). 321262629Sdelphij */ 322262629Sdelphij x = win->_curx; 323262629Sdelphij y = win->_cury; 324166124Srafan 325262629Sdelphij switch (CharOf(ch)) { 326262629Sdelphij case '\t': 327262629Sdelphij#if USE_REENTRANT 328262629Sdelphij tabsize = *ptrTabsize(sp); 329262629Sdelphij#else 330262629Sdelphij tabsize = TABSIZE; 331262629Sdelphij#endif 332262629Sdelphij x = (NCURSES_SIZE_T) (x + (tabsize - (x % tabsize))); 333262629Sdelphij /* 334262629Sdelphij * Space-fill the tab on the bottom line so that we'll get the 335262629Sdelphij * "correct" cursor position. 336262629Sdelphij */ 337262629Sdelphij if ((!win->_scroll && (y == win->_regbottom)) 338262629Sdelphij || (x <= win->_maxx)) { 339262629Sdelphij cchar_t blank = blankchar; 340262629Sdelphij AddAttr(blank, AttrOf(ch)); 341262629Sdelphij while (win->_curx < x) { 342262629Sdelphij if (wadd_wch_literal(win, blank) == ERR) 343262629Sdelphij return (ERR); 344166124Srafan } 345262629Sdelphij break; 346262629Sdelphij } else { 347262629Sdelphij wclrtoeol(win); 348262629Sdelphij win->_flags |= _WRAPPED; 349262629Sdelphij if (newline_forces_scroll(win, &y)) { 350262629Sdelphij x = win->_maxx; 351262629Sdelphij if (win->_scroll) { 352262629Sdelphij scroll(win); 353262629Sdelphij x = 0; 354166124Srafan } 355262629Sdelphij } else { 356262629Sdelphij x = 0; 357166124Srafan } 358166124Srafan } 359262629Sdelphij break; 360262629Sdelphij case '\n': 361262629Sdelphij wclrtoeol(win); 362262629Sdelphij if (newline_forces_scroll(win, &y)) { 363262629Sdelphij if (win->_scroll) 364262629Sdelphij scroll(win); 365262629Sdelphij else 366262629Sdelphij return (ERR); 367262629Sdelphij } 368262629Sdelphij /* FALLTHRU */ 369262629Sdelphij case '\r': 370262629Sdelphij x = 0; 371262629Sdelphij win->_flags &= ~_WRAPPED; 372262629Sdelphij break; 373262629Sdelphij case '\b': 374262629Sdelphij if (x == 0) 375262629Sdelphij return (OK); 376262629Sdelphij x--; 377262629Sdelphij win->_flags &= ~_WRAPPED; 378262629Sdelphij break; 379262629Sdelphij default: 380262629Sdelphij if ((s = wunctrl(&ch)) != 0) { 381262629Sdelphij while (*s) { 382262629Sdelphij cchar_t sch; 383262629Sdelphij SetChar(sch, *s++, AttrOf(ch)); 384262629Sdelphij if_EXT_COLORS(SetPair(sch, GetPair(ch))); 385262629Sdelphij if (wadd_wch_literal(win, sch) == ERR) 386262629Sdelphij return ERR; 387262629Sdelphij } 388262629Sdelphij return OK; 389262629Sdelphij } 390262629Sdelphij return ERR; 391166124Srafan } 392166124Srafan 393262629Sdelphij win->_curx = x; 394262629Sdelphij win->_cury = y; 395262629Sdelphij 396262629Sdelphij return OK; 397262629Sdelphij} 398262629Sdelphij 399262629Sdelphij/* 400262629Sdelphij * The versions below call _nc_synchook(). We wanted to avoid this in the 401262629Sdelphij * version exported for string puts; they'll call _nc_synchook once at end 402262629Sdelphij * of run. 403262629Sdelphij */ 404262629Sdelphij 405262629Sdelphij/* These are actual entry points */ 406262629Sdelphij 407262629SdelphijNCURSES_EXPORT(int) 408262629Sdelphijwadd_wch(WINDOW *win, const cchar_t *wch) 409262629Sdelphij{ 410262629Sdelphij int code = ERR; 411262629Sdelphij 412262629Sdelphij TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wadd_wch(%p, %s)"), 413262629Sdelphij (void *) win, 414262629Sdelphij _tracecchar_t(wch))); 415262629Sdelphij 416262629Sdelphij if (win && (wadd_wch_nosync(win, *wch) != ERR)) { 417262629Sdelphij _nc_synchook(win); 418262629Sdelphij code = OK; 419262629Sdelphij } 420262629Sdelphij 421166124Srafan TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code)); 422166124Srafan return (code); 423166124Srafan} 424262629Sdelphij 425262629SdelphijNCURSES_EXPORT(int) 426262629Sdelphijwecho_wchar(WINDOW *win, const cchar_t *wch) 427262629Sdelphij{ 428262629Sdelphij int code = ERR; 429262629Sdelphij 430262629Sdelphij TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), 431262629Sdelphij (void *) win, 432262629Sdelphij _tracecchar_t(wch))); 433262629Sdelphij 434262629Sdelphij if (win && (wadd_wch_nosync(win, *wch) != ERR)) { 435262629Sdelphij bool save_immed = win->_immed; 436262629Sdelphij win->_immed = TRUE; 437262629Sdelphij _nc_synchook(win); 438262629Sdelphij win->_immed = save_immed; 439262629Sdelphij code = OK; 440262629Sdelphij } 441262629Sdelphij TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code)); 442262629Sdelphij return (code); 443262629Sdelphij} 444