cursor.c revision 4419d26b
1/* $XTermId: cursor.c,v 1.82 2022/02/13 18:20:53 tom Exp $ */ 2 3/* 4 * Copyright 2002-2021,2022 by Thomas E. Dickey 5 * 6 * All Rights Reserved 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the 29 * sale, use or other dealings in this Software without prior written 30 * authorization. 31 * 32 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 33 * 34 * All Rights Reserved 35 * 36 * Permission to use, copy, modify, and distribute this software and its 37 * documentation for any purpose and without fee is hereby granted, 38 * provided that the above copyright notice appear in all copies and that 39 * both that copyright notice and this permission notice appear in 40 * supporting documentation, and that the name of Digital Equipment 41 * Corporation not be used in advertising or publicity pertaining to 42 * distribution of the software without specific, written prior permission. 43 * 44 * 45 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 46 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 47 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 48 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 49 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 50 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 51 * SOFTWARE. 52 */ 53 54/* cursor.c */ 55 56#include <xterm.h> 57#include <data.h> 58#include <menu.h> 59 60#include <assert.h> 61 62/* 63 * Moves the cursor to the specified position, checking for bounds. 64 * (this includes scrolling regions) 65 * The origin is considered to be 0, 0 for this procedure. 66 */ 67void 68CursorSet(TScreen *screen, int row, int col, unsigned flags) 69{ 70 int use_row = row; 71 int use_col = col; 72 int max_col = screen->max_col; 73 int max_row = screen->max_row; 74 75 if (flags & ORIGIN) { 76 use_col += screen->lft_marg; 77 max_col = screen->rgt_marg; 78 } 79 use_col = (use_col < 0 ? 0 : use_col); 80 set_cur_col(screen, (use_col <= max_col ? use_col : max_col)); 81 82 if (flags & ORIGIN) { 83 use_row += screen->top_marg; 84 max_row = screen->bot_marg; 85 } 86 use_row = (use_row < 0 ? 0 : use_row); 87 set_cur_row(screen, (use_row <= max_row ? use_row : max_row)); 88 89 ResetWrap(screen); 90 91 TRACE(("CursorSet(%d,%d) margins V[%d..%d] H[%d..%d] -> %d,%d %s\n", 92 row, col, 93 screen->top_marg, 94 screen->bot_marg, 95 screen->lft_marg, 96 screen->rgt_marg, 97 screen->cur_row, 98 screen->cur_col, 99 ((flags & ORIGIN) ? "origin" : "normal"))); 100} 101 102/* 103 * moves the cursor left n, no wrap around 104 */ 105void 106CursorBack(XtermWidget xw, int n) 107{ 108#define WRAP_MASK (REVERSEWRAP | WRAPAROUND) 109 TScreen *screen = TScreenOf(xw); 110 int rev; 111 int left = ScrnLeftMargin(xw); 112 int before = screen->cur_col; 113 114 if ((rev = ((xw->flags & WRAP_MASK) == WRAP_MASK)) != 0 115 && screen->do_wrap) { 116 n--; 117 } 118 119 /* if the cursor is already before the left-margin, we have to let it go */ 120 if (before < left) 121 left = 0; 122 123 if ((screen->cur_col -= n) < left) { 124 if (rev) { 125 int in_row = ScrnRightMargin(xw) - left + 1; 126 int offset = (in_row * screen->cur_row) + screen->cur_col - left; 127 if ((before == left) && 128 ScrnIsColInMargins(screen, before) && 129 ScrnIsRowInMargins(screen, screen->cur_row) && 130 screen->cur_row == screen->top_marg) { 131 offset = (screen->bot_marg + 1) * in_row - 1; 132 } else if (offset < 0) { 133 int length = in_row * MaxRows(screen); 134 offset += ((-offset) / length + 1) * length; 135 } 136 set_cur_row(screen, (offset / in_row)); 137 set_cur_col(screen, (offset % in_row) + left); 138 do_xevents(xw); 139 } else { 140 set_cur_col(screen, left); 141 } 142 } 143 ResetWrap(screen); 144} 145 146/* 147 * moves the cursor forward n, no wraparound 148 */ 149void 150CursorForward(XtermWidget xw, int n) 151{ 152 TScreen *screen = TScreenOf(xw); 153#if OPT_DEC_CHRSET 154 LineData *ld = getLineData(screen, screen->cur_row); 155#endif 156 int next = screen->cur_col + n; 157 int max; 158 159 if (IsLeftRightMode(xw)) { 160 max = screen->rgt_marg; 161 if (screen->cur_col > max) 162 max = screen->max_col; 163 } else { 164 max = LineMaxCol(screen, ld); 165 } 166 167 if (next > max) 168 next = max; 169 170 set_cur_col(screen, next); 171 ResetWrap(screen); 172} 173 174/* 175 * moves the cursor down n, no scrolling. 176 * Won't pass bottom margin or bottom of screen. 177 */ 178void 179CursorDown(TScreen *screen, int n) 180{ 181 int max; 182 int next = screen->cur_row + n; 183 184 max = (screen->cur_row > screen->bot_marg ? 185 screen->max_row : screen->bot_marg); 186 if (next > max) 187 next = max; 188 if (next > screen->max_row) 189 next = screen->max_row; 190 191 set_cur_row(screen, next); 192 ResetWrap(screen); 193} 194 195/* 196 * moves the cursor up n, no linestarving. 197 * Won't pass top margin or top of screen. 198 */ 199void 200CursorUp(TScreen *screen, int n) 201{ 202 int min; 203 int next = screen->cur_row - n; 204 205 min = ((screen->cur_row < screen->top_marg) 206 ? 0 207 : screen->top_marg); 208 if (next < min) 209 next = min; 210 if (next < 0) 211 next = 0; 212 213 set_cur_row(screen, next); 214 ResetWrap(screen); 215} 216 217/* 218 * Moves cursor down amount lines, scrolls if necessary. 219 * Won't leave scrolling region. No carriage return. 220 */ 221void 222xtermIndex(XtermWidget xw, int amount) 223{ 224 TScreen *screen = TScreenOf(xw); 225 226 /* 227 * indexing when below scrolling region is cursor down. 228 * if cursor high enough, no scrolling necessary. 229 */ 230 if (screen->cur_row > screen->bot_marg 231 || screen->cur_row + amount <= screen->bot_marg 232 || (IsLeftRightMode(xw) 233 && !ScrnIsColInMargins(screen, screen->cur_col))) { 234 CursorDown(screen, amount); 235 } else { 236 int j; 237 CursorDown(screen, j = screen->bot_marg - screen->cur_row); 238 xtermScroll(xw, amount - j); 239 } 240} 241 242/* 243 * Moves cursor up amount lines, reverse scrolls if necessary. 244 * Won't leave scrolling region. No carriage return. 245 */ 246void 247RevIndex(XtermWidget xw, int amount) 248{ 249 TScreen *screen = TScreenOf(xw); 250 251 /* 252 * reverse indexing when above scrolling region is cursor up. 253 * if cursor low enough, no reverse indexing needed 254 */ 255 if (screen->cur_row < screen->top_marg 256 || screen->cur_row - amount >= screen->top_marg 257 || (IsLeftRightMode(xw) 258 && !ScrnIsColInMargins(screen, screen->cur_col))) { 259 CursorUp(screen, amount); 260 } else { 261 RevScroll(xw, amount - (screen->cur_row - screen->top_marg)); 262 CursorUp(screen, screen->cur_row - screen->top_marg); 263 } 264} 265 266/* 267 * Moves Cursor To First Column In Line 268 * (Note: xterm doesn't implement SLH, SLL which would affect use of this) 269 */ 270void 271CarriageReturn(XtermWidget xw) 272{ 273 TScreen *screen = TScreenOf(xw); 274 int left = ScrnLeftMargin(xw); 275 int col; 276 277 if (xw->flags & ORIGIN) { 278 col = left; 279 } else if (screen->cur_col >= left) { 280 col = left; 281 } else { 282 /* 283 * If origin-mode is not active, it is possible to use cursor 284 * addressing outside the margins. In that case we will go to the 285 * first column rather than following the margin. 286 */ 287 col = 0; 288 } 289 290 set_cur_col(screen, col); 291 ResetWrap(screen); 292 do_xevents(xw); 293} 294 295/* 296 * When resizing the window, if we're showing the alternate screen, we still 297 * have to adjust the saved cursor from the normal screen to account for 298 * shifting of the saved-line region in/out of the viewable window. 299 */ 300void 301AdjustSavedCursor(XtermWidget xw, int adjust) 302{ 303 TScreen *screen = TScreenOf(xw); 304 305 if (screen->whichBuf) { 306 SavedCursor *sc = &screen->sc[0]; 307 308 if (adjust > 0) { 309 TRACE(("AdjustSavedCursor %d -> %d\n", sc->row, sc->row - adjust)); 310 sc->row += adjust; 311 } 312 } 313} 314 315/* 316 * Save Cursor and Attributes 317 */ 318void 319CursorSave2(XtermWidget xw, SavedCursor * sc) 320{ 321 TScreen *screen = TScreenOf(xw); 322 323 sc->saved = True; 324 sc->row = screen->cur_row; 325 sc->col = screen->cur_col; 326 sc->flags = xw->flags; 327 sc->curgl = screen->curgl; 328 sc->curgr = screen->curgr; 329 sc->wrap_flag = screen->do_wrap; 330#if OPT_ISO_COLORS 331 sc->cur_foreground = xw->cur_foreground; 332 sc->cur_background = xw->cur_background; 333 sc->sgr_foreground = xw->sgr_foreground; 334 sc->sgr_38_xcolors = xw->sgr_38_xcolors; 335#endif 336 saveCharsets(screen, sc->gsets); 337} 338 339void 340CursorSave(XtermWidget xw) 341{ 342 TScreen *screen = TScreenOf(xw); 343 CursorSave2(xw, &screen->sc[screen->whichBuf]); 344} 345 346/* 347 * We save/restore all visible attributes, plus wrapping, origin mode, and the 348 * selective erase attribute. 349 * 350 * This is documented, but some of the documentation is incorrect. 351 * 352 * Page 270 of the VT420 manual (2nd edition) says that DECSC saves these 353 * items: 354 * 355 * Cursor position 356 * * Character attributes set by the SGR command 357 * * Character sets (G0, G1, G2, or G3) currently in GL and GR 358 * * Wrap flag (autowrap or no autowrap) 359 * * State of origin mode (DECOM) 360 * * Selective erase attribute 361 * * Any single shift 2 (SS2) or single shift 3 (SS3) functions sent 362 * 363 * The VT520 manual has the same information (page 5-120). 364 * 365 * However, DEC 070 (29-June-1990), pages 5-186 to 5-191, describes 366 * save/restore operations, but makes no mention of "wrap". 367 * 368 * Mattias Engdegård, who has investigated wrapping behavior of different 369 * terminals, 370 * 371 * https://github.com/mattiase/wraptest 372 * 373 * states 374 * The LCF is saved/restored by the Save/Restore Cursor (DECSC/DECRC) 375 * control sequences. The DECAWM flag is not included in the state 376 * managed by these operations. 377 * 378 * DEC 070 does mention the ANSI color text extension saying that it, too, is 379 * saved/restored. 380 */ 381#define DECSC_FLAGS (ATTRIBUTES|ORIGIN|PROTECTED) 382 383/* 384 * Restore Cursor and Attributes 385 */ 386void 387CursorRestore2(XtermWidget xw, SavedCursor * sc) 388{ 389 TScreen *screen = TScreenOf(xw); 390 391 /* Restore the character sets, unless we never did a save-cursor op. 392 * In that case, we'll reset the character sets. 393 */ 394 if (sc->saved) { 395 restoreCharsets(screen, sc->gsets); 396 screen->curgl = sc->curgl; 397 screen->curgr = sc->curgr; 398 } else { 399 resetCharsets(screen); 400 } 401 402 UIntClr(xw->flags, DECSC_FLAGS); 403 UIntSet(xw->flags, sc->flags & DECSC_FLAGS); 404 if ((xw->flags & ORIGIN)) { 405 CursorSet(screen, 406 sc->row - screen->top_marg, 407 ((xw->flags & LEFT_RIGHT) 408 ? sc->col - screen->lft_marg 409 : sc->col), 410 xw->flags); 411 } else { 412 CursorSet(screen, sc->row, sc->col, xw->flags); 413 } 414 screen->do_wrap = sc->wrap_flag; /* after CursorSet/ResetWrap */ 415 416#if OPT_ISO_COLORS 417 xw->sgr_foreground = sc->sgr_foreground; 418 xw->sgr_38_xcolors = sc->sgr_38_xcolors; 419 SGR_Foreground(xw, (xw->flags & FG_COLOR) ? sc->cur_foreground : -1); 420 SGR_Background(xw, (xw->flags & BG_COLOR) ? sc->cur_background : -1); 421#endif 422} 423 424void 425CursorRestore(XtermWidget xw) 426{ 427 TScreen *screen = TScreenOf(xw); 428 CursorRestore2(xw, &screen->sc[screen->whichBuf]); 429} 430 431/* 432 * Move the cursor to the first column of the n-th next line. 433 */ 434void 435CursorNextLine(XtermWidget xw, int count) 436{ 437 TScreen *screen = TScreenOf(xw); 438 439 CursorDown(screen, count < 1 ? 1 : count); 440 CarriageReturn(xw); 441 do_xevents(xw); 442} 443 444/* 445 * Move the cursor to the first column of the n-th previous line. 446 */ 447void 448CursorPrevLine(XtermWidget xw, int count) 449{ 450 TScreen *screen = TScreenOf(xw); 451 452 CursorUp(screen, count < 1 ? 1 : count); 453 CarriageReturn(xw); 454 do_xevents(xw); 455} 456 457/* 458 * Return col/row values which can be passed to CursorSet() preserving the 459 * current col/row, e.g., accounting for DECOM. 460 */ 461int 462CursorCol(XtermWidget xw) 463{ 464 TScreen *screen = TScreenOf(xw); 465 int result = screen->cur_col; 466 if (xw->flags & ORIGIN) { 467 result -= ScrnLeftMargin(xw); 468 if (result < 0) 469 result = 0; 470 } 471 return result; 472} 473 474int 475CursorRow(XtermWidget xw) 476{ 477 TScreen *screen = TScreenOf(xw); 478 int result = screen->cur_row; 479 if (xw->flags & ORIGIN) { 480 result -= screen->top_marg; 481 if (result < 0) 482 result = 0; 483 } 484 return result; 485} 486 487#if OPT_TRACE 488int 489set_cur_row(TScreen *screen, int value) 490{ 491 TRACE(("set_cur_row %d vs %d\n", value, screen ? LastRowNumber(screen) : -1)); 492 493 assert(screen != 0); 494 assert(value >= 0); 495 assert(value <= LastRowNumber(screen)); 496 if_STATUS_LINE(screen, { 497 value = LastRowNumber(screen); 498 }); 499 screen->cur_row = value; 500 return value; 501} 502 503int 504set_cur_col(TScreen *screen, int value) 505{ 506 TRACE(("set_cur_col %d vs %d\n", value, screen ? screen->max_col : -1)); 507 508 assert(screen != 0); 509 assert(value >= 0); 510 assert(value <= screen->max_col); 511 screen->cur_col = value; 512 return value; 513} 514#endif /* OPT_TRACE */ 515/* 516 * vile:cmode fk=utf-8 517 */ 518