1/* $XTermId: doublechr.c,v 1.110 2024/12/01 19:38:29 tom Exp $ */
2
3/*
4 * Copyright 1997-2022,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#include <xterm.h>
34#include <data.h>
35#include <fontutils.h>
36
37#include <assert.h>
38
39#define WhichCgsId(flag) (((flag) & BOLD) ? gcCBold : gcCNorm)
40
41/*
42 * The first column is all that matters for double-size characters (since the
43 * controls apply to a whole line).  However, it's easier to maintain the
44 * information for special fonts by writing to all cells.
45 */
46#if OPT_DEC_CHRSET
47
48static void
49repaint_line(XtermWidget xw, unsigned newChrSet)
50{
51    TScreen *screen = TScreenOf(xw);
52    LineData *ld;
53    int curcol = screen->cur_col;
54    int currow = screen->cur_row;
55    int width = MaxCols(screen);
56    unsigned len = (unsigned) width;
57
58    assert(width > 0);
59
60    /*
61     * Ignore repetition.
62     */
63    if (!IsLeftRightMode(xw)
64	&& (ld = getLineData(screen, currow)) != NULL) {
65	unsigned oldChrSet = GetLineDblCS(ld);
66
67	if (oldChrSet != newChrSet) {
68	    TRACE(("repaint_line(%2d,%2d) (%s -> %s)\n", currow, screen->cur_col,
69		   visibleDblChrset(oldChrSet),
70		   visibleDblChrset(newChrSet)));
71	    HideCursor(xw);
72
73	    /* If switching from single-width, keep the cursor in the visible part
74	     * of the line.
75	     */
76	    if (CSET_DOUBLE(newChrSet)) {
77		width /= 2;
78		if (curcol > width)
79		    curcol = width;
80	    }
81
82	    /*
83	     * ScrnRefresh won't paint blanks for us if we're switching between a
84	     * single-size and double-size font.  So we paint our own.
85	     */
86	    ClearCurBackground(xw,
87			       currow,
88			       0,
89			       1,
90			       len,
91			       (unsigned) LineFontWidth(screen, ld));
92
93	    SetLineDblCS(ld, newChrSet);
94
95	    set_cur_col(screen, 0);
96	    ScrnUpdate(xw, currow, 0, 1, (int) len, True);
97	    set_cur_col(screen, curcol);
98	}
99    }
100}
101#endif
102
103/*
104 * Set the line to double-height characters.  The 'top' flag denotes whether
105 * we'll be using it for the top (true) or bottom (false) of the line.
106 */
107void
108xterm_DECDHL(XtermWidget xw, Bool top)
109{
110#if OPT_DEC_CHRSET
111    repaint_line(xw, (unsigned) (top ? CSET_DHL_TOP : CSET_DHL_BOT));
112#else
113    (void) xw;
114    (void) top;
115#endif
116}
117
118/*
119 * Set the line to single-width characters (the normal state).
120 */
121void
122xterm_DECSWL(XtermWidget xw)
123{
124#if OPT_DEC_CHRSET
125    repaint_line(xw, CSET_SWL);
126#else
127    (void) xw;
128#endif
129}
130
131/*
132 * Set the line to double-width characters
133 */
134void
135xterm_DECDWL(XtermWidget xw)
136{
137#if OPT_DEC_CHRSET
138    repaint_line(xw, CSET_DWL);
139#else
140    (void) xw;
141#endif
142}
143
144/*
145 * Reset all lines on the screen to single-width/single-height.
146 */
147void
148xterm_ResetDouble(XtermWidget xw)
149{
150#if OPT_DEC_CHRSET
151    TScreen *screen = TScreenOf(xw);
152    Boolean changed = False;
153    unsigned code;
154    int row;
155
156    for (row = 0; row < screen->max_row; ++row) {
157	LineData *ld;
158
159	if ((ld = getLineData(screen, ROW2INX(screen, row))) != NULL) {
160	    code = GetLineDblCS(ld);
161	    if (code != CSET_SWL) {
162		SetLineDblCS(ld, CSET_SWL);
163		changed = True;
164	    }
165	}
166    }
167    if (changed) {
168	xtermRepaint(xw);
169    }
170#else
171    (void) xw;
172#endif
173}
174
175#if OPT_DEC_CHRSET
176static void
177discard_font(XtermWidget xw, int n)
178{
179    TScreen *screen = TScreenOf(xw);
180    XTermFonts *data = getDoubleFont(screen, n);
181
182    TRACE(("discard_font chrset=%d %s\n", data->chrset,
183	   (data->fn != NULL) ? data->fn : "<no-name>"));
184
185    data->chrset = 0;
186    data->flags = 0;
187    FreeAndNull(data->fn);
188    xtermCloseFont(xw, data);
189
190    screen->fonts_used -= 1;
191    while (n < screen->fonts_used) {
192	screen->double_fonts[n] = screen->double_fonts[n + 1];
193	++n;
194    }
195}
196
197/* push back existing fonts and create a new entry */
198static XTermFonts *
199pushback_font(XtermWidget xw, const XTermFonts * source)
200{
201    TScreen *screen = TScreenOf(xw);
202    XTermFonts *data = getDoubleFont(screen, 0);
203    int n;
204
205    if (screen->fonts_used >= screen->cache_doublesize) {
206	TRACE(("pushback_font: discard oldest\n"));
207	discard_font(xw, screen->fonts_used - 1);
208    } else {
209	screen->fonts_used += 1;
210    }
211
212    for (n = screen->fonts_used; n > 0; n--)
213	data[n] = data[n - 1];
214    data[0] = *source;
215
216    TRACE(("pushback_font -> (NEW:%d)\n", screen->fonts_used));
217
218    return data;
219}
220
221static int
222xterm_Double_index(const XTermDraw * params)
223{
224    XTermDraw local = *params;
225    int n;
226    int result = -1;
227    TScreen *screen = TScreenOf(local.xw);
228    XTermFonts *data = getDoubleFont(screen, 0);
229
230    local.attr_flags &= BOLD;
231    TRACE(("xterm_Double_index chrset=%#x, flags=%#x\n", local.this_chrset, local.attr_flags));
232
233    for (n = 0; n < screen->fonts_used; n++) {
234	if (data[n].chrset == (unsigned) local.this_chrset
235	    && data[n].flags == local.attr_flags) {
236	    if (n != 0) {
237		XTermFonts save;
238		TRACE(("...xterm_Double_index -> %d (OLD:%d)\n", n, screen->fonts_used));
239		save = data[n];
240		while (n > 0) {
241		    data[n] = data[n - 1];
242		    n--;
243		}
244		data[n] = save;
245	    }
246	    result = n;
247	    break;
248	}
249    }
250
251    return result;
252}
253
254/*
255 * Lookup/cache a GC for the double-size character display.  We save up to
256 * NUM_CHRSET values.
257 */
258GC
259xterm_DoubleGC(XTermDraw * params, GC old_gc, int *inxp)
260{
261    TScreen *screen = TScreenOf(params->xw);
262    VTwin *cgsWin = WhichVWin(screen);
263    char *name;
264    GC result = NULL;
265
266    if ((name = xtermSpecialFont(params)) != NULL) {
267	CgsEnum cgsId = WhichCgsId(params->attr_flags);
268	Boolean found = False;
269	XTermFonts *data = NULL;
270	int n;
271
272	if ((n = xterm_Double_index(params)) >= 0) {
273	    data = getDoubleFont(screen, n);
274	    if (data->fn != NULL) {
275		if (!strcmp(data->fn, name)
276		    && data->fs != NULL) {
277		    found = True;
278		    FreeAndNull(name);
279		} else {
280		    discard_font(params->xw, n);
281		}
282	    }
283	}
284
285	if (!found && name != NULL) {
286	    XTermFonts temp;
287
288	    TRACE(("xterm_DoubleGC %s %d: %s\n",
289		   (params->attr_flags & BOLD) ? "BOLD" : "NORM", n, name));
290
291	    memset(&temp, 0, sizeof(temp));
292	    temp.fn = name;
293	    temp.chrset = params->this_chrset;
294	    temp.flags = (params->attr_flags & BOLD);
295	    temp.warn = fwResource;
296
297	    if (!xtermOpenFont(params->xw, name, &temp, NULL, False)) {
298		XTermDraw local = *params;
299		char *nname;
300
301		/* Retry with * in resolutions */
302		local.draw_flags |= NORESOLUTION;
303		nname = xtermSpecialFont(&local);
304		if (nname != NULL) {
305		    found = (Boolean) xtermOpenFont(params->xw, nname, &temp,
306						    NULL, False);
307		    free(nname);
308		}
309	    } else {
310		found = True;
311	    }
312	    free(name);
313
314	    if (found) {
315		n = 0;
316		data = pushback_font(params->xw, &temp);
317	    }
318
319	    TRACE(("-> %s\n", found ? "OK" : "FAIL"));
320	}
321
322	if (found) {
323	    setCgsCSet(params->xw, cgsWin, cgsId, params->this_chrset);
324	    setCgsFont(params->xw, cgsWin, cgsId, data);
325	    setCgsFore(params->xw, cgsWin, cgsId, getCgsFore(params->xw,
326							     cgsWin, old_gc));
327	    setCgsBack(params->xw, cgsWin, cgsId, getCgsBack(params->xw,
328							     cgsWin, old_gc));
329	    result = getCgsGC(params->xw, cgsWin, cgsId);
330	    *inxp = n;
331	} else if (params->attr_flags & BOLD) {
332	    UIntClr(params->attr_flags, BOLD);
333	    result = xterm_DoubleGC(params, old_gc, inxp);
334	}
335    }
336
337    return result;
338}
339
340#if OPT_RENDERFONT
341/*
342 * Like xterm_DoubleGC(), but returning an Xft font.
343 */
344XTermXftFonts *
345xterm_DoubleFT(XTermDraw * params, unsigned chrset, unsigned attr_flags)
346{
347    XTermXftFonts *result;
348    TScreen *screen = TScreenOf(params->xw);
349    unsigned num = (chrset & CSET_DWL);
350
351    if ((attr_flags &= BOLD) != 0)
352	num |= 4;
353
354    result = &screen->double_xft_fonts[num];
355    if (XftFp(result) == NULL) {
356	getDoubleXftFont(params, result, chrset, attr_flags);
357    }
358    return result;
359}
360
361void
362freeall_DoubleFT(XtermWidget xw)
363{
364    TScreen *screen = TScreenOf(xw);
365    unsigned num;
366
367    for (num = 0; num < XtNumber(screen->double_xft_fonts); ++num) {
368	closeCachedXft(screen, XftFp(&screen->double_xft_fonts[num]));
369	XftFp(&screen->double_xft_fonts[num]) = NULL;
370	XftIs(&screen->double_xft_fonts[num]) = xcEmpty;
371    }
372}
373#endif /* OPT_RENDERFONT */
374
375#endif /* OPT_DEC_CHRSET */
376