Home | History | Annotate | Line # | Download | only in dist
      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 
     48 static void
     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)) != 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  */
    107 void
    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
    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
    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
    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))) != 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
    176 static void
    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 != 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 */
    198 static XTermFonts *
    199 pushback_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 
    221 static int
    222 xterm_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  */
    258 GC
    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 = 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  */
    344 XTermXftFonts *
    345 xterm_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 
    361 void
    362 freeall_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