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