Home | History | Annotate | Line # | Download | only in dist
      1 /* $XTermId: testxmc.c,v 1.55 2024/12/01 20:27:00 tom Exp $ */
      2 
      3 /*
      4  * Copyright 1997-2020,2024 by Thomas E. Dickey
      5  *
      6  *                         All Rights Reserved
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the
     10  * "Software"), to deal in the Software without restriction, including
     11  * without limitation the rights to use, copy, modify, merge, publish,
     12  * distribute, sublicense, and/or sell copies of the Software, and to
     13  * permit persons to whom the Software is furnished to do so, subject to
     14  * the following conditions:
     15  *
     16  * The above copyright notice and this permission notice shall be included
     17  * in all copies or substantial portions of the Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     22  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
     23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     26  *
     27  * Except as contained in this notice, the name(s) of the above copyright
     28  * holders shall not be used in advertising or otherwise to promote the
     29  * sale, use or other dealings in this Software without prior written
     30  * authorization.
     31  */
     32 
     33 /*
     34  * This module provides test support for curses applications that must work
     35  * with terminals that have the xmc (magic cookie) glitch.  The xmc_glitch
     36  * resource denotes the number of spaces that are emitted when switching to or
     37  * from standout (reverse) mode.  Some terminals implement this by storing the
     38  * attribute controls in the character cell that is skipped.  So if the cell is
     39  * overwritten by text, then the attribute change in the cell is cancelled,
     40  * causing attributes to the left of the change to propagate.
     41  *
     42  * We implement the glitch by writing a character that won't be mistaken for
     43  * other normal characters (and mapping normal writes to that character to a
     44  * different one).
     45  *
     46  * Since xmc isn't normally part of xterm, we document it here rather than in
     47  * the man-page.  This module is driven by resources rather than by the
     48  * termcap/terminfo description to make it a little more flexible for testing
     49  * purposes.
     50  *
     51  * Resources:
     52  *
     53  * xmcGlitch (class XmcGlitch)
     54  *	When true, enables this extension.  The default is `0', which disables
     55  *	the module.  (termcap sg, terminfo xmc).
     56  *
     57  * xmcAttributes (class XmcAttributes)
     58  *	The attributes for which we'll generate a glitch, as a bitmask.
     59  *
     60  *		INVERSE		1
     61  *		UNDERLINE	2
     62  *		BOLD		4
     63  *		BLINK		8
     64  *
     65  *	The default is `1' (INVERSE).  Some terminals emit glitches for
     66  *	underline.  Just for completeness, we recognize all of the video
     67  *	attributes.
     68  *
     69  * xmcInline (class XmcInline)
     70  *	When true, limits the extent of an SGR change to the current line.
     71  *	The default is `false'.  (No termcap or terminfo equivalent, though
     72  *	there are comments in some entries relating to this issue).
     73  *
     74  * xmcMoveSGR (class XmcMoveSGR)
     75  *	When false, a cursor movement will leave a glitch when SGR's are
     76  *	active.  The default is `true'.  (termcap ms, terminfo msgr).
     77  *
     78  * TODO:
     79  *	When xmc is active, the terminfo max_attributes (ma) capability is
     80  *	assumed to be 1.
     81  *
     82  *	The xmcAttributes resource should also apply to alternate character
     83  *	sets and to color.
     84  */
     85 
     86 #include <xterm.h>
     87 #include <data.h>
     88 
     89 #define MARK_ON(a)  (Bool) ((my_attrs & a) != 0 && (xw->flags & (whichone = CharOf(a))) == 0)
     90 #define MARK_OFF(a) (Bool) ((my_attrs & a) != 0 && (xw->flags & (whichone = CharOf(a))) != 0)
     91 
     92 void
     93 Mark_XMC(XtermWidget xw, int param)
     94 {
     95     static IChar *glitch;
     96 
     97     TScreen *screen = TScreenOf(xw);
     98     Bool found = False;
     99     unsigned my_attrs = CharOf(screen->xmc_attributes & XMC_FLAGS);
    100     unsigned whichone = 0;
    101 
    102     if (glitch == NULL) {
    103 	unsigned len = screen->xmc_glitch;
    104 	glitch = TypeMallocN(IChar, len);
    105 	if (glitch == NULL) {
    106 	    xtermWarning("Not enough core for xmc glitch mode\n");
    107 	    return;
    108 	} else {
    109 	    while (len--)
    110 		glitch[len] = XMC_GLITCH;
    111 	}
    112     }
    113     switch (param) {
    114     case -1:			/* DEFAULT */
    115     case 0:			/* FALLTHRU */
    116 	found = MARK_OFF((xw->flags & XMC_FLAGS));
    117 	break;
    118     case 1:
    119 	found = MARK_ON(BOLD);
    120 	break;
    121     case 4:
    122 	found = MARK_ON(UNDERLINE);
    123 	break;
    124     case 5:
    125 	found = MARK_ON(BLINK);
    126 	break;
    127     case 7:
    128 	found = MARK_ON(INVERSE);
    129 	break;
    130     case 22:
    131 	found = MARK_OFF(BOLD);
    132 	break;
    133     case 24:
    134 	found = MARK_OFF(UNDERLINE);
    135 	break;
    136     case 25:
    137 	found = MARK_OFF(BLINK);
    138 	break;
    139     case 27:
    140 	found = MARK_OFF(INVERSE);
    141 	break;
    142     }
    143 
    144     /*
    145      * Write a glitch with the attributes temporarily set to the new(er)
    146      * ones.
    147      */
    148     if (found) {
    149 	unsigned save = xw->flags;
    150 	xw->flags ^= whichone;
    151 	TRACE(("XMC Writing glitch (%d/%d) after SGR %d\n", my_attrs,
    152 	       whichone, param));
    153 	dotext(xw, (DECNRCM_codes) '?', glitch, screen->xmc_glitch);
    154 	xw->flags = save;
    155     }
    156 }
    157 
    158 /*
    159  * Force a glitch on cursor movement when we're in standout mode and not at the
    160  * end of a line.
    161  */
    162 void
    163 Jump_XMC(XtermWidget xw)
    164 {
    165     TScreen *screen = TScreenOf(xw);
    166     if (!screen->move_sgr_ok
    167 	&& screen->cur_col <= LineMaxCol(screen,
    168 					 getLineData(screen, screen->cur_row))) {
    169 	Mark_XMC(xw, -1);
    170     }
    171 }
    172 
    173 /*
    174  * After writing text to the screen, resolve mismatch between the current
    175  * location and any attributes that would have been set by preceding locations.
    176  */
    177 void
    178 Resolve_XMC(XtermWidget xw)
    179 {
    180     TScreen *screen = TScreenOf(xw);
    181     LineData *ld;
    182     Bool changed = False;
    183     IAttr start;
    184     IAttr my_attrs = CharOf(screen->xmc_attributes & XMC_FLAGS);
    185     int row = screen->cur_row;
    186     int col = screen->cur_col;
    187 
    188     /* Find the preceding cell.
    189      */
    190     ld = getLineData(screen, row);
    191     if (ld->charData[col] != XMC_GLITCH) {
    192 	if (col != 0) {
    193 	    col--;
    194 	} else if (!screen->xmc_inline && row != 0) {
    195 	    ld = getLineData(screen, --row);
    196 	    col = LineMaxCol(screen, ld);
    197 	}
    198     }
    199     start = (ld->attribs[col] & my_attrs);
    200 
    201     /* Now propagate the starting state until we reach a cell which holds
    202      * a glitch.
    203      */
    204     for (;;) {
    205 	if (col < LineMaxCol(screen, ld)) {
    206 	    col++;
    207 	} else if (!screen->xmc_inline && row < screen->max_row) {
    208 	    col = 0;
    209 	    ld = getLineData(screen, ++row);
    210 	} else
    211 	    break;
    212 	if (ld->charData[col] == XMC_GLITCH)
    213 	    break;
    214 	if ((ld->attribs[col] & my_attrs) != start) {
    215 	    ld->attribs[col] =
    216 		(IAttr) (start | (ld->attribs[col] & ~my_attrs));
    217 	    changed = True;
    218 	}
    219     }
    220 
    221     TRACE(("XMC %s (%s:%d/%d) from %d,%d to %d,%d\n",
    222 	   changed ? "Ripple" : "Nochange",
    223 	   BtoS(xw->flags & my_attrs),
    224 	   my_attrs, start,
    225 	   screen->cur_row, screen->cur_col,
    226 	   row, col));
    227 
    228     if (changed) {
    229 	ScrnUpdate(xw, screen->cur_row, 0, row + 1 - screen->cur_row,
    230 		   MaxCols(screen), True);
    231     }
    232 }
    233