diff options
Diffstat (limited to 'screen.c')
-rw-r--r-- | screen.c | 2854 |
1 files changed, 2854 insertions, 0 deletions
diff --git a/screen.c b/screen.c new file mode 100644 index 0000000..dc56dc4 --- /dev/null +++ b/screen.c @@ -0,0 +1,2854 @@ +/* $XTermId: screen.c,v 1.452 2012/05/08 08:36:43 tom Exp $ */ + +/* + * Copyright 1999-2011,2012 by Thomas E. Dickey + * + * All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above copyright + * holders shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization. + * + * + * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Digital Equipment + * Corporation not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * + * + * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR + * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* screen.c */ + +#include <stdio.h> +#include <xterm.h> +#include <error.h> +#include <data.h> +#include <xcharmouse.h> +#include <xterm_io.h> + +#include <X11/Xatom.h> + +#if OPT_WIDE_CHARS +#include <fontutils.h> +#endif + +#include <menu.h> + +#include <assert.h> +#include <signal.h> + +#define inSaveBuf(screen, buf, inx) \ + ((buf) == (screen)->saveBuf_index && \ + ((inx) < (screen)->savelines || (screen)->savelines == 0)) + +#define getMinRow(screen) ((xw->flags & ORIGIN) ? (screen)->top_marg : 0) +#define getMaxRow(screen) ((xw->flags & ORIGIN) ? (screen)->bot_marg : (screen)->max_row) +#define getMinCol(screen) ((xw->flags & ORIGIN) ? (screen)->lft_marg : 0) +#define getMaxCol(screen) ((xw->flags & ORIGIN) ? (screen)->rgt_marg : (screen)->max_col) + +#define MoveLineData(base, dst, src, len) \ + memmove(scrnHeadAddr(screen, base, (unsigned) (dst)), \ + scrnHeadAddr(screen, base, (unsigned) (src)), \ + (size_t) scrnHeadSize(screen, (unsigned) (len))) + +#define SaveLineData(base, src, len) \ + (void) ScrnPointers(screen, len); \ + memcpy (screen->save_ptr, \ + scrnHeadAddr(screen, base, src), \ + (size_t) scrnHeadSize(screen, (unsigned) (len))) + +#define RestoreLineData(base, dst, len) \ + memcpy (scrnHeadAddr(screen, base, dst), \ + screen->save_ptr, \ + (size_t) scrnHeadSize(screen, (unsigned) (len))) + +#if OPT_SAVE_LINES +#define VisBuf(screen) screen->editBuf_index[screen->whichBuf] +#else +#define VisBuf(screen) scrnHeadAddr(screen, screen->saveBuf_index, (unsigned) savelines) +#endif + +/* + * ScrnPtr's can point to different types of data. + */ +#define SizeofScrnPtr(name) \ + (unsigned) sizeof(*((LineData *)0)->name) + +/* + * The pointers in LineData point into a block of text allocated as a single + * chunk for the given number of rows. Ensure that these pointers are aligned + * at least to int-boundaries. + */ +#define AlignMask() (sizeof(int) - 1) +#define IsAligned(value) (((unsigned long) (value) & AlignMask()) == 0) + +#define AlignValue(value) \ + if (!IsAligned(value)) \ + value = (value | (unsigned) AlignMask()) + 1 + +#define SetupScrnPtr(dst,src,type) \ + dst = (type *) (void *) src; \ + assert(IsAligned(dst)); \ + src += skipNcol##type + +#define ScrnBufAddr(ptrs, offset) (ScrnBuf) ((void *) ((char *) (ptrs) + (offset))) +#define LineDataAddr(ptrs, offset) (LineData *) ((void *) ((char *) (ptrs) + (offset))) + +#if OPT_TRACE > 1 +static void +traceScrnBuf(const char *tag, TScreen * screen, ScrnBuf sb, unsigned len) +{ + unsigned j; + + TRACE(("traceScrnBuf %s\n", tag)); + for (j = 0; j < len; ++j) { + LineData *src = (LineData *) scrnHeadAddr(screen, sb, j); + TRACE(("%p %s%3d:%s\n", + src, ((int) j >= screen->savelines) ? "*" : " ", + j, visibleIChars(src->charData, src->lineSize))); + } + TRACE(("...traceScrnBuf %s\n", tag)); +} + +#define TRACE_SCRNBUF(tag, screen, sb, len) traceScrnBuf(tag, screen, sb, len) +#else +#define TRACE_SCRNBUF(tag, screen, sb, len) /*nothing */ +#endif + +static unsigned +scrnHeadSize(TScreen * screen, unsigned count) +{ + unsigned result = SizeOfLineData; + + (void) screen; + +#if OPT_WIDE_CHARS + if (screen->wide_chars) { + result += (unsigned) screen->lineExtra; + } +#endif + result *= count; + + return result; +} + +ScrnBuf +scrnHeadAddr(TScreen * screen, ScrnBuf base, unsigned offset) +{ + unsigned size = scrnHeadSize(screen, offset); + ScrnBuf result = ScrnBufAddr(base, size); + + assert((int) offset >= 0); + + return result; +} + +/* + * Given a block of data, build index to it in the 'base' parameter. + */ +void +setupLineData(TScreen * screen, ScrnBuf base, Char * data, unsigned nrow, unsigned ncol) +{ + unsigned i; + unsigned offset = 0; + unsigned jump = scrnHeadSize(screen, 1); + LineData *ptr; +#if OPT_WIDE_CHARS + unsigned j; +#endif + /* these names are based on types */ + unsigned skipNcolChar; + unsigned skipNcolCharData; +#if OPT_ISO_COLORS + unsigned skipNcolCellColor; +#endif + + AlignValue(ncol); + + skipNcolChar = (ncol * SizeofScrnPtr(attribs)); + skipNcolCharData = (ncol * SizeofScrnPtr(charData)); +#if OPT_ISO_COLORS + skipNcolCellColor = (ncol * SizeofScrnPtr(color)); +#endif + + for (i = 0; i < nrow; i++, offset += jump) { + ptr = LineDataAddr(base, offset); + + ptr->lineSize = (Dimension) ncol; + ptr->bufHead = 0; +#if OPT_DEC_CHRSET + SetLineDblCS(ptr, 0); +#endif + SetupScrnPtr(ptr->attribs, data, Char); +#if OPT_ISO_COLORS + SetupScrnPtr(ptr->color, data, CellColor); +#endif + SetupScrnPtr(ptr->charData, data, CharData); +#if OPT_WIDE_CHARS + if (screen->wide_chars) { + unsigned extra = (unsigned) screen->max_combining; + + ptr->combSize = (Char) extra; + for (j = 0; j < extra; ++j) { + SetupScrnPtr(ptr->combData[j], data, CharData); + } + } +#endif + } +} + +#define ExtractScrnData(name) \ + memcpy(dstPtrs->name, \ + ((LineData *) srcPtrs)->name,\ + dstCols * sizeof(dstPtrs->name[0])); \ + nextPtr += (srcCols * sizeof(dstPtrs->name[0])) + +/* + * As part of reallocating the screen buffer when resizing, extract from + * the old copy of the screen buffer the data which will be used in the + * new copy of the screen buffer. + */ +static void +extractScrnData(TScreen * screen, + ScrnBuf dstPtrs, + ScrnBuf srcPtrs, + unsigned nrows, + unsigned move_down) +{ + unsigned j; + + TRACE(("extractScrnData(nrows %d)\n", nrows)); + + TRACE_SCRNBUF("extract from", screen, srcPtrs, nrows); + for (j = 0; j < nrows; j++) { + LineData *dst = (LineData *) scrnHeadAddr(screen, + dstPtrs, j + move_down); + LineData *src = (LineData *) scrnHeadAddr(screen, + srcPtrs, j); + copyLineData(dst, src); + } +} + +static ScrnPtr * +allocScrnHead(TScreen * screen, unsigned nrow) +{ + ScrnPtr *result; + unsigned size = scrnHeadSize(screen, 1); + + result = (ScrnPtr *) calloc((size_t) nrow, (size_t) size); + if (result == 0) + SysError(ERROR_SCALLOC); + + TRACE(("allocScrnHead %d -> %d -> %p..%p\n", nrow, nrow * size, + (void *) result, + (char *) result + (nrow * size) - 1)); + return result; +} + +/* + * Return the size of a line's data. + */ +static unsigned +sizeofScrnRow(TScreen * screen, unsigned ncol) +{ + unsigned result; + unsigned sizeAttribs; +#if OPT_ISO_COLORS + unsigned sizeColors; +#endif + + (void) screen; + + result = (ncol * (unsigned) sizeof(CharData)); + AlignValue(result); + +#if OPT_WIDE_CHARS + if (screen->wide_chars) { + result *= (unsigned) (1 + screen->max_combining); + } +#endif + + sizeAttribs = (ncol * SizeofScrnPtr(attribs)); + AlignValue(sizeAttribs); + result += sizeAttribs; + +#if OPT_ISO_COLORS + sizeColors = (ncol * SizeofScrnPtr(color)); + AlignValue(sizeColors); + result += sizeColors; +#endif + + return result; +} + +Char * +allocScrnData(TScreen * screen, unsigned nrow, unsigned ncol) +{ + Char *result; + size_t length; + + AlignValue(ncol); + length = (nrow * sizeofScrnRow(screen, ncol)); + if ((result = (Char *) calloc(length, sizeof(Char))) == 0) + SysError(ERROR_SCALLOC2); + + TRACE(("allocScrnData %ux%u -> %lu -> %p..%p\n", + nrow, ncol, (unsigned long) length, result, result + length - 1)); + return result; +} + +/* + * Allocates memory for a 2-dimensional array of chars and returns a pointer + * thereto. Each line is formed from a set of char arrays, with an index + * (i.e., the ScrnBuf type). The first pointer in the index is reserved for + * per-line flags, and does not point to data. + * + * After the per-line flags, we have a series of pointers to char arrays: The + * first one is the actual character array, the second one is the attributes, + * the third is the foreground and background colors, and the fourth denotes + * the character set. + * + * We store it all as pointers, because of alignment considerations. + */ +ScrnBuf +allocScrnBuf(XtermWidget xw, unsigned nrow, unsigned ncol, Char ** addr) +{ + TScreen *screen = TScreenOf(xw); + ScrnBuf base = 0; + + if (nrow != 0) { + base = allocScrnHead(screen, nrow); + *addr = allocScrnData(screen, nrow, ncol); + + setupLineData(screen, base, *addr, nrow, ncol); + } + + TRACE(("allocScrnBuf %dx%d ->%p\n", nrow, ncol, (void *) base)); + return (base); +} + +#if OPT_SAVE_LINES +/* + * Copy line-data from the visible (edit) buffer to the save-lines buffer. + */ +static void +saveEditBufLines(TScreen * screen, ScrnBuf sb, unsigned n) +{ + unsigned j; + + TRACE(("...copying %d lines from editBuf to saveBuf\n", n)); +#if OPT_FIFO_LINES + (void) sb; +#endif + for (j = 0; j < n; ++j) { +#if OPT_FIFO_LINES + LineData *dst = addScrollback(screen); +#else + unsigned k = (screen->savelines + j - n); + LineData *dst = (LineData *) scrnHeadAddr(screen, sb, k); +#endif + LineData *src = getLineData(screen, (int) j); + copyLineData(dst, src); + } +} + +/* + * Copy line-data from the save-lines buffer to the visible (edit) buffer. + */ +static void +unsaveEditBufLines(TScreen * screen, ScrnBuf sb, unsigned n) +{ + unsigned j; + + TRACE(("...copying %d lines from saveBuf to editBuf\n", n)); + for (j = 0; j < n; ++j) { + int extra = (int) (n - j); + LineData *dst = (LineData *) scrnHeadAddr(screen, sb, j); +#if OPT_FIFO_LINES + LineData *src; + + if ((screen->saved_fifo - extra) <= 0) { + TRACE(("...FIXME: must clear text!\n")); + continue; + } + src = getScrollback(screen, -extra); +#else + unsigned k = (screen->savelines - extra); + LineData *src = (LineData *) scrnHeadAddr(screen, + screen->saveBuf_index, k); +#endif + copyLineData(dst, src); + } +} +#endif + +/* + * This is called when the screen is resized. + * Returns the number of lines the text was moved down (neg for up). + * (Return value only necessary with SouthWestGravity.) + */ +static int +Reallocate(XtermWidget xw, + ScrnBuf * sbuf, + Char ** sbufaddr, + unsigned nrow, + unsigned ncol, + unsigned oldrow) +{ + TScreen *screen = TScreenOf(xw); + ScrnBuf oldBufHead; + ScrnBuf newBufHead; + Char *newBufData; + unsigned minrows; + Char *oldBufData; + int move_down = 0, move_up = 0; + + if (sbuf == NULL || *sbuf == NULL) { + return 0; + } + + oldBufData = *sbufaddr; + + TRACE(("Reallocate %dx%d -> %dx%d\n", oldrow, MaxCols(screen), nrow, ncol)); + + /* + * realloc sbuf, the pointers to all the lines. + * If the screen shrinks, remove lines off the top of the buffer + * if resizeGravity resource says to do so. + */ + TRACE(("Check move_up, nrow %d vs oldrow %d (resizeGravity %s)\n", + nrow, oldrow, + BtoS(GravityIsSouthWest(xw)))); + if (GravityIsSouthWest(xw)) { + if (nrow < oldrow) { + /* Remove lines off the top of the buffer if necessary. */ + move_up = (int) (oldrow - nrow) + - (TScreenOf(xw)->max_row - TScreenOf(xw)->cur_row); + if (move_up < 0) + move_up = 0; + /* Overlapping move here! */ + TRACE(("move_up %d\n", move_up)); + if (move_up) { + ScrnBuf dst = *sbuf; + unsigned len = (unsigned) ((int) oldrow - move_up); + + TRACE_SCRNBUF("before move_up", screen, dst, oldrow); + SaveLineData(dst, 0, (size_t) move_up); + MoveLineData(dst, 0, (size_t) move_up, len); + RestoreLineData(dst, len, (size_t) move_up); + TRACE_SCRNBUF("after move_up", screen, dst, oldrow); + } + } + } + oldBufHead = *sbuf; + *sbuf = allocScrnHead(screen, (unsigned) nrow); + newBufHead = *sbuf; + + /* + * Create the new buffer space and copy old buffer contents there, line by + * line. + */ + newBufData = allocScrnData(screen, nrow, ncol); + *sbufaddr = newBufData; + + minrows = (oldrow < nrow) ? oldrow : nrow; + if (GravityIsSouthWest(xw)) { + if (nrow > oldrow) { + /* move data down to bottom of expanded screen */ + move_down = Min((int) (nrow - oldrow), TScreenOf(xw)->savedlines); + } + } + + setupLineData(screen, newBufHead, *sbufaddr, nrow, ncol); + extractScrnData(screen, newBufHead, oldBufHead, minrows, +#if OPT_SAVE_LINES + 0 +#else + (unsigned) move_down +#endif + ); + free(oldBufHead); + + /* Now free the old data */ + free(oldBufData); + + TRACE(("...Reallocate %dx%d ->%p\n", nrow, ncol, (void *) newBufHead)); + return move_down ? move_down : -move_up; /* convert to rows */ +} + +#if OPT_WIDE_CHARS +/* + * This function reallocates memory if changing the number of Buf offsets. + * The code is based on Reallocate(). + */ +static void +ReallocateBufOffsets(XtermWidget xw, + ScrnBuf * sbuf, + Char ** sbufaddr, + unsigned nrow, + unsigned ncol) +{ + TScreen *screen = TScreenOf(xw); + unsigned i; + ScrnBuf newBufHead; + Char *oldBufData; + ScrnBuf oldBufHead; + + unsigned old_jump = scrnHeadSize(screen, 1); + unsigned new_jump; + unsigned new_ptrs = 1 + (unsigned) (screen->max_combining); + unsigned dstCols = ncol; + unsigned srcCols = ncol; + LineData *dstPtrs; + LineData *srcPtrs; + Char *nextPtr; + + assert(nrow != 0); + assert(ncol != 0); + + oldBufData = *sbufaddr; + oldBufHead = *sbuf; + + /* + * Allocate a new LineData array, retain the old one until we've copied + * the data that it points to, as well as non-pointer data, e.g., bufHead. + * + * Turn on wide-chars temporarily when constructing pointers, since that is + * used to decide whether to address the combData[] array, which affects + * the length of the LineData structure. + */ + screen->wide_chars = True; + + new_jump = scrnHeadSize(screen, 1); + newBufHead = allocScrnHead(screen, nrow); + *sbufaddr = allocScrnData(screen, nrow, ncol); + setupLineData(screen, newBufHead, *sbufaddr, nrow, ncol); + + screen->wide_chars = False; + + nextPtr = *sbufaddr; + + srcPtrs = (LineData *) oldBufHead; + dstPtrs = (LineData *) newBufHead; + for (i = 0; i < nrow; i++) { + dstPtrs->bufHead = srcPtrs->bufHead; + ExtractScrnData(attribs); +#if OPT_ISO_COLORS + ExtractScrnData(color); +#endif + ExtractScrnData(charData); + + nextPtr += ncol * new_ptrs; + srcPtrs = LineDataAddr(srcPtrs, old_jump); + dstPtrs = LineDataAddr(dstPtrs, new_jump); + } + + /* Now free the old data */ + free(oldBufData); + free(oldBufHead); + + *sbuf = newBufHead; + + TRACE(("ReallocateBufOffsets %dx%d ->%p\n", nrow, ncol, *sbufaddr)); +} + +#if OPT_FIFO_LINES +/* + * Allocate a new FIFO index. + */ +static void +ReallocateFifoIndex(XtermWidget xw) +{ + TScreen *screen = TScreenOf(xw); + + if (screen->savelines > 0 && screen->saveBuf_index != 0) { + ScrnBuf newBufHead; + LineData *dstPtrs; + LineData *srcPtrs; + unsigned i; + unsigned old_jump = scrnHeadSize(screen, 1); + unsigned new_jump; + + screen->wide_chars = True; + newBufHead = allocScrnHead(screen, (unsigned) screen->savelines); + new_jump = scrnHeadSize(screen, 1); + + srcPtrs = (LineData *) screen->saveBuf_index; + dstPtrs = (LineData *) newBufHead; + + for (i = 0; i < (unsigned) screen->savelines; ++i) { + memcpy(dstPtrs, srcPtrs, SizeOfLineData); + srcPtrs = LineDataAddr(srcPtrs, old_jump); + dstPtrs = LineDataAddr(dstPtrs, new_jump); + } + + screen->wide_chars = False; + free(screen->saveBuf_index); + screen->saveBuf_index = newBufHead; + } +} +#endif + +/* + * This function dynamically adds support for wide-characters. + */ +void +ChangeToWide(XtermWidget xw) +{ + TScreen *screen = TScreenOf(xw); + + if (screen->wide_chars) + return; + + TRACE(("ChangeToWide\n")); + if (xtermLoadWideFonts(xw, True)) { + int whichBuf = screen->whichBuf; + +#if !OPT_FIFO_LINES || !OPT_SAVE_LINES + int savelines = screen->scrollWidget ? screen->savelines : 0; + + if (savelines < 0) + savelines = 0; +#endif + + /* + * If we're displaying the alternate screen, switch the pointers back + * temporarily so ReallocateBufOffsets() will operate on the proper + * data in the alternate buffer. + */ + if (screen->whichBuf) + SwitchBufPtrs(screen, 0); + +#if OPT_SAVE_LINES +#if OPT_FIFO_LINES + ReallocateFifoIndex(xw); +#else + ReallocateBufOffsets(xw, + &screen->saveBuf_index, + &screen->saveBuf_data, + (unsigned) savelines, + (unsigned) MaxCols(screen)); +#endif + if (screen->editBuf_index[0]) { + ReallocateBufOffsets(xw, + &screen->editBuf_index[0], + &screen->editBuf_data[0], + (unsigned) MaxRows(screen), + (unsigned) MaxCols(screen)); + } +#else + ReallocateBufOffsets(xw, + &screen->saveBuf_index, + &screen->saveBuf_data, + (unsigned) (MaxRows(screen) + savelines), + (unsigned) MaxCols(screen)); +#endif + if (screen->editBuf_index[1]) { + ReallocateBufOffsets(xw, + &screen->editBuf_index[1], + &screen->editBuf_data[1], + (unsigned) MaxRows(screen), + (unsigned) MaxCols(screen)); + } + + screen->wide_chars = True; + screen->visbuf = VisBuf(screen); + + /* + * Switch the pointers back before we start painting on the screen. + */ + if (whichBuf) + SwitchBufPtrs(screen, whichBuf); + + update_font_utf8_mode(); + SetVTFont(xw, screen->menu_font_number, True, NULL); + } + TRACE(("...ChangeToWide\n")); +} +#endif + +/* + * Clear cells, no side-effects. + */ +void +CopyCells(TScreen * screen, LineData * src, LineData * dst, int col, int len) +{ + if (len > 0) { + int n; + int last = col + len; + + for (n = col; n < last; ++n) { + dst->charData[n] = src->charData[n]; + dst->attribs[n] = src->attribs[n]; + } + + if_OPT_ISO_COLORS(screen, { + for (n = col; n < last; ++n) { + dst->color[n] = src->color[n]; + } + }); + if_OPT_WIDE_CHARS(screen, { + size_t off; + for (n = col; n < last; ++n) { + for_each_combData(off, src) { + dst->combData[off][n] = src->combData[off][n]; + } + } + }); + } +} + +/* + * Clear cells, no side-effects. + */ +void +ClearCells(XtermWidget xw, int flags, unsigned len, int row, int col) +{ + if (len != 0) { + TScreen *screen = TScreenOf(xw); + LineData *ld; + unsigned n; + + ld = getLineData(screen, row); + + flags = (int) ((unsigned) flags | TERM_COLOR_FLAGS(xw)); + + for (n = 0; n < len; ++n) + ld->charData[(unsigned) col + n] = (CharData) ' '; + + memset(ld->attribs + col, flags, (size_t) len); + + if_OPT_ISO_COLORS(screen, { + CellColor p = xtermColorPair(xw); + for (n = 0; n < len; ++n) { + ld->color[(unsigned) col + n] = p; + } + }); + if_OPT_WIDE_CHARS(screen, { + size_t off; + for_each_combData(off, ld) { + memset(ld->combData[off] + col, 0, (size_t) len * sizeof(CharData)); + } + }); + } +} + +/* + * Clear data in the screen-structure (no I/O). + * Check for wide-character damage as well, clearing the damaged cells. + */ +void +ScrnClearCells(XtermWidget xw, int row, int col, unsigned len) +{ +#if OPT_WIDE_CHARS + TScreen *screen = TScreenOf(xw); +#endif + int flags = 0; + + if_OPT_WIDE_CHARS(screen, { + int kl; + int kr; + + if (DamagedCells(screen, len, &kl, &kr, row, col) + && kr >= kl) { + ClearCells(xw, flags, (unsigned) (kr - kl + 1), row, kl); + } + }); + ClearCells(xw, flags, len, row, col); +} + +/* + * Disown the selection and repaint the area that is highlighted so it is no + * longer highlighted. + */ +void +ScrnDisownSelection(XtermWidget xw) +{ + if (ScrnHaveSelection(TScreenOf(xw))) { + if (TScreenOf(xw)->keepSelection) { + UnhiliteSelection(xw); + } else { + DisownSelection(xw); + } + } +} + +/* + * Writes str into buf at screen's current row and column. Characters are set + * to match flags. + */ +void +ScrnWriteText(XtermWidget xw, + IChar * str, + unsigned flags, + unsigned cur_fg_bg, + unsigned length) +{ + TScreen *screen = TScreenOf(xw); + LineData *ld; + Char *attrs; + int avail = MaxCols(screen) - screen->cur_col; + IChar *chars; +#if OPT_WIDE_CHARS + IChar starcol1; +#endif + unsigned n; + unsigned real_width = visual_width(str, length); + + (void) cur_fg_bg; + + if (real_width + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) { + real_width = (unsigned) (MaxCols(screen) - screen->cur_col); + } + + if (avail <= 0) + return; + if (length > (unsigned) avail) + length = (unsigned) avail; + if (length == 0 || real_width == 0) + return; + + ld = getLineData(screen, screen->cur_row); + + chars = ld->charData + screen->cur_col; + attrs = ld->attribs + screen->cur_col; + +#if OPT_WIDE_CHARS + starcol1 = *chars; +#endif + + /* write blanks if we're writing invisible text */ + for (n = 0; n < length; ++n) { + if ((flags & INVISIBLE)) + chars[n] = ' '; + else + chars[n] = str[n]; + } + +#if OPT_BLINK_TEXT + if ((flags & BLINK) && !(screen->blink_as_bold)) { + LineSetBlinked(ld); + } +#endif + + if_OPT_WIDE_CHARS(screen, { + + if (real_width != length) { + IChar *char1 = chars; + if (screen->cur_col + && starcol1 == HIDDEN_CHAR + && isWide((int) char1[-1])) { + char1[-1] = (CharData) ' '; + } + /* if we are overwriting the right hand half of a + wide character, make the other half vanish */ + while (length) { + int ch = (int) str[0]; + + *char1++ = *str++; + length--; + + if (isWide(ch)) { + *char1++ = (CharData) HIDDEN_CHAR; + } + } + + if (*char1 == HIDDEN_CHAR + && char1[-1] == HIDDEN_CHAR) { + *char1 = (CharData) ' '; + } + /* if we are overwriting the left hand half of a + wide character, make the other half vanish */ + } else { + if (screen->cur_col + && starcol1 == HIDDEN_CHAR + && isWide((int) chars[-1])) { + chars[-1] = (CharData) ' '; + } + /* if we are overwriting the right hand half of a + wide character, make the other half vanish */ + if (chars[length] == HIDDEN_CHAR + && isWide((int) chars[length - 1])) { + chars[length] = (CharData) ' '; + } + } + }); + + flags &= ATTRIBUTES; + flags |= CHARDRAWN; + memset(attrs, (Char) flags, (size_t) real_width); + + if_OPT_WIDE_CHARS(screen, { + size_t off; + for_each_combData(off, ld) { + memset(ld->combData[off] + screen->cur_col, + 0, + real_width * sizeof(CharData)); + } + }); + if_OPT_ISO_COLORS(screen, { + unsigned j; + for (j = 0; j < real_width; ++j) + ld->color[screen->cur_col + (int) j] = (CellColor) cur_fg_bg; + }); + +#if OPT_WIDE_CHARS + screen->last_written_col = screen->cur_col + (int) real_width - 1; + screen->last_written_row = screen->cur_row; +#endif + + if_OPT_XMC_GLITCH(screen, { + Resolve_XMC(xw); + }); + + return; +} + +/* + * Saves pointers to the n lines beginning at sb + where, and clears the lines + */ +static void +ScrnClearLines(XtermWidget xw, ScrnBuf sb, int where, unsigned n, unsigned size) +{ + TScreen *screen = TScreenOf(xw); + ScrnPtr *base; + unsigned jump = scrnHeadSize(screen, 1); + unsigned i; + LineData *work; + unsigned flags = TERM_COLOR_FLAGS(xw); +#if OPT_ISO_COLORS + unsigned j; +#endif + + TRACE(("ScrnClearLines(%s:where %d, n %d, size %d)\n", + (sb == screen->saveBuf_index) ? "save" : "edit", + where, n, size)); + + assert(n != 0); + assert(size != 0); + + /* save n lines at where */ + SaveLineData(sb, (unsigned) where, (size_t) n); + + /* clear contents of old rows */ + base = screen->save_ptr; + for (i = 0; i < n; ++i) { + work = (LineData *) base; + work->bufHead = 0; +#if OPT_DEC_CHRSET + SetLineDblCS(work, 0); +#endif + + memset(work->charData, 0, size * sizeof(CharData)); + if (TERM_COLOR_FLAGS(xw)) { + memset(work->attribs, (int) flags, (size_t) size); +#if OPT_ISO_COLORS + { + CellColor p = xtermColorPair(xw); + for (j = 0; j < size; ++j) { + work->color[j] = p; + } + } +#endif + } else { + memset(work->attribs, 0, (size_t) size); +#if OPT_ISO_COLORS + memset(work->color, 0, size * sizeof(work->color[0])); +#endif + } +#if OPT_WIDE_CHARS + if (screen->wide_chars) { + size_t off; + + for (off = 0; off < work->combSize; ++off) { + memset(work->combData[off], 0, size * sizeof(CharData)); + } + } +#endif + base = ScrnBufAddr(base, jump); + } +} + +/* + * We're always ensured of having a visible buffer, but may not have saved + * lines. Check the pointer that's sure to work. + */ +#if OPT_SAVE_LINES +#define OkAllocBuf(screen) (screen->editBuf_index[0] != 0) +#else +#define OkAllocBuf(screen) (screen->saveBuf_index != 0) +#endif + +void +ScrnAllocBuf(XtermWidget xw) +{ + TScreen *screen = TScreenOf(xw); + + if (!OkAllocBuf(screen)) { + int nrows = MaxRows(screen); +#if !OPT_SAVE_LINES + int savelines = screen->scrollWidget ? screen->savelines : 0; +#endif + + TRACE(("ScrnAllocBuf %dx%d (%d)\n", + nrows, MaxCols(screen), screen->savelines)); + +#if OPT_SAVE_LINES + if (screen->savelines != 0) { +#if OPT_FIFO_LINES + /* for FIFO, we only need space for the index - addScrollback inits */ + screen->saveBuf_index = allocScrnHead(screen, + (unsigned) (screen->savelines)); +#else + screen->saveBuf_index = allocScrnBuf(xw, + (unsigned) screen->savelines, + (unsigned) MaxCols(screen), + &screen->saveBuf_data); +#endif + } else { + screen->saveBuf_index = 0; + } + screen->editBuf_index[0] = allocScrnBuf(xw, + (unsigned) nrows, + (unsigned) MaxCols(screen), + &screen->editBuf_data[0]); +#else /* !OPT_SAVE_LINES */ + screen->saveBuf_index = allocScrnBuf(xw, + (unsigned) (nrows + screen->savelines), + (unsigned) (MaxCols(screen)), + &screen->saveBuf_data); +#endif /* OPT_SAVE_LINES */ + screen->visbuf = VisBuf(screen); + } + return; +} + +size_t +ScrnPointers(TScreen * screen, size_t len) +{ + size_t result = scrnHeadSize(screen, (unsigned) len); + + if (result > screen->save_len) { + if (screen->save_len) + screen->save_ptr = (ScrnPtr *) realloc(screen->save_ptr, result); + else + screen->save_ptr = (ScrnPtr *) malloc(result); + screen->save_len = len; + if (screen->save_ptr == 0) + SysError(ERROR_SAVE_PTR); + } + TRACE2(("ScrnPointers %ld ->%p\n", (long) len, screen->save_ptr)); + return result; +} + +/* + * Inserts n blank lines at sb + where, treating last as a bottom margin. + */ +void +ScrnInsertLine(XtermWidget xw, ScrnBuf sb, int last, int where, unsigned n) +{ + TScreen *screen = TScreenOf(xw); + unsigned size = (unsigned) MaxCols(screen); + + TRACE(("ScrnInsertLine(last %d, where %d, n %d, size %d)\n", + last, where, n, size)); + + assert(where >= 0); + assert(last >= (int) n); + assert(last >= where); + + assert(n != 0); + assert(size != 0); + + /* save n lines at bottom */ + ScrnClearLines(xw, sb, (last -= (int) n - 1), n, size); + + /* + * WARNING, overlapping copy operation. Move down lines (pointers). + * + * +----|---------|--------+ + * + * is copied in the array to: + * + * +--------|---------|----+ + */ + assert(last >= where); + /* + * This will never shift from the saveBuf to editBuf, so there is no need + * to handle that case. + */ + MoveLineData(sb, + (unsigned) (where + (int) n), + (unsigned) where, + (unsigned) (last - where)); + + /* reuse storage for new lines at where */ + RestoreLineData(sb, (unsigned) where, n); +} + +/* + * Deletes n lines at sb + where, treating last as a bottom margin. + */ +void +ScrnDeleteLine(XtermWidget xw, ScrnBuf sb, int last, int where, unsigned n) +{ + TScreen *screen = TScreenOf(xw); + unsigned size = (unsigned) MaxCols(screen); + + TRACE(("ScrnDeleteLine(%s:last %d, where %d, n %d, size %d)\n", + (sb == screen->saveBuf_index) ? "save" : "edit", + last, where, n, size)); + + assert(where >= 0); + assert(last >= where + (int) n - 1); + + assert(n != 0); + assert(size != 0); + + /* move up lines */ + last -= ((int) n - 1); +#if OPT_SAVE_LINES + if (inSaveBuf(screen, sb, where)) { +#if !OPT_FIFO_LINES + int from = where + n; +#endif + + /* we shouldn't be editing the saveBuf, only scroll into it */ + assert(last >= screen->savelines); + + if (sb != 0) { +#if OPT_FIFO_LINES + /* copy lines from editBuf to saveBuf (allocating as we go...) */ + saveEditBufLines(screen, sb, n); +#else + ScrnClearLines(xw, sb, where, n, size); + + /* move the pointers within saveBuf */ + TRACE(("...%smoving pointers in saveBuf (compare %d %d)\n", + ((screen->savelines > from) + ? "" + : "SKIP "), + screen->savelines, + from)); + if (screen->savelines > from) { + MoveLineData(sb, + (unsigned) where, + (unsigned) from, + (unsigned) (screen->savelines - from)); + } + + /* reuse storage in saveBuf */ + TRACE(("...reuse %d lines storage in saveBuf\n", n)); + RestoreLineData(sb, (unsigned) screen->savelines - n, n); + + /* copy lines from editBuf to saveBuf (into the reused storage) */ + saveEditBufLines(screen, sb, n); +#endif + } + + /* adjust variables to fall-thru into changes only to editBuf */ + TRACE(("...adjusting variables, to work on editBuf alone\n")); + last -= screen->savelines; + where = 0; + sb = screen->visbuf; + } +#endif + /* + * Scroll the visible buffer (editBuf). + */ + ScrnClearLines(xw, sb, where, n, size); + + MoveLineData(sb, + (unsigned) where, + (unsigned) (where + (int) n), + (size_t) (last - where)); + + /* reuse storage for new bottom lines */ + RestoreLineData(sb, (unsigned) last, n); +} + +/* + * Inserts n blanks in screen at current row, col. Size is the size of each + * row. + */ +void +ScrnInsertChar(XtermWidget xw, unsigned n) +{ +#define MemMove(data) \ + for (j = last; j >= (col + (int) n); --j) \ + data[j] = data[j - (int) n] + + TScreen *screen = TScreenOf(xw); + int first = ScrnLeftMargin(xw); + int last = ScrnRightMargin(xw); + int row = screen->cur_row; + int col = screen->cur_col; + int j; + LineData *ld; + + if (col < first || col > last) { + TRACE(("ScrnInsertChar - col %d outside [%d..%d]\n", col, first, last)); + return; + } else if (last <= (col + (int) n)) { + n = (unsigned) (last - col); + } + + assert(screen->cur_col >= 0); + assert(screen->cur_row >= 0); + assert(n > 0); + assert(last >= (int) n); + + if_OPT_WIDE_CHARS(screen, { + int xx = screen->cur_row; + int kl; + int kr = screen->cur_col; + if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) { + ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl); + } + kr = last - (int) n + 1; + if (DamagedCells(screen, n, &kl, (int *) 0, xx, kr) && kr > kl) { + ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl); + } + }); + + if ((ld = getLineData(screen, row)) != 0) { + MemMove(ld->charData); + MemMove(ld->attribs); + + if_OPT_ISO_COLORS(screen, { + MemMove(ld->color); + }); + if_OPT_WIDE_CHARS(screen, { + size_t off; + for_each_combData(off, ld) { + MemMove(ld->combData[off]); + } + }); + } + ClearCells(xw, CHARDRAWN, n, row, col); + +#undef MemMove +} + +/* + * Deletes n characters at current row, col. + */ +void +ScrnDeleteChar(XtermWidget xw, unsigned n) +{ +#define MemMove(data) \ + for (j = col; j <= last - (int) n; ++j) \ + data[j] = data[j + (int) n] + + TScreen *screen = TScreenOf(xw); + int first = ScrnLeftMargin(xw); + int last = ScrnRightMargin(xw) + 1; + int row = screen->cur_row; + int col = screen->cur_col; + int j; + LineData *ld; + + if (col < first || col > last) { + TRACE(("ScrnDeleteChar - col %d outside [%d..%d]\n", col, first, last)); + return; + } else if (last <= (col + (int) n)) { + n = (unsigned) (last - col); + } + + assert(screen->cur_col >= 0); + assert(screen->cur_row >= 0); + assert(n > 0); + assert(last > (int) n); + + if_OPT_WIDE_CHARS(screen, { + int kl; + int kr; + if (DamagedCells(screen, n, &kl, &kr, + screen->cur_row, + screen->cur_col)) + ClearCells(xw, 0, (unsigned) (kr - kl + 1), row, kl); + }); + + if ((ld = getLineData(screen, row)) != 0) { + MemMove(ld->charData); + MemMove(ld->attribs); + + if_OPT_ISO_COLORS(screen, { + MemMove(ld->color); + }); + if_OPT_WIDE_CHARS(screen, { + size_t off; + for_each_combData(off, ld) { + MemMove(ld->combData[off]); + } + }); + LineClrWrapped(ld); + if (screen->show_wrap_marks) { + ShowWrapMarks(xw, row, ld); + } + } + ClearCells(xw, 0, n, row, (last - (int) n)); + +#undef MemMove +} + +/* + * This is useful for debugging both xterm and applications that may manipulate + * its line-wrapping state. + */ +void +ShowWrapMarks(XtermWidget xw, int row, LineData * ld) +{ + TScreen *screen = TScreenOf(xw); + Boolean set = (Boolean) LineTstWrapped(ld); + CgsEnum cgsId = set ? gcVTcursFilled : gcVTcursReverse; + VTwin *currentWin = WhichVWin(screen); + int y = row * FontHeight(screen) + screen->border; + int x = LineCursorX(screen, ld, screen->max_col + 1); + + TRACE2(("ShowWrapMarks %d:%s\n", row, BtoS(set))); + + XFillRectangle(screen->display, VWindow(screen), + getCgsGC(xw, currentWin, cgsId), + x, y, + (unsigned) screen->border, + (unsigned) FontHeight(screen)); +} + +/* + * Repaints the area enclosed by the parameters. + * Requires: (toprow, leftcol), (toprow + nrows, leftcol + ncols) are + * coordinates of characters in screen; + * nrows and ncols positive. + * all dimensions are based on single-characters. + */ +void +ScrnRefresh(XtermWidget xw, + int toprow, + int leftcol, + int nrows, + int ncols, + Bool force) /* ... leading/trailing spaces */ +{ + TScreen *screen = TScreenOf(xw); + LineData *ld; + int y = toprow * FontHeight(screen) + screen->border; + int row; + int maxrow = toprow + nrows - 1; + int scrollamt = screen->scroll_amt; + unsigned gc_changes = 0; +#ifdef __CYGWIN__ + static char first_time = 1; +#endif + static int recurse = 0; + + TRACE(("ScrnRefresh top %d (%d,%d) - (%d,%d)%s {{\n", + screen->topline, toprow, leftcol, + nrows, ncols, + force ? " force" : "")); + + if (screen->cursorp.col >= leftcol + && screen->cursorp.col <= (leftcol + ncols - 1) + && screen->cursorp.row >= ROW2INX(screen, toprow) + && screen->cursorp.row <= ROW2INX(screen, maxrow)) + screen->cursor_state = OFF; + + for (row = toprow; row <= maxrow; y += FontHeight(screen), row++) { +#if OPT_ISO_COLORS + CellColor *fb = 0; +#define ColorOf(col) (CellColor) (fb ? fb[col] : 0) +#endif +#if OPT_WIDE_CHARS + int wideness = 0; +#endif +#define BLANK_CEL(cell) (chars[cell] == ' ') + IChar *chars; + Char *attrs; + int col = leftcol; + int maxcol = leftcol + ncols - 1; + int hi_col = maxcol; + int lastind; + unsigned flags; + unsigned test; + CellColor fg_bg = 0; + unsigned fg = 0, bg = 0; + int x; + GC gc; + Bool hilite; + + (void) fg; + (void) bg; +#if !OPT_ISO_COLORS + fg_bg = 0; +#endif + + if (row < screen->top_marg || row > screen->bot_marg) + lastind = row; + else + lastind = row - scrollamt; + + if (lastind < 0 || lastind > screen->max_row) + continue; + + TRACE2(("ScrnRefresh row=%d lastind=%d ->%d\n", + row, lastind, ROW2INX(screen, lastind))); + + if ((ld = getLineData(screen, ROW2INX(screen, lastind))) == 0 + || ld->charData == 0 + || ld->attribs == 0) { + break; + } + + if (screen->show_wrap_marks) { + ShowWrapMarks(xw, lastind, ld); + } + + if (maxcol >= (int) ld->lineSize) { + maxcol = ld->lineSize - 1; + hi_col = maxcol; + } + + chars = ld->charData; + attrs = ld->attribs; + + if_OPT_WIDE_CHARS(screen, { + /* This fixes an infinite recursion bug, that leads + to display anomalies. It seems to be related to + problems with the selection. */ + if (recurse < 3) { + /* adjust to redraw all of a widechar if we just wanted + to draw the right hand half */ + if (leftcol > 0 && + chars[leftcol] == HIDDEN_CHAR && + isWide((int) chars[leftcol - 1])) { + leftcol--; + ncols++; + col = leftcol; + } + } else { + xtermWarning("This should not happen. Why is it so?\n"); + } + }); + + if (row < screen->startH.row || row > screen->endH.row || + (row == screen->startH.row && maxcol < screen->startH.col) || + (row == screen->endH.row && col >= screen->endH.col)) { +#if OPT_DEC_CHRSET + /* + * Temporarily change dimensions to double-sized characters so + * we can reuse the recursion on this function. + */ + if (CSET_DOUBLE(GetLineDblCS(ld))) { + col /= 2; + maxcol /= 2; + } +#endif + /* + * If row does not intersect selection; don't hilite blanks. + */ + if (!force) { + while (col <= maxcol && (attrs[col] & ~BOLD) == 0 && + BLANK_CEL(col)) + col++; + + while (col <= maxcol && (attrs[maxcol] & ~BOLD) == 0 && + BLANK_CEL(maxcol)) + maxcol--; + } +#if OPT_DEC_CHRSET + if (CSET_DOUBLE(GetLineDblCS(ld))) { + col *= 2; + maxcol *= 2; + } +#endif + hilite = False; + } else { + /* row intersects selection; split into pieces of single type */ + if (row == screen->startH.row && col < screen->startH.col) { + recurse++; + ScrnRefresh(xw, row, col, 1, screen->startH.col - col, + force); + col = screen->startH.col; + } + if (row == screen->endH.row && maxcol >= screen->endH.col) { + recurse++; + ScrnRefresh(xw, row, screen->endH.col, 1, + maxcol - screen->endH.col + 1, force); + maxcol = screen->endH.col - 1; + } + + /* + * If we're highlighting because the user is doing cut/paste, + * trim the trailing blanks from the highlighted region so we're + * showing the actual extent of the text that'll be cut. If + * we're selecting a blank line, we'll highlight one column + * anyway. + * + * We don't do this if the mouse-hilite mode is set because that + * would be too confusing. + * + * The default if the highlightSelection resource isn't set will + * highlight the whole width of the terminal, which is easy to + * see, but harder to use (because trailing blanks aren't as + * apparent). + */ + if (screen->highlight_selection + && screen->send_mouse_pos != VT200_HIGHLIGHT_MOUSE) { + hi_col = screen->max_col; + while (hi_col > 0 && !(attrs[hi_col] & CHARDRAWN)) + hi_col--; + } + + /* remaining piece should be hilited */ + hilite = True; + } + + if (col > maxcol) + continue; + + /* + * Go back to double-sized character dimensions if the line has + * double-width characters. Note that 'hi_col' is already in the + * right units. + */ + if_OPT_DEC_CHRSET({ + if (CSET_DOUBLE(GetLineDblCS(ld))) { + col /= 2; + maxcol /= 2; + } + }); + + flags = attrs[col]; + + if_OPT_WIDE_CHARS(screen, { + wideness = isWide((int) chars[col]); + }); + + if_OPT_ISO_COLORS(screen, { + fb = ld->color; + fg_bg = ColorOf(col); + fg = extract_fg(xw, fg_bg, flags); + bg = extract_bg(xw, fg_bg, flags); + }); + + gc = updatedXtermGC(xw, flags, fg_bg, hilite); + gc_changes |= (flags & (FG_COLOR | BG_COLOR)); + + x = LineCursorX(screen, ld, col); + lastind = col; + + for (; col <= maxcol; col++) { + if ((attrs[col] != flags) + || (hilite && (col > hi_col)) +#if OPT_ISO_COLORS + || ((flags & FG_COLOR) + && (extract_fg(xw, ColorOf(col), attrs[col]) != fg)) + || ((flags & BG_COLOR) + && (extract_bg(xw, ColorOf(col), attrs[col]) != bg)) +#endif +#if OPT_WIDE_CHARS + || (isWide((int) chars[col]) != wideness + && chars[col] != HIDDEN_CHAR) +#endif + ) { + assert(col >= lastind); + TRACE(("ScrnRefresh looping drawXtermText %d..%d:%s\n", + lastind, col, + visibleIChars((&chars[lastind]), + (unsigned) (col - lastind)))); + + test = flags; + checkVeryBoldColors(test, fg); + + x = drawXtermText(xw, test & DRAWX_MASK, gc, x, y, + GetLineDblCS(ld), + &chars[lastind], + (unsigned) (col - lastind), 0); + + if_OPT_WIDE_CHARS(screen, { + int i; + size_t off; + + for_each_combData(off, ld) { + IChar *com_off = ld->combData[off]; + + for (i = lastind; i < col; i++) { + int my_x = LineCursorX(screen, ld, i); + IChar base = chars[i]; + + if (isWide((int) base)) + my_x = LineCursorX(screen, ld, i - 1); + + if (com_off[i] != 0) + drawXtermText(xw, + (test & DRAWX_MASK) + | NOBACKGROUND, + gc, my_x, y, + GetLineDblCS(ld), + com_off + i, + 1, isWide((int) base)); + } + } + }); + + resetXtermGC(xw, flags, hilite); + + lastind = col; + + if (hilite && (col > hi_col)) + hilite = False; + + flags = attrs[col]; + if_OPT_ISO_COLORS(screen, { + fg_bg = ColorOf(col); + fg = extract_fg(xw, fg_bg, flags); + bg = extract_bg(xw, fg_bg, flags); + }); + if_OPT_WIDE_CHARS(screen, { + wideness = isWide((int) chars[col]); + }); + + gc = updatedXtermGC(xw, flags, fg_bg, hilite); + gc_changes |= (flags & (FG_COLOR | BG_COLOR)); + } + + if (chars[col] == 0) { + chars[col] = ' '; + } + } + + assert(col >= lastind); + TRACE(("ScrnRefresh calling drawXtermText %d..%d:%s\n", + lastind, col, + visibleIChars(&chars[lastind], (unsigned) (col - lastind)))); + + test = flags; + checkVeryBoldColors(test, fg); + + drawXtermText(xw, test & DRAWX_MASK, gc, x, y, + GetLineDblCS(ld), + &chars[lastind], + (unsigned) (col - lastind), 0); + + if_OPT_WIDE_CHARS(screen, { + int i; + size_t off; + + for_each_combData(off, ld) { + IChar *com_off = ld->combData[off]; + + for (i = lastind; i < col; i++) { + int my_x = LineCursorX(screen, ld, i); + int base = (int) chars[i]; + + if (isWide(base)) + my_x = LineCursorX(screen, ld, i - 1); + + if (com_off[i] != 0) + drawXtermText(xw, + (test & DRAWX_MASK) + | NOBACKGROUND, + gc, my_x, y, + GetLineDblCS(ld), + com_off + i, + 1, isWide(base)); + } + } + }); + + resetXtermGC(xw, flags, hilite); + } + + /* + * If we're in color mode, reset the various GC's to the current + * screen foreground and background so that other functions (e.g., + * ClearRight) will get the correct colors. + */ + if_OPT_ISO_COLORS(screen, { + if (gc_changes & FG_COLOR) + SGR_Foreground(xw, xw->cur_foreground); + if (gc_changes & BG_COLOR) + SGR_Background(xw, xw->cur_background); + }); + +#if defined(__CYGWIN__) && defined(TIOCSWINSZ) + if (first_time == 1) { + TTYSIZE_STRUCT ts; + + first_time = 0; + TTYSIZE_ROWS(ts) = nrows; + TTYSIZE_COLS(ts) = ncols; + ts.ws_xpixel = xw->core.width; + ts.ws_ypixel = xw->core.height; + SET_TTYSIZE(screen->respond, ts); + } +#endif + recurse--; + + TRACE(("...}} ScrnRefresh\n")); + return; +} + +/* + * Call this wrapper to ScrnRefresh() when the data has changed. If the + * refresh region overlaps the selection, we will release the primary selection. + */ +void +ScrnUpdate(XtermWidget xw, + int toprow, + int leftcol, + int nrows, + int ncols, + Bool force) /* ... leading/trailing spaces */ +{ + TScreen *screen = TScreenOf(xw); + + if (ScrnHaveSelection(screen) + && (toprow <= screen->endH.row) + && (toprow + nrows - 1 >= screen->startH.row)) { + ScrnDisownSelection(xw); + } + ScrnRefresh(xw, toprow, leftcol, nrows, ncols, force); +} + +/* + * Sets the rows first though last of the buffer of screen to spaces. + * Requires first <= last; first, last are rows of screen->buf. + */ +void +ClearBufRows(XtermWidget xw, + int first, + int last) +{ + TScreen *screen = TScreenOf(xw); + unsigned len = (unsigned) MaxCols(screen); + int row; + + TRACE(("ClearBufRows %d..%d\n", first, last)); + for (row = first; row <= last; row++) { + LineData *ld = getLineData(screen, row); + if (ld != 0) { + if_OPT_DEC_CHRSET({ + /* clearing the whole row resets the doublesize characters */ + SetLineDblCS(ld, CSET_SWL); + }); + LineClrWrapped(ld); + if (screen->show_wrap_marks) { + ShowWrapMarks(xw, row, ld); + } + ClearCells(xw, 0, len, row, 0); + } + } +} + +/* + Resizes screen: + 1. If new window would have fractional characters, sets window size so as to + discard fractional characters and returns -1. + Minimum screen size is 1 X 1. + Note that this causes another ExposeWindow event. + 2. Enlarges screen->buf if necessary. New space is appended to the bottom + and to the right + 3. Reduces screen->buf if necessary. Old space is removed from the bottom + and from the right + 4. Cursor is positioned as closely to its former position as possible + 5. Sets screen->max_row and screen->max_col to reflect new size + 6. Maintains the inner border (and clears the border on the screen). + 7. Clears origin mode and sets scrolling region to be entire screen. + 8. Returns 0 + */ +int +ScreenResize(XtermWidget xw, + int width, + int height, + unsigned *flags) +{ + TScreen *screen = TScreenOf(xw); + int code, rows, cols; + int border = 2 * screen->border; + int move_down_by = 0; +#ifdef TTYSIZE_STRUCT + TTYSIZE_STRUCT ts; +#endif + Window tw = VWindow(screen); + + TRACE(("ScreenResize %dx%d border %d font %dx%d\n", + height, width, border, + FontHeight(screen), FontWidth(screen))); + + assert(width > 0); + assert(height > 0); + + if (screen->is_running) { + /* clear the right and bottom internal border because of NorthWest + gravity might have left junk on the right and bottom edges */ + if (width >= (int) FullWidth(screen)) { + XClearArea(screen->display, tw, + FullWidth(screen), 0, /* right edge */ + 0, (unsigned) height, /* from top to bottom */ + False); + } + if (height >= (int) FullHeight(screen)) { + XClearArea(screen->display, tw, + 0, FullHeight(screen), /* bottom */ + (unsigned) width, 0, /* all across the bottom */ + False); + } + } + + TRACE(("...computing rows/cols: %.2f %.2f\n", + (double) (height - border) / FontHeight(screen), + (double) (width - border - ScrollbarWidth(screen)) / FontWidth(screen))); + + rows = (height - border) / FontHeight(screen); + cols = (width - border - ScrollbarWidth(screen)) / FontWidth(screen); + if (rows < 1) + rows = 1; + if (cols < 1) + cols = 1; + + /* update buffers if the screen has changed size */ + if (MaxRows(screen) != rows || MaxCols(screen) != cols) { + int whichBuf = 0; + int delta_rows = rows - MaxRows(screen); +#if OPT_TRACE + int delta_cols = cols - MaxCols(screen); +#endif + + TRACE(("...ScreenResize chars %dx%d delta %dx%d\n", + rows, cols, delta_rows, delta_cols)); + + if (screen->is_running) { +#if !OPT_FIFO_LINES + int savelines = (screen->scrollWidget + ? screen->savelines + : 0); +#endif + if (screen->cursor_state) + HideCursor(); +#if OPT_SAVE_LINES + /* + * The non-visible buffer is simple, since we will not copy data + * to/from the saved-lines. Do that first. + */ + if (screen->editBuf_index[!screen->whichBuf]) { + (void) Reallocate(xw, + &screen->editBuf_index[!screen->whichBuf], + &screen->editBuf_data[!screen->whichBuf], + (unsigned) rows, + (unsigned) cols, + (unsigned) MaxRows(screen)); + } + + /* + * The save-lines buffer may change width, but will not change its + * height. Deal with the cases where we copy data to/from the + * saved-lines buffer. + */ + if (GravityIsSouthWest(xw) + && delta_rows + && screen->saveBuf_index != 0) { + + if (delta_rows < 0) { + unsigned move_up = (unsigned) (-delta_rows); + ScrnBuf dst = screen->saveBuf_index; + +#if OPT_FIFO_LINES + int amount = ((MaxRows(screen) - (int) move_up - 1) + - screen->cur_row); + + if (amount < 0) { + /* move line-data from visible-buffer to save-buffer */ + saveEditBufLines(screen, dst, (unsigned) -amount); + move_down_by = amount; + } else { + move_down_by = 0; + } +#else /* !OPT_FIFO_LINES */ + int amount = screen->savelines - (int) move_up; + + TRACE_SCRNBUF("before save", screen, dst, screen->savelines); + + /* shift lines in save-buffer to make room */ + TRACE(("...%smoving pointers in saveBuf (compare %d %d)\n", + (amount > 0 + ? "" + : "SKIP "), + screen->savelines, + move_up)); + if (amount > 0) { + SaveLineData(dst, 0, move_up); + + MoveLineData(dst, + 0, + move_up, + (unsigned) amount); + + TRACE(("...reuse %d lines storage in saveBuf\n", move_up)); + RestoreLineData(dst, + (unsigned) amount, + move_up); + TRACE_SCRNBUF("restoresave", screen, dst, screen->savelines); + } + + /* copy line-data from visible-buffer to save-buffer */ + saveEditBufLines(screen, dst, move_up); + + /* after data is copied, reallocate saved-lines */ + (void) Reallocate(xw, + &screen->saveBuf_index, + &screen->saveBuf_data, + (unsigned) savelines, + (unsigned) cols, + (unsigned) savelines); + TRACE_SCRNBUF("reallocSAVE", + screen, + screen->saveBuf_index, + savelines); +#endif /* OPT_FIFO_LINES */ + + /* decrease size of visible-buffer */ + (void) Reallocate(xw, + &screen->editBuf_index[screen->whichBuf], + &screen->editBuf_data[screen->whichBuf], + (unsigned) rows, + (unsigned) cols, + (unsigned) MaxRows(screen)); + TRACE_SCRNBUF("reallocEDIT", + screen, + screen->editBuf_index[screen->whichBuf], + rows); + } else { + unsigned move_down = (unsigned) delta_rows; +#if OPT_FIFO_LINES + long unsave_fifo; +#else + ScrnBuf src = screen->saveBuf_index; +#endif + ScrnBuf dst; + int amount; + + if ((int) move_down > screen->savedlines) { + move_down = (unsigned) screen->savedlines; + } + move_down_by = (int) move_down; + amount = rows - (int) move_down; + + /* increase size of visible-buffer */ + (void) Reallocate(xw, + &screen->editBuf_index[screen->whichBuf], + &screen->editBuf_data[screen->whichBuf], + (unsigned) rows, + (unsigned) cols, + (unsigned) MaxRows(screen)); + + dst = screen->editBuf_index[screen->whichBuf]; + TRACE_SCRNBUF("reallocEDIT", screen, dst, rows); + + TRACE(("...%smoving pointers in editBuf (compare %d %d)\n", + (amount > 0 + ? "" + : "SKIP "), + rows, + move_down)); + if (amount > 0) { + /* shift lines in visible-buffer to make room */ + SaveLineData(dst, (unsigned) amount, (size_t) move_down); + + MoveLineData(dst, + move_down, + 0, + (unsigned) amount); + + TRACE(("...reuse %d lines storage in editBuf\n", move_down)); + RestoreLineData(dst, + 0, + move_down); + + TRACE_SCRNBUF("shifted", screen, dst, rows); + } + + /* copy line-data from save-buffer to visible-buffer */ + unsaveEditBufLines(screen, dst, move_down); + TRACE_SCRNBUF("copied", screen, dst, rows); + +#if OPT_FIFO_LINES + unsave_fifo = (long) move_down; + if (screen->saved_fifo < (int) unsave_fifo) + unsave_fifo = screen->saved_fifo; + + /* free up storage in fifo from the copied lines */ + while (unsave_fifo-- > 0) { + deleteScrollback(screen, -1); + screen->saved_fifo--; + } +#else + amount = (screen->savelines - (int) move_down); + TRACE(("...%smoving pointers in saveBuf (compare %d %d)\n", + (amount > 0 + ? "" + : "SKIP "), + rows, + move_down)); + if (amount > 0) { + /* shift lines in save-buffer to account for copy */ + src = screen->saveBuf_index; + SaveLineData(src, amount, move_down); + + MoveLineData(src, + move_down, + 0, + (unsigned) amount); + + TRACE(("...reuse %d lines storage in saveBuf\n", move_down)); + RestoreLineData(src, + 0, + move_down); + } +#endif + + /* recover storage in save-buffer */ + } + } else { +#if !OPT_FIFO_LINES + (void) Reallocate(xw, + &screen->saveBuf_index, + &screen->saveBuf_data, + (unsigned) savelines, + (unsigned) cols, + (unsigned) savelines); +#endif + (void) Reallocate(xw, + &screen->editBuf_index[screen->whichBuf], + &screen->editBuf_data[screen->whichBuf], + (unsigned) rows, + (unsigned) cols, + (unsigned) MaxRows(screen)); + } +#else /* !OPT_SAVE_LINES */ + if (screen->whichBuf + && GravityIsSouthWest(xw)) { + /* swap buffer pointers back to make this work */ + whichBuf = screen->whichBuf; + SwitchBufPtrs(screen, 0); + } else { + whichBuf = 0; + } + if (screen->editBuf_index[1]) + (void) Reallocate(xw, + &screen->editBuf_index[1], + &screen->editBuf_data[1], + (unsigned) rows, + (unsigned) cols, + (unsigned) MaxRows(screen)); + move_down_by = Reallocate(xw, + &screen->saveBuf_index, + &screen->saveBuf_data, + (unsigned) (rows + savelines), + (unsigned) cols, + (unsigned) (MaxRows(screen) + savelines)); +#endif /* OPT_SAVE_LINES */ + screen->visbuf = VisBuf(screen); + } + + AdjustSavedCursor(xw, move_down_by); + set_max_row(screen, screen->max_row + delta_rows); + set_max_col(screen, cols - 1); + + if (screen->is_running) { + if (GravityIsSouthWest(xw)) { + screen->savedlines -= move_down_by; + if (screen->savedlines < 0) + screen->savedlines = 0; + if (screen->savedlines > screen->savelines) + screen->savedlines = screen->savelines; + if (screen->topline < -screen->savedlines) + screen->topline = -screen->savedlines; + set_cur_row(screen, screen->cur_row + move_down_by); + screen->cursorp.row += move_down_by; + ScrollSelection(screen, move_down_by, True); + + if (whichBuf) + SwitchBufPtrs(screen, whichBuf); /* put the pointers back */ + } + } + + /* adjust scrolling region */ + set_tb_margins(screen, 0, screen->max_row); + set_lr_margins(screen, 0, screen->max_col); + UIntClr(*flags, ORIGIN); + + if (screen->cur_row > screen->max_row) + set_cur_row(screen, screen->max_row); + if (screen->cur_col > screen->max_col) + set_cur_col(screen, screen->max_col); + + screen->fullVwin.height = height - border; + screen->fullVwin.width = width - border - screen->fullVwin.sb_info.width; + + } else if (FullHeight(screen) == height && FullWidth(screen) == width) + return (0); /* nothing has changed at all */ + + screen->fullVwin.fullheight = (Dimension) height; + screen->fullVwin.fullwidth = (Dimension) width; + + ResizeScrollBar(xw); + ResizeSelection(screen, rows, cols); + +#ifndef NO_ACTIVE_ICON + if (screen->iconVwin.window) { + XWindowChanges changes; + screen->iconVwin.width = + MaxCols(screen) * screen->iconVwin.f_width; + + screen->iconVwin.height = + MaxRows(screen) * screen->iconVwin.f_height; + + changes.width = screen->iconVwin.fullwidth = + (Dimension) ((unsigned) screen->iconVwin.width + + 2 * xw->misc.icon_border_width); + + changes.height = screen->iconVwin.fullheight = + (Dimension) ((unsigned) screen->iconVwin.height + + 2 * xw->misc.icon_border_width); + + changes.border_width = (int) xw->misc.icon_border_width; + + TRACE(("resizing icon window %dx%d\n", changes.height, changes.width)); + XConfigureWindow(XtDisplay(xw), screen->iconVwin.window, + CWWidth | CWHeight | CWBorderWidth, &changes); + } +#endif /* NO_ACTIVE_ICON */ + +#ifdef TTYSIZE_STRUCT + /* Set tty's idea of window size */ + TTYSIZE_ROWS(ts) = (ttySize_t) rows; + TTYSIZE_COLS(ts) = (ttySize_t) cols; +#ifdef USE_STRUCT_WINSIZE + ts.ws_xpixel = (ttySize_t) width; + ts.ws_ypixel = (ttySize_t) height; +#endif + code = SET_TTYSIZE(screen->respond, ts); + TRACE(("return %d from SET_TTYSIZE %dx%d\n", code, rows, cols)); + (void) code; + +#if defined(SIGWINCH) && defined(TIOCGPGRP) + if (screen->pid > 1) { + int pgrp; + + TRACE(("getting process-group\n")); + if (ioctl(screen->respond, TIOCGPGRP, &pgrp) != -1) { + TRACE(("sending SIGWINCH to process group %d\n", pgrp)); + kill_process_group(pgrp, SIGWINCH); + } + } +#endif /* SIGWINCH */ + +#else + TRACE(("ScreenResize cannot do anything to pty\n")); +#endif /* TTYSIZE_STRUCT */ + return (0); +} + +/* + * Return true if any character cell starting at [row,col], for len-cells is + * nonnull. + */ +Bool +non_blank_line(TScreen * screen, + int row, + int col, + int len) +{ + int i; + Bool found = False; + LineData *ld = getLineData(screen, row); + + if (ld != 0) { + for (i = col; i < len; i++) { + if (ld->charData[i]) { + found = True; + break; + } + } + } + return found; +} + +/* + * Limit/map rectangle parameters. + */ +#define minRectRow(screen) (getMinRow(screen) + 1) +#define minRectCol(screen) (getMinCol(screen) + 1) +#define maxRectRow(screen) (getMaxRow(screen) + 1) +#define maxRectCol(screen) (getMaxCol(screen) + 1) + +static int +limitedParseRow(XtermWidget xw, int row) +{ + TScreen *screen = TScreenOf(xw); + int min_row = minRectRow(screen); + int max_row = maxRectRow(screen); + + if (xw->flags & ORIGIN) + row += screen->top_marg; + + if (row < min_row) + row = min_row; + else if (row > max_row) + row = max_row; + + return row; +} + +static int +limitedParseCol(XtermWidget xw, int col) +{ + TScreen *screen = TScreenOf(xw); + int min_col = minRectCol(screen); + int max_col = maxRectCol(screen); + + if (xw->flags & ORIGIN) + col += screen->lft_marg; + + if (col < min_col) + col = min_col; + else if (col > max_col) + col = max_col; + + return col; +} + +#define LimitedParse(num, func, dft) \ + func(xw, (nparams > num) ? params[num] : dft) + +/* + * Copy the rectangle boundaries into a struct, providing default values as + * needed. + */ +void +xtermParseRect(XtermWidget xw, int nparams, int *params, XTermRect * target) +{ + TScreen *screen = TScreenOf(xw); + + memset(target, 0, sizeof(*target)); + target->top = LimitedParse(0, limitedParseRow, minRectRow(screen)); + target->left = LimitedParse(1, limitedParseCol, minRectCol(screen)); + target->bottom = LimitedParse(2, limitedParseRow, maxRectRow(screen)); + target->right = LimitedParse(3, limitedParseCol, maxRectCol(screen)); + TRACE(("parsed rectangle %d,%d %d,%d\n", + target->top, + target->left, + target->bottom, + target->right)); +} + +static Bool +validRect(XtermWidget xw, XTermRect * target) +{ + TScreen *screen = TScreenOf(xw); + + TRACE(("comparing against screensize %dx%d\n", + maxRectRow(screen), + maxRectCol(screen))); + return (target != 0 + && target->top >= minRectRow(screen) + && target->left >= minRectCol(screen) + && target->top <= target->bottom + && target->left <= target->right + && target->top <= maxRectRow(screen) + && target->right <= maxRectCol(screen)); +} + +/* + * Fills a rectangle with the given 8-bit character and video-attributes. + * Colors and double-size attribute are unmodified. + */ +void +ScrnFillRectangle(XtermWidget xw, + XTermRect * target, + int value, + unsigned flags, + Bool keepColors) +{ + TScreen *screen = TScreenOf(xw); + + TRACE(("filling rectangle with '%c' flags %#x\n", value, flags)); + if (validRect(xw, target)) { + LineData *ld; + unsigned left = (unsigned) (target->left - 1); + unsigned size = (unsigned) (target->right - (int) left); + unsigned attrs = flags; + int row, col; + + (void) size; + + attrs &= ATTRIBUTES; + attrs |= CHARDRAWN; + for (row = target->bottom - 1; row >= (target->top - 1); row--) { + ld = getLineData(screen, row); + + TRACE(("filling %d [%d..%d]\n", row, left, left + size)); + + /* + * Fill attributes, preserving colors. + */ + for (col = (int) left; col < target->right; ++col) { + unsigned temp = ld->attribs[col]; + + if (!keepColors) { + UIntClr(temp, (FG_COLOR | BG_COLOR)); + } + temp = attrs | (temp & (FG_COLOR | BG_COLOR)) | CHARDRAWN; + ld->attribs[col] = (Char) temp; +#if OPT_ISO_COLORS + if (attrs & (FG_COLOR | BG_COLOR)) { + if_OPT_ISO_COLORS(screen, { + ld->color[col] = xtermColorPair(xw); + }); + } +#endif + } + + for (col = (int) left; col < target->right; ++col) + ld->charData[col] = (CharData) value; + + if_OPT_WIDE_CHARS(screen, { + size_t off; + for_each_combData(off, ld) { + memset(ld->combData[off] + left, 0, size * sizeof(CharData)); + } + }) + } + ScrnUpdate(xw, + target->top - 1, + target->left - 1, + (target->bottom - target->top) + 1, + (target->right - target->left) + 1, + False); + } +} + +#if OPT_DEC_RECTOPS +/* + * Copies the source rectangle to the target location, including video + * attributes. + * + * This implementation ignores page numbers. + * + * The reference manual does not indicate if it handles overlapping copy + * properly - so we make a local copy of the source rectangle first, then apply + * the target from that. + */ +void +ScrnCopyRectangle(XtermWidget xw, XTermRect * source, int nparam, int *params) +{ + TScreen *screen = TScreenOf(xw); + + TRACE(("copying rectangle\n")); + + if (validRect(xw, source)) { + XTermRect target; + xtermParseRect(xw, + ((nparam > 3) ? 2 : (nparam - 1)), + params, + &target); + if (validRect(xw, &target)) { + Cardinal high = (Cardinal) (source->bottom - source->top) + 1; + Cardinal wide = (Cardinal) (source->right - source->left) + 1; + Cardinal size = (high * wide); + int row, col; + Cardinal j, k; + LineData *ld; + + CellData *cells = newCellData(xw, size); + + if (cells != 0) { + + TRACE(("OK - make copy %dx%d\n", high, wide)); + target.bottom = target.top + (int) (high - 1); + target.right = target.left + (int) (wide - 1); + + for (row = source->top - 1; row < source->bottom; ++row) { + ld = getLineData(screen, row); + j = (Cardinal) (row - (source->top - 1)); + for (col = source->left - 1; col < source->right; ++col) { + k = (Cardinal) (col - (source->left - 1)); + saveCellData(screen, cells, + (j * wide) + k, + ld, col); + } + } + for (row = target.top - 1; row < target.bottom; ++row) { + ld = getLineData(screen, row); + j = (Cardinal) (row - (target.top - 1)); + for (col = target.left - 1; col < target.right; ++col) { + k = (Cardinal) (col - (target.left - 1)); + if (row >= getMinRow(screen) + && row <= getMaxRow(screen) + && col >= getMinCol(screen) + && col <= getMaxCol(screen)) { + if (j < high && k < wide) { + restoreCellData(screen, cells, + (j * wide) + k, + ld, col); + } else { + /* EMPTY */ + /* FIXME - clear the target cell? */ + } + ld->attribs[col] |= CHARDRAWN; + } + } +#if OPT_BLINK_TEXT + if (LineHasBlinking(screen, ld)) { + LineSetBlinked(ld); + } else { + LineClrBlinked(ld); + } +#endif + } + free(cells); + + ScrnUpdate(xw, + (target.top - 1), + (target.left - 1), + (target.bottom - target.top) + 1, + ((target.right - target.left) + 1), + False); + } + } + } +} + +/* + * Modifies the video-attributes only - so selection (not a video attribute) is + * unaffected. Colors and double-size flags are unaffected as well. + * + * FIXME: our representation for "invisible" does not work with this operation, + * since the attribute byte is fully-allocated for other flags. The logic + * is shown for INVISIBLE because it's harmless, and useful in case the + * CHARDRAWN or PROTECTED flags are reassigned. + */ +void +ScrnMarkRectangle(XtermWidget xw, + XTermRect * target, + Bool reverse, + int nparam, + int *params) +{ + TScreen *screen = TScreenOf(xw); + Bool exact = (screen->cur_decsace == 2); + + TRACE(("%s %s\n", + reverse ? "reversing" : "marking", + (exact + ? "rectangle" + : "region"))); + + if (validRect(xw, target)) { + LineData *ld; + int top = target->top - 1; + int bottom = target->bottom - 1; + int row, col; + int n; + + for (row = top; row <= bottom; ++row) { + int left = ((exact || (row == top)) + ? (target->left - 1) + : getMinCol(screen)); + int right = ((exact || (row == bottom)) + ? (target->right - 1) + : getMaxCol(screen)); + + ld = getLineData(screen, row); + + TRACE(("marking %d [%d..%d]\n", row, left, right)); + for (col = left; col <= right; ++col) { + unsigned flags = ld->attribs[col]; + + for (n = 0; n < nparam; ++n) { +#if OPT_TRACE + if (row == top && col == left) + TRACE(("attr param[%d] %d\n", n + 1, params[n])); +#endif + if (reverse) { + switch (params[n]) { + case 1: + flags ^= BOLD; + break; + case 4: + flags ^= UNDERLINE; + break; + case 5: + flags ^= BLINK; + break; + case 7: + flags ^= INVERSE; + break; + case 8: + flags ^= INVISIBLE; + break; + } + } else { + switch (params[n]) { + case 0: + UIntClr(flags, SGR_MASK); + break; + case 1: + flags |= BOLD; + break; + case 4: + flags |= UNDERLINE; + break; + case 5: + flags |= BLINK; + break; + case 7: + flags |= INVERSE; + break; + case 8: + flags |= INVISIBLE; + break; + case 22: + UIntClr(flags, BOLD); + break; + case 24: + UIntClr(flags, UNDERLINE); + break; + case 25: + UIntClr(flags, BLINK); + break; + case 27: + UIntClr(flags, INVERSE); + break; + case 28: + UIntClr(flags, INVISIBLE); + break; + } + } + } +#if OPT_TRACE + if (row == top && col == left) + TRACE(("first mask-change is %#x\n", + ld->attribs[col] ^ flags)); +#endif + ld->attribs[col] = (Char) flags; + } + } + ScrnRefresh(xw, + (target->top - 1), + (exact ? (target->left - 1) : getMinCol(screen)), + (target->bottom - target->top) + 1, + (exact + ? ((target->right - target->left) + 1) + : (getMaxCol(screen) - getMinCol(screen) + 1)), + False); + } +} + +/* + * Resets characters to space, except where prohibited by DECSCA. Video + * attributes (including color) are untouched. + */ +void +ScrnWipeRectangle(XtermWidget xw, + XTermRect * target) +{ + TScreen *screen = TScreenOf(xw); + + TRACE(("wiping rectangle\n")); + + if (validRect(xw, target)) { + LineData *ld; + int top = target->top - 1; + int bottom = target->bottom - 1; + int row, col; + + for (row = top; row <= bottom; ++row) { + int left = (target->left - 1); + int right = (target->right - 1); + + TRACE(("wiping %d [%d..%d]\n", row, left, right)); + + ld = getLineData(screen, row); + for (col = left; col <= right; ++col) { + if (!((screen->protected_mode == DEC_PROTECT) + && (ld->attribs[col] & PROTECTED))) { + ld->attribs[col] |= CHARDRAWN; + ld->charData[col] = ' '; + if_OPT_WIDE_CHARS(screen, { + size_t off; + for_each_combData(off, ld) { + ld->combData[off][col] = '\0'; + } + }) + } + } + } + ScrnUpdate(xw, + (target->top - 1), + (target->left - 1), + (target->bottom - target->top) + 1, + ((target->right - target->left) + 1), + False); + } +} + +/* + * Compute a checksum, ignoring the page number (since we have only one page). + */ +void +xtermCheckRect(XtermWidget xw, + int nparam, + int *params, + int *result) +{ + TScreen *screen = TScreenOf(xw); + XTermRect target; + LineData *ld; + + *result = 0; + if (nparam > 2) { + nparam -= 2; + params += 2; + } + xtermParseRect(xw, nparam, params, &target); + if (validRect(xw, &target)) { + int top = target.top - 1; + int bottom = target.bottom - 1; + int row, col; + + for (row = top; row <= bottom; ++row) { + int left = (target.left - 1); + int right = (target.right - 1); + + ld = getLineData(screen, row); + for (col = left; col <= right; ++col) { + if (ld->attribs[col] & CHARDRAWN) { + *result += (int) ld->charData[col]; + if_OPT_WIDE_CHARS(screen, { + size_t off; + for_each_combData(off, ld) { + *result += (int) ld->combData[off][col]; + } + }) + } + } + } + } +} +#endif /* OPT_DEC_RECTOPS */ + +#if OPT_MAXIMIZE + +static void +set_resize_increments(XtermWidget xw) +{ + TScreen *screen = TScreenOf(xw); + int min_width = (2 * screen->border) + screen->fullVwin.sb_info.width; + int min_height = (2 * screen->border); + XSizeHints sizehints; + + memset(&sizehints, 0, sizeof(XSizeHints)); + sizehints.width_inc = FontWidth(screen); + sizehints.height_inc = FontHeight(screen); + sizehints.flags = PResizeInc; + XSetWMNormalHints(screen->display, VShellWindow(xw), &sizehints); + + XtVaSetValues(SHELL_OF(xw), + XtNbaseWidth, min_width, + XtNbaseHeight, min_height, + XtNminWidth, min_width + FontWidth(screen), + XtNminHeight, min_height + FontHeight(screen), + XtNwidthInc, FontWidth(screen), + XtNheightInc, FontHeight(screen), + (XtPointer) 0); + + XFlush(XtDisplay(xw)); +} + +static void +unset_resize_increments(XtermWidget xw) +{ + TScreen *screen = TScreenOf(xw); + XSizeHints sizehints; + + memset(&sizehints, 0, sizeof(XSizeHints)); + sizehints.width_inc = 1; + sizehints.height_inc = 1; + sizehints.flags = PResizeInc; + XSetWMNormalHints(screen->display, VShellWindow(xw), &sizehints); + + XtVaSetValues(SHELL_OF(xw), + XtNwidthInc, 1, + XtNheightInc, 1, + (XtPointer) 0); + + XFlush(XtDisplay(xw)); +} + +static void +netwm_fullscreen(XtermWidget xw, int operation) +{ + TScreen *screen = TScreenOf(xw); + XEvent e; + Display *dpy = screen->display; + Window window = VShellWindow(xw); + Atom atom_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + Atom atom_state = XInternAtom(dpy, "_NET_WM_STATE", False); + + memset(&e, 0, sizeof(e)); + e.xclient.type = ClientMessage; + e.xclient.message_type = atom_state; + e.xclient.display = dpy; + e.xclient.window = window; + e.xclient.format = 32; + e.xclient.data.l[0] = operation; + e.xclient.data.l[1] = (long) atom_fullscreen; + + XSendEvent(dpy, DefaultRootWindow(dpy), False, + SubstructureRedirectMask, &e); +} + +/* + * Check if the "fullscreen" property is supported on the root window. + * The XGetWindowProperty function returns a list of Atom's which corresponds + * to the output of xprop. The actual list (ignore the manpage, which refers + * to an array of 32-bit values) is constructed by _XRead32, which uses long + * as a datatype. + * + * Alternatively, we could check _NET_WM_ALLOWED_ACTIONS on the application's + * window. + */ +static Boolean +probe_netwm_fullscreen_capability(XtermWidget xw) +{ + TScreen *screen = TScreenOf(xw); + Display *dpy = screen->display; + Atom atom_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + Atom atom_supported = XInternAtom(dpy, "_NET_SUPPORTED", False); + Atom actual_type; + int actual_format; + long long_offset = 0; + long long_length = 128; /* number of items to ask for at a time */ + unsigned int i; + unsigned long nitems, bytes_after; + unsigned char *args; + long *ldata; + Boolean netwm_fullscreen_capability = False; + int rc; + + while (!netwm_fullscreen_capability) { + rc = XGetWindowProperty(dpy, + DefaultRootWindow(dpy), + atom_supported, + long_offset, + long_length, + False, /* do not delete */ + AnyPropertyType, /* req_type */ + &actual_type, /* actual_type_return */ + &actual_format, /* actual_format_return */ + &nitems, /* nitems_return */ + &bytes_after, /* bytes_after_return */ + &args /* prop_return */ + ); + if (rc != Success + || actual_type != XA_ATOM) { + break; + } + ldata = (long *) (void *) args; + for (i = 0; i < nitems; i++) { + if ((Atom) ldata[i] == atom_fullscreen) { + netwm_fullscreen_capability = True; + break; + } + } + XFree(ldata); + + if (!netwm_fullscreen_capability) { + if (bytes_after != 0) { + long remaining = (long) (bytes_after / sizeof(long)); + if (long_length > remaining) + long_length = remaining; + long_offset += (long) nitems; + } else { + break; + } + } + } + + return netwm_fullscreen_capability; +} + +/* + * Enable/disable fullscreen mode for the xterm widget, if the window manager + * supports that feature. + */ +void +FullScreen(XtermWidget xw, Bool enabled) +{ + TScreen *screen = TScreenOf(xw); + + static Boolean initialized = False; + static Boolean netwm_fullscreen_capability = False; + + TRACE(("FullScreen %s\n", BtoS(enabled))); + + if (resource.fullscreen == esNever) { + initialized = True; + netwm_fullscreen_capability = False; + } else if (!initialized) { + initialized = True; + netwm_fullscreen_capability = probe_netwm_fullscreen_capability(xw); + } + + if (netwm_fullscreen_capability) { + if (enabled) { + unset_resize_increments(xw); + netwm_fullscreen(xw, 1); + } else { + set_resize_increments(xw); + netwm_fullscreen(xw, 0); + } + screen->fullscreen = (Boolean) enabled; + update_fullscreen(); + } else { + Bell(xw, XkbBI_MinorError, 100); + } +} +#endif /* OPT_MAXIMIZE */ |