From 88d87b43c4101885e5338f7d36bb10c72736cf06 Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Fri, 14 Oct 2016 21:37:53 -0400 Subject: [PATCH] Added support for display of unicode characters --- buf.c | 5 +- charset.c | 7 +- edit.h | 9 +- tests/tests.c | 2 +- tools/fontdemo.c | 298 +++++++++++++++++++++++++++++++++++++++++++++++ tools/runes.c | 118 +++++++++++++++++++ xedit.c | 249 +++++++++++++++++++-------------------- 7 files changed, 549 insertions(+), 139 deletions(-) create mode 100644 tools/fontdemo.c create mode 100644 tools/runes.c diff --git a/buf.c b/buf.c index 91c6892..7a523bf 100644 --- a/buf.c +++ b/buf.c @@ -9,7 +9,7 @@ #include "edit.h" typedef struct { - char* buf; + uint8_t* buf; size_t len; } FMap; @@ -21,7 +21,7 @@ static FMap fmap(char* path) { (fstat(fd, &sb) < 0) || (sb.st_size == 0)) return file; - file.buf = (char*)mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); + file.buf = (uint8_t*)mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); file.len = sb.st_size; if (file.buf == MAP_FAILED) die("memory mapping of file failed"); @@ -224,4 +224,3 @@ unsigned buf_setcol(Buf* buf, unsigned pos, unsigned col) { unsigned len = buf_eol(buf, pos) - bol; return buf_byrune(buf, bol, (len > col ? col : len)); } - diff --git a/charset.c b/charset.c index 0aaa329..24bf4e3 100644 --- a/charset.c +++ b/charset.c @@ -23,14 +23,15 @@ static const char Utf8Valid[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0, }; -int charset(const char* buf, size_t len) { +int charset(const uint8_t* buf, size_t len) { /* look for a BOM and parse it */ for (size_t i = 0; i < (sizeof(BOMS)/sizeof(BOMS[0])); i++) - if (!strncmp(buf, BOMS[i].seq, BOMS[i].len)) + if (!strncmp((char*)buf, BOMS[i].seq, BOMS[i].len)) return BOMS[i].type; /* look for bytes that are invalid in utf-8 */ int type = UTF_8; - for (size_t i = 0; type && (i < len); i++) + size_t i = 0; + for (i = 0; type && (i < len); i++) type = Utf8Valid[(int)buf[i]]; return type; } diff --git a/edit.h b/edit.h index 4202cd1..5fa99f1 100644 --- a/edit.h +++ b/edit.h @@ -27,7 +27,7 @@ enum { UTF_32LE, }; -int charset(const char* buf, size_t len); +int charset(const uint8_t* buf, size_t len); size_t utf8encode(char str[UTF_MAX], Rune rune); bool utf8decode(Rune* rune, size_t* length, int byte); Rune fgetrune(FILE* f); @@ -183,7 +183,7 @@ void screen_status(char* fmt, ...); /* Miscellaneous Functions *****************************************************************************/ -void die(char* msg); +void die(const char* fmt, ...); /* Color Scheme Handling *****************************************************************************/ @@ -232,6 +232,7 @@ enum { TabWidth = 4, /* maximum number of spaces used to represent a tab */ ScrollLines = 1, /* number of lines to scroll by for mouse wheel scrolling */ BufSize = 8192, /* default buffer size */ + MaxFonts = 16 /* maximum number of fonts to cache */ }; static const Color Palette[][2] = { @@ -256,9 +257,9 @@ static const Color Palette[][2] = { /* choose the font to use for xft */ #ifdef __MACH__ -#define FONTNAME "Inconsolata:pixelsize=14:antialias=true:autohint=true" +#define FONTNAME "Monaco:size=10:antialias=true:autohint=true" #else -#define FONTNAME "Liberation Mono:pixelsize=14:antialias=true:autohint=true" +#define FONTNAME "Monaco:size=10.5:antialias=true:autohint=true" #endif #define DEFAULT_COLORSCHEME DARK diff --git a/tests/tests.c b/tests/tests.c index 428a740..2c92b66 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -7,7 +7,7 @@ unsigned CursorPos; unsigned TargetCol; enum ColorScheme ColorBase; -void die(char* m) { +void die(const char* m, ...) { (void)m; } diff --git a/tools/fontdemo.c b/tools/fontdemo.c new file mode 100644 index 0000000..ca9954a --- /dev/null +++ b/tools/fontdemo.c @@ -0,0 +1,298 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +uint32_t Msg1[] = { + 0x5916, 0x56fd, 0x8a9e, 0x306e, 0x5b66, 0x7fd2, 0x3068, 0x6559, 0x6388 +}; + +uint32_t Msg2[] = { + 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x20, 0x4c, + 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x54, 0x65, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67 +}; + +struct { + Display* display; + Visual* visual; + Colormap colormap; + unsigned depth; + int screen; + GC gc; + Window window; + Pixmap pixmap; + XftDraw* xft; + int width; + int height; + int rows; + int cols; + XIC xic; + XIM xim; +} X; + +static void die(const char* msgfmt, ...) { + va_list args; + va_start(args, msgfmt); + fprintf(stderr, "Error: "); + vfprintf(stderr, msgfmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(EXIT_FAILURE); +} + +static XftColor xftcolor(uint32_t c) { + XftColor xc; + xc.color.red = ((c & 0x00FF0000) >> 8); + xc.color.green = ((c & 0x0000FF00)); + xc.color.blue = ((c & 0x000000FF) << 8); + xc.color.alpha = UINT16_MAX; + XftColorAllocValue(X.display, X.visual, X.colormap, &xc.color, &xc); + return xc; +} + +static int init(void) { + signal(SIGPIPE, SIG_IGN); // Ignore the SIGPIPE signal + /* open the X display and get basic attributes */ + if (!(X.display = XOpenDisplay(0))) + die("cannot open display"); + Window root = DefaultRootWindow(X.display); + XWindowAttributes wa; + XGetWindowAttributes(X.display, root, &wa); + X.visual = wa.visual; + X.colormap = wa.colormap; + X.screen = DefaultScreen(X.display); + X.depth = DefaultDepth(X.display, X.screen); + + /* create the main window */ + X.window = XCreateSimpleWindow(X.display, root, 0, 0, 640, 480, 0, 0, WhitePixel(X.display, X.screen)); + XSetWindowAttributes swa; + swa.backing_store = WhenMapped; + swa.bit_gravity = NorthWestGravity; + XChangeWindowAttributes(X.display, X.window, CWBackingStore|CWBitGravity, &swa); + XStoreName(X.display, X.window, "edit"); + XSelectInput(X.display, X.window, StructureNotifyMask|ExposureMask|FocusChangeMask); + + /* simulate an initial resize and map the window */ + XConfigureEvent ce; + ce.type = ConfigureNotify; + ce.width = 640; + ce.height = 480; + XSendEvent(X.display, X.window, False, StructureNotifyMask, (XEvent *)&ce); + XMapWindow(X.display, X.window); + + /* set input methods */ + if((X.xim = XOpenIM(X.display, 0, 0, 0))) + X.xic = XCreateIC(X.xim, XNInputStyle, XIMPreeditNothing|XIMStatusNothing, XNClientWindow, X.window, XNFocusWindow, X.window, NULL); + + /* initialize the graphics context */ + XGCValues gcv; + gcv.foreground = WhitePixel(X.display, X.screen); + gcv.graphics_exposures = False; + X.gc = XCreateGC(X.display, X.window, GCForeground|GCGraphicsExposures, &gcv); + + /* initialize pixmap and drawing context */ + X.pixmap = XCreatePixmap(X.display, X.window, 640, 480, X.depth); + X.xft = XftDrawCreate(X.display, X.pixmap, X.visual, X.colormap); + + return XConnectionNumber(X.display); +} + +static void handle_event(XEvent* e) { + switch (e->type) { + case FocusIn: if (X.xic) XSetICFocus(X.xic); break; + case FocusOut: if (X.xic) XUnsetICFocus(X.xic); break; + case ConfigureNotify: // Resize the window + if (e->xconfigure.width != X.width || e->xconfigure.height != X.height) { + X.width = e->xconfigure.width; + X.height = e->xconfigure.height; + X.pixmap = XCreatePixmap(X.display, X.window, X.width, X.height, X.depth); + X.xft = XftDrawCreate(X.display, X.pixmap, X.visual, X.colormap); + } + break; + } +} + +/*****************************************************************************/ + +typedef uint32_t Rune; + +static char FontName[] = "Liberation Mono:size=14:antialias=true:autohint=true"; + +/* globals */ +#define MaxFonts 16 +struct { + struct { + int height; + int width; + int ascent; + int descent; + XftFont* match; + FcFontSet* set; + FcPattern* pattern; + } base; + struct { + XftFont* font; + Rune unicodep; + } cache[MaxFonts]; + int ncached; +} Fonts; + +void font_init(void); +void font_find(XftGlyphFontSpec* spec, Rune rune); +int font_makespecs(XftGlyphFontSpec* specs, const Rune* runes, int len, int x, int y); +void font_drawspecs(const XftGlyphFontSpec* specs, int len, XftColor* fg, XftColor* bg, int x, int y); + +void draw_runes(unsigned x, unsigned y, XftColor* fg, XftColor* bg, Rune* runes, size_t rlen); +static void redraw(void); + +int main(int argc, char** argv) { + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + init(); + font_init(); + XEvent e; + while (true) { + XPeekEvent(X.display,&e); + while (XPending(X.display)) { + XNextEvent(X.display, &e); + if (!XFilterEvent(&e, None)) + handle_event(&e); + } + redraw(); + } + return 0; +} + +static void redraw(void) { + /* Allocate the colors */ + XftColor bkgclr = xftcolor(0x002b36); + XftColor txtclr = xftcolor(0x839496); + /* draw the background colors */ + XftDrawRect(X.xft, &bkgclr, 0, 0, X.width, X.height); + /* Draw text */ + draw_runes(0, 0, &txtclr, &bkgclr, (FcChar32*)(Msg1), (sizeof(Msg1)/sizeof(Msg1[0]))); + draw_runes(0, 1, &txtclr, &bkgclr, (FcChar32*)(Msg1), (sizeof(Msg1)/sizeof(Msg1[0]))); + draw_runes(0, 2, &txtclr, &bkgclr, (FcChar32*)(Msg2), (sizeof(Msg2)/sizeof(Msg2[0]))); + /* flush pixels to the screen */ + XCopyArea(X.display, X.pixmap, X.window, X.gc, 0, 0, X.width, X.height, 0, 0); + XFlush(X.display); +} + +/*****************************************************************************/ + +void draw_runes(unsigned x, unsigned y, XftColor* fg, XftColor* bg, Rune* runes, size_t rlen) { + XftGlyphFontSpec specs[rlen]; + size_t nspecs = font_makespecs(specs, runes, rlen, x, y); + font_drawspecs(specs, nspecs, fg, bg, x, y); +} + +void font_init(void) { + /* init the library and the base font pattern */ + if (!FcInit()) + die("Could not init fontconfig.\n"); + FcPattern* pattern = FcNameParse((FcChar8 *)FontName); + if (!pattern) + die("st: can't open font %s\n", FontName); + + /* load the base font */ + FcResult result; + FcPattern* match = XftFontMatch(X.display, X.screen, pattern, &result); + if (!match || !(Fonts.base.match = XftFontOpenPattern(X.display, match))) + die("could not load default font: %s", FontName); + + /* get base font extents */ + XGlyphInfo extents; + const FcChar8 ascii[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; + XftTextExtentsUtf8(X.display, Fonts.base.match, ascii, sizeof(ascii), &extents); + Fonts.base.set = NULL; + Fonts.base.pattern = FcPatternDuplicate(pattern); + Fonts.base.ascent = Fonts.base.match->ascent; + Fonts.base.descent = Fonts.base.match->descent; + Fonts.base.height = Fonts.base.ascent + Fonts.base.descent; + Fonts.base.width = ((extents.xOff + (sizeof(ascii) - 1)) / sizeof(ascii)); + FcPatternDestroy(pattern); +} + +void font_find(XftGlyphFontSpec* spec, Rune rune) { + /* if the rune is in the base font, set it and return */ + FT_UInt glyphidx = XftCharIndex(X.display, Fonts.base.match, rune); + if (glyphidx) { + puts("font: default"); + spec->font = Fonts.base.match; + spec->glyph = glyphidx; + return; + } + /* Otherwise check the cache */ + for (int f = 0; f < Fonts.ncached; f++) { + glyphidx = XftCharIndex(X.display, Fonts.cache[f].font, rune); + /* Fond a suitable font or found a default font */ + if (glyphidx || (!glyphidx && Fonts.cache[f].unicodep == rune)) { + puts("font: match"); + spec->font = Fonts.cache[f].font; + spec->glyph = glyphidx; + return; + } + } + /* if all other options fail, ask fontconfig for a suitable font */ + FcResult fcres; + if (!Fonts.base.set) + Fonts.base.set = FcFontSort(0, Fonts.base.pattern, 1, 0, &fcres); + FcFontSet* fcsets[] = { Fonts.base.set }; + FcPattern* fcpattern = FcPatternDuplicate(Fonts.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 (Fonts.ncached >= MaxFonts) { + Fonts.ncached = MaxFonts - 1; + XftFontClose(X.display, Fonts.cache[Fonts.ncached].font); + } + Fonts.cache[Fonts.ncached].font = XftFontOpenPattern(X.display, fontmatch); + Fonts.cache[Fonts.ncached].unicodep = rune; + spec->glyph = XftCharIndex(X.display, Fonts.cache[Fonts.ncached].font, rune); + spec->font = Fonts.cache[Fonts.ncached].font; + Fonts.ncached++; + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + puts("font: fontconfig"); +} + +int font_makespecs(XftGlyphFontSpec* specs, const Rune* runes, int len, int x, int y) { + int winx = x * Fonts.base.width, winy = y * Fonts.base.height, xp, yp; + int numspecs = 0; + for (int i = 0, xp = winx, yp = winy + Fonts.base.ascent; i < len; ++i) { + if (!runes[i]) continue; + font_find(&(specs[numspecs]), runes[i]); + int runewidth = wcwidth(runes[i]) * Fonts.base.width; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + return numspecs; +} + +void font_drawspecs(const XftGlyphFontSpec* specs, int len, XftColor* fg, XftColor* bg, int x, int y) { + //int charlen = len; // * ((base.mode & ATTR_WIDE) ? 2 : 1); + //int winx = x * Fonts.base.width, winy = y * Fonts.base.height, + // width = charlen * Fonts.base.width; + //XRectangle r; + + /* Render the glyphs. */ + XftDrawGlyphFontSpec(X.xft, fg, specs, len); +} + + diff --git a/tools/runes.c b/tools/runes.c new file mode 100644 index 0000000..8a0be2b --- /dev/null +++ b/tools/runes.c @@ -0,0 +1,118 @@ +/** + @brief Simple UTF-8 encoding and decoding routines. + @author Michael D. Lowis + @license BSD 2-clause License +*/ + +#include +#include +#include + +enum { + UTF_MAX = 6u, /* maximum number of bytes that make up a rune */ + RUNE_SELF = 0x80, /* byte values larger than this are *not* ascii */ + RUNE_ERR = 0xFFFD, /* rune value representing an error */ + RUNE_MAX = 0x10FFFF, /* Maximum decodable rune value */ + RUNE_EOF = UINT32_MAX /* rune value representing end of file */ +}; + +typedef uint32_t Rune; + +const uint8_t UTF8_SeqBits[] = { 0x00u, 0x80u, 0xC0u, 0xE0u, 0xF0u, 0xF8u, 0xFCu, 0xFEu }; +const uint8_t UTF8_SeqMask[] = { 0x00u, 0xFFu, 0x1Fu, 0x0Fu, 0x07u, 0x03u, 0x01u, 0x00u }; +const uint8_t UTF8_SeqLens[] = { 0x01u, 0x00u, 0x02u, 0x03u, 0x04u, 0x05u, 0x06u, 0x00u }; + +static bool runevalid(Rune val) { + return (val <= RUNE_MAX) + && ((val & 0xFFFEu) != 0xFFFEu) + && ((val < 0xD800u) || (val > 0xDFFFu)) + && ((val < 0xFDD0u) || (val > 0xFDEFu)); +} + +static size_t runelen(Rune rune) { + if(!runevalid(rune)) + return 0; + else if(rune <= 0x7F) + return 1; + else if(rune <= 0x07FF) + return 2; + else if(rune <= 0xFFFF) + return 3; + else + return 4; +} + +static uint8_t utfseq(uint8_t byte) { + for (int i = 1; i < 8; i++) + if ((byte & UTF8_SeqBits[i]) == UTF8_SeqBits[i-1]) + return UTF8_SeqLens[i-1]; + return 0; +} + +size_t utf8encode(char str[UTF_MAX], Rune rune) { + size_t len = runelen(rune); + str[0] = (len == 1 ? 0x00 : UTF8_SeqBits[len]) + | (UTF8_SeqMask[len] & (rune >> (6 * (len-1)))); + for (size_t i = 1; i < len; i++) + str[i] = 0x80u | (0x3Fu & (rune >> (6 * (len-i-1)))); + return len; +} + +bool utf8decode(Rune* rune, size_t* length, int byte) { + /* Handle the start of a new rune */ + if (*length == 0) { + /* If we were fed in an EOF as a start byte, handle it here */ + if (byte == EOF) { + *rune = RUNE_EOF; + } else { + /* Otherwise, decode the first byte of the rune */ + *length = utfseq(byte); + *rune = (*length == 0) ? RUNE_ERR : (byte & UTF8_SeqMask[*length]); + (*length)--; + } + /* Handle continuation bytes */ + } else if ((byte & 0xC0) == 0x80) { + /* add bits from continuation byte to rune value + * cannot overflow: 6 byte sequences contain 31 bits */ + *rune = (*rune << 6) | (byte & 0x3F); /* 10xxxxxx */ + (*length)--; + /* Sanity check the final rune value before finishing */ + if ((*length == 0) && !runevalid(*rune)) + *rune = RUNE_ERR; + /* Didn't get the continuation byte we expected */ + } else { + *rune = RUNE_ERR; + } + /* Tell the caller whether we finished or not */ + return ((*length == 0) || (*rune == RUNE_ERR)); +} + +Rune fgetrune(FILE* f) { + Rune rune = 0; + size_t length = 0; + while (!utf8decode(&rune, &length, fgetc(f))); + return rune; +} + +void fputrune(Rune rune, FILE* f) { + char utf[UTF_MAX] = {0}; + utf8encode(utf, rune); + fprintf(f, "%s", utf); +} + +void print_runes(FILE* in) { + if (!in) return; + Rune r; + while (RUNE_EOF != (r = fgetrune(in))) + printf("%#x\n", r); + fclose(in); +} + +int main(int argc, char** argv) { + if (argc == 1) + print_runes(stdin); + else + for (int i = 1; i < argc; i++) + print_runes(fopen(argv[i], "rb")); + return 0; +} diff --git a/xedit.c b/xedit.c index 69fdd53..3bc5684 100644 --- a/xedit.c +++ b/xedit.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -22,10 +23,6 @@ struct { Pixmap pixmap; XftDraw* xft; XftFont* font; - unsigned fheight; - unsigned fwidth; - unsigned fascent; - unsigned fdescent; int width; int height; int rows; @@ -34,100 +31,125 @@ struct { XIM xim; } X; -/*****************************************************************************/ +struct { + struct { + int height; + int width; + int ascent; + int descent; + XftFont* match; + FcFontSet* set; + FcPattern* pattern; + } base; + struct { + XftFont* font; + Rune unicodep; + } cache[MaxFonts]; + int ncached; +} Fonts; -struct XFont { - struct XFont* next; - XftFont* font; - FcPattern* pattern; -}* FontList = NULL; - -struct XFont* fontbyname(char* fontname) { - struct XFont* xfont = (struct XFont*)calloc(1, sizeof(struct XFont)); - if (!(xfont->font = XftFontOpenName(X.display, X.screen, fontname))) - die("cannot open default font"); - if (!(xfont->pattern = FcNameParse((FcChar8 *)FONTNAME))) - die("cannot convert fontname to font pattern"); - return xfont; -} +/*****************************************************************************/ -struct XFont* fontbypatt(FcPattern* pattern) { - struct XFont* xfont = (struct XFont*)calloc(1, sizeof(struct XFont)); - if (!(xfont->font = XftFontOpenPattern(X.display, pattern))) - die("cannot get font by pattern"); - return xfont; +void font_init(void) { + /* init the library and the base font pattern */ + if (!FcInit()) + die("Could not init fontconfig.\n"); + FcPattern* pattern = FcNameParse((FcChar8 *)FONTNAME); + if (!pattern) + die("st: can't open font %s\n", FONTNAME); + + /* load the base font */ + FcResult result; + FcPattern* match = XftFontMatch(X.display, X.screen, pattern, &result); + if (!match || !(Fonts.base.match = XftFontOpenPattern(X.display, match))) + die("could not load default font: %s", FONTNAME); + + /* get base font extents */ + XGlyphInfo extents; + const FcChar8 ascii[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; + XftTextExtentsUtf8(X.display, Fonts.base.match, ascii, sizeof(ascii), &extents); + Fonts.base.set = NULL; + Fonts.base.pattern = FcPatternDuplicate(pattern); + Fonts.base.ascent = Fonts.base.match->ascent; + Fonts.base.descent = Fonts.base.match->descent; + Fonts.base.height = Fonts.base.ascent + Fonts.base.descent; + Fonts.base.width = ((extents.xOff + (sizeof(ascii) - 1)) / sizeof(ascii)); + FcPatternDestroy(pattern); } -struct XFont* getfont(Rune r) { - /* check to see if the font is already loaded */ - struct XFont* xfont = FontList; - while (xfont) { - printf("does font have rune? %d\n", XftCharExists(X.display, xfont->font, r)); - if (XftCharExists(X.display, xfont->font, r)) { - //puts("found font in font list"); - return xfont; +void font_find(XftGlyphFontSpec* spec, Rune rune) { + /* if the rune is in the base font, set it and return */ + FT_UInt glyphidx = XftCharIndex(X.display, Fonts.base.match, rune); + if (glyphidx) { + spec->font = Fonts.base.match; + spec->glyph = glyphidx; + return; + } + /* Otherwise check the cache */ + for (int f = 0; f < Fonts.ncached; f++) { + glyphidx = XftCharIndex(X.display, Fonts.cache[f].font, rune); + /* Fond a suitable font or found a default font */ + if (glyphidx || (!glyphidx && Fonts.cache[f].unicodep == rune)) { + spec->font = Fonts.cache[f].font; + spec->glyph = glyphidx; + return; } - xfont = xfont->next; } - /* search for a new font that has the rune */ - XftResult result; + /* if all other options fail, ask fontconfig for a suitable font */ + FcResult fcres; + if (!Fonts.base.set) + Fonts.base.set = FcFontSort(0, Fonts.base.pattern, 1, 0, &fcres); + FcFontSet* fcsets[] = { Fonts.base.set }; + FcPattern* fcpattern = FcPatternDuplicate(Fonts.base.pattern); FcCharSet* fccharset = FcCharSetCreate(); - FcCharSetAddChar(fccharset, r); - FcPattern* fcpattern = FcPatternDuplicate(FontList->pattern); + FcCharSetAddChar(fccharset, rune); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); - FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); - FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + FcConfigSubstitute(0, fcpattern, FcMatchPattern); FcDefaultSubstitute(fcpattern); - FcPattern* match = XftFontMatch(X.display, X.screen, fcpattern, &result); - FcCharSetDestroy(fccharset); - FcPatternDestroy(fcpattern); - if (match) { - //puts("found new font, adding it to the list"); - struct XFont* newfont = fontbypatt(match); - struct XFont* lastfont = FontList; - printf("newfont: %p, %d\n", newfont->font, XftCharExists(X.display, newfont->font, r)); - if (XftCharExists(X.display, newfont->font, r)) { - for (; lastfont->next; lastfont = lastfont->next) - ; /* NOP */ - lastfont->next = newfont; - return newfont; - } else { - XftFontClose(X.display, newfont->font); - free(newfont); - } + FcPattern* fontmatch = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); + /* add the font to the cache and use it */ + if (Fonts.ncached >= MaxFonts) { + Fonts.ncached = MaxFonts - 1; + XftFontClose(X.display, Fonts.cache[Fonts.ncached].font); } - puts("failed to find a font with the given rune"); - return FontList; + Fonts.cache[Fonts.ncached].font = XftFontOpenPattern(X.display, fontmatch); + Fonts.cache[Fonts.ncached].unicodep = rune; + spec->glyph = XftCharIndex(X.display, Fonts.cache[Fonts.ncached].font, rune); + spec->font = Fonts.cache[Fonts.ncached].font; + Fonts.ncached++; + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); } -void fontinit(char* fontstr) -{ - double fontval; - float ceilf(float); - /* initialize fontconfig */ - if (!FcInit()) - die("Could not init fontconfig"); - /* get pattern from font name */ - FcPattern *pattern; - if (fontstr[0] == '-') { - pattern = XftXlfdParse(fontstr, False, False); - } else { - pattern = FcNameParse((FcChar8 *)fontstr); +int font_makespecs(XftGlyphFontSpec* specs, const Rune* runes, int len, int x, int y) { + int winx = x * Fonts.base.width, winy = y * Fonts.base.height; + int numspecs = 0; + for (int i = 0, xp = winx, yp = winy + Fonts.base.ascent; i < len; ++i) { + if (!runes[i]) continue; + font_find(&(specs[numspecs]), runes[i]); + int runewidth = wcwidth(runes[i]) * Fonts.base.width; + specs[numspecs].x = xp; + specs[numspecs].y = yp; + xp += runewidth; + numspecs++; } - if (!pattern) - die("failed to open default font"); - /* load the font by pattern */ - if (!(FontList = fontbypatt(pattern))) - die("failed to load the font"); - FcPatternDestroy(pattern); + return numspecs; } - /*****************************************************************************/ -void die(char* m) { - fprintf(stderr, "dying, %s\n", m); - exit(1); +void die(const char* msgfmt, ...) { + va_list args; + va_start(args, msgfmt); + fprintf(stderr, "Error: "); + vfprintf(stderr, msgfmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(EXIT_FAILURE); } static XftColor xftcolor(enum ColorId cid) { @@ -181,12 +203,7 @@ static int init(void) { XMapWindow(X.display, X.window); /* allocate font */ - //fontinit(FONTNAME); - FontList = fontbyname(FONTNAME); - X.fheight = FontList->font->height; - X.fwidth = FontList->font->max_advance_width; - X.fascent = FontList->font->ascent; - X.fdescent = FontList->font->descent; + font_init(); /* set input methods */ if ((X.xim = XOpenIM(X.display, 0, 0, 0))) @@ -255,8 +272,8 @@ static MouseEvent* getmouse(XEvent* e) { if (e->type == MotionNotify) { event.type = MouseMove; event.button = MOUSE_NONE; - event.x = e->xmotion.x / X.fwidth; - event.y = e->xmotion.y / (X.fascent + X.fdescent); + event.x = e->xmotion.x / Fonts.base.width; + event.y = e->xmotion.y / (Fonts.base.ascent + Fonts.base.descent); } else { event.type = (e->type = ButtonPress ? MouseDown : MouseUp); /* set the button id */ @@ -268,8 +285,8 @@ static MouseEvent* getmouse(XEvent* e) { case Button5: event.button = MOUSE_WHEELDOWN; break; default: event.button = MOUSE_NONE; break; } - event.x = e->xbutton.x / X.fwidth; - event.y = e->xbutton.y / (X.fascent + X.fdescent); + event.x = e->xbutton.x / Fonts.base.width; + event.y = e->xbutton.y / (Fonts.base.ascent + Fonts.base.descent); } return &event; } @@ -288,42 +305,18 @@ static void handle_event(XEvent* e) { X.xft = XftDrawCreate(X.display, X.pixmap, X.visual, X.colormap); screen_setsize( &Buffer, - X.height / X.fheight, - X.width / X.fwidth); + X.height / Fonts.base.height, + X.width / Fonts.base.width); } break; } } -#include - -static void draw_string(XftDraw* xft, XftColor* clr, unsigned x, unsigned y, Rune* r, unsigned len) { - struct XFont* curfont = NULL; - for (unsigned i = 0, n = 0; i < len; i += n, n = 0) { - //printf("i: %d, n: %d\n", i, n); - Rune* str = r+i; - struct XFont* newfont = NULL; - curfont = getfont(str[n]); - while (curfont == (newfont = getfont(str[n]))) { - n++; - } - if (n) { - //printf("printing: %p %u\n", curfont, n); - //if (!curfont) curfont = FontList; - XftDrawString32(xft, clr, (curfont ? curfont->font : FontList->font), x, y, str, n); - } - } - //for (unsigned i = 0; i < len;) { - // struct XFont* newfont = NULL; - // Rune* curr = r+i; - // unsigned n = 0; - // while (curfont == (newfont = getfont(curr[n]))) - // n++; - // if (!curfont) curfont = FontList; - // XftDrawString32(xft, clr, curfont->font, x, y, curr, n); - // curfont = newfont; - // i += n; - //} +void draw_runes(unsigned x, unsigned y, XftColor* fg, XftColor* bg, Rune* runes, size_t rlen) { + (void)bg; + XftGlyphFontSpec specs[rlen]; + size_t nspecs = font_makespecs(specs, runes, rlen, x, y); + XftDrawGlyphFontSpec(X.xft, fg, specs, nspecs); } static void redraw(void) { @@ -335,8 +328,8 @@ static void redraw(void) { /* draw the background colors */ XftDrawRect(X.xft, &bkgclr, 0, 0, X.width, X.height); - XftDrawRect(X.xft, >rclr, 79 * X.fwidth, 0, X.fwidth, X.height); - XftDrawRect(X.xft, &txtclr, 0, 0, X.width, X.fheight + X.fdescent); + XftDrawRect(X.xft, >rclr, 79 * Fonts.base.width, 0, Fonts.base.width, X.height); + XftDrawRect(X.xft, &txtclr, 0, 0, X.width, Fonts.base.height); /* update the screen buffer and retrieve cursor coordinates */ unsigned csrx, csry; @@ -352,17 +345,17 @@ static void redraw(void) { ); for (unsigned y = 0; y < nrows; y++) { Row* row = screen_getrow(y); - draw_string(X.xft, (y == 0 ? &bkgclr : &txtclr), 0, (y+1) * X.fheight, row->cols, row->len); + draw_runes(0, y, (y == 0 ? &bkgclr : &txtclr), NULL, row->cols, row->len); } /* Place cursor on screen */ Rune csrrune = screen_getcell(csry,csrx); if (Buffer.insert_mode) { - XftDrawRect(X.xft, &csrclr, csrx * X.fwidth, csry * X.fheight + X.fdescent, 2, X.fheight); + XftDrawRect(X.xft, &csrclr, csrx * Fonts.base.width, csry * Fonts.base.height, 2, Fonts.base.height); } else { unsigned width = ('\t' == buf_get(&Buffer, CursorPos) ? (TabWidth - (csrx % TabWidth)) : 1); - XftDrawRect(X.xft, &csrclr, csrx * X.fwidth, csry * X.fheight + X.fdescent, width * X.fwidth, X.fheight); - draw_string(X.xft, &bkgclr, csrx * X.fwidth, (csry+1) * X.fheight, (FcChar32*)&csrrune, 1); + XftDrawRect(X.xft, &csrclr, csrx * Fonts.base.width, csry * Fonts.base.height, width * Fonts.base.width, Fonts.base.height); + draw_runes(csrx, csry, &bkgclr, NULL, (FcChar32*)&csrrune, 1); } /* flush pixels to the screen */ -- 2.49.0