1 |
|
|
/* $OpenBSD: lib_mvcur.c,v 1.13 2010/01/12 23:22:07 nicm Exp $ */ |
2 |
|
|
|
3 |
|
|
/**************************************************************************** |
4 |
|
|
* Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * |
5 |
|
|
* * |
6 |
|
|
* Permission is hereby granted, free of charge, to any person obtaining a * |
7 |
|
|
* copy of this software and associated documentation files (the * |
8 |
|
|
* "Software"), to deal in the Software without restriction, including * |
9 |
|
|
* without limitation the rights to use, copy, modify, merge, publish, * |
10 |
|
|
* distribute, distribute with modifications, sublicense, and/or sell * |
11 |
|
|
* copies of the Software, and to permit persons to whom the Software is * |
12 |
|
|
* furnished to do so, subject to the following conditions: * |
13 |
|
|
* * |
14 |
|
|
* The above copyright notice and this permission notice shall be included * |
15 |
|
|
* in all copies or substantial portions of the Software. * |
16 |
|
|
* * |
17 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * |
18 |
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * |
19 |
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * |
20 |
|
|
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * |
21 |
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * |
22 |
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * |
23 |
|
|
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. * |
24 |
|
|
* * |
25 |
|
|
* Except as contained in this notice, the name(s) of the above copyright * |
26 |
|
|
* holders shall not be used in advertising or otherwise to promote the * |
27 |
|
|
* sale, use or other dealings in this Software without prior written * |
28 |
|
|
* authorization. * |
29 |
|
|
****************************************************************************/ |
30 |
|
|
|
31 |
|
|
/**************************************************************************** |
32 |
|
|
* Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * |
33 |
|
|
* and: Eric S. Raymond <esr@snark.thyrsus.com> * |
34 |
|
|
* and: Thomas E. Dickey 1996-on * |
35 |
|
|
****************************************************************************/ |
36 |
|
|
|
37 |
|
|
/* |
38 |
|
|
** lib_mvcur.c |
39 |
|
|
** |
40 |
|
|
** The routines for moving the physical cursor and scrolling: |
41 |
|
|
** |
42 |
|
|
** void _nc_mvcur_init(void) |
43 |
|
|
** |
44 |
|
|
** void _nc_mvcur_resume(void) |
45 |
|
|
** |
46 |
|
|
** int mvcur(int old_y, int old_x, int new_y, int new_x) |
47 |
|
|
** |
48 |
|
|
** void _nc_mvcur_wrap(void) |
49 |
|
|
** |
50 |
|
|
** Comparisons with older movement optimizers: |
51 |
|
|
** SVr3 curses mvcur() can't use cursor_to_ll or auto_left_margin. |
52 |
|
|
** 4.4BSD curses can't use cuu/cud/cuf/cub/hpa/vpa/tab/cbt for local |
53 |
|
|
** motions. It doesn't use tactics based on auto_left_margin. Weirdly |
54 |
|
|
** enough, it doesn't use its own hardware-scrolling routine to scroll up |
55 |
|
|
** destination lines for out-of-bounds addresses! |
56 |
|
|
** old ncurses optimizer: less accurate cost computations (in fact, |
57 |
|
|
** it was broken and had to be commented out!). |
58 |
|
|
** |
59 |
|
|
** Compile with -DMAIN to build an interactive tester/timer for the movement |
60 |
|
|
** optimizer. You can use it to investigate the optimizer's behavior. |
61 |
|
|
** You can also use it for tuning the formulas used to determine whether |
62 |
|
|
** or not full optimization is attempted. |
63 |
|
|
** |
64 |
|
|
** This code has a nasty tendency to find bugs in terminfo entries, because it |
65 |
|
|
** exercises the non-cup movement capabilities heavily. If you think you've |
66 |
|
|
** found a bug, try deleting subsets of the following capabilities (arranged |
67 |
|
|
** in decreasing order of suspiciousness): it, tab, cbt, hpa, vpa, cuu, cud, |
68 |
|
|
** cuf, cub, cuu1, cud1, cuf1, cub1. It may be that one or more are wrong. |
69 |
|
|
** |
70 |
|
|
** Note: you should expect this code to look like a resource hog in a profile. |
71 |
|
|
** That's because it does a lot of I/O, through the tputs() calls. The I/O |
72 |
|
|
** cost swamps the computation overhead (and as machines get faster, this |
73 |
|
|
** will become even more true). Comments in the test exerciser at the end |
74 |
|
|
** go into detail about tuning and how you can gauge the optimizer's |
75 |
|
|
** effectiveness. |
76 |
|
|
**/ |
77 |
|
|
|
78 |
|
|
/**************************************************************************** |
79 |
|
|
* |
80 |
|
|
* Constants and macros for optimizer tuning. |
81 |
|
|
* |
82 |
|
|
****************************************************************************/ |
83 |
|
|
|
84 |
|
|
/* |
85 |
|
|
* The average overhead of a full optimization computation in character |
86 |
|
|
* transmission times. If it's too high, the algorithm will be a bit |
87 |
|
|
* over-biased toward using cup rather than local motions; if it's too |
88 |
|
|
* low, the algorithm may spend more time than is strictly optimal |
89 |
|
|
* looking for non-cup motions. Profile the optimizer using the `t' |
90 |
|
|
* command of the exerciser (see below), and round to the nearest integer. |
91 |
|
|
* |
92 |
|
|
* Yes, I (esr) thought about computing expected overhead dynamically, say |
93 |
|
|
* by derivation from a running average of optimizer times. But the |
94 |
|
|
* whole point of this optimization is to *decrease* the frequency of |
95 |
|
|
* system calls. :-) |
96 |
|
|
*/ |
97 |
|
|
#define COMPUTE_OVERHEAD 1 /* I use a 90MHz Pentium @ 9.6Kbps */ |
98 |
|
|
|
99 |
|
|
/* |
100 |
|
|
* LONG_DIST is the distance we consider to be just as costly to move over as a |
101 |
|
|
* cup sequence is to emit. In other words, it's the length of a cup sequence |
102 |
|
|
* adjusted for average computation overhead. The magic number is the length |
103 |
|
|
* of "\033[yy;xxH", the typical cup sequence these days. |
104 |
|
|
*/ |
105 |
|
|
#define LONG_DIST (8 - COMPUTE_OVERHEAD) |
106 |
|
|
|
107 |
|
|
/* |
108 |
|
|
* Tell whether a motion is optimizable by local motions. Needs to be cheap to |
109 |
|
|
* compute. In general, all the fast moves go to either the right or left edge |
110 |
|
|
* of the screen. So any motion to a location that is (a) further away than |
111 |
|
|
* LONG_DIST and (b) further inward from the right or left edge than LONG_DIST, |
112 |
|
|
* we'll consider nonlocal. |
113 |
|
|
*/ |
114 |
|
|
#define NOT_LOCAL(fy, fx, ty, tx) ((tx > LONG_DIST) \ |
115 |
|
|
&& (tx < screen_columns - 1 - LONG_DIST) \ |
116 |
|
|
&& (abs(ty-fy) + abs(tx-fx) > LONG_DIST)) |
117 |
|
|
|
118 |
|
|
/**************************************************************************** |
119 |
|
|
* |
120 |
|
|
* External interfaces |
121 |
|
|
* |
122 |
|
|
****************************************************************************/ |
123 |
|
|
|
124 |
|
|
/* |
125 |
|
|
* For this code to work OK, the following components must live in the |
126 |
|
|
* screen structure: |
127 |
|
|
* |
128 |
|
|
* int _char_padding; // cost of character put |
129 |
|
|
* int _cr_cost; // cost of (carriage_return) |
130 |
|
|
* int _cup_cost; // cost of (cursor_address) |
131 |
|
|
* int _home_cost; // cost of (cursor_home) |
132 |
|
|
* int _ll_cost; // cost of (cursor_to_ll) |
133 |
|
|
*#if USE_HARD_TABS |
134 |
|
|
* int _ht_cost; // cost of (tab) |
135 |
|
|
* int _cbt_cost; // cost of (back_tab) |
136 |
|
|
*#endif USE_HARD_TABS |
137 |
|
|
* int _cub1_cost; // cost of (cursor_left) |
138 |
|
|
* int _cuf1_cost; // cost of (cursor_right) |
139 |
|
|
* int _cud1_cost; // cost of (cursor_down) |
140 |
|
|
* int _cuu1_cost; // cost of (cursor_up) |
141 |
|
|
* int _cub_cost; // cost of (parm_cursor_left) |
142 |
|
|
* int _cuf_cost; // cost of (parm_cursor_right) |
143 |
|
|
* int _cud_cost; // cost of (parm_cursor_down) |
144 |
|
|
* int _cuu_cost; // cost of (parm_cursor_up) |
145 |
|
|
* int _hpa_cost; // cost of (column_address) |
146 |
|
|
* int _vpa_cost; // cost of (row_address) |
147 |
|
|
* int _ech_cost; // cost of (erase_chars) |
148 |
|
|
* int _rep_cost; // cost of (repeat_char) |
149 |
|
|
* |
150 |
|
|
* The USE_HARD_TABS switch controls whether it is reliable to use tab/backtabs |
151 |
|
|
* for local motions. On many systems, it's not, due to uncertainties about |
152 |
|
|
* tab delays and whether or not tabs will be expanded in raw mode. If you |
153 |
|
|
* have parm_right_cursor, tab motions don't win you a lot anyhow. |
154 |
|
|
*/ |
155 |
|
|
|
156 |
|
|
#include <curses.priv.h> |
157 |
|
|
#include <term.h> |
158 |
|
|
#include <ctype.h> |
159 |
|
|
|
160 |
|
|
MODULE_ID("$Id: lib_mvcur.c,v 1.13 2010/01/12 23:22:07 nicm Exp $") |
161 |
|
|
|
162 |
|
|
#define WANT_CHAR(y, x) SP->_newscr->_line[y].text[x] /* desired state */ |
163 |
|
|
#define BAUDRATE cur_term->_baudrate /* bits per second */ |
164 |
|
|
|
165 |
|
|
#if defined(MAIN) || defined(NCURSES_TEST) |
166 |
|
|
#include <sys/time.h> |
167 |
|
|
|
168 |
|
|
static bool profiling = FALSE; |
169 |
|
|
static float diff; |
170 |
|
|
#endif /* MAIN */ |
171 |
|
|
|
172 |
|
|
#define OPT_SIZE 512 |
173 |
|
|
|
174 |
|
|
static int normalized_cost(const char *const cap, int affcnt); |
175 |
|
|
|
176 |
|
|
/**************************************************************************** |
177 |
|
|
* |
178 |
|
|
* Initialization/wrapup (including cost pre-computation) |
179 |
|
|
* |
180 |
|
|
****************************************************************************/ |
181 |
|
|
|
182 |
|
|
#ifdef TRACE |
183 |
|
|
static int |
184 |
|
|
trace_cost_of(const char *capname, const char *cap, int affcnt) |
185 |
|
|
{ |
186 |
|
|
int result = _nc_msec_cost(cap, affcnt); |
187 |
|
|
TR(TRACE_CHARPUT | TRACE_MOVE, |
188 |
|
|
("CostOf %s %d %s", capname, result, _nc_visbuf(cap))); |
189 |
|
|
return result; |
190 |
|
|
} |
191 |
|
|
#define CostOf(cap,affcnt) trace_cost_of(#cap,cap,affcnt); |
192 |
|
|
|
193 |
|
|
static int |
194 |
|
|
trace_normalized_cost(const char *capname, const char *cap, int affcnt) |
195 |
|
|
{ |
196 |
|
|
int result = normalized_cost(cap, affcnt); |
197 |
|
|
TR(TRACE_CHARPUT | TRACE_MOVE, |
198 |
|
|
("NormalizedCost %s %d %s", capname, result, _nc_visbuf(cap))); |
199 |
|
|
return result; |
200 |
|
|
} |
201 |
|
|
#define NormalizedCost(cap,affcnt) trace_normalized_cost(#cap,cap,affcnt); |
202 |
|
|
|
203 |
|
|
#else |
204 |
|
|
|
205 |
|
|
#define CostOf(cap,affcnt) _nc_msec_cost(cap,affcnt); |
206 |
|
|
#define NormalizedCost(cap,affcnt) normalized_cost(cap,affcnt); |
207 |
|
|
|
208 |
|
|
#endif |
209 |
|
|
|
210 |
|
|
NCURSES_EXPORT(int) |
211 |
|
|
_nc_msec_cost(const char *const cap, int affcnt) |
212 |
|
|
/* compute the cost of a given operation */ |
213 |
|
|
{ |
214 |
|
|
if (cap == 0) |
215 |
|
|
return (INFINITY); |
216 |
|
|
else { |
217 |
|
|
const char *cp; |
218 |
|
|
float cum_cost = 0.0; |
219 |
|
|
|
220 |
|
|
for (cp = cap; *cp; cp++) { |
221 |
|
|
/* extract padding, either mandatory or required */ |
222 |
|
|
if (cp[0] == '$' && cp[1] == '<' && strchr(cp, '>')) { |
223 |
|
|
float number = 0.0; |
224 |
|
|
|
225 |
|
|
for (cp += 2; *cp != '>'; cp++) { |
226 |
|
|
if (isdigit(UChar(*cp))) |
227 |
|
|
number = number * 10 + (*cp - '0'); |
228 |
|
|
else if (*cp == '*') |
229 |
|
|
number *= affcnt; |
230 |
|
|
else if (*cp == '.' && (*++cp != '>') && isdigit(UChar(*cp))) |
231 |
|
|
number += (*cp - '0') / 10.0; |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
#if NCURSES_NO_PADDING |
235 |
|
|
if (!GetNoPadding(SP)) |
236 |
|
|
#endif |
237 |
|
|
cum_cost += number * 10; |
238 |
|
|
} else |
239 |
|
|
cum_cost += SP->_char_padding; |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
return ((int) cum_cost); |
243 |
|
|
} |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
static int |
247 |
|
|
normalized_cost(const char *const cap, int affcnt) |
248 |
|
|
/* compute the effective character-count for an operation (round up) */ |
249 |
|
|
{ |
250 |
|
|
int cost = _nc_msec_cost(cap, affcnt); |
251 |
|
|
if (cost != INFINITY) |
252 |
|
|
cost = (cost + SP->_char_padding - 1) / SP->_char_padding; |
253 |
|
|
return cost; |
254 |
|
|
} |
255 |
|
|
|
256 |
|
|
static void |
257 |
|
|
reset_scroll_region(void) |
258 |
|
|
/* Set the scroll-region to a known state (the default) */ |
259 |
|
|
{ |
260 |
|
|
if (change_scroll_region) { |
261 |
|
|
TPUTS_TRACE("change_scroll_region"); |
262 |
|
|
putp(TPARM_2(change_scroll_region, 0, screen_lines - 1)); |
263 |
|
|
} |
264 |
|
|
} |
265 |
|
|
|
266 |
|
|
NCURSES_EXPORT(void) |
267 |
|
|
_nc_mvcur_resume(void) |
268 |
|
|
/* what to do at initialization time and after each shellout */ |
269 |
|
|
{ |
270 |
|
|
/* initialize screen for cursor access */ |
271 |
|
|
if (enter_ca_mode) { |
272 |
|
|
TPUTS_TRACE("enter_ca_mode"); |
273 |
|
|
putp(enter_ca_mode); |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
/* |
277 |
|
|
* Doing this here rather than in _nc_mvcur_wrap() ensures that |
278 |
|
|
* ncurses programs will see a reset scroll region even if a |
279 |
|
|
* program that messed with it died ungracefully. |
280 |
|
|
* |
281 |
|
|
* This also undoes the effects of terminal init strings that assume |
282 |
|
|
* they know the screen size. This is useful when you're running |
283 |
|
|
* a vt100 emulation through xterm. |
284 |
|
|
*/ |
285 |
|
|
reset_scroll_region(); |
286 |
|
|
SP->_cursrow = SP->_curscol = -1; |
287 |
|
|
|
288 |
|
|
/* restore cursor shape */ |
289 |
|
|
if (SP->_cursor != -1) { |
290 |
|
|
int cursor = SP->_cursor; |
291 |
|
|
SP->_cursor = -1; |
292 |
|
|
curs_set(cursor); |
293 |
|
|
} |
294 |
|
|
} |
295 |
|
|
|
296 |
|
|
NCURSES_EXPORT(void) |
297 |
|
|
_nc_mvcur_init(void) |
298 |
|
|
/* initialize the cost structure */ |
299 |
|
|
{ |
300 |
|
|
if (isatty(fileno(SP->_ofp))) |
301 |
|
|
SP->_char_padding = ((BAUDBYTE * 1000 * 10) |
302 |
|
|
/ (BAUDRATE > 0 ? BAUDRATE : 9600)); |
303 |
|
|
else |
304 |
|
|
SP->_char_padding = 1; /* must be nonzero */ |
305 |
|
|
if (SP->_char_padding <= 0) |
306 |
|
|
SP->_char_padding = 1; /* must be nonzero */ |
307 |
|
|
TR(TRACE_CHARPUT | TRACE_MOVE, ("char_padding %d msecs", SP->_char_padding)); |
308 |
|
|
|
309 |
|
|
/* non-parameterized local-motion strings */ |
310 |
|
|
SP->_cr_cost = CostOf(carriage_return, 0); |
311 |
|
|
SP->_home_cost = CostOf(cursor_home, 0); |
312 |
|
|
SP->_ll_cost = CostOf(cursor_to_ll, 0); |
313 |
|
|
#if USE_HARD_TABS |
314 |
|
|
if (getenv("NCURSES_NO_HARD_TABS") == 0) { |
315 |
|
|
SP->_ht_cost = CostOf(tab, 0); |
316 |
|
|
SP->_cbt_cost = CostOf(back_tab, 0); |
317 |
|
|
} else { |
318 |
|
|
SP->_ht_cost = INFINITY; |
319 |
|
|
SP->_cbt_cost = INFINITY; |
320 |
|
|
} |
321 |
|
|
#endif /* USE_HARD_TABS */ |
322 |
|
|
SP->_cub1_cost = CostOf(cursor_left, 0); |
323 |
|
|
SP->_cuf1_cost = CostOf(cursor_right, 0); |
324 |
|
|
SP->_cud1_cost = CostOf(cursor_down, 0); |
325 |
|
|
SP->_cuu1_cost = CostOf(cursor_up, 0); |
326 |
|
|
|
327 |
|
|
SP->_smir_cost = CostOf(enter_insert_mode, 0); |
328 |
|
|
SP->_rmir_cost = CostOf(exit_insert_mode, 0); |
329 |
|
|
SP->_ip_cost = 0; |
330 |
|
|
if (insert_padding) { |
331 |
|
|
SP->_ip_cost = CostOf(insert_padding, 0); |
332 |
|
|
} |
333 |
|
|
|
334 |
|
|
/* |
335 |
|
|
* Assumption: if the terminal has memory_relative addressing, the |
336 |
|
|
* initialization strings or smcup will set single-page mode so we |
337 |
|
|
* can treat it like absolute screen addressing. This seems to be true |
338 |
|
|
* for all cursor_mem_address terminal types in the terminfo database. |
339 |
|
|
*/ |
340 |
|
|
SP->_address_cursor = cursor_address ? cursor_address : cursor_mem_address; |
341 |
|
|
|
342 |
|
|
/* |
343 |
|
|
* Parametrized local-motion strings. This static cost computation |
344 |
|
|
* depends on the following assumptions: |
345 |
|
|
* |
346 |
|
|
* (1) They never have * padding. In the entire master terminfo database |
347 |
|
|
* as of March 1995, only the obsolete Zenith Z-100 pc violates this. |
348 |
|
|
* (Proportional padding is found mainly in insert, delete and scroll |
349 |
|
|
* capabilities). |
350 |
|
|
* |
351 |
|
|
* (2) The average case of cup has two two-digit parameters. Strictly, |
352 |
|
|
* the average case for a 24 * 80 screen has ((10*10*(1 + 1)) + |
353 |
|
|
* (14*10*(1 + 2)) + (10*70*(2 + 1)) + (14*70*4)) / (24*80) = 3.458 |
354 |
|
|
* digits of parameters. On a 25x80 screen the average is 3.6197. |
355 |
|
|
* On larger screens the value gets much closer to 4. |
356 |
|
|
* |
357 |
|
|
* (3) The average case of cub/cuf/hpa/ech/rep has 2 digits of parameters |
358 |
|
|
* (strictly, (((10 * 1) + (70 * 2)) / 80) = 1.8750). |
359 |
|
|
* |
360 |
|
|
* (4) The average case of cud/cuu/vpa has 2 digits of parameters |
361 |
|
|
* (strictly, (((10 * 1) + (14 * 2)) / 24) = 1.5833). |
362 |
|
|
* |
363 |
|
|
* All these averages depend on the assumption that all parameter values |
364 |
|
|
* are equally probable. |
365 |
|
|
*/ |
366 |
|
|
SP->_cup_cost = CostOf(TPARM_2(SP->_address_cursor, 23, 23), 1); |
367 |
|
|
SP->_cub_cost = CostOf(TPARM_1(parm_left_cursor, 23), 1); |
368 |
|
|
SP->_cuf_cost = CostOf(TPARM_1(parm_right_cursor, 23), 1); |
369 |
|
|
SP->_cud_cost = CostOf(TPARM_1(parm_down_cursor, 23), 1); |
370 |
|
|
SP->_cuu_cost = CostOf(TPARM_1(parm_up_cursor, 23), 1); |
371 |
|
|
SP->_hpa_cost = CostOf(TPARM_1(column_address, 23), 1); |
372 |
|
|
SP->_vpa_cost = CostOf(TPARM_1(row_address, 23), 1); |
373 |
|
|
|
374 |
|
|
/* non-parameterized screen-update strings */ |
375 |
|
|
SP->_ed_cost = NormalizedCost(clr_eos, 1); |
376 |
|
|
SP->_el_cost = NormalizedCost(clr_eol, 1); |
377 |
|
|
SP->_el1_cost = NormalizedCost(clr_bol, 1); |
378 |
|
|
SP->_dch1_cost = NormalizedCost(delete_character, 1); |
379 |
|
|
SP->_ich1_cost = NormalizedCost(insert_character, 1); |
380 |
|
|
|
381 |
|
|
/* |
382 |
|
|
* If this is a bce-terminal, we want to bias the choice so we use clr_eol |
383 |
|
|
* rather than spaces at the end of a line. |
384 |
|
|
*/ |
385 |
|
|
if (back_color_erase) |
386 |
|
|
SP->_el_cost = 0; |
387 |
|
|
|
388 |
|
|
/* parameterized screen-update strings */ |
389 |
|
|
SP->_dch_cost = NormalizedCost(TPARM_1(parm_dch, 23), 1); |
390 |
|
|
SP->_ich_cost = NormalizedCost(TPARM_1(parm_ich, 23), 1); |
391 |
|
|
SP->_ech_cost = NormalizedCost(TPARM_1(erase_chars, 23), 1); |
392 |
|
|
SP->_rep_cost = NormalizedCost(TPARM_2(repeat_char, ' ', 23), 1); |
393 |
|
|
|
394 |
|
|
SP->_cup_ch_cost = NormalizedCost(TPARM_2(SP->_address_cursor, 23, 23), 1); |
395 |
|
|
SP->_hpa_ch_cost = NormalizedCost(TPARM_1(column_address, 23), 1); |
396 |
|
|
SP->_cuf_ch_cost = NormalizedCost(TPARM_1(parm_right_cursor, 23), 1); |
397 |
|
|
SP->_inline_cost = min(SP->_cup_ch_cost, |
398 |
|
|
min(SP->_hpa_ch_cost, |
399 |
|
|
SP->_cuf_ch_cost)); |
400 |
|
|
|
401 |
|
|
/* |
402 |
|
|
* If save_cursor is used within enter_ca_mode, we should not use it for |
403 |
|
|
* scrolling optimization, since the corresponding restore_cursor is not |
404 |
|
|
* nested on the various terminals (vt100, xterm, etc.) which use this |
405 |
|
|
* feature. |
406 |
|
|
*/ |
407 |
|
|
if (save_cursor != 0 |
408 |
|
|
&& enter_ca_mode != 0 |
409 |
|
|
&& strstr(enter_ca_mode, save_cursor) != 0) { |
410 |
|
|
T(("...suppressed sc/rc capability due to conflict with smcup/rmcup")); |
411 |
|
|
save_cursor = 0; |
412 |
|
|
restore_cursor = 0; |
413 |
|
|
} |
414 |
|
|
|
415 |
|
|
/* |
416 |
|
|
* A different, possibly better way to arrange this would be to set |
417 |
|
|
* SP->_endwin = TRUE at window initialization time and let this be |
418 |
|
|
* called by doupdate's return-from-shellout code. |
419 |
|
|
*/ |
420 |
|
|
_nc_mvcur_resume(); |
421 |
|
|
} |
422 |
|
|
|
423 |
|
|
NCURSES_EXPORT(void) |
424 |
|
|
_nc_mvcur_wrap(void) |
425 |
|
|
/* wrap up cursor-addressing mode */ |
426 |
|
|
{ |
427 |
|
|
/* leave cursor at screen bottom */ |
428 |
|
|
mvcur(-1, -1, screen_lines - 1, 0); |
429 |
|
|
|
430 |
|
|
/* set cursor to normal mode */ |
431 |
|
|
if (SP->_cursor != -1) { |
432 |
|
|
int cursor = SP->_cursor; |
433 |
|
|
curs_set(1); |
434 |
|
|
SP->_cursor = cursor; |
435 |
|
|
} |
436 |
|
|
|
437 |
|
|
if (exit_ca_mode) { |
438 |
|
|
TPUTS_TRACE("exit_ca_mode"); |
439 |
|
|
putp(exit_ca_mode); |
440 |
|
|
} |
441 |
|
|
/* |
442 |
|
|
* Reset terminal's tab counter. There's a long-time bug that |
443 |
|
|
* if you exit a "curses" program such as vi or more, tab |
444 |
|
|
* forward, and then backspace, the cursor doesn't go to the |
445 |
|
|
* right place. The problem is that the kernel counts the |
446 |
|
|
* escape sequences that reset things as column positions. |
447 |
|
|
* Utter a \r to reset this invisibly. |
448 |
|
|
*/ |
449 |
|
|
_nc_outch('\r'); |
450 |
|
|
} |
451 |
|
|
|
452 |
|
|
/**************************************************************************** |
453 |
|
|
* |
454 |
|
|
* Optimized cursor movement |
455 |
|
|
* |
456 |
|
|
****************************************************************************/ |
457 |
|
|
|
458 |
|
|
/* |
459 |
|
|
* Perform repeated-append, returning cost |
460 |
|
|
*/ |
461 |
|
|
static NCURSES_INLINE int |
462 |
|
|
repeated_append(string_desc * target, int total, int num, int repeat, const char *src) |
463 |
|
|
{ |
464 |
|
|
size_t need = repeat * strlen(src); |
465 |
|
|
|
466 |
|
|
if (need < target->s_size) { |
467 |
|
|
while (repeat-- > 0) { |
468 |
|
|
if (_nc_safe_strcat(target, src)) { |
469 |
|
|
total += num; |
470 |
|
|
} else { |
471 |
|
|
total = INFINITY; |
472 |
|
|
break; |
473 |
|
|
} |
474 |
|
|
} |
475 |
|
|
} else { |
476 |
|
|
total = INFINITY; |
477 |
|
|
} |
478 |
|
|
return total; |
479 |
|
|
} |
480 |
|
|
|
481 |
|
|
#ifndef NO_OPTIMIZE |
482 |
|
|
#define NEXTTAB(fr) (fr + init_tabs - (fr % init_tabs)) |
483 |
|
|
|
484 |
|
|
/* |
485 |
|
|
* Assume back_tab (CBT) does not wrap backwards at the left margin, return |
486 |
|
|
* a negative value at that point to simplify the loop. |
487 |
|
|
*/ |
488 |
|
|
#define LASTTAB(fr) ((fr > 0) ? ((fr - 1) / init_tabs) * init_tabs : -1) |
489 |
|
|
|
490 |
|
|
static int |
491 |
|
|
relative_move(string_desc * target, int from_y, int from_x, int to_y, int |
492 |
|
|
to_x, bool ovw) |
493 |
|
|
/* move via local motions (cuu/cuu1/cud/cud1/cub1/cub/cuf1/cuf/vpa/hpa) */ |
494 |
|
|
{ |
495 |
|
|
string_desc save; |
496 |
|
|
int n, vcost = 0, hcost = 0; |
497 |
|
|
|
498 |
|
|
(void) _nc_str_copy(&save, target); |
499 |
|
|
|
500 |
|
|
if (to_y != from_y) { |
501 |
|
|
vcost = INFINITY; |
502 |
|
|
|
503 |
|
|
if (row_address != 0 |
504 |
|
|
&& _nc_safe_strcat(target, TPARM_1(row_address, to_y))) { |
505 |
|
|
vcost = SP->_vpa_cost; |
506 |
|
|
} |
507 |
|
|
|
508 |
|
|
if (to_y > from_y) { |
509 |
|
|
n = (to_y - from_y); |
510 |
|
|
|
511 |
|
|
if (parm_down_cursor |
512 |
|
|
&& SP->_cud_cost < vcost |
513 |
|
|
&& _nc_safe_strcat(_nc_str_copy(target, &save), |
514 |
|
|
TPARM_1(parm_down_cursor, n))) { |
515 |
|
|
vcost = SP->_cud_cost; |
516 |
|
|
} |
517 |
|
|
|
518 |
|
|
if (cursor_down |
519 |
|
|
&& (*cursor_down != '\n' || SP->_nl) |
520 |
|
|
&& (n * SP->_cud1_cost < vcost)) { |
521 |
|
|
vcost = repeated_append(_nc_str_copy(target, &save), 0, |
522 |
|
|
SP->_cud1_cost, n, cursor_down); |
523 |
|
|
} |
524 |
|
|
} else { /* (to_y < from_y) */ |
525 |
|
|
n = (from_y - to_y); |
526 |
|
|
|
527 |
|
|
if (parm_up_cursor |
528 |
|
|
&& SP->_cuu_cost < vcost |
529 |
|
|
&& _nc_safe_strcat(_nc_str_copy(target, &save), |
530 |
|
|
TPARM_1(parm_up_cursor, n))) { |
531 |
|
|
vcost = SP->_cuu_cost; |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
if (cursor_up && (n * SP->_cuu1_cost < vcost)) { |
535 |
|
|
vcost = repeated_append(_nc_str_copy(target, &save), 0, |
536 |
|
|
SP->_cuu1_cost, n, cursor_up); |
537 |
|
|
} |
538 |
|
|
} |
539 |
|
|
|
540 |
|
|
if (vcost == INFINITY) |
541 |
|
|
return (INFINITY); |
542 |
|
|
} |
543 |
|
|
|
544 |
|
|
save = *target; |
545 |
|
|
|
546 |
|
|
if (to_x != from_x) { |
547 |
|
|
char str[OPT_SIZE]; |
548 |
|
|
string_desc check; |
549 |
|
|
|
550 |
|
|
hcost = INFINITY; |
551 |
|
|
|
552 |
|
|
if (column_address |
553 |
|
|
&& _nc_safe_strcat(_nc_str_copy(target, &save), |
554 |
|
|
TPARM_1(column_address, to_x))) { |
555 |
|
|
hcost = SP->_hpa_cost; |
556 |
|
|
} |
557 |
|
|
|
558 |
|
|
if (to_x > from_x) { |
559 |
|
|
n = to_x - from_x; |
560 |
|
|
|
561 |
|
|
if (parm_right_cursor |
562 |
|
|
&& SP->_cuf_cost < hcost |
563 |
|
|
&& _nc_safe_strcat(_nc_str_copy(target, &save), |
564 |
|
|
TPARM_1(parm_right_cursor, n))) { |
565 |
|
|
hcost = SP->_cuf_cost; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
if (cursor_right) { |
569 |
|
|
int lhcost = 0; |
570 |
|
|
|
571 |
|
|
(void) _nc_str_init(&check, str, sizeof(str)); |
572 |
|
|
|
573 |
|
|
#if USE_HARD_TABS |
574 |
|
|
/* use hard tabs, if we have them, to do as much as possible */ |
575 |
|
|
if (init_tabs > 0 && tab) { |
576 |
|
|
int nxt, fr; |
577 |
|
|
|
578 |
|
|
for (fr = from_x; (nxt = NEXTTAB(fr)) <= to_x; fr = nxt) { |
579 |
|
|
lhcost = repeated_append(&check, lhcost, |
580 |
|
|
SP->_ht_cost, 1, tab); |
581 |
|
|
if (lhcost == INFINITY) |
582 |
|
|
break; |
583 |
|
|
} |
584 |
|
|
|
585 |
|
|
n = to_x - fr; |
586 |
|
|
from_x = fr; |
587 |
|
|
} |
588 |
|
|
#endif /* USE_HARD_TABS */ |
589 |
|
|
|
590 |
|
|
if (n <= 0 || n >= (int) check.s_size) |
591 |
|
|
ovw = FALSE; |
592 |
|
|
#if BSD_TPUTS |
593 |
|
|
/* |
594 |
|
|
* If we're allowing BSD-style padding in tputs, don't generate |
595 |
|
|
* a string with a leading digit. Otherwise, that will be |
596 |
|
|
* interpreted as a padding value rather than sent to the |
597 |
|
|
* screen. |
598 |
|
|
*/ |
599 |
|
|
if (ovw |
600 |
|
|
&& n > 0 |
601 |
|
|
&& n < (int) check.s_size |
602 |
|
|
&& vcost == 0 |
603 |
|
|
&& str[0] == '\0') { |
604 |
|
|
int wanted = CharOf(WANT_CHAR(to_y, from_x)); |
605 |
|
|
if (is8bits(wanted) && isdigit(wanted)) |
606 |
|
|
ovw = FALSE; |
607 |
|
|
} |
608 |
|
|
#endif |
609 |
|
|
/* |
610 |
|
|
* If we have no attribute changes, overwrite is cheaper. |
611 |
|
|
* Note: must suppress this by passing in ovw = FALSE whenever |
612 |
|
|
* WANT_CHAR would return invalid data. In particular, this |
613 |
|
|
* is true between the time a hardware scroll has been done |
614 |
|
|
* and the time the structure WANT_CHAR would access has been |
615 |
|
|
* updated. |
616 |
|
|
*/ |
617 |
|
|
if (ovw) { |
618 |
|
|
int i; |
619 |
|
|
|
620 |
|
|
for (i = 0; i < n; i++) { |
621 |
|
|
NCURSES_CH_T ch = WANT_CHAR(to_y, from_x + i); |
622 |
|
|
if (!SameAttrOf(ch, SCREEN_ATTRS(SP)) |
623 |
|
|
#if USE_WIDEC_SUPPORT |
624 |
|
|
|| !Charable(ch) |
625 |
|
|
#endif |
626 |
|
|
) { |
627 |
|
|
ovw = FALSE; |
628 |
|
|
break; |
629 |
|
|
} |
630 |
|
|
} |
631 |
|
|
} |
632 |
|
|
if (ovw) { |
633 |
|
|
int i; |
634 |
|
|
|
635 |
|
|
for (i = 0; i < n; i++) |
636 |
|
|
*check.s_tail++ = (char) CharOf(WANT_CHAR(to_y, |
637 |
|
|
from_x + i)); |
638 |
|
|
*check.s_tail = '\0'; |
639 |
|
|
check.s_size -= n; |
640 |
|
|
lhcost += n * SP->_char_padding; |
641 |
|
|
} else { |
642 |
|
|
lhcost = repeated_append(&check, lhcost, SP->_cuf1_cost, |
643 |
|
|
n, cursor_right); |
644 |
|
|
} |
645 |
|
|
|
646 |
|
|
if (lhcost < hcost |
647 |
|
|
&& _nc_safe_strcat(_nc_str_copy(target, &save), str)) { |
648 |
|
|
hcost = lhcost; |
649 |
|
|
} |
650 |
|
|
} |
651 |
|
|
} else { /* (to_x < from_x) */ |
652 |
|
|
n = from_x - to_x; |
653 |
|
|
|
654 |
|
|
if (parm_left_cursor |
655 |
|
|
&& SP->_cub_cost < hcost |
656 |
|
|
&& _nc_safe_strcat(_nc_str_copy(target, &save), |
657 |
|
|
TPARM_1(parm_left_cursor, n))) { |
658 |
|
|
hcost = SP->_cub_cost; |
659 |
|
|
} |
660 |
|
|
|
661 |
|
|
if (cursor_left) { |
662 |
|
|
int lhcost = 0; |
663 |
|
|
|
664 |
|
|
(void) _nc_str_init(&check, str, sizeof(str)); |
665 |
|
|
|
666 |
|
|
#if USE_HARD_TABS |
667 |
|
|
if (init_tabs > 0 && back_tab) { |
668 |
|
|
int nxt, fr; |
669 |
|
|
|
670 |
|
|
for (fr = from_x; (nxt = LASTTAB(fr)) >= to_x; fr = nxt) { |
671 |
|
|
lhcost = repeated_append(&check, lhcost, |
672 |
|
|
SP->_cbt_cost, 1, back_tab); |
673 |
|
|
if (lhcost == INFINITY) |
674 |
|
|
break; |
675 |
|
|
} |
676 |
|
|
|
677 |
|
|
n = fr - to_x; |
678 |
|
|
} |
679 |
|
|
#endif /* USE_HARD_TABS */ |
680 |
|
|
|
681 |
|
|
lhcost = repeated_append(&check, lhcost, SP->_cub1_cost, n, cursor_left); |
682 |
|
|
|
683 |
|
|
if (lhcost < hcost |
684 |
|
|
&& _nc_safe_strcat(_nc_str_copy(target, &save), str)) { |
685 |
|
|
hcost = lhcost; |
686 |
|
|
} |
687 |
|
|
} |
688 |
|
|
} |
689 |
|
|
|
690 |
|
|
if (hcost == INFINITY) |
691 |
|
|
return (INFINITY); |
692 |
|
|
} |
693 |
|
|
|
694 |
|
|
return (vcost + hcost); |
695 |
|
|
} |
696 |
|
|
#endif /* !NO_OPTIMIZE */ |
697 |
|
|
|
698 |
|
|
/* |
699 |
|
|
* With the machinery set up above, it's conceivable that |
700 |
|
|
* onscreen_mvcur could be modified into a recursive function that does |
701 |
|
|
* an alpha-beta search of motion space, as though it were a chess |
702 |
|
|
* move tree, with the weight function being boolean and the search |
703 |
|
|
* depth equated to length of string. However, this would jack up the |
704 |
|
|
* computation cost a lot, especially on terminals without a cup |
705 |
|
|
* capability constraining the search tree depth. So we settle for |
706 |
|
|
* the simpler method below. |
707 |
|
|
*/ |
708 |
|
|
|
709 |
|
|
static NCURSES_INLINE int |
710 |
|
|
onscreen_mvcur(int yold, int xold, int ynew, int xnew, bool ovw) |
711 |
|
|
/* onscreen move from (yold, xold) to (ynew, xnew) */ |
712 |
|
|
{ |
713 |
|
|
string_desc result; |
714 |
|
|
char buffer[OPT_SIZE]; |
715 |
|
|
int tactic = 0, newcost, usecost = INFINITY; |
716 |
|
|
int t5_cr_cost; |
717 |
|
|
|
718 |
|
|
#if defined(MAIN) || defined(NCURSES_TEST) |
719 |
|
|
struct timeval before, after; |
720 |
|
|
|
721 |
|
|
gettimeofday(&before, NULL); |
722 |
|
|
#endif /* MAIN */ |
723 |
|
|
|
724 |
|
|
#define NullResult _nc_str_null(&result, sizeof(buffer)) |
725 |
|
|
#define InitResult _nc_str_init(&result, buffer, sizeof(buffer)) |
726 |
|
|
|
727 |
|
|
/* tactic #0: use direct cursor addressing */ |
728 |
|
|
if (_nc_safe_strcpy(InitResult, TPARM_2(SP->_address_cursor, ynew, xnew))) { |
729 |
|
|
tactic = 0; |
730 |
|
|
usecost = SP->_cup_cost; |
731 |
|
|
|
732 |
|
|
#if defined(TRACE) || defined(NCURSES_TEST) |
733 |
|
|
if (!(_nc_optimize_enable & OPTIMIZE_MVCUR)) |
734 |
|
|
goto nonlocal; |
735 |
|
|
#endif /* TRACE */ |
736 |
|
|
|
737 |
|
|
/* |
738 |
|
|
* We may be able to tell in advance that the full optimization |
739 |
|
|
* will probably not be worth its overhead. Also, don't try to |
740 |
|
|
* use local movement if the current attribute is anything but |
741 |
|
|
* A_NORMAL...there are just too many ways this can screw up |
742 |
|
|
* (like, say, local-movement \n getting mapped to some obscure |
743 |
|
|
* character because A_ALTCHARSET is on). |
744 |
|
|
*/ |
745 |
|
|
if (yold == -1 || xold == -1 || NOT_LOCAL(yold, xold, ynew, xnew)) { |
746 |
|
|
#if defined(MAIN) || defined(NCURSES_TEST) |
747 |
|
|
if (!profiling) { |
748 |
|
|
(void) fputs("nonlocal\n", stderr); |
749 |
|
|
goto nonlocal; /* always run the optimizer if profiling */ |
750 |
|
|
} |
751 |
|
|
#else |
752 |
|
|
goto nonlocal; |
753 |
|
|
#endif /* MAIN */ |
754 |
|
|
} |
755 |
|
|
} |
756 |
|
|
#ifndef NO_OPTIMIZE |
757 |
|
|
/* tactic #1: use local movement */ |
758 |
|
|
if (yold != -1 && xold != -1 |
759 |
|
|
&& ((newcost = relative_move(NullResult, yold, xold, ynew, xnew, |
760 |
|
|
ovw)) != INFINITY) |
761 |
|
|
&& newcost < usecost) { |
762 |
|
|
tactic = 1; |
763 |
|
|
usecost = newcost; |
764 |
|
|
} |
765 |
|
|
|
766 |
|
|
/* tactic #2: use carriage-return + local movement */ |
767 |
|
|
if (yold != -1 && carriage_return |
768 |
|
|
&& ((newcost = relative_move(NullResult, yold, 0, ynew, xnew, ovw)) |
769 |
|
|
!= INFINITY) |
770 |
|
|
&& SP->_cr_cost + newcost < usecost) { |
771 |
|
|
tactic = 2; |
772 |
|
|
usecost = SP->_cr_cost + newcost; |
773 |
|
|
} |
774 |
|
|
|
775 |
|
|
/* tactic #3: use home-cursor + local movement */ |
776 |
|
|
if (cursor_home |
777 |
|
|
&& ((newcost = relative_move(NullResult, 0, 0, ynew, xnew, ovw)) != INFINITY) |
778 |
|
|
&& SP->_home_cost + newcost < usecost) { |
779 |
|
|
tactic = 3; |
780 |
|
|
usecost = SP->_home_cost + newcost; |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
/* tactic #4: use home-down + local movement */ |
784 |
|
|
if (cursor_to_ll |
785 |
|
|
&& ((newcost = relative_move(NullResult, screen_lines - 1, 0, ynew, |
786 |
|
|
xnew, ovw)) != INFINITY) |
787 |
|
|
&& SP->_ll_cost + newcost < usecost) { |
788 |
|
|
tactic = 4; |
789 |
|
|
usecost = SP->_ll_cost + newcost; |
790 |
|
|
} |
791 |
|
|
|
792 |
|
|
/* |
793 |
|
|
* tactic #5: use left margin for wrap to right-hand side, |
794 |
|
|
* unless strange wrap behavior indicated by xenl might hose us. |
795 |
|
|
*/ |
796 |
|
|
t5_cr_cost = (xold > 0 ? SP->_cr_cost : 0); |
797 |
|
|
if (auto_left_margin && !eat_newline_glitch |
798 |
|
|
&& yold > 0 && cursor_left |
799 |
|
|
&& ((newcost = relative_move(NullResult, yold - 1, screen_columns - |
800 |
|
|
1, ynew, xnew, ovw)) != INFINITY) |
801 |
|
|
&& t5_cr_cost + SP->_cub1_cost + newcost < usecost) { |
802 |
|
|
tactic = 5; |
803 |
|
|
usecost = t5_cr_cost + SP->_cub1_cost + newcost; |
804 |
|
|
} |
805 |
|
|
|
806 |
|
|
/* |
807 |
|
|
* These cases are ordered by estimated relative frequency. |
808 |
|
|
*/ |
809 |
|
|
if (tactic) |
810 |
|
|
InitResult; |
811 |
|
|
switch (tactic) { |
812 |
|
|
case 1: |
813 |
|
|
(void) relative_move(&result, yold, xold, ynew, xnew, ovw); |
814 |
|
|
break; |
815 |
|
|
case 2: |
816 |
|
|
(void) _nc_safe_strcpy(&result, carriage_return); |
817 |
|
|
(void) relative_move(&result, yold, 0, ynew, xnew, ovw); |
818 |
|
|
break; |
819 |
|
|
case 3: |
820 |
|
|
(void) _nc_safe_strcpy(&result, cursor_home); |
821 |
|
|
(void) relative_move(&result, 0, 0, ynew, xnew, ovw); |
822 |
|
|
break; |
823 |
|
|
case 4: |
824 |
|
|
(void) _nc_safe_strcpy(&result, cursor_to_ll); |
825 |
|
|
(void) relative_move(&result, screen_lines - 1, 0, ynew, xnew, ovw); |
826 |
|
|
break; |
827 |
|
|
case 5: |
828 |
|
|
if (xold > 0) |
829 |
|
|
(void) _nc_safe_strcat(&result, carriage_return); |
830 |
|
|
(void) _nc_safe_strcat(&result, cursor_left); |
831 |
|
|
(void) relative_move(&result, yold - 1, screen_columns - 1, ynew, |
832 |
|
|
xnew, ovw); |
833 |
|
|
break; |
834 |
|
|
} |
835 |
|
|
#endif /* !NO_OPTIMIZE */ |
836 |
|
|
|
837 |
|
|
nonlocal: |
838 |
|
|
#if defined(MAIN) || defined(NCURSES_TEST) |
839 |
|
|
gettimeofday(&after, NULL); |
840 |
|
|
diff = after.tv_usec - before.tv_usec |
841 |
|
|
+ (after.tv_sec - before.tv_sec) * 1000000; |
842 |
|
|
if (!profiling) |
843 |
|
|
(void) fprintf(stderr, |
844 |
|
|
"onscreen: %d microsec, %f 28.8Kbps char-equivalents\n", |
845 |
|
|
(int) diff, diff / 288); |
846 |
|
|
#endif /* MAIN */ |
847 |
|
|
|
848 |
|
|
if (usecost != INFINITY) { |
849 |
|
|
TPUTS_TRACE("mvcur"); |
850 |
|
|
tputs(buffer, 1, _nc_outch); |
851 |
|
|
SP->_cursrow = ynew; |
852 |
|
|
SP->_curscol = xnew; |
853 |
|
|
return (OK); |
854 |
|
|
} else |
855 |
|
|
return (ERR); |
856 |
|
|
} |
857 |
|
|
|
858 |
|
|
NCURSES_EXPORT(int) |
859 |
|
|
mvcur(int yold, int xold, int ynew, int xnew) |
860 |
|
|
/* optimized cursor move from (yold, xold) to (ynew, xnew) */ |
861 |
|
|
{ |
862 |
|
|
NCURSES_CH_T oldattr; |
863 |
|
|
int code; |
864 |
|
|
|
865 |
|
|
TR(TRACE_CALLS | TRACE_MOVE, (T_CALLED("mvcur(%d,%d,%d,%d)"), |
866 |
|
|
yold, xold, ynew, xnew)); |
867 |
|
|
|
868 |
|
|
if (SP == 0) { |
869 |
|
|
code = ERR; |
870 |
|
|
} else if (yold == ynew && xold == xnew) { |
871 |
|
|
code = OK; |
872 |
|
|
} else { |
873 |
|
|
|
874 |
|
|
/* |
875 |
|
|
* Most work here is rounding for terminal boundaries getting the |
876 |
|
|
* column position implied by wraparound or the lack thereof and |
877 |
|
|
* rolling up the screen to get ynew on the screen. |
878 |
|
|
*/ |
879 |
|
|
if (xnew >= screen_columns) { |
880 |
|
|
ynew += xnew / screen_columns; |
881 |
|
|
xnew %= screen_columns; |
882 |
|
|
} |
883 |
|
|
|
884 |
|
|
/* |
885 |
|
|
* Force restore even if msgr is on when we're in an alternate |
886 |
|
|
* character set -- these have a strong tendency to screw up the CR & |
887 |
|
|
* LF used for local character motions! |
888 |
|
|
*/ |
889 |
|
|
oldattr = SCREEN_ATTRS(SP); |
890 |
|
|
if ((AttrOf(oldattr) & A_ALTCHARSET) |
891 |
|
|
|| (AttrOf(oldattr) && !move_standout_mode)) { |
892 |
|
|
TR(TRACE_CHARPUT, ("turning off (%#lx) %s before move", |
893 |
|
|
(unsigned long) AttrOf(oldattr), |
894 |
|
|
_traceattr(AttrOf(oldattr)))); |
895 |
|
|
(void) VIDATTR(A_NORMAL, 0); |
896 |
|
|
} |
897 |
|
|
|
898 |
|
|
if (xold >= screen_columns) { |
899 |
|
|
int l; |
900 |
|
|
|
901 |
|
|
if (SP->_nl) { |
902 |
|
|
l = (xold + 1) / screen_columns; |
903 |
|
|
yold += l; |
904 |
|
|
if (yold >= screen_lines) |
905 |
|
|
l -= (yold - screen_lines - 1); |
906 |
|
|
|
907 |
|
|
if (l > 0) { |
908 |
|
|
if (carriage_return) { |
909 |
|
|
TPUTS_TRACE("carriage_return"); |
910 |
|
|
putp(carriage_return); |
911 |
|
|
} else |
912 |
|
|
_nc_outch('\r'); |
913 |
|
|
xold = 0; |
914 |
|
|
|
915 |
|
|
while (l > 0) { |
916 |
|
|
if (newline) { |
917 |
|
|
TPUTS_TRACE("newline"); |
918 |
|
|
putp(newline); |
919 |
|
|
} else |
920 |
|
|
_nc_outch('\n'); |
921 |
|
|
l--; |
922 |
|
|
} |
923 |
|
|
} |
924 |
|
|
} else { |
925 |
|
|
/* |
926 |
|
|
* If caller set nonl(), we cannot really use newlines to |
927 |
|
|
* position to the next row. |
928 |
|
|
*/ |
929 |
|
|
xold = -1; |
930 |
|
|
yold = -1; |
931 |
|
|
} |
932 |
|
|
} |
933 |
|
|
|
934 |
|
|
if (yold > screen_lines - 1) |
935 |
|
|
yold = screen_lines - 1; |
936 |
|
|
if (ynew > screen_lines - 1) |
937 |
|
|
ynew = screen_lines - 1; |
938 |
|
|
|
939 |
|
|
/* destination location is on screen now */ |
940 |
|
|
code = onscreen_mvcur(yold, xold, ynew, xnew, TRUE); |
941 |
|
|
|
942 |
|
|
/* |
943 |
|
|
* Restore attributes if we disabled them before moving. |
944 |
|
|
*/ |
945 |
|
|
if (!SameAttrOf(oldattr, SCREEN_ATTRS(SP))) { |
946 |
|
|
TR(TRACE_CHARPUT, ("turning on (%#lx) %s after move", |
947 |
|
|
(unsigned long) AttrOf(oldattr), |
948 |
|
|
_traceattr(AttrOf(oldattr)))); |
949 |
|
|
(void) VIDATTR(AttrOf(oldattr), GetPair(oldattr)); |
950 |
|
|
} |
951 |
|
|
} |
952 |
|
|
returnCode(code); |
953 |
|
|
} |
954 |
|
|
|
955 |
|
|
#if defined(TRACE) || defined(NCURSES_TEST) |
956 |
|
|
NCURSES_EXPORT_VAR(int) _nc_optimize_enable = OPTIMIZE_ALL; |
957 |
|
|
#endif |
958 |
|
|
|
959 |
|
|
#if defined(MAIN) || defined(NCURSES_TEST) |
960 |
|
|
/**************************************************************************** |
961 |
|
|
* |
962 |
|
|
* Movement optimizer test code |
963 |
|
|
* |
964 |
|
|
****************************************************************************/ |
965 |
|
|
|
966 |
|
|
#include <tic.h> |
967 |
|
|
#include <dump_entry.h> |
968 |
|
|
#include <time.h> |
969 |
|
|
|
970 |
|
|
NCURSES_EXPORT_VAR(const char *) _nc_progname = "mvcur"; |
971 |
|
|
|
972 |
|
|
static unsigned long xmits; |
973 |
|
|
|
974 |
|
|
/* these override lib_tputs.c */ |
975 |
|
|
NCURSES_EXPORT(int) |
976 |
|
|
tputs(const char *string, int affcnt GCC_UNUSED, int (*outc) (int) GCC_UNUSED) |
977 |
|
|
/* stub tputs() that dumps sequences in a visible form */ |
978 |
|
|
{ |
979 |
|
|
if (profiling) |
980 |
|
|
xmits += strlen(string); |
981 |
|
|
else |
982 |
|
|
(void) fputs(_nc_visbuf(string), stdout); |
983 |
|
|
return (OK); |
984 |
|
|
} |
985 |
|
|
|
986 |
|
|
NCURSES_EXPORT(int) |
987 |
|
|
putp(const char *string) |
988 |
|
|
{ |
989 |
|
|
return (tputs(string, 1, _nc_outch)); |
990 |
|
|
} |
991 |
|
|
|
992 |
|
|
NCURSES_EXPORT(int) |
993 |
|
|
_nc_outch(int ch) |
994 |
|
|
{ |
995 |
|
|
putc(ch, stdout); |
996 |
|
|
return OK; |
997 |
|
|
} |
998 |
|
|
|
999 |
|
|
NCURSES_EXPORT(int) |
1000 |
|
|
delay_output(int ms GCC_UNUSED) |
1001 |
|
|
{ |
1002 |
|
|
return OK; |
1003 |
|
|
} |
1004 |
|
|
|
1005 |
|
|
static char tname[PATH_MAX]; |
1006 |
|
|
|
1007 |
|
|
static void |
1008 |
|
|
load_term(void) |
1009 |
|
|
{ |
1010 |
|
|
(void) setupterm(tname, STDOUT_FILENO, NULL); |
1011 |
|
|
} |
1012 |
|
|
|
1013 |
|
|
static int |
1014 |
|
|
roll(int n) |
1015 |
|
|
{ |
1016 |
|
|
int i, j; |
1017 |
|
|
|
1018 |
|
|
i = (RAND_MAX / n) * n; |
1019 |
|
|
while ((j = rand()) >= i) |
1020 |
|
|
continue; |
1021 |
|
|
return (j % n); |
1022 |
|
|
} |
1023 |
|
|
|
1024 |
|
|
int |
1025 |
|
|
main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED) |
1026 |
|
|
{ |
1027 |
|
|
strlcpy(tname, sizeof(tname), getenv("TERM")); |
1028 |
|
|
load_term(); |
1029 |
|
|
_nc_setupscreen(lines, columns, stdout, FALSE, 0); |
1030 |
|
|
baudrate(); |
1031 |
|
|
|
1032 |
|
|
_nc_mvcur_init(); |
1033 |
|
|
NC_BUFFERED(FALSE); |
1034 |
|
|
|
1035 |
|
|
(void) puts("The mvcur tester. Type ? for help"); |
1036 |
|
|
|
1037 |
|
|
fputs("smcup:", stdout); |
1038 |
|
|
putchar('\n'); |
1039 |
|
|
|
1040 |
|
|
for (;;) { |
1041 |
|
|
int fy, fx, ty, tx, n, i; |
1042 |
|
|
char buf[BUFSIZ], capname[BUFSIZ]; |
1043 |
|
|
|
1044 |
|
|
(void) fputs("> ", stdout); |
1045 |
|
|
if (fgets(buf, sizeof(buf), stdin) == NULL) { |
1046 |
|
|
if (ferror(stdin)) |
1047 |
|
|
fputs("ferror on stdin", stderr); |
1048 |
|
|
break; |
1049 |
|
|
} |
1050 |
|
|
|
1051 |
|
|
if (buf[0] == '?') { |
1052 |
|
|
(void) puts("? -- display this help message"); |
1053 |
|
|
(void) |
1054 |
|
|
puts("fy fx ty tx -- (4 numbers) display (fy,fx)->(ty,tx) move"); |
1055 |
|
|
(void) puts("s[croll] n t b m -- display scrolling sequence"); |
1056 |
|
|
(void) |
1057 |
|
|
printf("r[eload] -- reload terminal info for %s\n", |
1058 |
|
|
termname()); |
1059 |
|
|
(void) |
1060 |
|
|
puts("l[oad] <term> -- load terminal info for type <term>"); |
1061 |
|
|
(void) puts("d[elete] <cap> -- delete named capability"); |
1062 |
|
|
(void) puts("i[nspect] -- display terminal capabilities"); |
1063 |
|
|
(void) |
1064 |
|
|
puts("c[ost] -- dump cursor-optimization cost table"); |
1065 |
|
|
(void) puts("o[optimize] -- toggle movement optimization"); |
1066 |
|
|
(void) |
1067 |
|
|
puts("t[orture] <num> -- torture-test with <num> random moves"); |
1068 |
|
|
(void) puts("q[uit] -- quit the program"); |
1069 |
|
|
} else if (sscanf(buf, "%d %d %d %d", &fy, &fx, &ty, &tx) == 4) { |
1070 |
|
|
struct timeval before, after; |
1071 |
|
|
|
1072 |
|
|
putchar('"'); |
1073 |
|
|
|
1074 |
|
|
gettimeofday(&before, NULL); |
1075 |
|
|
mvcur(fy, fx, ty, tx); |
1076 |
|
|
gettimeofday(&after, NULL); |
1077 |
|
|
|
1078 |
|
|
printf("\" (%ld msec)\n", |
1079 |
|
|
(long) (after.tv_usec - before.tv_usec |
1080 |
|
|
+ (after.tv_sec - before.tv_sec) |
1081 |
|
|
* 1000000)); |
1082 |
|
|
} else if (sscanf(buf, "s %d %d %d %d", &fy, &fx, &ty, &tx) == 4) { |
1083 |
|
|
struct timeval before, after; |
1084 |
|
|
|
1085 |
|
|
putchar('"'); |
1086 |
|
|
|
1087 |
|
|
gettimeofday(&before, NULL); |
1088 |
|
|
_nc_scrolln(fy, fx, ty, tx); |
1089 |
|
|
gettimeofday(&after, NULL); |
1090 |
|
|
|
1091 |
|
|
printf("\" (%ld msec)\n", |
1092 |
|
|
(long) (after.tv_usec - before.tv_usec + (after.tv_sec - |
1093 |
|
|
before.tv_sec) |
1094 |
|
|
* 1000000)); |
1095 |
|
|
} else if (buf[0] == 'r') { |
1096 |
|
|
(void) strlcpy(tname, sizeof(tname), termname()); |
1097 |
|
|
load_term(); |
1098 |
|
|
} else if (sscanf(buf, "l %s", tname) == 1) { |
1099 |
|
|
load_term(); |
1100 |
|
|
} else if (sscanf(buf, "d %s", capname) == 1) { |
1101 |
|
|
struct name_table_entry const *np = _nc_find_entry(capname, |
1102 |
|
|
_nc_get_hash_table(FALSE)); |
1103 |
|
|
|
1104 |
|
|
if (np == NULL) |
1105 |
|
|
(void) printf("No such capability as \"%s\"\n", capname); |
1106 |
|
|
else { |
1107 |
|
|
switch (np->nte_type) { |
1108 |
|
|
case BOOLEAN: |
1109 |
|
|
cur_term->type.Booleans[np->nte_index] = FALSE; |
1110 |
|
|
(void) |
1111 |
|
|
printf("Boolean capability `%s' (%d) turned off.\n", |
1112 |
|
|
np->nte_name, np->nte_index); |
1113 |
|
|
break; |
1114 |
|
|
|
1115 |
|
|
case NUMBER: |
1116 |
|
|
cur_term->type.Numbers[np->nte_index] = ABSENT_NUMERIC; |
1117 |
|
|
(void) printf("Number capability `%s' (%d) set to -1.\n", |
1118 |
|
|
np->nte_name, np->nte_index); |
1119 |
|
|
break; |
1120 |
|
|
|
1121 |
|
|
case STRING: |
1122 |
|
|
cur_term->type.Strings[np->nte_index] = ABSENT_STRING; |
1123 |
|
|
(void) printf("String capability `%s' (%d) deleted.\n", |
1124 |
|
|
np->nte_name, np->nte_index); |
1125 |
|
|
break; |
1126 |
|
|
} |
1127 |
|
|
} |
1128 |
|
|
} else if (buf[0] == 'i') { |
1129 |
|
|
dump_init((char *) NULL, F_TERMINFO, S_TERMINFO, 70, 0, FALSE); |
1130 |
|
|
dump_entry(&cur_term->type, FALSE, TRUE, 0, 0); |
1131 |
|
|
putchar('\n'); |
1132 |
|
|
} else if (buf[0] == 'o') { |
1133 |
|
|
if (_nc_optimize_enable & OPTIMIZE_MVCUR) { |
1134 |
|
|
_nc_optimize_enable &= ~OPTIMIZE_MVCUR; |
1135 |
|
|
(void) puts("Optimization is now off."); |
1136 |
|
|
} else { |
1137 |
|
|
_nc_optimize_enable |= OPTIMIZE_MVCUR; |
1138 |
|
|
(void) puts("Optimization is now on."); |
1139 |
|
|
} |
1140 |
|
|
} |
1141 |
|
|
/* |
1142 |
|
|
* You can use the `t' test to profile and tune the movement |
1143 |
|
|
* optimizer. Use iteration values in three digits or more. |
1144 |
|
|
* At above 5000 iterations the profile timing averages are stable |
1145 |
|
|
* to within a millisecond or three. |
1146 |
|
|
* |
1147 |
|
|
* The `overhead' field of the report will help you pick a |
1148 |
|
|
* COMPUTE_OVERHEAD figure appropriate for your processor and |
1149 |
|
|
* expected line speed. The `total estimated time' is |
1150 |
|
|
* computation time plus a character-transmission time |
1151 |
|
|
* estimate computed from the number of transmits and the baud |
1152 |
|
|
* rate. |
1153 |
|
|
* |
1154 |
|
|
* Use this together with the `o' command to get a read on the |
1155 |
|
|
* optimizer's effectiveness. Compare the total estimated times |
1156 |
|
|
* for `t' runs of the same length in both optimized and un-optimized |
1157 |
|
|
* modes. As long as the optimized times are less, the optimizer |
1158 |
|
|
* is winning. |
1159 |
|
|
*/ |
1160 |
|
|
else if (sscanf(buf, "t %d", &n) == 1) { |
1161 |
|
|
float cumtime = 0.0, perchar; |
1162 |
|
|
int speeds[] = |
1163 |
|
|
{2400, 9600, 14400, 19200, 28800, 38400, 0}; |
1164 |
|
|
|
1165 |
|
|
srand((unsigned) (getpid() + time((time_t *) 0))); |
1166 |
|
|
profiling = TRUE; |
1167 |
|
|
xmits = 0; |
1168 |
|
|
for (i = 0; i < n; i++) { |
1169 |
|
|
/* |
1170 |
|
|
* This does a move test between two random locations, |
1171 |
|
|
* Random moves probably short-change the optimizer, |
1172 |
|
|
* which will work better on the short moves probably |
1173 |
|
|
* typical of doupdate()'s usage pattern. Still, |
1174 |
|
|
* until we have better data... |
1175 |
|
|
*/ |
1176 |
|
|
#ifdef FIND_COREDUMP |
1177 |
|
|
int from_y = roll(lines); |
1178 |
|
|
int to_y = roll(lines); |
1179 |
|
|
int from_x = roll(columns); |
1180 |
|
|
int to_x = roll(columns); |
1181 |
|
|
|
1182 |
|
|
printf("(%d,%d) -> (%d,%d)\n", from_y, from_x, to_y, to_x); |
1183 |
|
|
mvcur(from_y, from_x, to_y, to_x); |
1184 |
|
|
#else |
1185 |
|
|
mvcur(roll(lines), roll(columns), roll(lines), roll(columns)); |
1186 |
|
|
#endif /* FIND_COREDUMP */ |
1187 |
|
|
if (diff) |
1188 |
|
|
cumtime += diff; |
1189 |
|
|
} |
1190 |
|
|
profiling = FALSE; |
1191 |
|
|
|
1192 |
|
|
/* |
1193 |
|
|
* Average milliseconds per character optimization time. |
1194 |
|
|
* This is the key figure to watch when tuning the optimizer. |
1195 |
|
|
*/ |
1196 |
|
|
perchar = cumtime / n; |
1197 |
|
|
|
1198 |
|
|
(void) printf("%d moves (%ld chars) in %d msec, %f msec each:\n", |
1199 |
|
|
n, xmits, (int) cumtime, perchar); |
1200 |
|
|
|
1201 |
|
|
for (i = 0; speeds[i]; i++) { |
1202 |
|
|
/* |
1203 |
|
|
* Total estimated time for the moves, computation and |
1204 |
|
|
* transmission both. Transmission time is an estimate |
1205 |
|
|
* assuming 9 bits/char, 8 bits + 1 stop bit. |
1206 |
|
|
*/ |
1207 |
|
|
float totalest = cumtime + xmits * 9 * 1e6 / speeds[i]; |
1208 |
|
|
|
1209 |
|
|
/* |
1210 |
|
|
* Per-character optimization overhead in character transmits |
1211 |
|
|
* at the current speed. Round this to the nearest integer |
1212 |
|
|
* to figure COMPUTE_OVERHEAD for the speed. |
1213 |
|
|
*/ |
1214 |
|
|
float overhead = speeds[i] * perchar / 1e6; |
1215 |
|
|
|
1216 |
|
|
(void) |
1217 |
|
|
printf("%6d bps: %3.2f char-xmits overhead; total estimated time %15.2f\n", |
1218 |
|
|
speeds[i], overhead, totalest); |
1219 |
|
|
} |
1220 |
|
|
} else if (buf[0] == 'c') { |
1221 |
|
|
(void) printf("char padding: %d\n", SP->_char_padding); |
1222 |
|
|
(void) printf("cr cost: %d\n", SP->_cr_cost); |
1223 |
|
|
(void) printf("cup cost: %d\n", SP->_cup_cost); |
1224 |
|
|
(void) printf("home cost: %d\n", SP->_home_cost); |
1225 |
|
|
(void) printf("ll cost: %d\n", SP->_ll_cost); |
1226 |
|
|
#if USE_HARD_TABS |
1227 |
|
|
(void) printf("ht cost: %d\n", SP->_ht_cost); |
1228 |
|
|
(void) printf("cbt cost: %d\n", SP->_cbt_cost); |
1229 |
|
|
#endif /* USE_HARD_TABS */ |
1230 |
|
|
(void) printf("cub1 cost: %d\n", SP->_cub1_cost); |
1231 |
|
|
(void) printf("cuf1 cost: %d\n", SP->_cuf1_cost); |
1232 |
|
|
(void) printf("cud1 cost: %d\n", SP->_cud1_cost); |
1233 |
|
|
(void) printf("cuu1 cost: %d\n", SP->_cuu1_cost); |
1234 |
|
|
(void) printf("cub cost: %d\n", SP->_cub_cost); |
1235 |
|
|
(void) printf("cuf cost: %d\n", SP->_cuf_cost); |
1236 |
|
|
(void) printf("cud cost: %d\n", SP->_cud_cost); |
1237 |
|
|
(void) printf("cuu cost: %d\n", SP->_cuu_cost); |
1238 |
|
|
(void) printf("hpa cost: %d\n", SP->_hpa_cost); |
1239 |
|
|
(void) printf("vpa cost: %d\n", SP->_vpa_cost); |
1240 |
|
|
} else if (buf[0] == 'x' || buf[0] == 'q') |
1241 |
|
|
break; |
1242 |
|
|
else |
1243 |
|
|
(void) puts("Invalid command."); |
1244 |
|
|
} |
1245 |
|
|
|
1246 |
|
|
(void) fputs("rmcup:", stdout); |
1247 |
|
|
_nc_mvcur_wrap(); |
1248 |
|
|
putchar('\n'); |
1249 |
|
|
|
1250 |
|
|
return (0); |
1251 |
|
|
} |
1252 |
|
|
|
1253 |
|
|
#endif /* MAIN */ |
1254 |
|
|
|
1255 |
|
|
/* lib_mvcur.c ends here */ |