]> git.mdlowis.com Git - archive/tide-ocaml.git/commitdiff
first crack at loading alternative fonts for runes
authorMichael D. Lowis <mike.lowis@gentex.com>
Mon, 18 Dec 2017 13:31:41 +0000 (08:31 -0500)
committerMichael D. Lowis <mike.lowis@gentex.com>
Mon, 18 Dec 2017 13:31:41 +0000 (08:31 -0500)
lib/internals.h
lib/rope.ml
lib/x11.ml
lib/x11_prims.c

index 48b1237194e1fffa93df892f9a795db0bf9e7103..0786453663fd2d5ce9808bc918c94de4e5da2dfd 100644 (file)
@@ -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;
index c3c8006365c43541361e816e36f9de9e420482d8..a73e20cc81583e75abc16af0133b56978194f8c6 100644 (file)
@@ -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))
 
index 5893d844c5f8175e78c86535ba6b76a6afe10d2a..c6ec306df4d04ec031ea84ae6d115d1561dcfef4 100644 (file)
@@ -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
index 316824af19ba9b446d8f9b3ac59813f49c6d6c6e..3c82d2bd0d15d389b67097555b059484edfc7dc2 100644 (file)
@@ -3,6 +3,18 @@
 #include <unistd.h>
 #include <poll.h>
 
+#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