1 /* $XTermId: doublechr.c,v 1.104 2020/12/10 19:43:26 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 #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 
48 static void
repaint_line(XtermWidget xw,unsigned newChrSet)49 repaint_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)) != 0) {
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  */
107 void
xterm_DECDHL(XtermWidget xw,Bool top)108 xterm_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  */
121 void
xterm_DECSWL(XtermWidget xw)122 xterm_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  */
134 void
xterm_DECDWL(XtermWidget xw)135 xterm_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  */
147 void
xterm_ResetDouble(XtermWidget xw)148 xterm_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))) != 0) {
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
176 static void
discard_font(XtermWidget xw,int n)177 discard_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 != 0) ? 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 */
198 static XTermFonts *
pushback_font(XtermWidget xw,XTermFonts * source)199 pushback_font(XtermWidget xw, 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 
221 static int
xterm_Double_index(XTermDraw * params)222 xterm_Double_index(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  */
258 GC
xterm_DoubleGC(XTermDraw * params,GC old_gc,int * inxp)259 xterm_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 = 0;
265 
266     if ((name = xtermSpecialFont(params))
267 	!= 0) {
268 	CgsEnum cgsId = WhichCgsId(params->attr_flags);
269 	Boolean found = False;
270 	XTermFonts *data = 0;
271 	int n;
272 
273 	if ((n = xterm_Double_index(params)) >= 0) {
274 	    data = getDoubleFont(screen, n);
275 	    if (data->fn != 0) {
276 		if (!strcmp(data->fn, name)
277 		    && data->fs != 0) {
278 		    found = True;
279 		    FreeAndNull(name);
280 		} else {
281 		    discard_font(params->xw, n);
282 		}
283 	    }
284 	}
285 
286 	if (!found && name != NULL) {
287 	    XTermFonts temp;
288 
289 	    TRACE(("xterm_DoubleGC %s %d: %s\n",
290 		   (params->attr_flags & BOLD) ? "BOLD" : "NORM", n, name));
291 
292 	    memset(&temp, 0, sizeof(temp));
293 	    temp.fn = name;
294 	    temp.chrset = params->this_chrset;
295 	    temp.flags = (params->attr_flags & BOLD);
296 	    temp.warn = fwResource;
297 
298 	    if (!xtermOpenFont(params->xw, name, &temp, False)) {
299 		XTermDraw local = *params;
300 		char *nname;
301 
302 		/* Retry with * in resolutions */
303 		local.draw_flags |= NORESOLUTION;
304 		nname = xtermSpecialFont(&local);
305 		if (nname != 0) {
306 		    found = (Boolean) xtermOpenFont(params->xw, nname, &temp,
307 						    False);
308 		    free(nname);
309 		}
310 	    } else {
311 		found = True;
312 	    }
313 	    free(name);
314 
315 	    if (found) {
316 		n = 0;
317 		data = pushback_font(params->xw, &temp);
318 	    }
319 
320 	    TRACE(("-> %s\n", found ? "OK" : "FAIL"));
321 	}
322 
323 	if (found) {
324 	    setCgsCSet(params->xw, cgsWin, cgsId, params->this_chrset);
325 	    setCgsFont(params->xw, cgsWin, cgsId, data);
326 	    setCgsFore(params->xw, cgsWin, cgsId, getCgsFore(params->xw,
327 							     cgsWin, old_gc));
328 	    setCgsBack(params->xw, cgsWin, cgsId, getCgsBack(params->xw,
329 							     cgsWin, old_gc));
330 	    result = getCgsGC(params->xw, cgsWin, cgsId);
331 	    *inxp = n;
332 	} else if (params->attr_flags & BOLD) {
333 	    UIntClr(params->attr_flags, BOLD);
334 	    result = xterm_DoubleGC(params, old_gc, inxp);
335 	}
336     }
337 
338     return result;
339 }
340 
341 #if OPT_RENDERFONT
342 /*
343  * Like xterm_DoubleGC(), but returning an Xft font.
344  */
345 XftFont *
xterm_DoubleFT(XTermDraw * params,unsigned chrset,unsigned attr_flags)346 xterm_DoubleFT(XTermDraw * params, unsigned chrset, unsigned attr_flags)
347 {
348     XftFont *result;
349     TScreen *screen = TScreenOf(params->xw);
350     unsigned num = (chrset & CSET_DWL);
351 
352     if ((attr_flags &= BOLD) != 0)
353 	num |= 4;
354 
355     if ((result = screen->double_xft_fonts[num]) == 0) {
356 	result = getDoubleXftFont(params, chrset, attr_flags);
357 	screen->double_xft_fonts[num] = result;
358     }
359     return result;
360 }
361 
362 void
freeall_DoubleFT(XtermWidget xw)363 freeall_DoubleFT(XtermWidget xw)
364 {
365     TScreen *screen = TScreenOf(xw);
366     unsigned num;
367 
368     for (num = 0; num < XtNumber(screen->double_xft_fonts); ++num) {
369 	closeCachedXft(screen, screen->double_xft_fonts[num]);
370 	screen->double_xft_fonts[num] = 0;
371     }
372 }
373 #endif /* OPT_RENDERFONT */
374 
375 #endif /* OPT_DEC_CHRSET */
376