From: Michael D. Lowis Date: Mon, 18 Dec 2017 13:31:41 +0000 (-0500) Subject: first crack at loading alternative fonts for runes X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=5a8789c44d71b9794218b0df453e3a0daa1fc464;p=archive%2Ftide-ocaml.git first crack at loading alternative fonts for runes --- diff --git a/lib/internals.h b/lib/internals.h index 48b1237..0786453 100644 --- a/lib/internals.h +++ b/lib/internals.h @@ -85,12 +85,15 @@ struct X { XrmDatabase db; }; +/* typedef struct XFont { XftFont* font; + FcPattern* set; FcPattern* pattern; int height; struct XFont* next; } XFont; +*/ typedef struct XText { struct XText* next; diff --git a/lib/rope.ml b/lib/rope.ml index c3c8006..a73e20c 100644 --- a/lib/rope.ml +++ b/lib/rope.ml @@ -72,10 +72,10 @@ let rec utfbeg rope pos = let rec decode rope i len rune = let byte = (getc rope i) in - if not (is_cont_byte byte) then - (0xFFFD, i + 1) - else if len == 0 then - (rune, i + 1) + if len == 0 then + (rune, i) + else if not (is_cont_byte byte) then + (0xFFFD, i) else decode rope (i + 1) (len - 1) ((rune lsl 6) lor (byte land 0x3F)) diff --git a/lib/x11.ml b/lib/x11.ml index 5893d84..c6ec306 100644 --- a/lib/x11.ml +++ b/lib/x11.ml @@ -36,9 +36,7 @@ type xcfgvar = type font = { font: xfont; - patt: xfontpatt; height: int; - next: font; } type glyph = { @@ -109,8 +107,60 @@ external font_glyph : font -> int -> glyph external draw_glyph : int -> glyph -> (int * int) -> int = "x11_draw_glyph" +let (font_cache : font option array) = Array.make 8 None let glyph_cache = Hashtbl.create 127 + +(* +let unbox = function +| Some v -> v +| None -> failwith "Expected some got none" + +let load_match font rune i = + Printf.printf "load_match %d %d\n" rune i; + (match font_cache.(i) with + | Some f -> font_unload f; + | None -> ()); + let fmatch = (font_match font rune) in + Array.set font_cache i (Some fmatch); + font_glyph fmatch rune + +let rec get_cache_glyph font rune i = + if i < 8 then + match font_cache.(i) with + | None -> + load_match font rune i + | Some f -> + if font_hasglyph f rune then + font_glyph f rune + else + get_cache_glyph font rune (i + 1) + else + load_match font rune 7 + +let clear_font_cache () = + print_endline "clearing cache"; + let clear_entry i f = match f with + | Some f -> (if i > 0 then (font_unload f)); None + | None -> None + in Array.mapi clear_entry font_cache + +let rec get_font_glyph font rune = + match font_cache.(0) with + | None -> + print_endline "changing root font"; + Array.set font_cache 0 (Some font); + font_glyph font rune + | Some f -> + if f != font then + (clear_font_cache (); get_font_glyph font rune) + else + get_cache_glyph font rune 0 +*) + + + + let cache_update rune glyph = Hashtbl.replace glyph_cache rune glyph; glyph diff --git a/lib/x11_prims.c b/lib/x11_prims.c index 316824a..3c82d2b 100644 --- a/lib/x11_prims.c +++ b/lib/x11_prims.c @@ -3,6 +3,18 @@ #include #include +#define FontCacheSize 8 + +typedef struct XFont { + struct { + XftFont* match; + FcFontSet* set; + FcPattern* pattern; + } base; + XftFont* cache[FontCacheSize]; + int ncached; +} XFont; + static int error_handler(Display* disp, XErrorEvent* ev); static char* readprop(Window win, Atom prop); static void create_window(int height, int width); @@ -215,38 +227,127 @@ CAMLprim value x11_font_load(value fontname) { caml_failwith("Could not init fontconfig"); initialized = true; } - - /* find and load the base font */ - XftFont* xftfont; + /* load the font */ FcResult result; + XFont* font = calloc(1,sizeof(XFont)); FcPattern* pattern = FcNameParse((FcChar8 *)String_val(fontname)); if (!pattern) - caml_failwith("cannot open font"); + caml_failwith("could not load font"); FcPattern* match = XftFontMatch(X.display, X.screen, pattern, &result); - if (!match || !(xftfont = XftFontOpenPattern(X.display, match))) - caml_failwith("could not load default font"); - - /* populate the stats and return the font */ - value font = mkvariant(0, 4, - xftfont, FcPatternDuplicate(pattern), Val_int(xftfont->ascent + xftfont->descent), - mkvariant(0,0)); + if (!match || !(font->base.match = XftFontOpenPattern(X.display, match))) + caml_failwith("could not load font"); + font->base.set = NULL; + font->base.pattern = FcPatternDuplicate(pattern); FcPatternDestroy(pattern); - CAMLreturn(font); + /* populate the stats and return the font */ + CAMLreturn( + mkvariant(0, 2, font, + Val_int(font->base.match->ascent + font->base.match->descent))); +} + +/* +CAMLprim value x11_font_unload(value font) { + CAMLparam1(font); + XftFontClose(X.display, (XftFont*)Field(font,0)); + CAMLreturn(Val_unit); +} + +CAMLprim value x11_font_match(value font, value rune) { + CAMLparam2(font, rune); + + FcResult result; + XftFont* xftfont = (XftFont*)Field(font,0); + + FcFontSet* fcsets[] = { FcFontSort(0, xftfont->pattern, 1, 0, &result) }; + FcPattern* fcpattern = FcPatternDuplicate(xftfont->pattern); + FcCharSet* fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + FcConfigSubstitute(0, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + FcPattern* fcmatch = FcFontSetMatch(0, fcsets, 1, fcpattern, &result); + XftFont* newfont = XftFontOpenPattern(X.display, fcmatch); + //FcPatternPrint(newfont->pattern); + fprintf(stderr,"loading match\n"); + + FcFontSetDestroy(fcsets[0]); + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcmatch); + + CAMLreturn( mkvariant(0, 2, newfont, Field(font,1)) ); +} + +CAMLprim value x11_font_hasglyph(value font, value rune) { + CAMLparam2(font, rune); + XftFont* xfont = (XftFont*)Field(font, 0); + int val = XftCharIndex(X.display, xfont, Int_val(rune)); + CAMLreturn( Val_int(val > 0) ); +} +*/ + +void get_glyph(XFont* font, XftGlyphFontSpec* spec, uint32_t rune) { + /* if the rune is in the base font, set it and return */ + FT_UInt glyphidx = XftCharIndex(X.display, font->base.match, rune); + if (glyphidx) { + spec->font = font->base.match; + spec->glyph = glyphidx; + return; + } + /* Otherwise check the cache */ + for (int f = 0; f < font->ncached; f++) { + glyphidx = XftCharIndex(X.display, font->cache[f], rune); + /* Fond a suitable font or found a default font */ + //if (glyphidx || (!glyphidx && font->cache[f]->unicodep == rune)) { + if (glyphidx) { + spec->font = font->cache[f]; + spec->glyph = glyphidx; + return; + } + } + /* if all other options fail, ask fontconfig for a suitable font */ + FcResult fcres; + if (!font->base.set) + font->base.set = FcFontSort(0, font->base.pattern, 1, 0, &fcres); + FcFontSet* fcsets[] = { font->base.set }; + FcPattern* fcpattern = FcPatternDuplicate(font->base.pattern); + FcCharSet* fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + FcConfigSubstitute(0, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + FcPattern* fontmatch = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); + /* add the font to the cache and use it */ + if (font->ncached >= FontCacheSize) { + font->ncached = FontCacheSize - 1; + XftFontClose(X.display, font->cache[font->ncached]); + } + font->cache[font->ncached] = XftFontOpenPattern(X.display, fontmatch); + spec->glyph = XftCharIndex(X.display, font->cache[font->ncached], rune); + spec->font = font->cache[font->ncached]; + font->ncached++; + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); } CAMLprim value x11_font_glyph(value font, value rune) { CAMLparam2(font, rune); CAMLlocal1(glyph); /* search for the rune in currently loaded fonts */ - FcChar32 codept = Int_val(rune); - XftFont* xfont = (XftFont*)Field(font, 0); - FT_UInt glyphidx = XftCharIndex(X.display, xfont, codept); XGlyphInfo extents; - XftTextExtents32 (X.display, xfont, &codept, 1, &extents); + XftGlyphFontSpec spec; + FcChar32 codept = Int_val(rune); + XFont* xfont = (XFont*)Field(font, 0); + get_glyph(xfont, &spec, (uint32_t)codept); + XftTextExtents32(X.display, spec.font, &codept, 1, &extents); /* create the glyph structure */ glyph = caml_alloc(8, 0); - Store_field(glyph, 0, (value)xfont); - Store_field(glyph, 1, Val_int(glyphidx)); + Store_field(glyph, 0, (value)spec.font); + Store_field(glyph, 1, Val_int(spec.glyph)); Store_field(glyph, 2, rune); Store_field(glyph, 3, Val_int(extents.width)); Store_field(glyph, 4, Val_int(extents.x)); @@ -279,7 +380,7 @@ CAMLprim value x11_draw_glyph(value color, value glyph, value coord) { textchunk->specs = realloc(textchunk->specs, textchunk->nspecs * sizeof(XftGlyphFontSpec)); XftFont* font = (XftFont*)Field(glyph,0); XftGlyphFontSpec spec = { - .font = (XftFont*)Field(glyph,0), + .font = font, .glyph = intfield(glyph,1), .x = intfield(coord,0), .y = intfield(coord,1) + font->ascent