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