2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1995-1999 by Sun Microsystems, Inc.
2N/A * All rights reserved.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/* LINTLIBRARY */
2N/A
2N/A/*
2N/A * doupdate.c
2N/A *
2N/A * XCurses Library
2N/A *
2N/A * Copyright 1990, 1995 by Mortice Kern Systems Inc. All rights reserved.
2N/A *
2N/A */
2N/A
2N/A#ifdef M_RCSID
2N/A#ifndef lint
2N/Astatic char const rcsID[] =
2N/A"$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/"
2N/A"libxcurses/src/libc/xcurses/rcs/doupdate.c 1.22 1998/06/04 12:13:38 "
2N/A"cbates Exp $";
2N/A#endif
2N/A#endif
2N/A
2N/A#include <sys/isa_defs.h>
2N/A#include <private.h>
2N/A#include <string.h>
2N/A#include <signal.h>
2N/A
2N/A#undef SIGTSTP
2N/A
2N/A/*
2N/A * This value is the ideal length for the cursor addressing sequence
2N/A * being four bytes long, ie. "<escape><cursor addressing code><row><col>".
2N/A * eg. VT52 - "\EYrc" or ADM3A - "\E=rc"
2N/A */
2N/A#define JUMP_SIZE 4
2N/A
2N/A/*
2N/A * This value is the ideal length for the clear-to-eol sequence
2N/A * being two bytes long, ie "<escape><clear eol code>".
2N/A */
2N/A#define CEOL_SIZE 2
2N/A
2N/A#define GOTO(r, c) ((void) __m_mvcur(curscr->_cury, curscr->_curx,\
2N/A r, c, __m_outc), curscr->_cury = r, curscr->_curx = c)
2N/A
2N/Atypedef struct cost_op {
2N/A short cost;
2N/A short op;
2N/A} lcost;
2N/A
2N/Atypedef void (*t_action)(int, int);
2N/A
2N/A
2N/A#define LC(i, j) (lc[(i) * (LINES + 1) + (j)])
2N/A
2N/Astatic lcost *lc = NULL;
2N/A#if defined(_LP64)
2N/Astatic unsigned int *nhash = NULL;
2N/A#else
2N/Astatic unsigned long *nhash = NULL;
2N/A#endif
2N/Astatic t_action *del = NULL;
2N/Astatic t_action *ins_rep = NULL;
2N/A
2N/Astatic WINDOW *newscr;
2N/A
2N/Astatic void erase_bottom(int, int);
2N/Astatic void clear_bottom(int);
2N/Astatic void complex(void);
2N/Astatic int cost(int, int);
2N/Astatic void lines_delete(int, int);
2N/Astatic void lines_insert(int, int);
2N/Astatic void lines_replace(int, int);
2N/Astatic void script(int, int);
2N/Astatic int scroll_up(int);
2N/Astatic void simple(void);
2N/Astatic void text_replace(int);
2N/A#if 0
2N/Astatic int scroll_dn(int);
2N/A#endif
2N/A
2N/A
2N/A/*
2N/A * Wrapper that streams Curses output.
2N/A *
2N/A * All escape sequences going to the screen come through here.
2N/A * All ordinary characters go to the screen via the putc in doupdate.c
2N/A */
2N/Aint
2N/A__m_outc(int ch)
2N/A{
2N/A return (putc(ch, __m_screen->_of));
2N/A}
2N/A
2N/A/*
2N/A * Allocate or grow doupdate() structures.
2N/A */
2N/Aint
2N/A__m_doupdate_init(void)
2N/A{
2N/A void *new;
2N/A static short nlines = 0;
2N/A
2N/A if (lines <= 0)
2N/A return (-1);
2N/A
2N/A if (lines <= nlines)
2N/A return (0);
2N/A
2N/A new = malloc((lines + 1) * (lines + 1) * sizeof (*lc));
2N/A if (new == NULL)
2N/A return (-1);
2N/A if (lc != NULL)
2N/A free(lc);
2N/A lc = (lcost *) new;
2N/A
2N/A new = malloc((lines + lines) * sizeof (*del));
2N/A if (new == NULL)
2N/A return (-1);
2N/A if (del != NULL)
2N/A free(del);
2N/A del = (t_action *) new;
2N/A ins_rep = del + lines;
2N/A
2N/A new = malloc(lines * sizeof (*nhash));
2N/A if (new == NULL)
2N/A return (-1);
2N/A if (nhash != NULL)
2N/A free(nhash);
2N/A#if defined(_LP64)
2N/A nhash = (unsigned int *) new;
2N/A#else
2N/A nhash = (unsigned long *) new;
2N/A#endif
2N/A
2N/A nlines = lines;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Aerase_bottom(int start, int end)
2N/A{
2N/A int i;
2N/A
2N/A for (i = start; i < end; ++i) {
2N/A (void) __m_cc_erase(curscr, i, 0, i, curscr->_maxx - 1);
2N/A __m_cc_hash(curscr, __m_screen->_hash, i);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Clear from the start of the current row to bottom of screen.
2N/A */
2N/Astatic void
2N/Aclear_bottom(int y)
2N/A{
2N/A /* Restore default color pair before doing area clears. */
2N/A if (back_color_erase)
2N/A (void) vid_puts(WA_NORMAL, 0, (void *) 0, __m_outc);
2N/A
2N/A if (y == 0 && clear_screen != NULL) {
2N/A (void) TPUTS(clear_screen, 1, __m_outc);
2N/A } else {
2N/A (void) __m_mvcur(-1, -1, y, 0, __m_outc);
2N/A if (clr_eos != NULL) {
2N/A (void) TPUTS(clr_eos, 1, __m_outc);
2N/A } else if (clr_eol != NULL) {
2N/A for (;;) {
2N/A (void) TPUTS(clr_eol, 1, __m_outc);
2N/A if (LINES <= y)
2N/A break;
2N/A (void) __m_mvcur(y, 0, y + 1, 0, __m_outc);
2N/A ++y;
2N/A }
2N/A }
2N/A }
2N/A
2N/A curscr->_cury = y;
2N/A curscr->_curx = 0;
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * Rewrite of text_replace() implementation by C. Bates of MKS
2N/A *
2N/A * This code creates a list of 'regions' for each test line which
2N/A * is to be replaced. Each region describes a portion of the line.
2N/A * Logic is performed on the list of regions, then the regions
2N/A * are used to generate output.
2N/A */
2N/Atypedef struct LineRegion {
2N/A int col; /* Starting column of region */
2N/A int size; /* Size of region */
2N/A /* 0: Difference Region, 1: Common Region, 2: Delete Region */
2N/A int type;
2N/A} LineRegion;
2N/A#define REGION_DIFFERENT 0
2N/A#define REGION_COMMON 1
2N/A#define REGION_DELETE 2
2N/A
2N/A#define DELETE_SEARCH_LIMIT 4
2N/A#define DELETE_THRESHOLD 10
2N/A
2N/Astatic LineRegion regions[1024];
2N/Aint nRegions = 0;
2N/A
2N/A/*
2N/A * Return the first column of the completely blank End-of-line
2N/A */
2N/Astatic int
2N/A_find_blank_tail(int row)
2N/A{
2N/A cchar_t *nptr;
2N/A int tail = COLS;
2N/A
2N/A if (!clr_eol)
2N/A return (COLS);
2N/A /*
2N/A * Find start of blank tail region.
2N/A */
2N/A nptr = &newscr->_line[row][COLS];
2N/A for (; 0 < tail; --tail) {
2N/A if (!__m_cc_compare(--nptr, &newscr->_bg, 1))
2N/A break;
2N/A }
2N/A return (tail);
2N/A}
2N/A
2N/A/*
2N/A * Send all the characters in the region to the terminal
2N/A */
2N/Astatic void
2N/A_writeRegion(int row, LineRegion region)
2N/A{
2N/A short npair;
2N/A attr_t nattr;
2N/A int i;
2N/A cchar_t *optr = &curscr->_line[row][region.col];
2N/A cchar_t *nptr = &newscr->_line[row][region.col];
2N/A
2N/A for (i = 0; i < region.size; i++, nptr++, optr++) {
2N/A nattr = nptr->_at;
2N/A npair = nptr->_co;
2N/A
2N/A /*
2N/A * Change attribute state.
2N/A */
2N/A if ((ATTR_STATE != nattr) || (optr->_at != nattr) ||
2N/A (cur_term->_co != npair)) {
2N/A (void) vid_puts(nattr, npair, NULL, __m_outc);
2N/A }
2N/A /*
2N/A * Don't display internal characters.
2N/A */
2N/A if (nptr->_f)
2N/A (void) __m_cc_write(nptr);
2N/A
2N/A /*
2N/A * Update copy of screen image.
2N/A */
2N/A *optr = *nptr;
2N/A curscr->_curx = region.col + i + 1;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Delete some characters from the terminal for this region
2N/A */
2N/Astatic void
2N/A_deleteRegion(int row, LineRegion region)
2N/A{
2N/A int i;
2N/A cchar_t *optr = &curscr->_line[row][region.col];
2N/A
2N/A if ((region.size <= 1) || !parm_dch) {
2N/A for (i = 0; i < region.size; i++)
2N/A (void) TPUTS(delete_character, 1, __m_outc);
2N/A } else {
2N/A (void) TPUTS(tparm(parm_dch, (long)region.size,
2N/A 0, 0, 0, 0, 0, 0, 0, 0), region.size, __m_outc);
2N/A }
2N/A for (i = region.col; i < COLS - region.size; i++) {
2N/A /*
2N/A * Delete the chars in the image of the real screen
2N/A */
2N/A *optr = *(optr + region.size);
2N/A optr++;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Use clr_eol control if possible
2N/A */
2N/Astatic void
2N/A_clearToEOL(int row, int tail)
2N/A{
2N/A if (tail < COLS) {
2N/A GOTO(row, tail);
2N/A /*
2N/A * Restore default color pair before area clear.
2N/A */
2N/A if (back_color_erase)
2N/A (void) vid_puts(WA_NORMAL, 0, NULL, __m_outc);
2N/A
2N/A (void) TPUTS(clr_eol, 1, __m_outc);
2N/A (void) __m_cc_erase(curscr, row, tail, row, COLS - 1);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Delete leading common region
2N/A */
2N/Astatic void
2N/A_normalizeRegions1(void)
2N/A{
2N/A int iRegion;
2N/A
2N/A /*
2N/A * Delete leading common region
2N/A */
2N/A if (regions[0].type == REGION_COMMON) {
2N/A nRegions--;
2N/A for (iRegion = 0; iRegion < nRegions; iRegion++) {
2N/A regions[iRegion] = regions[iRegion + 1];
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Give each region a size, then delete all trailing common regions
2N/A */
2N/Astatic void
2N/A_normalizeRegions2(void)
2N/A{
2N/A int iRegion;
2N/A
2N/A for (iRegion = 0; iRegion < nRegions - 1; iRegion++) {
2N/A regions[iRegion].size = regions[iRegion + 1].col -
2N/A regions[iRegion].col;
2N/A }
2N/A regions[nRegions - 1].size = COLS - regions[nRegions - 1].col;
2N/A
2N/A /*
2N/A * Delete trailing common regions
2N/A */
2N/A while (regions[nRegions - 1].type == REGION_COMMON)
2N/A nRegions--;
2N/A}
2N/A
2N/A/*
2N/A * Tiny common regions are merged into adjacent difference regions
2N/A */
2N/Astatic void
2N/A_mergeTinyRegions(void)
2N/A{
2N/A int from;
2N/A int to;
2N/A for (from = 1, to = 1; from < nRegions; ) {
2N/A if ((regions[from].type == REGION_COMMON) &&
2N/A (regions[from].size < JUMP_SIZE)) {
2N/A /*
2N/A * Merge out tiny common regions
2N/A */
2N/A regions[to - 1].size += regions[from].size;
2N/A /*
2N/A * Now join adjacent non-common regions
2N/A */
2N/A if (++from < nRegions)
2N/A regions[to - 1].size += regions[from++].size;
2N/A } else {
2N/A regions[to++] = regions[from++];
2N/A }
2N/A }
2N/A nRegions = to;
2N/A}
2N/A
2N/A/*
2N/A * Create the initial list of regions for this row
2N/A */
2N/Astatic int
2N/A_findRegions(int row)
2N/A{
2N/A int cmp;
2N/A int old_cmp;
2N/A int col;
2N/A int bestDeleteCount;
2N/A cchar_t *nptr = &newscr->_line[row][0];
2N/A cchar_t *optr = &curscr->_line[row][0];
2N/A
2N/A col = 0;
2N/A nRegions = 0;
2N/A bestDeleteCount = 0;
2N/A if ((__m_screen->_flags & S_INS_DEL_CHAR) &&
2N/A (parm_dch || delete_character)) {
2N/A int bestFit = 0;
2N/A int deletePoint;
2N/A int deleteCount;
2N/A int matches;
2N/A
2N/A /*
2N/A * Skip to first difference
2N/A */
2N/A for (col = 0; col < COLS; col++) {
2N/A if (!__m_cc_compare(&optr[col], &nptr[col], 1))
2N/A break;
2N/A }
2N/A deletePoint = col;
2N/A for (deleteCount = 1; deleteCount < DELETE_SEARCH_LIMIT;
2N/A deleteCount++) {
2N/A matches = 0;
2N/A for (col = deletePoint; col < COLS - deleteCount;
2N/A col++) {
2N/A if (__m_cc_compare(&optr[col + deleteCount],
2N/A &nptr[col], 1))
2N/A matches++;
2N/A else
2N/A break;
2N/A }
2N/A if (matches > bestFit) {
2N/A bestFit = matches;
2N/A bestDeleteCount = deleteCount;
2N/A }
2N/A }
2N/A if (bestFit > DELETE_THRESHOLD) {
2N/A regions[nRegions].type = REGION_DELETE;
2N/A regions[nRegions].col = deletePoint;
2N/A regions[nRegions].size = bestDeleteCount;
2N/A nRegions++;
2N/A col = deletePoint + bestDeleteCount;
2N/A } else {
2N/A col = 0;
2N/A nRegions = 0;
2N/A /* Forget trying to use character delete */
2N/A bestDeleteCount = 0;
2N/A }
2N/A }
2N/A for (old_cmp = -1; col + bestDeleteCount < COLS; col++) {
2N/A cmp = __m_cc_compare(&optr[col + bestDeleteCount],
2N/A &nptr[col], 1);
2N/A if (cmp != old_cmp) {
2N/A regions[nRegions].type = cmp ? REGION_COMMON :
2N/A REGION_DIFFERENT;
2N/A regions[nRegions].col = col;
2N/A regions[nRegions].size = 0; /* Determine later */
2N/A nRegions++;
2N/A old_cmp = cmp;
2N/A }
2N/A }
2N/A if (bestDeleteCount) {
2N/A /*
2N/A * Force update of end-of-line if delete is to be used
2N/A */
2N/A regions[nRegions].type = REGION_DIFFERENT;
2N/A regions[nRegions].col = col;
2N/A regions[nRegions].size = 0; /* Determine later */
2N/A nRegions++;
2N/A }
2N/A _normalizeRegions1();
2N/A if (nRegions == 0)
2N/A return (0); /* No difference regions */
2N/A
2N/A _normalizeRegions2();
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * Determine if Clr-EOL optimization can be used, and
2N/A * adjust regions accordingly
2N/A */
2N/Astatic int
2N/A_ceolAdjustRegions(int row)
2N/A{
2N/A int iRegion;
2N/A int blankEolStart = _find_blank_tail(row);
2N/A
2N/A for (iRegion = 0; iRegion < nRegions; iRegion++) {
2N/A switch (regions[iRegion].type) {
2N/A case REGION_DIFFERENT:
2N/A if (regions[iRegion].col >= blankEolStart) {
2N/A /*
2N/A * Delete this and all following regions
2N/A */
2N/A nRegions = iRegion;
2N/A return (blankEolStart);
2N/A }
2N/A if (regions[iRegion].col + regions[iRegion].size >
2N/A blankEolStart) {
2N/A /*
2N/A * Truncate this region to end
2N/A * where blank EOL starts
2N/A */
2N/A regions[iRegion].size = blankEolStart -
2N/A regions[iRegion].col;
2N/A /*
2N/A * Delete all following regions
2N/A */
2N/A nRegions = iRegion + 1;
2N/A return (blankEolStart);
2N/A }
2N/A break;
2N/A case REGION_COMMON:
2N/A break;
2N/A case REGION_DELETE: /* Scrap the whole thing */
2N/A return (COLS);
2N/A }
2N/A }
2N/A return (COLS); /* Couldn't use Clear EOL optimization */
2N/A}
2N/A
2N/A/*
2N/A * Generate output, based on region list
2N/A */
2N/Astatic void
2N/A_updateRegions(int row)
2N/A{
2N/A int ceolStart;
2N/A int iRegion;
2N/A
2N/A ceolStart = _ceolAdjustRegions(row);
2N/A
2N/A /*
2N/A * regions are guaranteed to start with a non-common region.
2N/A * tiny common regions have also been merged into
2N/A * bracketting common-regions.
2N/A */
2N/A if (nRegions) {
2N/A for (iRegion = 0; iRegion < nRegions; iRegion++) {
2N/A switch (regions[iRegion].type) {
2N/A case REGION_COMMON:
2N/A break;
2N/A case REGION_DELETE:
2N/A /*
2N/A * Start of non-common region
2N/A */
2N/A GOTO(row, regions[iRegion].col);
2N/A _deleteRegion(row, regions[iRegion]);
2N/A break;
2N/A case REGION_DIFFERENT:
2N/A /*
2N/A * Star of non-common region
2N/A */
2N/A GOTO(row, regions[iRegion].col);
2N/A _writeRegion(row, regions[iRegion]);
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A if (ceolStart != COLS) {
2N/A _clearToEOL(row, ceolStart);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * The new text_replace algorithm, which uses regions
2N/A */
2N/Astatic void
2N/Atext_replace(int row)
2N/A{
2N/A if (!_findRegions(row))
2N/A return;
2N/A _mergeTinyRegions();
2N/A _updateRegions(row);
2N/A
2N/A /*
2N/A * Line wrapping checks.
2N/A */
2N/A if (COLS <= curscr->_curx) {
2N/A --curscr->_curx;
2N/A if (auto_right_margin && (row < LINES - 1)) {
2N/A if (eat_newline_glitch) {
2N/A (void) __m_outc('\r');
2N/A (void) __m_outc('\n');
2N/A }
2N/A ++curscr->_cury;
2N/A curscr->_curx = 0;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Replace a block of lines.
2N/A * Only ever used for complex().
2N/A */
2N/Astatic void
2N/Alines_replace(int from, int to_1)
2N/A{
2N/A for (; from < to_1; ++from)
2N/A text_replace(from);
2N/A}
2N/A
2N/A/*
2N/A * Delete a block of lines.
2N/A * Only ever used for complex().
2N/A */
2N/Astatic void
2N/Alines_delete(int from, int to_1)
2N/A{
2N/A int count = to_1 - from;
2N/A
2N/A if (LINES <= to_1) {
2N/A erase_bottom(from, LINES);
2N/A clear_bottom(from);
2N/A } else {
2N/A GOTO(from, 0);
2N/A (void) winsdelln(curscr, -count);
2N/A
2N/A if (parm_delete_line != NULL) {
2N/A /*
2N/A * Assume that the sequence to delete more than one
2N/A * line is faster than repeated single delete_lines.
2N/A */
2N/A (void) TPUTS(tparm(parm_delete_line, (long)count,
2N/A 0, 0, 0, 0, 0, 0, 0, 0), count, __m_outc);
2N/A } else if (delete_line != NULL) {
2N/A while (from++ < to_1)
2N/A (void) TPUTS(delete_line, 1, __m_outc);
2N/A } else {
2N/A /* Error -- what to do. */
2N/A return;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Insert a block of lines.
2N/A * Only ever used for complex().
2N/A *
2N/A * We must assume that insert_line and parm_insert_line reset the
2N/A * cursor column to zero. Therefore it is text_replace() responsiblity
2N/A * to move the cursor to the correct column to begin the update.
2N/A */
2N/Astatic void
2N/Alines_insert(int from, int to_1)
2N/A{
2N/A int row;
2N/A int count = to_1 - from;
2N/A
2N/A /*
2N/A * Position the cursor and insert a block of lines into the screen
2N/A * image now, insert lines into the physical screen, then draw the
2N/A * new screen lines.
2N/A */
2N/A GOTO(from, 0);
2N/A (void) winsdelln(curscr, count);
2N/A
2N/A if (parm_insert_line != NULL) {
2N/A /*
2N/A * Assume that the sequence to insert more than one line is
2N/A * faster than repeated single insert_lines.
2N/A */
2N/A (void) TPUTS(tparm(parm_insert_line, (long)count,
2N/A 0, 0, 0, 0, 0, 0, 0, 0), count, __m_outc);
2N/A } else if (insert_line != NULL) {
2N/A /*
2N/A * For the single line insert we use to iterate moving
2N/A * the cursor, inserting, and then drawing a line. That
2N/A * would appear to be slow but visually appealing. However,
2N/A * people on slow terminals want speed and those on fast
2N/A * terminal won't see it.
2N/A */
2N/A for (row = from; row < to_1; ++row)
2N/A (void) TPUTS(insert_line, 1, __m_outc);
2N/A } else {
2N/A /* Error -- what to do. */
2N/A return;
2N/A }
2N/A
2N/A for (row = from; row < to_1; ++row)
2N/A text_replace(row);
2N/A}
2N/A
2N/Astatic int
2N/Ascroll_up(int n)
2N/A{
2N/A int count = n;
2N/A int start, finish, to;
2N/A
2N/A if (scroll_forward != NULL) {
2N/A GOTO(LINES-1, 0);
2N/A while (0 < n--)
2N/A (void) TPUTS(scroll_forward, 1, __m_outc);
2N/A } else if (parm_delete_line != NULL && 1 < n) {
2N/A GOTO(0, 0);
2N/A (void) TPUTS(tparm(parm_delete_line, (long)n,
2N/A 0, 0, 0, 0, 0, 0, 0, 0), n, __m_outc);
2N/A } else if (delete_line != NULL) {
2N/A GOTO(0, 0);
2N/A while (0 < n--)
2N/A (void) TPUTS(delete_line, 1, __m_outc);
2N/A } else {
2N/A return (0);
2N/A }
2N/A
2N/A /* Scroll recorded image. */
2N/A start = 0;
2N/A finish = count-1;
2N/A to = lines;
2N/A
2N/A (void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1);
2N/A (void) __m_ptr_move((void **) curscr->_line,
2N/A curscr->_maxy, start, finish, to);
2N/A
2N/A simple();
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A#if 0
2N/Astatic int
2N/Ascroll_dn(int n)
2N/A{
2N/A int count = n;
2N/A int start, finish, to;
2N/A
2N/A if (LINES < n)
2N/A return (0);
2N/A
2N/A if (scroll_reverse != NULL) {
2N/A GOTO(0, 0);
2N/A while (0 < n--)
2N/A (void) TPUTS(scroll_reverse, 1, __m_outc);
2N/A } else if (parm_insert_line != NULL && 1 < n) {
2N/A GOTO(0, 0);
2N/A (void) TPUTS(tparm(parm_insert_line, (long)n,
2N/A 0, 0, 0, 0, 0, 0, 0, 0), n, __m_outc);
2N/A } else if (insert_line != NULL) {
2N/A GOTO(0, 0);
2N/A while (0 < n--)
2N/A (void) TPUTS(insert_line, 1, __m_outc);
2N/A } else {
2N/A return (0);
2N/A }
2N/A
2N/A /* Scroll recorded image. */
2N/A start = lines - count;
2N/A finish = lines - 1;
2N/A to = 0;
2N/A
2N/A (void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1);
2N/A (void) __m_ptr_move((void **) curscr->_line,
2N/A curscr->_maxy, start, finish, to);
2N/A
2N/A simple();
2N/A
2N/A return (1);
2N/A}
2N/A#endif
2N/A
2N/A/*
2N/A * Dynamic programming algorithm for the string edit problem.
2N/A *
2N/A * This is a modified Gosling cost algorithm that takes into account
2N/A * null/move operations.
2N/A *
2N/A * Costs for move, delete, replace, and insert are 0, 1, 2, and 3
2N/A * repectively.
2N/A */
2N/A#define MOVE_COST 0
2N/A#define REPLACE_COST 10
2N/A#define INSERT_COST 12
2N/A#define DELETE_COST 1
2N/A
2N/Astatic int
2N/Acost(int fr, int lr)
2N/A{
2N/A lcost *lcp;
2N/A int or, nr, cc;
2N/A#if defined(_LP64)
2N/A unsigned int *ohash = __m_screen->_hash;
2N/A#else
2N/A unsigned long *ohash = __m_screen->_hash;
2N/A#endif
2N/A
2N/A /*
2N/A * Prepare initial row and column of cost matrix.
2N/A *
2N/A * 0 3 6 9 ...
2N/A * 1
2N/A * 2
2N/A * 3
2N/A * :
2N/A */
2N/A LC(fr, fr).cost = MOVE_COST;
2N/A for (cc = 1, ++lr, nr = fr+1; nr <= lr; ++nr, ++cc) {
2N/A /* Top row is 3, 6, 9, ... */
2N/A LC(fr, nr).cost = cc * INSERT_COST;
2N/A LC(fr, nr).op = 'i';
2N/A
2N/A /* Left column is 1, 2, 3, ... */
2N/A LC(nr, fr).cost = cc * DELETE_COST;
2N/A LC(nr, fr).op = 'd';
2N/A }
2N/A
2N/A for (--lr, or = fr; or <= lr; ++or) {
2N/A for (nr = fr; nr <= lr; ++nr) {
2N/A lcp = &LC(or + 1, nr + 1);
2N/A
2N/A /* Assume move op. */
2N/A lcp->cost = LC(or, nr).cost;
2N/A lcp->op = 'm';
2N/A
2N/A if (ohash[or] != nhash[nr]) {
2N/A /* Lines are different, assume replace op. */
2N/A lcp->cost += REPLACE_COST;
2N/A lcp->op = 'r';
2N/A }
2N/A
2N/A /* Compare insert op. */
2N/A if ((cc = LC(or + 1, nr).cost + INSERT_COST) <
2N/A lcp->cost) {
2N/A lcp->cost = cc;
2N/A lcp->op = 'i';
2N/A }
2N/A
2N/A /* Compare delete op. */
2N/A if ((cc = LC(or, nr + 1).cost + DELETE_COST) <
2N/A lcp->cost) {
2N/A lcp->cost = cc;
2N/A lcp->op = 'd';
2N/A }
2N/A }
2N/A }
2N/A
2N/A return (LC(lr + 1, lr + 1).cost);
2N/A}
2N/A
2N/A/*
2N/A * Build edit script.
2N/A *
2N/A * Normally this would be a recursve routine doing the deletes, inserts,
2N/A * and replaces on individual lines. Instead we build the script so that
2N/A * we can later do the operations on a block basis. For terminals with
2N/A * parm_delete or parm_insert strings this will be better in terms of the
2N/A * number of characters sent to delete and insert a block of lines.
2N/A *
2N/A * Also we can optimize the script so that tail inserts become replaces.
2N/A * This saves unnecessary inserts operations when the tail can just be
2N/A * overwritten.
2N/A */
2N/Astatic void
2N/Ascript(int fr, int lr)
2N/A{
2N/A int i, j;
2N/A cchar_t *cp;
2N/A
2N/A i = j = lr + 1;
2N/A
2N/A (void) memset(del, 0, sizeof (*del) * LINES);
2N/A (void) memset(ins_rep, 0, sizeof (*ins_rep) * LINES);
2N/A
2N/A do {
2N/A /*
2N/A * We don't have to bounds check i or j becuase row fr and
2N/A * column fr of lc have been preset in order to guarantee the
2N/A * correct motion.
2N/A */
2N/A switch (LC(i, j).op) {
2N/A case 'i':
2N/A --j;
2N/A ins_rep[j] = lines_insert;
2N/A break;
2N/A case 'd':
2N/A --i;
2N/A del[i] = lines_delete;
2N/A break;
2N/A case 'm':
2N/A --i;
2N/A --j;
2N/A break;
2N/A case 'r':
2N/A --i;
2N/A --j;
2N/A ins_rep[j] = lines_replace;
2N/A break;
2N/A }
2N/A } while (fr < i || fr < j);
2N/A
2N/A /* Optimize Tail Inserts */
2N/A for (i = LINES-1; 0 <= i && ins_rep[i] == lines_insert; --i) {
2N/A /* Make each character in the screen line image invalid. */
2N/A for (cp = curscr->_line[i], j = 0; j < COLS; ++j, ++cp)
2N/A cp->_n = -1;
2N/A ins_rep[i] = lines_replace;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Complex update algorithm using insert/delete line operations.
2N/A *
2N/A * References:
2N/A * [MyM86] E.W. Myers & W. Miller, Row Replacement Algorithms for
2N/A * Screen Editors, TR 86-19, Dept. Computer Science, U. of Arizona
2N/A * [MyM87] E.W. Myers & W. Miller, A Simple Row Replacement Method,
2N/A * TR 86-28, Dept. Computer Science, U. of Arizona
2N/A * [Mil87] W. Miller, A Software Tools Sampler, Prentice-Hall, 1987
2N/A * [Gos81] James Gosling, A redisplay algorithm, Proceedings of the
2N/A * ACM Symposium on Text Manipulation, SIGPLAN Notices,
2N/A * 16(6) June 1981, pg 123-129
2N/A *
2N/A * All the above were reviewed and experimented with. Due to the nature of
2N/A * Curses' having to handling overlapping WINDOWs, the only suitable
2N/A * algorithum is [Gos81]. The others are better suited to editor type
2N/A * applications that have one window being the entire terminal screen.
2N/A *
2N/A */
2N/Astatic void
2N/Acomplex(void)
2N/A{
2N/A int fr = -1;
2N/A int i, j, lr;
2N/A t_action func;
2N/A
2N/A /* Find block of lines to change */
2N/A for (i = 0; i < LINES; ++i) {
2N/A if (newscr->_first[i] < newscr->_last[i]) {
2N/A /* Compute new hash. */
2N/A __m_cc_hash(newscr, nhash, i);
2N/A if (fr == -1)
2N/A fr = i;
2N/A lr = i;
2N/A } else {
2N/A /* Line not dirty so hash same as before. */
2N/A nhash[i] = __m_screen->_hash[i];
2N/A }
2N/A }
2N/A
2N/A if (fr != -1) {
2N/A /* Gosling */
2N/A (void) cost(fr, lr);
2N/A script(fr, lr);
2N/A
2N/A /* Do deletes first in reverse order. */
2N/A for (j = lr; fr <= j; --j) {
2N/A if (del[j] != (t_action) 0) {
2N/A for (i = j-1; fr <= i; --i)
2N/A if (del[i] == (t_action) 0)
2N/A break;
2N/A
2N/A lines_delete(i+1, j+1);
2N/A j = i;
2N/A }
2N/A }
2N/A
2N/A /* Do insert/replace in forward order. */
2N/A for (i = fr; i <= lr; ++i) {
2N/A if ((func = ins_rep[i]) != (t_action) 0) {
2N/A /* Find size of block */
2N/A for (j = i; j <= lr && ins_rep[j] == func; ++j)
2N/A ;
2N/A (*func)(i, j);
2N/A i = j - 1;
2N/A }
2N/A }
2N/A /*
2N/A * _line[], which contains pointers to screen lines,
2N/A * may be shuffled.
2N/A */
2N/A for (i = fr; i <= lr; ++i) {
2N/A /* Save new hash for next update. */
2N/A __m_screen->_hash[i] = nhash[i];
2N/A
2N/A /* Mark line as untouched. */
2N/A newscr->_first[i] = newscr->_maxx;
2N/A newscr->_last[i] = -1;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Simple screen update algorithm
2N/A *
2N/A * We perform a simple incremental update of the terminal screen.
2N/A * Only the segment of a line that was touched is replaced on the
2N/A * line.
2N/A */
2N/Astatic void
2N/Asimple(void)
2N/A{
2N/A int row;
2N/A
2N/A for (row = 0; row < newscr->_maxy; ++row) {
2N/A if (newscr->_first[row] < newscr->_last[row]) {
2N/A text_replace(row);
2N/A
2N/A /* Mark line as untouched. */
2N/A newscr->_first[row] = newscr->_maxx;
2N/A newscr->_last[row] = -1;
2N/A __m_cc_hash(curscr, __m_screen->_hash, row);
2N/A }
2N/A }
2N/A
2N/A newscr->_flags &= ~W_REDRAW_WINDOW;
2N/A}
2N/A
2N/Avoid
2N/Awtouchln_hard(WINDOW *w, int y, int n)
2N/A{
2N/A int last;
2N/A
2N/A last = w->_maxx;
2N/A
2N/A for (; (y < w->_maxy) && (0 < n); ++y, --n) {
2N/A /*
2N/A * Force compare in doupdate to fail.
2N/A * Touch should be unconditional
2N/A */
2N/A(void) memset(&__m_screen->_curscr->_line[w->_begy + y][w->_begx],
2N/A 0xff, last * sizeof (cchar_t));
2N/A }
2N/A}
2N/A/*
2N/A * Send all changes made to _newscr to the physical terminal.
2N/A *
2N/A * If idlok() is set TRUE then doupdate will try and use hardware insert
2N/A * and delete line sequences in an effort to optimize output. idlok()
2N/A * should really only be used in applications that want a proper scrolling
2N/A * effect.
2N/A *
2N/A * Added scroll heuristic to handle special case where a full size window
2N/A * with full size scroll region, will scroll the window and replace dirty
2N/A * lines instead of performing usual cost/script operations.
2N/A */
2N/Aint
2N/Adoupdate(void)
2N/A{
2N/A#ifdef SIGTSTP
2N/A int (*oldsig)(int) = signal(SIGTSTP, SIG_IGN);
2N/A#endif
2N/A
2N/A if (pollTypeahead()) {
2N/A return (OK);
2N/A }
2N/A newscr = __m_screen->_newscr;
2N/A
2N/A if (__m_screen->_flags & S_ENDWIN) {
2N/A /* Return from temporary escape done with endwin(). */
2N/A __m_screen->_flags &= ~S_ENDWIN;
2N/A
2N/A (void) reset_prog_mode();
2N/A if (enter_ca_mode != NULL)
2N/A (void) TPUTS(enter_ca_mode, 1, __m_outc);
2N/A if (keypad_xmit != NULL)
2N/A (void) TPUTS(keypad_xmit, 1, __m_outc);
2N/A if (ena_acs != NULL)
2N/A (void) TPUTS(ena_acs, 1, __m_outc);
2N/A
2N/A /* Force redraw of screen. */
2N/A newscr->_flags |= W_CLEAR_WINDOW;
2N/A }
2N/A /*
2N/A * When redrawwing a window, we not only assume that line
2N/A * noise may have lost characters, but line noise may have
2N/A * generated bogus characters on the screen outside the
2N/A * the window in question, in which case redraw the entire
2N/A * screen to be sure.
2N/A */
2N/A if ((newscr->_flags & (W_CLEAR_WINDOW | W_REDRAW_WINDOW)) ||
2N/A (curscr->_flags & W_CLEAR_WINDOW)) {
2N/A erase_bottom(0, newscr->_maxy);
2N/A clear_bottom(0);
2N/A (void) wtouchln(newscr, 0, newscr->_maxy, 1);
2N/A newscr->_flags &= ~W_CLEAR_WINDOW;
2N/A curscr->_flags &= ~W_CLEAR_WINDOW;
2N/A }
2N/A
2N/A /*
2N/A * Scrolling heuristic should only be used if lines being
2N/A * scrolled are clean because scrolling overrides updates
2N/A *
2N/A * Right now, the following code should always turn off
2N/A * scrolling, because the internal scroll touches the
2N/A * scrolled lines. This thing requires a lot more care
2N/A * than I have right now...
2N/A */
2N/A if (newscr->_scroll) {
2N/A int y;
2N/A for (y = 0; y < newscr->_maxy; ++y) {
2N/A if (0 <= newscr->_last[y]) {
2N/A newscr->_scroll = 0;
2N/A }
2N/A }
2N/A newscr->_scroll = 0; /* Just fudge it for now ... */
2N/A }
2N/A if (newscr->_flags & W_REDRAW_WINDOW) {
2N/A simple();
2N/A } else {
2N/A if (newscr->_scroll == 0) {
2N/A if (__m_screen->_flags & S_INS_DEL_LINE) {
2N/A complex();
2N/A } else {
2N/A simple();
2N/A }
2N/A } else {
2N/A if (!scroll_up(newscr->_scroll)) {
2N/A if (__m_screen->_flags & S_INS_DEL_LINE) {
2N/A complex();
2N/A } else {
2N/A simple();
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (!(newscr->_flags & W_LEAVE_CURSOR)) {
2N/A GOTO(newscr->_cury, newscr->_curx);
2N/A }
2N/A
2N/A if (!(curscr->_flags & W_FLUSH)) {
2N/A (void) fflush(__m_screen->_of);
2N/A }
2N/A
2N/A newscr->_scroll = curscr->_scroll = 0;
2N/A
2N/A /* Send labels to terminal that supports them. */
2N/A __m_slk_doupdate();
2N/A#ifdef SIGTSTP
2N/A signal(SIGTSTP, oldsig);
2N/A#endif
2N/A
2N/A return (OK);
2N/A}
2N/A
2N/A/*
2N/A * If true, the implementation may use hardware insert and delete,
2N/A * character features of the terminal. The window parameter
2N/A * is ignored.
2N/A */
2N/A/* ARGSUSED */
2N/Avoid
2N/Aidcok(WINDOW *w, bool bf)
2N/A{
2N/A __m_screen->_flags &= ~S_INS_DEL_CHAR;
2N/A if (bf)
2N/A __m_screen->_flags |= S_INS_DEL_CHAR;
2N/A}
2N/A
2N/A/*
2N/A * If true, the implementation may use hardware insert, delete,
2N/A * and scroll line features of the terminal. The window parameter
2N/A * is ignored.
2N/A */
2N/A/* ARGSUSED */
2N/Aint
2N/Aidlok(WINDOW *w, bool bf)
2N/A{
2N/A __m_screen->_flags &= ~S_INS_DEL_LINE;
2N/A if (bf && has_il())
2N/A __m_screen->_flags |= S_INS_DEL_LINE;
2N/A
2N/A return (OK);
2N/A}
2N/A
2N/A/*
2N/A * Use the POSIX 32-bit CRC function to compute a hash value
2N/A * for the window line.
2N/A */
2N/Avoid
2N/A#if defined(_LP64)
2N/A__m_cc_hash(WINDOW *w, unsigned int *array, int y)
2N/A#else
2N/A__m_cc_hash(WINDOW *w, unsigned long *array, int y)
2N/A#endif
2N/A{
2N/A array[y] = 0;
2N/A m_crcposix(&array[y], (unsigned char *) w->_line[y],
2N/A (size_t)(w->_maxx * sizeof (**w->_line)));
2N/A}