150276Speter/**************************************************************************** 2262685Sdelphij * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc. * 350276Speter * * 450276Speter * Permission is hereby granted, free of charge, to any person obtaining a * 550276Speter * copy of this software and associated documentation files (the * 650276Speter * "Software"), to deal in the Software without restriction, including * 750276Speter * without limitation the rights to use, copy, modify, merge, publish, * 850276Speter * distribute, distribute with modifications, sublicense, and/or sell * 950276Speter * copies of the Software, and to permit persons to whom the Software is * 1050276Speter * furnished to do so, subject to the following conditions: * 1150276Speter * * 1250276Speter * The above copyright notice and this permission notice shall be included * 1350276Speter * in all copies or substantial portions of the Software. * 1450276Speter * * 1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2250276Speter * * 2350276Speter * Except as contained in this notice, the name(s) of the above copyright * 2450276Speter * holders shall not be used in advertising or otherwise to promote the * 2550276Speter * sale, use or other dealings in this Software without prior written * 2650276Speter * authorization. * 2750276Speter ****************************************************************************/ 2850276Speter 2950276Speter/**************************************************************************** 3050276Speter * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 3150276Speter * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32166124Srafan * and: Thomas E. Dickey 1996-on * 33262629Sdelphij * and: Juergen Pfeifer 2009 * 3450276Speter ****************************************************************************/ 3550276Speter 3650276Speter/* 3750276Speter** lib_mvcur.c 3850276Speter** 3950276Speter** The routines for moving the physical cursor and scrolling: 4050276Speter** 4150276Speter** void _nc_mvcur_init(void) 4250276Speter** 4350276Speter** void _nc_mvcur_resume(void) 4450276Speter** 4550276Speter** int mvcur(int old_y, int old_x, int new_y, int new_x) 4650276Speter** 4750276Speter** void _nc_mvcur_wrap(void) 4850276Speter** 4950276Speter** Comparisons with older movement optimizers: 5050276Speter** SVr3 curses mvcur() can't use cursor_to_ll or auto_left_margin. 5150276Speter** 4.4BSD curses can't use cuu/cud/cuf/cub/hpa/vpa/tab/cbt for local 5250276Speter** motions. It doesn't use tactics based on auto_left_margin. Weirdly 5350276Speter** enough, it doesn't use its own hardware-scrolling routine to scroll up 5450276Speter** destination lines for out-of-bounds addresses! 5550276Speter** old ncurses optimizer: less accurate cost computations (in fact, 5650276Speter** it was broken and had to be commented out!). 5750276Speter** 5850276Speter** Compile with -DMAIN to build an interactive tester/timer for the movement 5950276Speter** optimizer. You can use it to investigate the optimizer's behavior. 6050276Speter** You can also use it for tuning the formulas used to determine whether 6150276Speter** or not full optimization is attempted. 6250276Speter** 6350276Speter** This code has a nasty tendency to find bugs in terminfo entries, because it 6450276Speter** exercises the non-cup movement capabilities heavily. If you think you've 6550276Speter** found a bug, try deleting subsets of the following capabilities (arranged 6650276Speter** in decreasing order of suspiciousness): it, tab, cbt, hpa, vpa, cuu, cud, 6750276Speter** cuf, cub, cuu1, cud1, cuf1, cub1. It may be that one or more are wrong. 6850276Speter** 6950276Speter** Note: you should expect this code to look like a resource hog in a profile. 7050276Speter** That's because it does a lot of I/O, through the tputs() calls. The I/O 7150276Speter** cost swamps the computation overhead (and as machines get faster, this 7250276Speter** will become even more true). Comments in the test exerciser at the end 7350276Speter** go into detail about tuning and how you can gauge the optimizer's 7450276Speter** effectiveness. 7550276Speter**/ 7650276Speter 7750276Speter/**************************************************************************** 7850276Speter * 7950276Speter * Constants and macros for optimizer tuning. 8050276Speter * 8150276Speter ****************************************************************************/ 8250276Speter 8350276Speter/* 8450276Speter * The average overhead of a full optimization computation in character 8550276Speter * transmission times. If it's too high, the algorithm will be a bit 8650276Speter * over-biased toward using cup rather than local motions; if it's too 8750276Speter * low, the algorithm may spend more time than is strictly optimal 8850276Speter * looking for non-cup motions. Profile the optimizer using the `t' 8950276Speter * command of the exerciser (see below), and round to the nearest integer. 9050276Speter * 9150276Speter * Yes, I (esr) thought about computing expected overhead dynamically, say 9250276Speter * by derivation from a running average of optimizer times. But the 9350276Speter * whole point of this optimization is to *decrease* the frequency of 9450276Speter * system calls. :-) 9550276Speter */ 9650276Speter#define COMPUTE_OVERHEAD 1 /* I use a 90MHz Pentium @ 9.6Kbps */ 9750276Speter 9850276Speter/* 9950276Speter * LONG_DIST is the distance we consider to be just as costly to move over as a 10050276Speter * cup sequence is to emit. In other words, it's the length of a cup sequence 10150276Speter * adjusted for average computation overhead. The magic number is the length 10250276Speter * of "\033[yy;xxH", the typical cup sequence these days. 10350276Speter */ 10450276Speter#define LONG_DIST (8 - COMPUTE_OVERHEAD) 10550276Speter 10650276Speter/* 10750276Speter * Tell whether a motion is optimizable by local motions. Needs to be cheap to 10850276Speter * compute. In general, all the fast moves go to either the right or left edge 10950276Speter * of the screen. So any motion to a location that is (a) further away than 11050276Speter * LONG_DIST and (b) further inward from the right or left edge than LONG_DIST, 11150276Speter * we'll consider nonlocal. 11250276Speter */ 113262629Sdelphij#define NOT_LOCAL(sp, fy, fx, ty, tx) ((tx > LONG_DIST) \ 114262629Sdelphij && (tx < screen_columns(sp) - 1 - LONG_DIST) \ 115166124Srafan && (abs(ty-fy) + abs(tx-fx) > LONG_DIST)) 11650276Speter 11750276Speter/**************************************************************************** 11850276Speter * 11950276Speter * External interfaces 12050276Speter * 12150276Speter ****************************************************************************/ 12250276Speter 12350276Speter/* 12450276Speter * For this code to work OK, the following components must live in the 12550276Speter * screen structure: 12650276Speter * 12750276Speter * int _char_padding; // cost of character put 12850276Speter * int _cr_cost; // cost of (carriage_return) 12950276Speter * int _cup_cost; // cost of (cursor_address) 13050276Speter * int _home_cost; // cost of (cursor_home) 13150276Speter * int _ll_cost; // cost of (cursor_to_ll) 13250276Speter *#if USE_HARD_TABS 13350276Speter * int _ht_cost; // cost of (tab) 13450276Speter * int _cbt_cost; // cost of (back_tab) 13550276Speter *#endif USE_HARD_TABS 13650276Speter * int _cub1_cost; // cost of (cursor_left) 13750276Speter * int _cuf1_cost; // cost of (cursor_right) 13850276Speter * int _cud1_cost; // cost of (cursor_down) 13950276Speter * int _cuu1_cost; // cost of (cursor_up) 14050276Speter * int _cub_cost; // cost of (parm_cursor_left) 14150276Speter * int _cuf_cost; // cost of (parm_cursor_right) 14250276Speter * int _cud_cost; // cost of (parm_cursor_down) 14350276Speter * int _cuu_cost; // cost of (parm_cursor_up) 14450276Speter * int _hpa_cost; // cost of (column_address) 14550276Speter * int _vpa_cost; // cost of (row_address) 14650276Speter * int _ech_cost; // cost of (erase_chars) 14750276Speter * int _rep_cost; // cost of (repeat_char) 14850276Speter * 14950276Speter * The USE_HARD_TABS switch controls whether it is reliable to use tab/backtabs 15050276Speter * for local motions. On many systems, it's not, due to uncertainties about 15150276Speter * tab delays and whether or not tabs will be expanded in raw mode. If you 15250276Speter * have parm_right_cursor, tab motions don't win you a lot anyhow. 15350276Speter */ 15450276Speter 15550276Speter#include <curses.priv.h> 15650276Speter#include <ctype.h> 15750276Speter 158262629Sdelphij#ifndef CUR 159262629Sdelphij#define CUR SP_TERMTYPE 160262629Sdelphij#endif 16150276Speter 162262685SdelphijMODULE_ID("$Id: lib_mvcur.c,v 1.133 2013/05/25 23:59:41 tom Exp $") 16350276Speter 164262629Sdelphij#define WANT_CHAR(sp, y, x) NewScreen(sp)->_line[y].text[x] /* desired state */ 165262629Sdelphij 166262629Sdelphij#if NCURSES_SP_FUNCS 167262629Sdelphij#define BAUDRATE(sp) sp->_term->_baudrate /* bits per second */ 168262629Sdelphij#else 169262629Sdelphij#define BAUDRATE(sp) cur_term->_baudrate /* bits per second */ 170262629Sdelphij#endif 171262629Sdelphij 17250276Speter#if defined(MAIN) || defined(NCURSES_TEST) 17350276Speter#include <sys/time.h> 17450276Speter 17550276Speterstatic bool profiling = FALSE; 17650276Speterstatic float diff; 17750276Speter#endif /* MAIN */ 17850276Speter 179262685Sdelphij#undef NCURSES_OUTC_FUNC 180262685Sdelphij#define NCURSES_OUTC_FUNC myOutCh 181262685Sdelphij 18250276Speter#define OPT_SIZE 512 18350276Speter 184262629Sdelphijstatic int normalized_cost(NCURSES_SP_DCLx const char *const cap, int affcnt); 18550276Speter 18650276Speter/**************************************************************************** 18750276Speter * 18850276Speter * Initialization/wrapup (including cost pre-computation) 18950276Speter * 19050276Speter ****************************************************************************/ 19150276Speter 19250276Speter#ifdef TRACE 19350276Speterstatic int 194262629Sdelphijtrace_cost_of(NCURSES_SP_DCLx const char *capname, const char *cap, int affcnt) 19550276Speter{ 196262629Sdelphij int result = NCURSES_SP_NAME(_nc_msec_cost) (NCURSES_SP_ARGx cap, affcnt); 19762449Speter TR(TRACE_CHARPUT | TRACE_MOVE, 19866963Speter ("CostOf %s %d %s", capname, result, _nc_visbuf(cap))); 19962449Speter return result; 20050276Speter} 201262629Sdelphij#define CostOf(cap,affcnt) trace_cost_of(NCURSES_SP_ARGx #cap, cap, affcnt) 20250276Speter 20350276Speterstatic int 204262629Sdelphijtrace_normalized_cost(NCURSES_SP_DCLx const char *capname, const char *cap, int affcnt) 20550276Speter{ 206262629Sdelphij int result = normalized_cost(NCURSES_SP_ARGx cap, affcnt); 20762449Speter TR(TRACE_CHARPUT | TRACE_MOVE, 20866963Speter ("NormalizedCost %s %d %s", capname, result, _nc_visbuf(cap))); 20962449Speter return result; 21050276Speter} 211262629Sdelphij#define NormalizedCost(cap,affcnt) trace_normalized_cost(NCURSES_SP_ARGx #cap, cap, affcnt) 21250276Speter 21350276Speter#else 21450276Speter 215262629Sdelphij#define CostOf(cap,affcnt) NCURSES_SP_NAME(_nc_msec_cost)(NCURSES_SP_ARGx cap, affcnt) 216262629Sdelphij#define NormalizedCost(cap,affcnt) normalized_cost(NCURSES_SP_ARGx cap, affcnt) 21750276Speter 21850276Speter#endif 21950276Speter 22076726SpeterNCURSES_EXPORT(int) 221262629SdelphijNCURSES_SP_NAME(_nc_msec_cost) (NCURSES_SP_DCLx const char *const cap, int affcnt) 22250276Speter/* compute the cost of a given operation */ 22350276Speter{ 22450276Speter if (cap == 0) 22562449Speter return (INFINITY); 22662449Speter else { 22762449Speter const char *cp; 22866963Speter float cum_cost = 0.0; 22950276Speter 23062449Speter for (cp = cap; *cp; cp++) { 23150276Speter /* extract padding, either mandatory or required */ 23262449Speter if (cp[0] == '$' && cp[1] == '<' && strchr(cp, '>')) { 23366963Speter float number = 0.0; 23450276Speter 23562449Speter for (cp += 2; *cp != '>'; cp++) { 23697049Speter if (isdigit(UChar(*cp))) 237262629Sdelphij number = number * 10 + (float) (*cp - '0'); 23850276Speter else if (*cp == '*') 239262629Sdelphij number *= (float) affcnt; 24097049Speter else if (*cp == '.' && (*++cp != '>') && isdigit(UChar(*cp))) 241262629Sdelphij number += (float) ((*cp - '0') / 10.0); 24250276Speter } 24350276Speter 24466963Speter#if NCURSES_NO_PADDING 245262629Sdelphij if (!GetNoPadding(SP_PARM)) 24662449Speter#endif 24762449Speter cum_cost += number * 10; 248262629Sdelphij } else if (SP_PARM) { 249262629Sdelphij cum_cost += (float) SP_PARM->_char_padding; 250262629Sdelphij } 25150276Speter } 25250276Speter 25362449Speter return ((int) cum_cost); 25450276Speter } 25550276Speter} 25650276Speter 257262629Sdelphij#if NCURSES_SP_FUNCS 258262629SdelphijNCURSES_EXPORT(int) 259262629Sdelphij_nc_msec_cost(const char *const cap, int affcnt) 260262629Sdelphij{ 261262629Sdelphij return NCURSES_SP_NAME(_nc_msec_cost) (CURRENT_SCREEN, cap, affcnt); 262262629Sdelphij} 263262629Sdelphij#endif 264262629Sdelphij 26562449Speterstatic int 266262629Sdelphijnormalized_cost(NCURSES_SP_DCLx const char *const cap, int affcnt) 26750276Speter/* compute the effective character-count for an operation (round up) */ 26850276Speter{ 269262629Sdelphij int cost = NCURSES_SP_NAME(_nc_msec_cost) (NCURSES_SP_ARGx cap, affcnt); 27062449Speter if (cost != INFINITY) 271262629Sdelphij cost = (cost + SP_PARM->_char_padding - 1) / SP_PARM->_char_padding; 27262449Speter return cost; 27350276Speter} 27450276Speter 27562449Speterstatic void 276262629Sdelphijreset_scroll_region(NCURSES_SP_DCL0) 27750276Speter/* Set the scroll-region to a known state (the default) */ 27850276Speter{ 27962449Speter if (change_scroll_region) { 280262685Sdelphij NCURSES_PUTP2("change_scroll_region", 281262685Sdelphij TPARM_2(change_scroll_region, 282262685Sdelphij 0, screen_lines(SP_PARM) - 1)); 28350276Speter } 28450276Speter} 28550276Speter 28676726SpeterNCURSES_EXPORT(void) 287262629SdelphijNCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_DCL0) 28850276Speter/* what to do at initialization time and after each shellout */ 28950276Speter{ 290262685Sdelphij if (!SP_PARM || !IsTermInfo(SP_PARM)) 291262629Sdelphij return; 292262629Sdelphij 29350276Speter /* initialize screen for cursor access */ 29462449Speter if (enter_ca_mode) { 295262685Sdelphij NCURSES_PUTP2("enter_ca_mode", enter_ca_mode); 29650276Speter } 29750276Speter 29850276Speter /* 29950276Speter * Doing this here rather than in _nc_mvcur_wrap() ensures that 30050276Speter * ncurses programs will see a reset scroll region even if a 30150276Speter * program that messed with it died ungracefully. 30250276Speter * 30350276Speter * This also undoes the effects of terminal init strings that assume 30450276Speter * they know the screen size. This is useful when you're running 30550276Speter * a vt100 emulation through xterm. 30650276Speter */ 307262629Sdelphij reset_scroll_region(NCURSES_SP_ARG); 308262629Sdelphij SP_PARM->_cursrow = SP_PARM->_curscol = -1; 30950276Speter 31050276Speter /* restore cursor shape */ 311262629Sdelphij if (SP_PARM->_cursor != -1) { 312262629Sdelphij int cursor = SP_PARM->_cursor; 313262629Sdelphij SP_PARM->_cursor = -1; 314262629Sdelphij NCURSES_SP_NAME(curs_set) (NCURSES_SP_ARGx cursor); 31550276Speter } 31650276Speter} 31750276Speter 318262629Sdelphij#if NCURSES_SP_FUNCS 31976726SpeterNCURSES_EXPORT(void) 320262629Sdelphij_nc_mvcur_resume(void) 321262629Sdelphij{ 322262629Sdelphij NCURSES_SP_NAME(_nc_mvcur_resume) (CURRENT_SCREEN); 323262629Sdelphij} 324262629Sdelphij#endif 325262629Sdelphij 326262629SdelphijNCURSES_EXPORT(void) 327262629SdelphijNCURSES_SP_NAME(_nc_mvcur_init) (NCURSES_SP_DCL0) 32850276Speter/* initialize the cost structure */ 32950276Speter{ 330262685Sdelphij if (SP_PARM->_ofp && isatty(fileno(SP_PARM->_ofp))) { 331262629Sdelphij SP_PARM->_char_padding = ((BAUDBYTE * 1000 * 10) 332262629Sdelphij / (BAUDRATE(SP_PARM) > 0 333262629Sdelphij ? BAUDRATE(SP_PARM) 334262629Sdelphij : 9600)); 335262685Sdelphij } else { 336262629Sdelphij SP_PARM->_char_padding = 1; /* must be nonzero */ 337262685Sdelphij } 338262629Sdelphij if (SP_PARM->_char_padding <= 0) 339262629Sdelphij SP_PARM->_char_padding = 1; /* must be nonzero */ 340262629Sdelphij TR(TRACE_CHARPUT | TRACE_MOVE, ("char_padding %d msecs", SP_PARM->_char_padding)); 34150276Speter 34250276Speter /* non-parameterized local-motion strings */ 343262629Sdelphij SP_PARM->_cr_cost = CostOf(carriage_return, 0); 344262629Sdelphij SP_PARM->_home_cost = CostOf(cursor_home, 0); 345262629Sdelphij SP_PARM->_ll_cost = CostOf(cursor_to_ll, 0); 34650276Speter#if USE_HARD_TABS 347166124Srafan if (getenv("NCURSES_NO_HARD_TABS") == 0) { 348262629Sdelphij SP_PARM->_ht_cost = CostOf(tab, 0); 349262629Sdelphij SP_PARM->_cbt_cost = CostOf(back_tab, 0); 350166124Srafan } else { 351262629Sdelphij SP_PARM->_ht_cost = INFINITY; 352262629Sdelphij SP_PARM->_cbt_cost = INFINITY; 353166124Srafan } 35450276Speter#endif /* USE_HARD_TABS */ 355262629Sdelphij SP_PARM->_cub1_cost = CostOf(cursor_left, 0); 356262629Sdelphij SP_PARM->_cuf1_cost = CostOf(cursor_right, 0); 357262629Sdelphij SP_PARM->_cud1_cost = CostOf(cursor_down, 0); 358262629Sdelphij SP_PARM->_cuu1_cost = CostOf(cursor_up, 0); 35950276Speter 360262629Sdelphij SP_PARM->_smir_cost = CostOf(enter_insert_mode, 0); 361262629Sdelphij SP_PARM->_rmir_cost = CostOf(exit_insert_mode, 0); 362262629Sdelphij SP_PARM->_ip_cost = 0; 36350276Speter if (insert_padding) { 364262629Sdelphij SP_PARM->_ip_cost = CostOf(insert_padding, 0); 36550276Speter } 36650276Speter 36750276Speter /* 36850276Speter * Assumption: if the terminal has memory_relative addressing, the 36950276Speter * initialization strings or smcup will set single-page mode so we 37050276Speter * can treat it like absolute screen addressing. This seems to be true 37150276Speter * for all cursor_mem_address terminal types in the terminfo database. 37250276Speter */ 373262629Sdelphij SP_PARM->_address_cursor = cursor_address ? cursor_address : cursor_mem_address; 37450276Speter 37550276Speter /* 37650276Speter * Parametrized local-motion strings. This static cost computation 37750276Speter * depends on the following assumptions: 37850276Speter * 37950276Speter * (1) They never have * padding. In the entire master terminfo database 38050276Speter * as of March 1995, only the obsolete Zenith Z-100 pc violates this. 38162449Speter * (Proportional padding is found mainly in insert, delete and scroll 38250276Speter * capabilities). 38350276Speter * 38450276Speter * (2) The average case of cup has two two-digit parameters. Strictly, 38550276Speter * the average case for a 24 * 80 screen has ((10*10*(1 + 1)) + 38650276Speter * (14*10*(1 + 2)) + (10*70*(2 + 1)) + (14*70*4)) / (24*80) = 3.458 38750276Speter * digits of parameters. On a 25x80 screen the average is 3.6197. 38850276Speter * On larger screens the value gets much closer to 4. 38950276Speter * 39050276Speter * (3) The average case of cub/cuf/hpa/ech/rep has 2 digits of parameters 39150276Speter * (strictly, (((10 * 1) + (70 * 2)) / 80) = 1.8750). 39250276Speter * 39350276Speter * (4) The average case of cud/cuu/vpa has 2 digits of parameters 39450276Speter * (strictly, (((10 * 1) + (14 * 2)) / 24) = 1.5833). 39550276Speter * 39650276Speter * All these averages depend on the assumption that all parameter values 39750276Speter * are equally probable. 39850276Speter */ 399262629Sdelphij SP_PARM->_cup_cost = CostOf(TPARM_2(SP_PARM->_address_cursor, 23, 23), 1); 400262629Sdelphij SP_PARM->_cub_cost = CostOf(TPARM_1(parm_left_cursor, 23), 1); 401262629Sdelphij SP_PARM->_cuf_cost = CostOf(TPARM_1(parm_right_cursor, 23), 1); 402262629Sdelphij SP_PARM->_cud_cost = CostOf(TPARM_1(parm_down_cursor, 23), 1); 403262629Sdelphij SP_PARM->_cuu_cost = CostOf(TPARM_1(parm_up_cursor, 23), 1); 404262629Sdelphij SP_PARM->_hpa_cost = CostOf(TPARM_1(column_address, 23), 1); 405262629Sdelphij SP_PARM->_vpa_cost = CostOf(TPARM_1(row_address, 23), 1); 40650276Speter 40750276Speter /* non-parameterized screen-update strings */ 408262629Sdelphij SP_PARM->_ed_cost = NormalizedCost(clr_eos, 1); 409262629Sdelphij SP_PARM->_el_cost = NormalizedCost(clr_eol, 1); 410262629Sdelphij SP_PARM->_el1_cost = NormalizedCost(clr_bol, 1); 411262629Sdelphij SP_PARM->_dch1_cost = NormalizedCost(delete_character, 1); 412262629Sdelphij SP_PARM->_ich1_cost = NormalizedCost(insert_character, 1); 41350276Speter 414166124Srafan /* 415166124Srafan * If this is a bce-terminal, we want to bias the choice so we use clr_eol 416166124Srafan * rather than spaces at the end of a line. 417166124Srafan */ 418166124Srafan if (back_color_erase) 419262629Sdelphij SP_PARM->_el_cost = 0; 420166124Srafan 42150276Speter /* parameterized screen-update strings */ 422262629Sdelphij SP_PARM->_dch_cost = NormalizedCost(TPARM_1(parm_dch, 23), 1); 423262629Sdelphij SP_PARM->_ich_cost = NormalizedCost(TPARM_1(parm_ich, 23), 1); 424262629Sdelphij SP_PARM->_ech_cost = NormalizedCost(TPARM_1(erase_chars, 23), 1); 425262629Sdelphij SP_PARM->_rep_cost = NormalizedCost(TPARM_2(repeat_char, ' ', 23), 1); 42650276Speter 427262629Sdelphij SP_PARM->_cup_ch_cost = NormalizedCost( 428262629Sdelphij TPARM_2(SP_PARM->_address_cursor, 429262629Sdelphij 23, 23), 430262629Sdelphij 1); 431262629Sdelphij SP_PARM->_hpa_ch_cost = NormalizedCost(TPARM_1(column_address, 23), 1); 432262629Sdelphij SP_PARM->_cuf_ch_cost = NormalizedCost(TPARM_1(parm_right_cursor, 23), 1); 433262629Sdelphij SP_PARM->_inline_cost = min(SP_PARM->_cup_ch_cost, 434262629Sdelphij min(SP_PARM->_hpa_ch_cost, 435262629Sdelphij SP_PARM->_cuf_ch_cost)); 43650276Speter 43750276Speter /* 43850276Speter * If save_cursor is used within enter_ca_mode, we should not use it for 43950276Speter * scrolling optimization, since the corresponding restore_cursor is not 44050276Speter * nested on the various terminals (vt100, xterm, etc.) which use this 44150276Speter * feature. 44250276Speter */ 44350276Speter if (save_cursor != 0 44462449Speter && enter_ca_mode != 0 44562449Speter && strstr(enter_ca_mode, save_cursor) != 0) { 44650276Speter T(("...suppressed sc/rc capability due to conflict with smcup/rmcup")); 44750276Speter save_cursor = 0; 44850276Speter restore_cursor = 0; 44950276Speter } 45050276Speter 45150276Speter /* 452262629Sdelphij * A different, possibly better way to arrange this would be to set the 453262629Sdelphij * SCREEN's _endwin to TRUE at window initialization time and let this be 45450276Speter * called by doupdate's return-from-shellout code. 45550276Speter */ 456262629Sdelphij NCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_ARG); 45750276Speter} 45850276Speter 459262629Sdelphij#if NCURSES_SP_FUNCS 46076726SpeterNCURSES_EXPORT(void) 461262629Sdelphij_nc_mvcur_init(void) 462262629Sdelphij{ 463262629Sdelphij NCURSES_SP_NAME(_nc_mvcur_init) (CURRENT_SCREEN); 464262629Sdelphij} 465262629Sdelphij#endif 466262629Sdelphij 467262629SdelphijNCURSES_EXPORT(void) 468262629SdelphijNCURSES_SP_NAME(_nc_mvcur_wrap) (NCURSES_SP_DCL0) 46950276Speter/* wrap up cursor-addressing mode */ 47050276Speter{ 47150276Speter /* leave cursor at screen bottom */ 472262629Sdelphij TINFO_MVCUR(NCURSES_SP_ARGx -1, -1, screen_lines(SP_PARM) - 1, 0); 47350276Speter 474262629Sdelphij if (!SP_PARM || !IsTermInfo(SP_PARM)) 475262629Sdelphij return; 476262629Sdelphij 47750276Speter /* set cursor to normal mode */ 478262629Sdelphij if (SP_PARM->_cursor != -1) { 479262629Sdelphij int cursor = SP_PARM->_cursor; 480262629Sdelphij NCURSES_SP_NAME(curs_set) (NCURSES_SP_ARGx 1); 481262629Sdelphij SP_PARM->_cursor = cursor; 482184989Srafan } 48350276Speter 48462449Speter if (exit_ca_mode) { 485262685Sdelphij NCURSES_PUTP2("exit_ca_mode", exit_ca_mode); 48650276Speter } 48750276Speter /* 48850276Speter * Reset terminal's tab counter. There's a long-time bug that 48950276Speter * if you exit a "curses" program such as vi or more, tab 49050276Speter * forward, and then backspace, the cursor doesn't go to the 49150276Speter * right place. The problem is that the kernel counts the 49250276Speter * escape sequences that reset things as column positions. 49350276Speter * Utter a \r to reset this invisibly. 49450276Speter */ 495262629Sdelphij NCURSES_SP_NAME(_nc_outch) (NCURSES_SP_ARGx '\r'); 49650276Speter} 49750276Speter 498262629Sdelphij#if NCURSES_SP_FUNCS 499262629SdelphijNCURSES_EXPORT(void) 500262629Sdelphij_nc_mvcur_wrap(void) 501262629Sdelphij{ 502262629Sdelphij NCURSES_SP_NAME(_nc_mvcur_wrap) (CURRENT_SCREEN); 503262629Sdelphij} 504262629Sdelphij#endif 505262629Sdelphij 50650276Speter/**************************************************************************** 50750276Speter * 50850276Speter * Optimized cursor movement 50950276Speter * 51050276Speter ****************************************************************************/ 51150276Speter 51250276Speter/* 51350276Speter * Perform repeated-append, returning cost 51450276Speter */ 515166124Srafanstatic NCURSES_INLINE int 51666963Speterrepeated_append(string_desc * target, int total, int num, int repeat, const char *src) 51750276Speter{ 518262629Sdelphij size_t need = (size_t) repeat * strlen(src); 51950276Speter 52066963Speter if (need < target->s_size) { 52166963Speter while (repeat-- > 0) { 52266963Speter if (_nc_safe_strcat(target, src)) { 52366963Speter total += num; 52466963Speter } else { 52566963Speter total = INFINITY; 52666963Speter break; 52762449Speter } 52850276Speter } 52962449Speter } else { 53062449Speter total = INFINITY; 53162449Speter } 53262449Speter return total; 53350276Speter} 53450276Speter 53550276Speter#ifndef NO_OPTIMIZE 53650276Speter#define NEXTTAB(fr) (fr + init_tabs - (fr % init_tabs)) 53750276Speter 53850276Speter/* 53950276Speter * Assume back_tab (CBT) does not wrap backwards at the left margin, return 54050276Speter * a negative value at that point to simplify the loop. 54150276Speter */ 54250276Speter#define LASTTAB(fr) ((fr > 0) ? ((fr - 1) / init_tabs) * init_tabs : -1) 54350276Speter 54450276Speterstatic int 545262629Sdelphijrelative_move(NCURSES_SP_DCLx 546262629Sdelphij string_desc * target, 547262629Sdelphij int from_y, 548262629Sdelphij int from_x, 549262629Sdelphij int to_y, 550262629Sdelphij int to_x, 551262685Sdelphij int ovw) 55250276Speter/* move via local motions (cuu/cuu1/cud/cud1/cub1/cub/cuf1/cuf/vpa/hpa) */ 55350276Speter{ 55466963Speter string_desc save; 55562449Speter int n, vcost = 0, hcost = 0; 55650276Speter 55766963Speter (void) _nc_str_copy(&save, target); 55850276Speter 55962449Speter if (to_y != from_y) { 56050276Speter vcost = INFINITY; 56150276Speter 56266963Speter if (row_address != 0 563166124Srafan && _nc_safe_strcat(target, TPARM_1(row_address, to_y))) { 564262629Sdelphij vcost = SP_PARM->_vpa_cost; 56550276Speter } 56650276Speter 56762449Speter if (to_y > from_y) { 56850276Speter n = (to_y - from_y); 56950276Speter 57066963Speter if (parm_down_cursor 571262629Sdelphij && SP_PARM->_cud_cost < vcost 57266963Speter && _nc_safe_strcat(_nc_str_copy(target, &save), 573166124Srafan TPARM_1(parm_down_cursor, n))) { 574262629Sdelphij vcost = SP_PARM->_cud_cost; 57550276Speter } 57650276Speter 57797049Speter if (cursor_down 578262629Sdelphij && (*cursor_down != '\n' || SP_PARM->_nl) 579262629Sdelphij && (n * SP_PARM->_cud1_cost < vcost)) { 58066963Speter vcost = repeated_append(_nc_str_copy(target, &save), 0, 581262629Sdelphij SP_PARM->_cud1_cost, n, cursor_down); 58250276Speter } 58362449Speter } else { /* (to_y < from_y) */ 58450276Speter n = (from_y - to_y); 58550276Speter 58666963Speter if (parm_up_cursor 587262629Sdelphij && SP_PARM->_cuu_cost < vcost 58866963Speter && _nc_safe_strcat(_nc_str_copy(target, &save), 589166124Srafan TPARM_1(parm_up_cursor, n))) { 590262629Sdelphij vcost = SP_PARM->_cuu_cost; 59150276Speter } 59250276Speter 593262629Sdelphij if (cursor_up && (n * SP_PARM->_cuu1_cost < vcost)) { 59466963Speter vcost = repeated_append(_nc_str_copy(target, &save), 0, 595262629Sdelphij SP_PARM->_cuu1_cost, n, cursor_up); 59650276Speter } 59750276Speter } 59850276Speter 59950276Speter if (vcost == INFINITY) 60062449Speter return (INFINITY); 60150276Speter } 60250276Speter 60366963Speter save = *target; 60450276Speter 60562449Speter if (to_x != from_x) { 60662449Speter char str[OPT_SIZE]; 60766963Speter string_desc check; 60850276Speter 60950276Speter hcost = INFINITY; 61050276Speter 61166963Speter if (column_address 61266963Speter && _nc_safe_strcat(_nc_str_copy(target, &save), 613166124Srafan TPARM_1(column_address, to_x))) { 614262629Sdelphij hcost = SP_PARM->_hpa_cost; 61550276Speter } 61650276Speter 61762449Speter if (to_x > from_x) { 61850276Speter n = to_x - from_x; 61950276Speter 62066963Speter if (parm_right_cursor 621262629Sdelphij && SP_PARM->_cuf_cost < hcost 62266963Speter && _nc_safe_strcat(_nc_str_copy(target, &save), 623166124Srafan TPARM_1(parm_right_cursor, n))) { 624262629Sdelphij hcost = SP_PARM->_cuf_cost; 62550276Speter } 62650276Speter 62762449Speter if (cursor_right) { 62862449Speter int lhcost = 0; 62950276Speter 63066963Speter (void) _nc_str_init(&check, str, sizeof(str)); 63150276Speter 63250276Speter#if USE_HARD_TABS 63350276Speter /* use hard tabs, if we have them, to do as much as possible */ 63462449Speter if (init_tabs > 0 && tab) { 63562449Speter int nxt, fr; 63650276Speter 63762449Speter for (fr = from_x; (nxt = NEXTTAB(fr)) <= to_x; fr = nxt) { 63866963Speter lhcost = repeated_append(&check, lhcost, 639262629Sdelphij SP_PARM->_ht_cost, 1, tab); 64050276Speter if (lhcost == INFINITY) 64162449Speter break; 64250276Speter } 64350276Speter 64450276Speter n = to_x - fr; 64550276Speter from_x = fr; 64650276Speter } 64750276Speter#endif /* USE_HARD_TABS */ 64850276Speter 64976726Speter if (n <= 0 || n >= (int) check.s_size) 65076726Speter ovw = FALSE; 65166963Speter#if BSD_TPUTS 65250276Speter /* 65350276Speter * If we're allowing BSD-style padding in tputs, don't generate 65450276Speter * a string with a leading digit. Otherwise, that will be 65550276Speter * interpreted as a padding value rather than sent to the 65650276Speter * screen. 65750276Speter */ 65850276Speter if (ovw 65962449Speter && n > 0 66066963Speter && n < (int) check.s_size 66162449Speter && vcost == 0 662166124Srafan && str[0] == '\0') { 663262629Sdelphij int wanted = CharOf(WANT_CHAR(SP_PARM, to_y, from_x)); 664166124Srafan if (is8bits(wanted) && isdigit(wanted)) 665166124Srafan ovw = FALSE; 666166124Srafan } 66750276Speter#endif 66850276Speter /* 66950276Speter * If we have no attribute changes, overwrite is cheaper. 67050276Speter * Note: must suppress this by passing in ovw = FALSE whenever 67150276Speter * WANT_CHAR would return invalid data. In particular, this 67250276Speter * is true between the time a hardware scroll has been done 67350276Speter * and the time the structure WANT_CHAR would access has been 67450276Speter * updated. 67550276Speter */ 67662449Speter if (ovw) { 67762449Speter int i; 67850276Speter 67976726Speter for (i = 0; i < n; i++) { 680262629Sdelphij NCURSES_CH_T ch = WANT_CHAR(SP_PARM, to_y, from_x + i); 681262629Sdelphij if (!SameAttrOf(ch, SCREEN_ATTRS(SP_PARM)) 68276726Speter#if USE_WIDEC_SUPPORT 68397049Speter || !Charable(ch) 68476726Speter#endif 68576726Speter ) { 68650276Speter ovw = FALSE; 68750276Speter break; 68850276Speter } 68976726Speter } 69050276Speter } 69162449Speter if (ovw) { 69262449Speter int i; 69350276Speter 69450276Speter for (i = 0; i < n; i++) 695262629Sdelphij *check.s_tail++ = (char) CharOf(WANT_CHAR(SP_PARM, to_y, 696184989Srafan from_x + i)); 69766963Speter *check.s_tail = '\0'; 698262629Sdelphij check.s_size -= (size_t) n; 699262629Sdelphij lhcost += n * SP_PARM->_char_padding; 700166124Srafan } else { 701262629Sdelphij lhcost = repeated_append(&check, lhcost, SP_PARM->_cuf1_cost, 70266963Speter n, cursor_right); 70350276Speter } 70450276Speter 70566963Speter if (lhcost < hcost 70666963Speter && _nc_safe_strcat(_nc_str_copy(target, &save), str)) { 70750276Speter hcost = lhcost; 70850276Speter } 70950276Speter } 71062449Speter } else { /* (to_x < from_x) */ 71150276Speter n = from_x - to_x; 71250276Speter 71366963Speter if (parm_left_cursor 714262629Sdelphij && SP_PARM->_cub_cost < hcost 71566963Speter && _nc_safe_strcat(_nc_str_copy(target, &save), 716166124Srafan TPARM_1(parm_left_cursor, n))) { 717262629Sdelphij hcost = SP_PARM->_cub_cost; 71850276Speter } 71950276Speter 72062449Speter if (cursor_left) { 72162449Speter int lhcost = 0; 72250276Speter 72366963Speter (void) _nc_str_init(&check, str, sizeof(str)); 72450276Speter 72550276Speter#if USE_HARD_TABS 72662449Speter if (init_tabs > 0 && back_tab) { 72762449Speter int nxt, fr; 72850276Speter 72962449Speter for (fr = from_x; (nxt = LASTTAB(fr)) >= to_x; fr = nxt) { 73066963Speter lhcost = repeated_append(&check, lhcost, 731262629Sdelphij SP_PARM->_cbt_cost, 732262629Sdelphij 1, back_tab); 73350276Speter if (lhcost == INFINITY) 73462449Speter break; 73550276Speter } 73650276Speter 73750276Speter n = fr - to_x; 73850276Speter } 73950276Speter#endif /* USE_HARD_TABS */ 74050276Speter 741262629Sdelphij lhcost = repeated_append(&check, lhcost, 742262629Sdelphij SP_PARM->_cub1_cost, 743262629Sdelphij n, cursor_left); 74450276Speter 74566963Speter if (lhcost < hcost 74666963Speter && _nc_safe_strcat(_nc_str_copy(target, &save), str)) { 74750276Speter hcost = lhcost; 74850276Speter } 74950276Speter } 75050276Speter } 75150276Speter 75250276Speter if (hcost == INFINITY) 75362449Speter return (INFINITY); 75450276Speter } 75550276Speter 75662449Speter return (vcost + hcost); 75750276Speter} 75850276Speter#endif /* !NO_OPTIMIZE */ 75950276Speter 76050276Speter/* 76150276Speter * With the machinery set up above, it's conceivable that 76250276Speter * onscreen_mvcur could be modified into a recursive function that does 76350276Speter * an alpha-beta search of motion space, as though it were a chess 76450276Speter * move tree, with the weight function being boolean and the search 76550276Speter * depth equated to length of string. However, this would jack up the 76650276Speter * computation cost a lot, especially on terminals without a cup 76750276Speter * capability constraining the search tree depth. So we settle for 76850276Speter * the simpler method below. 76950276Speter */ 77050276Speter 771166124Srafanstatic NCURSES_INLINE int 772262685Sdelphijonscreen_mvcur(NCURSES_SP_DCLx 773262685Sdelphij int yold, int xold, 774262685Sdelphij int ynew, int xnew, int ovw, 775262685Sdelphij NCURSES_SP_OUTC myOutCh) 77650276Speter/* onscreen move from (yold, xold) to (ynew, xnew) */ 77750276Speter{ 77866963Speter string_desc result; 77966963Speter char buffer[OPT_SIZE]; 78062449Speter int tactic = 0, newcost, usecost = INFINITY; 78162449Speter int t5_cr_cost; 78250276Speter 78350276Speter#if defined(MAIN) || defined(NCURSES_TEST) 78450276Speter struct timeval before, after; 78550276Speter 78650276Speter gettimeofday(&before, NULL); 78750276Speter#endif /* MAIN */ 78850276Speter 78966963Speter#define NullResult _nc_str_null(&result, sizeof(buffer)) 79066963Speter#define InitResult _nc_str_init(&result, buffer, sizeof(buffer)) 79166963Speter 79250276Speter /* tactic #0: use direct cursor addressing */ 793262629Sdelphij if (_nc_safe_strcpy(InitResult, TPARM_2(SP_PARM->_address_cursor, ynew, xnew))) { 79450276Speter tactic = 0; 795262629Sdelphij usecost = SP_PARM->_cup_cost; 79650276Speter 79750276Speter#if defined(TRACE) || defined(NCURSES_TEST) 79850276Speter if (!(_nc_optimize_enable & OPTIMIZE_MVCUR)) 79950276Speter goto nonlocal; 80050276Speter#endif /* TRACE */ 80150276Speter 80250276Speter /* 80350276Speter * We may be able to tell in advance that the full optimization 80450276Speter * will probably not be worth its overhead. Also, don't try to 80550276Speter * use local movement if the current attribute is anything but 80650276Speter * A_NORMAL...there are just too many ways this can screw up 80750276Speter * (like, say, local-movement \n getting mapped to some obscure 80850276Speter * character because A_ALTCHARSET is on). 80950276Speter */ 810262629Sdelphij if (yold == -1 || xold == -1 || NOT_LOCAL(SP_PARM, yold, xold, ynew, xnew)) { 81150276Speter#if defined(MAIN) || defined(NCURSES_TEST) 81262449Speter if (!profiling) { 81350276Speter (void) fputs("nonlocal\n", stderr); 81450276Speter goto nonlocal; /* always run the optimizer if profiling */ 81550276Speter } 81650276Speter#else 81750276Speter goto nonlocal; 81850276Speter#endif /* MAIN */ 81950276Speter } 82050276Speter } 82150276Speter#ifndef NO_OPTIMIZE 82250276Speter /* tactic #1: use local movement */ 82350276Speter if (yold != -1 && xold != -1 824262629Sdelphij && ((newcost = relative_move(NCURSES_SP_ARGx 825262629Sdelphij NullResult, 826262629Sdelphij yold, xold, 827262629Sdelphij ynew, xnew, ovw)) != INFINITY) 82862449Speter && newcost < usecost) { 82950276Speter tactic = 1; 83050276Speter usecost = newcost; 83150276Speter } 83250276Speter 83350276Speter /* tactic #2: use carriage-return + local movement */ 83450276Speter if (yold != -1 && carriage_return 835262629Sdelphij && ((newcost = relative_move(NCURSES_SP_ARGx 836262629Sdelphij NullResult, 837262629Sdelphij yold, 0, 838262629Sdelphij ynew, xnew, ovw)) != INFINITY) 839262629Sdelphij && SP_PARM->_cr_cost + newcost < usecost) { 84050276Speter tactic = 2; 841262629Sdelphij usecost = SP_PARM->_cr_cost + newcost; 84250276Speter } 84350276Speter 84450276Speter /* tactic #3: use home-cursor + local movement */ 84550276Speter if (cursor_home 846262629Sdelphij && ((newcost = relative_move(NCURSES_SP_ARGx 847262629Sdelphij NullResult, 848262629Sdelphij 0, 0, 849262629Sdelphij ynew, xnew, ovw)) != INFINITY) 850262629Sdelphij && SP_PARM->_home_cost + newcost < usecost) { 85150276Speter tactic = 3; 852262629Sdelphij usecost = SP_PARM->_home_cost + newcost; 85350276Speter } 85450276Speter 85550276Speter /* tactic #4: use home-down + local movement */ 85650276Speter if (cursor_to_ll 857262629Sdelphij && ((newcost = relative_move(NCURSES_SP_ARGx 858262629Sdelphij NullResult, 859262629Sdelphij screen_lines(SP_PARM) - 1, 0, 860262629Sdelphij ynew, xnew, ovw)) != INFINITY) 861262629Sdelphij && SP_PARM->_ll_cost + newcost < usecost) { 86250276Speter tactic = 4; 863262629Sdelphij usecost = SP_PARM->_ll_cost + newcost; 86450276Speter } 86550276Speter 86650276Speter /* 86750276Speter * tactic #5: use left margin for wrap to right-hand side, 86850276Speter * unless strange wrap behavior indicated by xenl might hose us. 86950276Speter */ 870262629Sdelphij t5_cr_cost = (xold > 0 ? SP_PARM->_cr_cost : 0); 87150276Speter if (auto_left_margin && !eat_newline_glitch 87250276Speter && yold > 0 && cursor_left 873262629Sdelphij && ((newcost = relative_move(NCURSES_SP_ARGx 874262629Sdelphij NullResult, 875262629Sdelphij yold - 1, screen_columns(SP_PARM) - 1, 876262629Sdelphij ynew, xnew, ovw)) != INFINITY) 877262629Sdelphij && t5_cr_cost + SP_PARM->_cub1_cost + newcost < usecost) { 87850276Speter tactic = 5; 879262629Sdelphij usecost = t5_cr_cost + SP_PARM->_cub1_cost + newcost; 88050276Speter } 88150276Speter 88250276Speter /* 88350276Speter * These cases are ordered by estimated relative frequency. 88450276Speter */ 88566963Speter if (tactic) 88666963Speter InitResult; 88762449Speter switch (tactic) { 88862449Speter case 1: 889262629Sdelphij (void) relative_move(NCURSES_SP_ARGx 890262629Sdelphij &result, 891262629Sdelphij yold, xold, 892262629Sdelphij ynew, xnew, ovw); 89362449Speter break; 89462449Speter case 2: 89566963Speter (void) _nc_safe_strcpy(&result, carriage_return); 896262629Sdelphij (void) relative_move(NCURSES_SP_ARGx 897262629Sdelphij &result, 898262629Sdelphij yold, 0, 899262629Sdelphij ynew, xnew, ovw); 90062449Speter break; 90162449Speter case 3: 90266963Speter (void) _nc_safe_strcpy(&result, cursor_home); 903262629Sdelphij (void) relative_move(NCURSES_SP_ARGx 904262629Sdelphij &result, 0, 0, 905262629Sdelphij ynew, xnew, ovw); 90662449Speter break; 90762449Speter case 4: 90866963Speter (void) _nc_safe_strcpy(&result, cursor_to_ll); 909262629Sdelphij (void) relative_move(NCURSES_SP_ARGx 910262629Sdelphij &result, 911262629Sdelphij screen_lines(SP_PARM) - 1, 0, 912262629Sdelphij ynew, xnew, ovw); 91362449Speter break; 91462449Speter case 5: 91562449Speter if (xold > 0) 91666963Speter (void) _nc_safe_strcat(&result, carriage_return); 91766963Speter (void) _nc_safe_strcat(&result, cursor_left); 918262629Sdelphij (void) relative_move(NCURSES_SP_ARGx 919262629Sdelphij &result, 920262629Sdelphij yold - 1, screen_columns(SP_PARM) - 1, 921262629Sdelphij ynew, xnew, ovw); 92262449Speter break; 92350276Speter } 92450276Speter#endif /* !NO_OPTIMIZE */ 92550276Speter 926166124Srafan nonlocal: 92750276Speter#if defined(MAIN) || defined(NCURSES_TEST) 92850276Speter gettimeofday(&after, NULL); 92950276Speter diff = after.tv_usec - before.tv_usec 93050276Speter + (after.tv_sec - before.tv_sec) * 1000000; 93150276Speter if (!profiling) 93262449Speter (void) fprintf(stderr, 933166124Srafan "onscreen: %d microsec, %f 28.8Kbps char-equivalents\n", 93466963Speter (int) diff, diff / 288); 93550276Speter#endif /* MAIN */ 93650276Speter 93762449Speter if (usecost != INFINITY) { 93850276Speter TPUTS_TRACE("mvcur"); 939262629Sdelphij NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 940262685Sdelphij buffer, 1, myOutCh); 941262629Sdelphij SP_PARM->_cursrow = ynew; 942262629Sdelphij SP_PARM->_curscol = xnew; 94362449Speter return (OK); 94462449Speter } else 94562449Speter return (ERR); 94650276Speter} 94750276Speter 948262685Sdelphij/* 949262685Sdelphij * optimized cursor move from (yold, xold) to (ynew, xnew) 950262685Sdelphij */ 951262685Sdelphijstatic int 952262685Sdelphij_nc_real_mvcur(NCURSES_SP_DCLx 953262685Sdelphij int yold, int xold, 954262685Sdelphij int ynew, int xnew, 955262685Sdelphij NCURSES_SP_OUTC myOutCh, 956262685Sdelphij int ovw) 95750276Speter{ 958166124Srafan NCURSES_CH_T oldattr; 959166124Srafan int code; 960166124Srafan 961262629Sdelphij TR(TRACE_CALLS | TRACE_MOVE, (T_CALLED("_nc_tinfo_mvcur(%p,%d,%d,%d,%d)"), 962262629Sdelphij (void *) SP_PARM, yold, xold, ynew, xnew)); 96350276Speter 964262629Sdelphij if (SP_PARM == 0) { 965166124Srafan code = ERR; 966166124Srafan } else if (yold == ynew && xold == xnew) { 967166124Srafan code = OK; 968166124Srafan } else { 96997049Speter 970166124Srafan /* 971166124Srafan * Most work here is rounding for terminal boundaries getting the 972166124Srafan * column position implied by wraparound or the lack thereof and 973166124Srafan * rolling up the screen to get ynew on the screen. 974166124Srafan */ 975262629Sdelphij if (xnew >= screen_columns(SP_PARM)) { 976262629Sdelphij ynew += xnew / screen_columns(SP_PARM); 977262629Sdelphij xnew %= screen_columns(SP_PARM); 978166124Srafan } 97950276Speter 980166124Srafan /* 981166124Srafan * Force restore even if msgr is on when we're in an alternate 982166124Srafan * character set -- these have a strong tendency to screw up the CR & 983166124Srafan * LF used for local character motions! 984166124Srafan */ 985262629Sdelphij oldattr = SCREEN_ATTRS(SP_PARM); 986166124Srafan if ((AttrOf(oldattr) & A_ALTCHARSET) 987166124Srafan || (AttrOf(oldattr) && !move_standout_mode)) { 988166124Srafan TR(TRACE_CHARPUT, ("turning off (%#lx) %s before move", 989166124Srafan (unsigned long) AttrOf(oldattr), 990166124Srafan _traceattr(AttrOf(oldattr)))); 991262629Sdelphij (void) VIDATTR(SP_PARM, A_NORMAL, 0); 992166124Srafan } 99350276Speter 994262629Sdelphij if (xold >= screen_columns(SP_PARM)) { 995166124Srafan int l; 99650276Speter 997262629Sdelphij if (SP_PARM->_nl) { 998262629Sdelphij l = (xold + 1) / screen_columns(SP_PARM); 999166124Srafan yold += l; 1000262629Sdelphij if (yold >= screen_lines(SP_PARM)) 1001262629Sdelphij l -= (yold - screen_lines(SP_PARM) - 1); 100250276Speter 1003166124Srafan if (l > 0) { 100497049Speter if (carriage_return) { 1005262685Sdelphij NCURSES_PUTP2("carriage_return", carriage_return); 1006262685Sdelphij } else { 1007262685Sdelphij myOutCh(NCURSES_SP_ARGx '\r'); 1008262685Sdelphij } 100997049Speter xold = 0; 1010166124Srafan 1011166124Srafan while (l > 0) { 1012166124Srafan if (newline) { 1013262685Sdelphij NCURSES_PUTP2("newline", newline); 1014262685Sdelphij } else { 1015262685Sdelphij myOutCh(NCURSES_SP_ARGx '\n'); 1016262685Sdelphij } 1017166124Srafan l--; 1018166124Srafan } 101997049Speter } 1020166124Srafan } else { 1021166124Srafan /* 1022166124Srafan * If caller set nonl(), we cannot really use newlines to 1023166124Srafan * position to the next row. 1024166124Srafan */ 1025166124Srafan xold = -1; 1026166124Srafan yold = -1; 102762449Speter } 102850276Speter } 102950276Speter 1030262629Sdelphij if (yold > screen_lines(SP_PARM) - 1) 1031262629Sdelphij yold = screen_lines(SP_PARM) - 1; 1032262629Sdelphij if (ynew > screen_lines(SP_PARM) - 1) 1033262629Sdelphij ynew = screen_lines(SP_PARM) - 1; 103450276Speter 1035166124Srafan /* destination location is on screen now */ 1036262685Sdelphij code = onscreen_mvcur(NCURSES_SP_ARGx yold, xold, ynew, xnew, ovw, myOutCh); 1037166124Srafan 1038166124Srafan /* 1039166124Srafan * Restore attributes if we disabled them before moving. 1040166124Srafan */ 1041262629Sdelphij if (!SameAttrOf(oldattr, SCREEN_ATTRS(SP_PARM))) { 1042166124Srafan TR(TRACE_CHARPUT, ("turning on (%#lx) %s after move", 1043166124Srafan (unsigned long) AttrOf(oldattr), 1044166124Srafan _traceattr(AttrOf(oldattr)))); 1045262629Sdelphij (void) VIDATTR(SP_PARM, AttrOf(oldattr), GetPair(oldattr)); 1046166124Srafan } 1047166124Srafan } 1048166124Srafan returnCode(code); 104950276Speter} 105050276Speter 1051262685Sdelphij/* 1052262685Sdelphij * These entrypoints are used within the library. 1053262685Sdelphij */ 1054262629SdelphijNCURSES_EXPORT(int) 1055262685SdelphijNCURSES_SP_NAME(_nc_mvcur) (NCURSES_SP_DCLx 1056262685Sdelphij int yold, int xold, 1057262685Sdelphij int ynew, int xnew) 1058262685Sdelphij{ 1059262685Sdelphij return _nc_real_mvcur(NCURSES_SP_ARGx yold, xold, ynew, xnew, 1060262685Sdelphij NCURSES_SP_NAME(_nc_outch), 1061262685Sdelphij TRUE); 1062262685Sdelphij} 1063262685Sdelphij 1064262685Sdelphij#if NCURSES_SP_FUNCS 1065262685SdelphijNCURSES_EXPORT(int) 1066262685Sdelphij_nc_mvcur(int yold, int xold, 1067262685Sdelphij int ynew, int xnew) 1068262685Sdelphij{ 1069262685Sdelphij return NCURSES_SP_NAME(_nc_mvcur) (CURRENT_SCREEN, yold, xold, ynew, xnew); 1070262685Sdelphij} 1071262685Sdelphij#endif 1072262685Sdelphij 1073262685Sdelphij#if defined(USE_TERM_DRIVER) 1074262685Sdelphij/* 1075262685Sdelphij * The terminal driver does not support the external "mvcur()". 1076262685Sdelphij */ 1077262685SdelphijNCURSES_EXPORT(int) 1078262685SdelphijTINFO_MVCUR(NCURSES_SP_DCLx int yold, int xold, int ynew, int xnew) 1079262685Sdelphij{ 1080262685Sdelphij return _nc_real_mvcur(NCURSES_SP_ARGx 1081262685Sdelphij yold, xold, 1082262685Sdelphij ynew, xnew, 1083262685Sdelphij NCURSES_SP_NAME(_nc_outch), 1084262685Sdelphij TRUE); 1085262685Sdelphij} 1086262685Sdelphij 1087262685Sdelphij#else /* !USE_TERM_DRIVER */ 1088262685Sdelphij 1089262685Sdelphij/* 1090262685Sdelphij * These entrypoints support users of the library. 1091262685Sdelphij */ 1092262685SdelphijNCURSES_EXPORT(int) 1093262685SdelphijNCURSES_SP_NAME(mvcur) (NCURSES_SP_DCLx int yold, int xold, int ynew, 1094262685Sdelphij int xnew) 1095262685Sdelphij{ 1096262685Sdelphij return _nc_real_mvcur(NCURSES_SP_ARGx 1097262685Sdelphij yold, xold, 1098262685Sdelphij ynew, xnew, 1099262685Sdelphij NCURSES_SP_NAME(_nc_putchar), 1100262685Sdelphij FALSE); 1101262685Sdelphij} 1102262685Sdelphij 1103262685Sdelphij#if NCURSES_SP_FUNCS 1104262685SdelphijNCURSES_EXPORT(int) 1105262629Sdelphijmvcur(int yold, int xold, int ynew, int xnew) 1106262629Sdelphij{ 1107262629Sdelphij return NCURSES_SP_NAME(mvcur) (CURRENT_SCREEN, yold, xold, ynew, xnew); 1108262629Sdelphij} 1109262629Sdelphij#endif 1110262685Sdelphij#endif /* USE_TERM_DRIVER */ 1111262629Sdelphij 111250276Speter#if defined(TRACE) || defined(NCURSES_TEST) 1113166124SrafanNCURSES_EXPORT_VAR(int) _nc_optimize_enable = OPTIMIZE_ALL; 111450276Speter#endif 111550276Speter 111650276Speter#if defined(MAIN) || defined(NCURSES_TEST) 111750276Speter/**************************************************************************** 111850276Speter * 111950276Speter * Movement optimizer test code 112050276Speter * 112150276Speter ****************************************************************************/ 112250276Speter 112350276Speter#include <tic.h> 112450276Speter#include <dump_entry.h> 1125174993Srafan#include <time.h> 112650276Speter 1127166124SrafanNCURSES_EXPORT_VAR(const char *) _nc_progname = "mvcur"; 112850276Speter 1129166124Srafanstatic unsigned long xmits; 113050276Speter 113162449Speter/* these override lib_tputs.c */ 113276726SpeterNCURSES_EXPORT(int) 113397049Spetertputs(const char *string, int affcnt GCC_UNUSED, int (*outc) (int) GCC_UNUSED) 113450276Speter/* stub tputs() that dumps sequences in a visible form */ 113550276Speter{ 113650276Speter if (profiling) 113750276Speter xmits += strlen(string); 113850276Speter else 113950276Speter (void) fputs(_nc_visbuf(string), stdout); 114062449Speter return (OK); 114150276Speter} 114250276Speter 114376726SpeterNCURSES_EXPORT(int) 114462449Speterputp(const char *string) 114550276Speter{ 114662449Speter return (tputs(string, 1, _nc_outch)); 114750276Speter} 114850276Speter 114976726SpeterNCURSES_EXPORT(int) 115062449Speter_nc_outch(int ch) 115150276Speter{ 115250276Speter putc(ch, stdout); 115350276Speter return OK; 115450276Speter} 115550276Speter 115676726SpeterNCURSES_EXPORT(int) 115762449Speterdelay_output(int ms GCC_UNUSED) 115850276Speter{ 115962449Speter return OK; 116062449Speter} 116162449Speter 1162166124Srafanstatic char tname[PATH_MAX]; 116362449Speter 116462449Speterstatic void 116562449Speterload_term(void) 116662449Speter{ 116750276Speter (void) setupterm(tname, STDOUT_FILENO, NULL); 116850276Speter} 116950276Speter 117062449Speterstatic int 117162449Speterroll(int n) 117250276Speter{ 117350276Speter int i, j; 117450276Speter 117550276Speter i = (RAND_MAX / n) * n; 117650276Speter while ((j = rand()) >= i) 117750276Speter continue; 117850276Speter return (j % n); 117950276Speter} 118050276Speter 118162449Speterint 118262449Spetermain(int argc GCC_UNUSED, char *argv[]GCC_UNUSED) 118350276Speter{ 1184174993Srafan strcpy(tname, getenv("TERM")); 118550276Speter load_term(); 1186166124Srafan _nc_setupscreen(lines, columns, stdout, FALSE, 0); 118750276Speter baudrate(); 118850276Speter 118950276Speter _nc_mvcur_init(); 119050276Speter NC_BUFFERED(FALSE); 119150276Speter 119250276Speter (void) puts("The mvcur tester. Type ? for help"); 119350276Speter 119450276Speter fputs("smcup:", stdout); 119550276Speter putchar('\n'); 119650276Speter 119762449Speter for (;;) { 119862449Speter int fy, fx, ty, tx, n, i; 119962449Speter char buf[BUFSIZ], capname[BUFSIZ]; 120050276Speter 120150276Speter (void) fputs("> ", stdout); 120250276Speter (void) fgets(buf, sizeof(buf), stdin); 120350276Speter 120462449Speter if (buf[0] == '?') { 120562449Speter (void) puts("? -- display this help message"); 120662449Speter (void) 120762449Speter puts("fy fx ty tx -- (4 numbers) display (fy,fx)->(ty,tx) move"); 120862449Speter (void) puts("s[croll] n t b m -- display scrolling sequence"); 120962449Speter (void) 121062449Speter printf("r[eload] -- reload terminal info for %s\n", 121166963Speter termname()); 121262449Speter (void) 121362449Speter puts("l[oad] <term> -- load terminal info for type <term>"); 121462449Speter (void) puts("d[elete] <cap> -- delete named capability"); 121562449Speter (void) puts("i[nspect] -- display terminal capabilities"); 121662449Speter (void) 121762449Speter puts("c[ost] -- dump cursor-optimization cost table"); 121862449Speter (void) puts("o[optimize] -- toggle movement optimization"); 121962449Speter (void) 122062449Speter puts("t[orture] <num> -- torture-test with <num> random moves"); 122162449Speter (void) puts("q[uit] -- quit the program"); 122262449Speter } else if (sscanf(buf, "%d %d %d %d", &fy, &fx, &ty, &tx) == 4) { 122350276Speter struct timeval before, after; 122450276Speter 122550276Speter putchar('"'); 122650276Speter 122750276Speter gettimeofday(&before, NULL); 122850276Speter mvcur(fy, fx, ty, tx); 122950276Speter gettimeofday(&after, NULL); 123050276Speter 123150276Speter printf("\" (%ld msec)\n", 123266963Speter (long) (after.tv_usec - before.tv_usec 123366963Speter + (after.tv_sec - before.tv_sec) 123466963Speter * 1000000)); 123562449Speter } else if (sscanf(buf, "s %d %d %d %d", &fy, &fx, &ty, &tx) == 4) { 123650276Speter struct timeval before, after; 123750276Speter 123850276Speter putchar('"'); 123950276Speter 124050276Speter gettimeofday(&before, NULL); 124150276Speter _nc_scrolln(fy, fx, ty, tx); 124250276Speter gettimeofday(&after, NULL); 124350276Speter 124450276Speter printf("\" (%ld msec)\n", 124566963Speter (long) (after.tv_usec - before.tv_usec + (after.tv_sec - 124666963Speter before.tv_sec) 124766963Speter * 1000000)); 124862449Speter } else if (buf[0] == 'r') { 124950276Speter (void) strcpy(tname, termname()); 125050276Speter load_term(); 125162449Speter } else if (sscanf(buf, "l %s", tname) == 1) { 125250276Speter load_term(); 125362449Speter } else if (sscanf(buf, "d %s", capname) == 1) { 125462449Speter struct name_table_entry const *np = _nc_find_entry(capname, 1255174993Srafan _nc_get_hash_table(FALSE)); 125650276Speter 125750276Speter if (np == NULL) 125850276Speter (void) printf("No such capability as \"%s\"\n", capname); 125962449Speter else { 126062449Speter switch (np->nte_type) { 126150276Speter case BOOLEAN: 126250276Speter cur_term->type.Booleans[np->nte_index] = FALSE; 126362449Speter (void) 126462449Speter printf("Boolean capability `%s' (%d) turned off.\n", 126566963Speter np->nte_name, np->nte_index); 126650276Speter break; 126750276Speter 126850276Speter case NUMBER: 126962449Speter cur_term->type.Numbers[np->nte_index] = ABSENT_NUMERIC; 127050276Speter (void) printf("Number capability `%s' (%d) set to -1.\n", 127166963Speter np->nte_name, np->nte_index); 127250276Speter break; 127350276Speter 127450276Speter case STRING: 127562449Speter cur_term->type.Strings[np->nte_index] = ABSENT_STRING; 127650276Speter (void) printf("String capability `%s' (%d) deleted.\n", 127766963Speter np->nte_name, np->nte_index); 127850276Speter break; 127950276Speter } 128050276Speter } 128162449Speter } else if (buf[0] == 'i') { 128262449Speter dump_init((char *) NULL, F_TERMINFO, S_TERMINFO, 70, 0, FALSE); 1283174993Srafan dump_entry(&cur_term->type, FALSE, TRUE, 0, 0); 128462449Speter putchar('\n'); 128562449Speter } else if (buf[0] == 'o') { 128662449Speter if (_nc_optimize_enable & OPTIMIZE_MVCUR) { 128762449Speter _nc_optimize_enable &= ~OPTIMIZE_MVCUR; 128862449Speter (void) puts("Optimization is now off."); 128962449Speter } else { 129062449Speter _nc_optimize_enable |= OPTIMIZE_MVCUR; 129162449Speter (void) puts("Optimization is now on."); 129262449Speter } 129350276Speter } 129450276Speter /* 129550276Speter * You can use the `t' test to profile and tune the movement 129650276Speter * optimizer. Use iteration values in three digits or more. 129750276Speter * At above 5000 iterations the profile timing averages are stable 129850276Speter * to within a millisecond or three. 129950276Speter * 130050276Speter * The `overhead' field of the report will help you pick a 130150276Speter * COMPUTE_OVERHEAD figure appropriate for your processor and 130250276Speter * expected line speed. The `total estimated time' is 130350276Speter * computation time plus a character-transmission time 130450276Speter * estimate computed from the number of transmits and the baud 130550276Speter * rate. 130650276Speter * 130750276Speter * Use this together with the `o' command to get a read on the 130850276Speter * optimizer's effectiveness. Compare the total estimated times 130950276Speter * for `t' runs of the same length in both optimized and un-optimized 131050276Speter * modes. As long as the optimized times are less, the optimizer 131150276Speter * is winning. 131250276Speter */ 131362449Speter else if (sscanf(buf, "t %d", &n) == 1) { 131466963Speter float cumtime = 0.0, perchar; 131562449Speter int speeds[] = 131662449Speter {2400, 9600, 14400, 19200, 28800, 38400, 0}; 131750276Speter 131862449Speter srand((unsigned) (getpid() + time((time_t *) 0))); 131950276Speter profiling = TRUE; 132050276Speter xmits = 0; 132162449Speter for (i = 0; i < n; i++) { 132250276Speter /* 132350276Speter * This does a move test between two random locations, 132450276Speter * Random moves probably short-change the optimizer, 132550276Speter * which will work better on the short moves probably 132650276Speter * typical of doupdate()'s usage pattern. Still, 132750276Speter * until we have better data... 132850276Speter */ 132950276Speter#ifdef FIND_COREDUMP 133050276Speter int from_y = roll(lines); 133150276Speter int to_y = roll(lines); 133250276Speter int from_x = roll(columns); 133350276Speter int to_x = roll(columns); 133450276Speter 133550276Speter printf("(%d,%d) -> (%d,%d)\n", from_y, from_x, to_y, to_x); 133650276Speter mvcur(from_y, from_x, to_y, to_x); 133750276Speter#else 133850276Speter mvcur(roll(lines), roll(columns), roll(lines), roll(columns)); 133950276Speter#endif /* FIND_COREDUMP */ 134050276Speter if (diff) 134150276Speter cumtime += diff; 134250276Speter } 134350276Speter profiling = FALSE; 134450276Speter 134550276Speter /* 134650276Speter * Average milliseconds per character optimization time. 134750276Speter * This is the key figure to watch when tuning the optimizer. 134850276Speter */ 134950276Speter perchar = cumtime / n; 135050276Speter 135150276Speter (void) printf("%d moves (%ld chars) in %d msec, %f msec each:\n", 135266963Speter n, xmits, (int) cumtime, perchar); 135350276Speter 135462449Speter for (i = 0; speeds[i]; i++) { 135550276Speter /* 135650276Speter * Total estimated time for the moves, computation and 135750276Speter * transmission both. Transmission time is an estimate 135850276Speter * assuming 9 bits/char, 8 bits + 1 stop bit. 135950276Speter */ 136050276Speter float totalest = cumtime + xmits * 9 * 1e6 / speeds[i]; 136150276Speter 136250276Speter /* 136350276Speter * Per-character optimization overhead in character transmits 136450276Speter * at the current speed. Round this to the nearest integer 136550276Speter * to figure COMPUTE_OVERHEAD for the speed. 136650276Speter */ 136750276Speter float overhead = speeds[i] * perchar / 1e6; 136850276Speter 136962449Speter (void) 137062449Speter printf("%6d bps: %3.2f char-xmits overhead; total estimated time %15.2f\n", 137166963Speter speeds[i], overhead, totalest); 137250276Speter } 137362449Speter } else if (buf[0] == 'c') { 1374262629Sdelphij (void) printf("char padding: %d\n", CURRENT_SCREEN->_char_padding); 1375262629Sdelphij (void) printf("cr cost: %d\n", CURRENT_SCREEN->_cr_cost); 1376262629Sdelphij (void) printf("cup cost: %d\n", CURRENT_SCREEN->_cup_cost); 1377262629Sdelphij (void) printf("home cost: %d\n", CURRENT_SCREEN->_home_cost); 1378262629Sdelphij (void) printf("ll cost: %d\n", CURRENT_SCREEN->_ll_cost); 137950276Speter#if USE_HARD_TABS 1380262629Sdelphij (void) printf("ht cost: %d\n", CURRENT_SCREEN->_ht_cost); 1381262629Sdelphij (void) printf("cbt cost: %d\n", CURRENT_SCREEN->_cbt_cost); 138250276Speter#endif /* USE_HARD_TABS */ 1383262629Sdelphij (void) printf("cub1 cost: %d\n", CURRENT_SCREEN->_cub1_cost); 1384262629Sdelphij (void) printf("cuf1 cost: %d\n", CURRENT_SCREEN->_cuf1_cost); 1385262629Sdelphij (void) printf("cud1 cost: %d\n", CURRENT_SCREEN->_cud1_cost); 1386262629Sdelphij (void) printf("cuu1 cost: %d\n", CURRENT_SCREEN->_cuu1_cost); 1387262629Sdelphij (void) printf("cub cost: %d\n", CURRENT_SCREEN->_cub_cost); 1388262629Sdelphij (void) printf("cuf cost: %d\n", CURRENT_SCREEN->_cuf_cost); 1389262629Sdelphij (void) printf("cud cost: %d\n", CURRENT_SCREEN->_cud_cost); 1390262629Sdelphij (void) printf("cuu cost: %d\n", CURRENT_SCREEN->_cuu_cost); 1391262629Sdelphij (void) printf("hpa cost: %d\n", CURRENT_SCREEN->_hpa_cost); 1392262629Sdelphij (void) printf("vpa cost: %d\n", CURRENT_SCREEN->_vpa_cost); 139362449Speter } else if (buf[0] == 'x' || buf[0] == 'q') 139450276Speter break; 139550276Speter else 139650276Speter (void) puts("Invalid command."); 139750276Speter } 139850276Speter 139950276Speter (void) fputs("rmcup:", stdout); 140050276Speter _nc_mvcur_wrap(); 140150276Speter putchar('\n'); 140250276Speter 140362449Speter return (0); 140450276Speter} 140550276Speter 140650276Speter#endif /* MAIN */ 140750276Speter 140850276Speter/* lib_mvcur.c ends here */ 1409