1 /* $XTermId: testxmc.c,v 1.54 2020/11/08 20:12:21 tom Exp $ */
2 
3 /*
4  * Copyright 1997-2019,2020 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
Mark_XMC(XtermWidget xw,int param)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 == 0) {
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
Jump_XMC(XtermWidget xw)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
Resolve_XMC(XtermWidget xw)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