1 /* $XTermId: cursor.c,v 1.77 2019/07/12 01:11:59 tom Exp $ */
2 
3 /*
4  * Copyright 2002-2018,2019 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  */
67 void
CursorSet(TScreen * screen,int row,int col,unsigned flags)68 CursorSet(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  */
105 void
CursorBack(XtermWidget xw,int n)106 CursorBack(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  */
149 void
CursorForward(XtermWidget xw,int n)150 CursorForward(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  */
178 void
CursorDown(TScreen * screen,int n)179 CursorDown(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  */
199 void
CursorUp(TScreen * screen,int n)200 CursorUp(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  */
221 void
xtermIndex(XtermWidget xw,int amount)222 xtermIndex(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  */
246 void
RevIndex(XtermWidget xw,int amount)247 RevIndex(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  */
270 void
CarriageReturn(XtermWidget xw)271 CarriageReturn(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  */
300 void
AdjustSavedCursor(XtermWidget xw,int adjust)301 AdjustSavedCursor(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  */
318 void
CursorSave(XtermWidget xw)319 CursorSave(XtermWidget xw)
320 {
321     TScreen *screen = TScreenOf(xw);
322     SavedCursor *sc = &screen->sc[screen->whichBuf];
323 
324     sc->saved = True;
325     sc->row = screen->cur_row;
326     sc->col = screen->cur_col;
327     sc->flags = xw->flags;
328     sc->curgl = screen->curgl;
329     sc->curgr = screen->curgr;
330     sc->wrap_flag = screen->do_wrap;
331 #if OPT_ISO_COLORS
332     sc->cur_foreground = xw->cur_foreground;
333     sc->cur_background = xw->cur_background;
334     sc->sgr_foreground = xw->sgr_foreground;
335     sc->sgr_38_xcolors = xw->sgr_38_xcolors;
336 #endif
337     saveCharsets(screen, sc->gsets);
338 }
339 
340 /*
341  * We save/restore all visible attributes, plus wrapping, origin mode, and the
342  * selective erase attribute.
343  */
344 #define DECSC_FLAGS (ATTRIBUTES|ORIGIN|PROTECTED)
345 
346 /*
347  * Restore Cursor and Attributes
348  */
349 void
CursorRestore(XtermWidget xw)350 CursorRestore(XtermWidget xw)
351 {
352     TScreen *screen = TScreenOf(xw);
353     SavedCursor *sc = &screen->sc[screen->whichBuf];
354 
355     /* Restore the character sets, unless we never did a save-cursor op.
356      * In that case, we'll reset the character sets.
357      */
358     if (sc->saved) {
359 	restoreCharsets(screen, sc->gsets);
360 	screen->curgl = sc->curgl;
361 	screen->curgr = sc->curgr;
362     } else {
363 	resetCharsets(screen);
364     }
365 
366     UIntClr(xw->flags, DECSC_FLAGS);
367     UIntSet(xw->flags, sc->flags & DECSC_FLAGS);
368     if ((xw->flags & ORIGIN)) {
369 	CursorSet(screen,
370 		  sc->row - screen->top_marg,
371 		  ((xw->flags & LEFT_RIGHT)
372 		   ? sc->col - screen->lft_marg
373 		   : sc->col),
374 		  xw->flags);
375     } else {
376 	CursorSet(screen, sc->row, sc->col, xw->flags);
377     }
378     screen->do_wrap = sc->wrap_flag;	/* after CursorSet/ResetWrap */
379 
380 #if OPT_ISO_COLORS
381     xw->sgr_foreground = sc->sgr_foreground;
382     xw->sgr_38_xcolors = sc->sgr_38_xcolors;
383     SGR_Foreground(xw, (xw->flags & FG_COLOR) ? sc->cur_foreground : -1);
384     SGR_Background(xw, (xw->flags & BG_COLOR) ? sc->cur_background : -1);
385 #endif
386 }
387 
388 /*
389  * Move the cursor to the first column of the n-th next line.
390  */
391 void
CursorNextLine(XtermWidget xw,int count)392 CursorNextLine(XtermWidget xw, int count)
393 {
394     TScreen *screen = TScreenOf(xw);
395 
396     CursorDown(screen, count < 1 ? 1 : count);
397     CarriageReturn(xw);
398     do_xevents(xw);
399 }
400 
401 /*
402  * Move the cursor to the first column of the n-th previous line.
403  */
404 void
CursorPrevLine(XtermWidget xw,int count)405 CursorPrevLine(XtermWidget xw, int count)
406 {
407     TScreen *screen = TScreenOf(xw);
408 
409     CursorUp(screen, count < 1 ? 1 : count);
410     CarriageReturn(xw);
411     do_xevents(xw);
412 }
413 
414 /*
415  * Return col/row values which can be passed to CursorSet() preserving the
416  * current col/row, e.g., accounting for DECOM.
417  */
418 int
CursorCol(XtermWidget xw)419 CursorCol(XtermWidget xw)
420 {
421     TScreen *screen = TScreenOf(xw);
422     int result = screen->cur_col;
423     if (xw->flags & ORIGIN) {
424 	result -= ScrnLeftMargin(xw);
425 	if (result < 0)
426 	    result = 0;
427     }
428     return result;
429 }
430 
431 int
CursorRow(XtermWidget xw)432 CursorRow(XtermWidget xw)
433 {
434     TScreen *screen = TScreenOf(xw);
435     int result = screen->cur_row;
436     if (xw->flags & ORIGIN) {
437 	result -= screen->top_marg;
438 	if (result < 0)
439 	    result = 0;
440     }
441     return result;
442 }
443 
444 #if OPT_TRACE
445 int
set_cur_row(TScreen * screen,int value)446 set_cur_row(TScreen *screen, int value)
447 {
448     TRACE(("set_cur_row %d vs %d\n", value, screen ? screen->max_row : -1));
449 
450     assert(screen != 0);
451     assert(value >= 0);
452     assert(value <= screen->max_row);
453     screen->cur_row = value;
454     return value;
455 }
456 
457 int
set_cur_col(TScreen * screen,int value)458 set_cur_col(TScreen *screen, int value)
459 {
460     TRACE(("set_cur_col %d vs %d\n", value, screen ? screen->max_col : -1));
461 
462     assert(screen != 0);
463     assert(value >= 0);
464     assert(value <= screen->max_col);
465     screen->cur_col = value;
466     return value;
467 }
468 #endif /* OPT_TRACE */
469