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
92void
93Mark_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 */
162void
163Jump_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 */
177void
178Resolve_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