1 /* $NetBSD: cr_put.c,v 1.30 2010/02/12 10:06:15 roy Exp $ */ 2 3 /* 4 * Copyright (c) 1981, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)cr_put.c 8.3 (Berkeley) 5/4/94"; 36 #else 37 __RCSID("$NetBSD: cr_put.c,v 1.30 2010/02/12 10:06:15 roy Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <string.h> 42 43 #include "curses.h" 44 #include "curses_private.h" 45 46 #define HARDTABS 8 47 48 /* 49 * Terminal driving and line formatting routines. Basic motion optimizations 50 * are done here as well as formatting lines (printing of control characters, 51 * line numbering and the like). 52 */ 53 54 /* Stub function for the users. */ 55 int 56 mvcur(int ly, int lx, int y, int x) 57 { 58 return (__mvcur(ly, lx, y, x, 0)); 59 } 60 61 static void fgoto __P((int)); 62 static int plod __P((int, int)); 63 static int plodput __P((int)); 64 static int tabcol __P((int, int)); 65 66 static int outcol, outline, destcol, destline; 67 68 /* 69 * Sync the position of the output cursor. Most work here is rounding for 70 * terminal boundaries getting the column position implied by wraparound or 71 * the lack thereof and rolling up the screen to get destline on the screen. 72 */ 73 int 74 __mvcur(int ly, int lx, int y, int x, int in_refresh) 75 { 76 #ifdef DEBUG 77 __CTRACE(__CTRACE_OUTPUT, 78 "mvcur: moving cursor from (%d, %d) to (%d, %d)\n", ly, lx, y, x); 79 #endif 80 destcol = x; 81 destline = y; 82 outcol = lx; 83 outline = ly; 84 fgoto(in_refresh); 85 return (OK); 86 } 87 88 static void 89 fgoto(in_refresh) 90 int in_refresh; 91 { 92 int c, l; 93 char *cgp; 94 95 #ifdef DEBUG 96 __CTRACE(__CTRACE_OUTPUT, "fgoto: in_refresh=%d\n", in_refresh); 97 #endif /* DEBUG */ 98 if (destcol >= COLS) { 99 destline += destcol / COLS; 100 destcol %= COLS; 101 } 102 if (outcol >= COLS) { 103 l = (outcol + 1) / COLS; 104 outline += l; 105 outcol %= COLS; 106 if (auto_left_margin == 0) { 107 while (l > 0) { 108 if (__pfast) { 109 if (carriage_return) 110 tputs(carriage_return, 111 0, __cputchar); 112 else 113 __cputchar('\r'); 114 } 115 if (cursor_down) 116 tputs(cursor_down, 0, __cputchar); 117 else 118 __cputchar('\n'); 119 l--; 120 } 121 outcol = 0; 122 } 123 if (outline > LINES - 1) { 124 destline -= outline - (LINES - 1); 125 outline = LINES - 1; 126 } 127 } 128 if (destline >= LINES) { 129 l = destline; 130 destline = LINES - 1; 131 if (outline < LINES - 1) { 132 c = destcol; 133 if (__pfast == 0 && !cursor_address) 134 destcol = 0; 135 fgoto(in_refresh); 136 destcol = c; 137 } 138 while (l >= LINES) { 139 /* The following linefeed (or simulation thereof) is 140 * supposed to scroll up the screen, since we are on 141 * the bottom line. We make the assumption that 142 * linefeed will scroll. If ns is in the capability 143 * list this won't work. We should probably have an 144 * sc capability but sf will generally take the place 145 * if it works. 146 * 147 * Superbee glitch: in the middle of the screen have to 148 * use esc B (down) because linefeed screws up in 149 * "Efficient Paging" (what a joke) mode (which is 150 * essential in some SB's because CRLF mode puts 151 * garbage in at end of memory), but you must use 152 * linefeed to scroll since down arrow won't go past 153 * memory end. I turned this off after recieving Paul 154 * Eggert's Superbee description which wins better. */ 155 if (cursor_down /* && !__tc_xb */ && __pfast) 156 tputs(cursor_down, 0, __cputchar); 157 else 158 __cputchar('\n'); 159 l--; 160 if (__pfast == 0) 161 outcol = 0; 162 } 163 } 164 if (destline < outline && !(cursor_address || cursor_up)) 165 destline = outline; 166 167 if (cursor_address && 168 (cgp = t_vparm(NULL, cursor_address, destline, destcol))) 169 { 170 /* 171 * Need this condition due to inconsistent behavior 172 * of backspace on the last column. 173 */ 174 #ifdef DEBUG 175 __CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp); 176 #endif /* DEBUG */ 177 if (outcol != COLS - 1 && 178 plod((int) strlen(cgp), in_refresh) > 0) 179 plod(0, in_refresh); 180 else 181 tputs(cgp, 0, __cputchar); 182 } else 183 plod(0, in_refresh); 184 outline = destline; 185 outcol = destcol; 186 } 187 /* 188 * Move (slowly) to destination. 189 * Hard thing here is using home cursor on really deficient terminals. 190 * Otherwise just use cursor motions, hacking use of tabs and overtabbing 191 * and backspace. 192 * 193 * XXX this needs to be revisited for wide characters since we may output 194 * XXX more than one byte for a character. 195 */ 196 197 static int plodcnt, plodflg; 198 199 static int 200 plodput(c) 201 int c; 202 { 203 if (plodflg) 204 --plodcnt; 205 else 206 __cputchar(c); 207 return (0); 208 } 209 210 static int 211 plod(cnt, in_refresh) 212 int cnt, in_refresh; 213 { 214 int i, j, k, soutcol, soutline; 215 216 #ifdef DEBUG 217 __CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n", 218 cnt, in_refresh); 219 #endif /* DEBUG */ 220 plodcnt = plodflg = cnt; 221 soutcol = outcol; 222 soutline = outline; 223 224 /* 225 * Consider homing and moving down/right from there, vs. moving 226 * directly with local motions to the right spot. 227 */ 228 if (cursor_home) { 229 /* 230 * i is the cost to home and tab/space to the right to get to 231 * the proper column. This assumes nd space costs 1 char. So 232 * i + destcol is cost of motion with home. 233 */ 234 if (__GT) 235 i = (destcol / HARDTABS) + (destcol % HARDTABS); 236 else 237 i = destcol; 238 239 /* j is cost to move locally without homing. */ 240 if (destcol >= outcol) { /* if motion is to the right */ 241 j = destcol / HARDTABS - outcol / HARDTABS; 242 if (__GT && j) 243 j += destcol % HARDTABS; 244 else 245 j = destcol - outcol; 246 } else 247 /* leftward motion only works if we can backspace. */ 248 if (outcol - destcol <= i) 249 /* Cheaper to backspace. */ 250 i = j = outcol - destcol; 251 else 252 /* Impossibly expensive. */ 253 j = i + 1; 254 255 /* k is the absolute value of vertical distance. */ 256 k = outline - destline; 257 if (k < 0) 258 k = -k; 259 j += k; 260 261 /* Decision. We may not have a choice if no up. */ 262 if (i + destline < j || (!cursor_up && destline < outline)) { 263 /* 264 * Cheaper to home. Do it now and pretend it's a 265 * regular local motion. 266 */ 267 tputs(cursor_home, 0, plodput); 268 outcol = outline = 0; 269 } else 270 if (cursor_to_ll) { 271 /* 272 * Quickly consider homing down and moving from 273 * there. Assume cost of ll is 2. 274 */ 275 k = (LINES - 1) - destline; 276 if (i + k + 2 < j && (k <= 0 || cursor_up)) { 277 tputs(cursor_to_ll, 0, plodput); 278 outcol = 0; 279 outline = LINES - 1; 280 } 281 } 282 } else 283 /* No home and no up means it's impossible. */ 284 if (!cursor_up && destline < outline) 285 return (-1); 286 if (__GT) 287 i = destcol % HARDTABS + destcol / HARDTABS; 288 else 289 i = destcol; 290 #ifdef notdef 291 if (back_tab && outcol > destcol && 292 (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) { 293 j *= (k = strlen(back_tab)); 294 if ((k += (destcol & 7)) > 4) 295 j += 8 - (destcol & 7); 296 else 297 j += k; 298 } else 299 #endif 300 j = outcol - destcol; 301 302 /* 303 * If we will later need a \n which will turn into a \r\n by the 304 * system or the terminal, then don't bother to try to \r. 305 */ 306 if ((__NONL || !__pfast) && outline < destline) 307 goto dontcr; 308 309 /* 310 * If the terminal will do a \r\n and there isn't room for it, then 311 * we can't afford a \r. 312 */ 313 if (!carriage_return && outline >= destline) 314 goto dontcr; 315 316 /* 317 * If it will be cheaper, or if we can't back up, then send a return 318 * preliminarily. 319 */ 320 if (j > i + 1 || outcol > destcol) { 321 /* 322 * BUG: this doesn't take the (possibly long) length of cr 323 * into account. 324 */ 325 if (carriage_return) 326 tputs(carriage_return, 0, plodput); 327 else 328 plodput('\r'); 329 if (!carriage_return) { 330 if (cursor_down) 331 tputs(cursor_down, 0, plodput); 332 else 333 plodput('\n'); 334 outline++; 335 } 336 outcol = 0; 337 } 338 dontcr:while (outline < destline) { 339 outline++; 340 if (cursor_down) 341 tputs(cursor_down, 0, plodput); 342 else 343 plodput('\n'); 344 if (plodcnt < 0) 345 goto out; 346 if (__NONL || __pfast == 0) 347 outcol = 0; 348 } 349 if (back_tab) 350 k = (int) strlen(back_tab); 351 while (outcol > destcol) { 352 if (plodcnt < 0) 353 goto out; 354 #ifdef notdef 355 if (back_tab && outcol - destcol > k + 4) { 356 tputs(back_tab, 0, plodput); 357 outcol--; 358 outcol &= ~7; 359 continue; 360 } 361 #endif 362 outcol--; 363 if (cursor_left) 364 tputs(cursor_left, 0, plodput); 365 else 366 plodput('\b'); 367 } 368 while (outline > destline) { 369 outline--; 370 tputs(cursor_up, 0, plodput); 371 if (plodcnt < 0) 372 goto out; 373 } 374 if (__GT && destcol - outcol > 1) { 375 for (;;) { 376 i = tabcol(outcol, HARDTABS); 377 if (i > destcol) 378 break; 379 if (tab) 380 tputs(tab, 0, plodput); 381 else 382 plodput('\t'); 383 outcol = i; 384 } 385 if (destcol - outcol > 4 && i < COLS) { 386 if (tab) 387 tputs(tab, 0, plodput); 388 else 389 plodput('\t'); 390 outcol = i; 391 while (outcol > destcol) { 392 outcol--; 393 if (cursor_left) 394 tputs(cursor_left, 0, plodput); 395 else 396 plodput('\b'); 397 } 398 } 399 } 400 while (outcol < destcol) { 401 /* 402 * Move one char to the right. We don't use nd space because 403 * it's better to just print the char we are moving over. 404 */ 405 if (in_refresh) 406 if (plodflg) /* Avoid a complex calculation. */ 407 plodcnt--; 408 else { 409 #ifndef HAVE_WCHAR 410 i = curscr->alines[outline]->line[outcol].ch 411 & __CHARTEXT; 412 if (curscr->alines[outline]->line[outcol].attr 413 == curscr->wattr) 414 __cputchar(i); 415 #else 416 if ((curscr->alines[outline]->line[outcol].attr 417 & WA_ATTRIBUTES) 418 == curscr->wattr) { 419 switch (WCOL(curscr->alines[outline]->line[outcol])) { 420 case 1: 421 __cputwchar(curscr->alines[outline]->line[outcol].ch); 422 __cursesi_putnsp(curscr->alines[outline]->line[outcol].nsp, 423 outline, 424 outcol); 425 #ifdef DEBUG 426 __CTRACE(__CTRACE_OUTPUT, 427 "plod: (%d,%d)WCOL(%d), " 428 "putwchar(%x)\n", 429 outline, outcol, 430 WCOL(curscr->alines[outline]->line[outcol]), 431 curscr->alines[outline]->line[outcol].ch); 432 #endif /* DEBUG */ 433 /*FALLTHROUGH*/ 434 case 0: 435 break; 436 default: 437 goto nondes; 438 } 439 } 440 #endif /* HAVE_WCHAR */ 441 else 442 goto nondes; 443 } 444 else 445 nondes: if (cursor_right) 446 tputs(cursor_right, 0, plodput); 447 else 448 plodput(' '); 449 outcol++; 450 if (plodcnt < 0) 451 goto out; 452 } 453 454 out: if (plodflg) { 455 outcol = soutcol; 456 outline = soutline; 457 } 458 #ifdef DEBUG 459 __CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt); 460 #endif /* DEBUG */ 461 return (plodcnt); 462 } 463 /* 464 * Return the column number that results from being in column col and 465 * hitting a tab, where tabs are set every ts columns. Work right for 466 * the case where col > COLS, even if ts does not divide COLS. 467 */ 468 static int 469 tabcol(col, ts) 470 int col, ts; 471 { 472 int offset; 473 474 if (col >= COLS) { 475 offset = COLS * (col / COLS); 476 col -= offset; 477 } else 478 offset = 0; 479 return (col + ts - (col % ts) + offset); 480 } 481