tPrint random text in the window using fontconfig - xmenu - drop-down menu for X11
(HTM) git clone git://git.z3bra.org/xmenu.git
(DIR) Log
(DIR) Files
(DIR) Refs
---
(DIR) commit 8695651f358780a5fe85ac4f5b5a0a8ac5e5a4d6
(DIR) parent 769a43b6022669b39f11685961727945be3a016c
(HTM) Author: Willy Goiffon <dev@z3bra.org>
Date: Sat, 9 Nov 2019 01:14:19 +0100
Print random text in the window using fontconfig
Thanks venam!!
https://venam.nixers.net/blog/unix/2018/09/02/fonts-xcb.html
Diffstat:
M config.mk | 2 +-
A utf8.h | 48 +++++++++++++++++++++++++++++++
A xcbft.h | 813 ++++++++++++++++++++++++++++++
M xmenu.c | 27 +++++++++++++++++++++++----
4 files changed, 885 insertions(+), 5 deletions(-)
---
(DIR) diff --git a/config.mk b/config.mk
t@@ -9,4 +9,4 @@ MANDIR = ${PREFIX}/man
CPPFLAGS = -DVERSION=\"${VERSION}\" -I/usr/include/freetype2
CFLAGS = $(CPPFLAGS) -Wall -Wextra -pedantic
LDFLAGS =
-LDLIBS = -lxcb -lfontconfig -lfreetype
+LDLIBS = -lxcb -lxcb-render -lxcb-render-util -lxcb-xrm -lfontconfig -lfreetype
(DIR) diff --git a/utf8.h b/utf8.h
t@@ -0,0 +1,48 @@
+#ifndef _UTF8_UTILS_
+#define _UTF8_UTILS_
+
+#include <fontconfig/fontconfig.h>
+
+struct utf_holder {
+ FcChar32 *str;
+ unsigned int length;
+};
+struct utf_holder char_to_uint32(char *str);
+void utf_holder_destroy(struct utf_holder holder);
+
+struct utf_holder
+char_to_uint32(char *str)
+{
+ struct utf_holder holder;
+ FcChar32 *output = NULL;
+ int length = 0, shift = 0;
+
+ // there should be less than or same as the strlen of str
+ output = (FcChar32 *)malloc(sizeof(FcChar32)*strlen(str));
+ if (!output) {
+ puts("couldn't allocate mem for char_to_uint32");
+ }
+
+ while (*(str+shift)) {
+ shift += FcUtf8ToUcs4(
+ (FcChar8*)(str+shift),
+ output+length,
+ strlen(str)-shift
+ );
+ length++;
+ }
+
+ holder.length = length;
+ holder.str = output;
+
+ return holder;
+}
+
+void
+utf_holder_destroy(struct utf_holder holder)
+{
+
+ free(holder.str);
+}
+
+#endif
(DIR) diff --git a/xcbft.h b/xcbft.h
t@@ -0,0 +1,813 @@
+#ifndef _XCBFT
+#define _XCBFT
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <math.h>
+
+#include <fontconfig/fontconfig.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+#include <xcb/xcb_renderutil.h>
+#include <xcb/xcb_xrm.h>
+
+#include "utf8.h"
+
+struct xcbft_patterns_holder {
+ FcPattern **patterns;
+ uint8_t length;
+};
+
+struct xcbft_face_holder {
+ FT_Face *faces;
+ uint8_t length;
+ FT_Library library;
+};
+
+struct xcbft_glyphset_and_advance {
+ xcb_render_glyphset_t glyphset;
+ FT_Vector advance;
+};
+
+// signatures
+bool xcbft_init(void);
+void xcbft_done(void);
+FcPattern* xcbft_query_fontsearch(FcChar8 *);
+struct xcbft_face_holder xcbft_query_by_char_support(
+ FcChar32, const FcPattern *, long);
+struct xcbft_patterns_holder xcbft_query_fontsearch_all(FcStrSet *);
+double xcbft_get_pixel_size(struct xcbft_patterns_holder patterns);
+struct xcbft_face_holder xcbft_load_faces(
+ struct xcbft_patterns_holder, long);
+FcStrSet* xcbft_extract_fontsearch_list(char *);
+void xcbft_patterns_holder_destroy(struct xcbft_patterns_holder);
+void xcbft_face_holder_destroy(struct xcbft_face_holder);
+FT_Vector xcbft_draw_text(xcb_connection_t*, xcb_drawable_t,
+ int16_t, int16_t, struct utf_holder, xcb_render_color_t,
+ struct xcbft_face_holder, long);
+xcb_render_picture_t xcbft_create_pen(xcb_connection_t*,
+ xcb_render_color_t);
+struct xcbft_glyphset_and_advance xcbft_load_glyphset(xcb_connection_t *,
+ struct xcbft_face_holder, struct utf_holder, long);
+FT_Vector xcbft_load_glyph(xcb_connection_t *, xcb_render_glyphset_t,
+ FT_Face, int);
+long xcbft_get_dpi(xcb_connection_t *);
+xcb_pixmap_t xcbft_create_text_pixmap(xcb_connection_t *,
+ struct utf_holder, xcb_render_color_t, xcb_render_color_t,
+ struct xcbft_patterns_holder, long);
+static uint32_t xcb_color_to_uint32(xcb_render_color_t);
+
+void
+xcbft_done(void)
+{
+ FcFini();
+}
+
+bool
+xcbft_init(void)
+{
+ FcBool status;
+
+ status = FcInit();
+ if (status == FcFalse) {
+ fprintf(stderr, "Could not initialize fontconfig");
+ }
+
+ return status == FcTrue;
+}
+
+// Inspired by https://www.codeproject.com/Articles/1202772/Color-Topics-for-Programmers
+static uint32_t
+xcb_color_to_uint32(xcb_render_color_t rgb)
+{
+ uint32_t sm1 = 65536 - 1; // from 2^16
+ uint32_t scale = 256; // to 2^8
+
+ return
+ (uint32_t) ( ((double)rgb.red/sm1 * (scale-1)) * scale * scale)
+ + (uint32_t) ( ((double)rgb.green/sm1 * (scale-1)) * scale)
+ + (uint32_t) ( ((double)rgb.blue/sm1 * (scale-1)) );
+}
+
+xcb_pixmap_t
+xcbft_create_text_pixmap(
+ xcb_connection_t *c,
+ struct utf_holder text,
+ xcb_render_color_t text_color,
+ xcb_render_color_t background_color,
+ struct xcbft_patterns_holder font_patterns,
+ long dpi)
+{
+ xcb_pixmap_t pmap, resize_pmap;
+ xcb_screen_t *screen;
+ screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
+ struct xcbft_face_holder faces;
+ double pix_size = 12;
+ uint32_t mask = 0;
+ uint32_t values[2];
+ xcb_gcontext_t gc;
+
+ pix_size = xcbft_get_pixel_size(font_patterns);
+ pmap = xcb_generate_id(c);
+ faces = xcbft_load_faces(font_patterns, dpi);
+
+ // 0.2 being the factor padding on both side
+ // 0.3 being the extra factor padding for the width protection
+ uint16_t width = (pix_size*text.length/1.6)+pix_size*0.7;
+ uint16_t height = pix_size+pix_size*0.4;
+
+ xcb_create_pixmap(c, screen->root_depth, pmap, screen->root, width, height);
+
+ xcb_rectangle_t rectangles[] = { { .x = 0, .y = 0,
+ .width = width, .height = height } };
+ gc = xcb_generate_id(c);
+ mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
+ values[0] = xcb_color_to_uint32(background_color) | 0xff000000;
+ values[1] = 0;
+ xcb_create_gc(c, gc, pmap, mask, values);
+ // draw a rectangle filling the whole pixmap with a single color
+ xcb_poly_fill_rectangle(c, pmap, gc, 1, rectangles);
+
+ FT_Vector advance = xcbft_draw_text(c, pmap,
+ 0.2*pix_size, 0.2*pix_size+pix_size, // x, y
+ text, text_color, faces, dpi);
+
+ resize_pmap = xcb_generate_id(c);
+ width = advance.x+pix_size*0.4; // 0.2 on both sides
+ xcb_create_pixmap(c, screen->root_depth, resize_pmap, screen->root, width, height);
+ xcb_copy_area(c, pmap, resize_pmap, gc, 0, 0, 0, 0, width, height);
+
+ xcb_free_pixmap(c, pmap);
+ xcbft_face_holder_destroy(faces);
+
+ return resize_pmap;
+}
+
+/*
+ * Do the font queries through fontconfig and return the info
+ *
+ * Assumes:
+ * Fontconfig is already init & cleaned outside
+ * the FcPattern return needs to be cleaned outside
+ */
+FcPattern*
+xcbft_query_fontsearch(FcChar8 *fontquery)
+{
+ FcBool status;
+ FcPattern *fc_finding_pattern, *pat_output;
+ FcResult result;
+
+ fc_finding_pattern = FcNameParse(fontquery);
+
+ // to match we need to fix the pattern (fill unspecified info)
+ FcDefaultSubstitute(fc_finding_pattern);
+ status = FcConfigSubstitute(NULL, fc_finding_pattern, FcMatchPattern);
+ if (status == FcFalse) {
+ fprintf(stderr, "could not perform config font substitution");
+ return NULL;
+ }
+
+ pat_output = FcFontMatch(NULL, fc_finding_pattern, &result);
+
+ FcPatternDestroy(fc_finding_pattern);
+ if (result == FcResultMatch) {
+ return pat_output;
+ } else if (result == FcResultNoMatch) {
+ fprintf(stderr, "there wasn't a match");
+ } else {
+ fprintf(stderr, "the match wasn't as good as it should be");
+ }
+ return NULL;
+}
+
+/*
+ * Query a font based on character support
+ * Optionally pass a pattern that it'll use as the base for the search
+ *
+ * fallback of font added to the list or somehow done when drawing,
+ * to do that we need to search by the charset we want to draw
+ * and if they are in one of the font already specified, thus need to
+ * know what the users want to insert as text. This needs more thinking
+ * to be decoupled.
+
+ Assumes the ft2 library is already loaded
+ Assumes the face will be cleaned outside
+ */
+struct xcbft_face_holder
+xcbft_query_by_char_support(FcChar32 character,
+ const FcPattern *copy_pattern, long dpi)
+{
+ FcBool status;
+ FcResult result;
+ FcCharSet *charset;
+ FcPattern *charset_pattern, *pat_output;
+ struct xcbft_patterns_holder patterns;
+ struct xcbft_face_holder faces;
+
+ faces.length = 0;
+
+ // add characters we need to a charset
+ charset = FcCharSetCreate();
+ FcCharSetAddChar(charset, character);
+
+ // if we pass a pattern then copy it to get something close
+ if (copy_pattern != NULL) {
+ charset_pattern = FcPatternDuplicate(copy_pattern);
+ } else {
+ charset_pattern = FcPatternCreate();
+ }
+
+ // use the charset for the pattern search
+ FcPatternAddCharSet(charset_pattern, FC_CHARSET, charset);
+ // also force it to be scalable
+ FcPatternAddBool(charset_pattern, FC_SCALABLE, FcTrue);
+
+ // default & config substitutions, the usual
+ FcDefaultSubstitute(charset_pattern);
+ status = FcConfigSubstitute(NULL, charset_pattern, FcMatchPattern);
+ if (status == FcFalse) {
+ fprintf(stderr, "could not perform config font substitution");
+ FcCharSetDestroy(charset);
+ return faces;
+ }
+
+ pat_output = FcFontMatch(NULL, charset_pattern, &result);
+
+ FcPatternDestroy(charset_pattern);
+
+ if (result != FcResultMatch) {
+ fprintf(stderr, "there wasn't a match");
+ FcCharSetDestroy(charset);
+ return faces;
+ }
+
+ patterns.patterns = malloc(sizeof(FcPattern *));
+ patterns.length = 1;
+ patterns.patterns[0] = pat_output;
+
+ faces = xcbft_load_faces(patterns, dpi);
+
+ // cleanup
+ xcbft_patterns_holder_destroy(patterns);
+ FcCharSetDestroy(charset);
+
+ return faces;
+}
+
+struct xcbft_patterns_holder
+xcbft_query_fontsearch_all(FcStrSet *queries)
+{
+ struct xcbft_patterns_holder font_patterns;
+ FcPattern *font_pattern;
+ uint8_t current_allocated;
+
+ font_patterns.patterns = NULL;
+ font_patterns.length = 0;
+
+ // start with 5, expand if needed
+ current_allocated = 5;
+ font_patterns.patterns = malloc(sizeof(FcPattern *)*current_allocated);
+
+ // safely iterate over set
+ FcStrList *iterator = FcStrListCreate(queries);
+ FcChar8 *fontquery = NULL;
+ FcStrListFirst(iterator);
+ while ((fontquery = FcStrListNext(iterator)) != NULL) {
+ font_pattern = xcbft_query_fontsearch(fontquery);
+ if (font_pattern != NULL) {
+ if (font_patterns.length + 1 > current_allocated) {
+ current_allocated += 5;
+ font_patterns.patterns = realloc(
+ font_patterns.patterns,
+ sizeof(FcPattern*) * current_allocated);
+ }
+ font_patterns.patterns[font_patterns.length] = font_pattern;
+ font_patterns.length++;
+ }
+ }
+ FcStrListDone(iterator);
+ // end of safely iterate over set
+
+ return font_patterns;
+}
+
+// There's no way to predict the width and height of every characters
+// as they can go outside the em, so let's just use whatever we have as
+// the biggest pixel size so far in all the loaded patterns
+double
+xcbft_get_pixel_size(struct xcbft_patterns_holder patterns)
+{
+ int i;
+ double maximum_pix_size;
+ FcValue fc_pixel_size;
+ FcResult result;
+
+ maximum_pix_size = 0;
+ for (i = 0; i < patterns.length; i++) {
+ result = FcPatternGet(patterns.patterns[i], FC_PIXEL_SIZE, 0, &fc_pixel_size);
+ if (result != FcResultMatch || fc_pixel_size.u.d == 0) {
+ fprintf(stderr, "font has no pixel size, using 12 by default");
+ fc_pixel_size.type = FcTypeInteger;
+ fc_pixel_size.u.d = 12.0;
+ }
+ if (fc_pixel_size.u.d > maximum_pix_size) {
+ maximum_pix_size = fc_pixel_size.u.d;
+ }
+ }
+
+ return maximum_pix_size;
+}
+
+struct xcbft_face_holder
+xcbft_load_faces(struct xcbft_patterns_holder patterns, long dpi)
+{
+ int i;
+ struct xcbft_face_holder faces;
+ FcResult result;
+ FcValue fc_file, fc_index, fc_matrix, fc_pixel_size;
+ FT_Matrix ft_matrix;
+ FT_Error error;
+ FT_Library library;
+
+ faces.length = 0;
+ error = FT_Init_FreeType(&library);
+ if (error != FT_Err_Ok) {
+ perror(NULL);
+ return faces;
+ }
+
+ // allocate the same size as patterns as it should be <= its length
+ faces.faces = malloc(sizeof(FT_Face)*patterns.length);
+
+ for (i = 0; i < patterns.length; i++) {
+ // get the information needed from the pattern
+ result = FcPatternGet(patterns.patterns[i], FC_FILE, 0, &fc_file);
+ if (result != FcResultMatch) {
+ fprintf(stderr, "font has not file location");
+ continue;
+ }
+ result = FcPatternGet(patterns.patterns[i], FC_INDEX, 0, &fc_index);
+ if (result != FcResultMatch) {
+ fprintf(stderr, "font has no index, using 0 by default");
+ fc_index.type = FcTypeInteger;
+ fc_index.u.i = 0;
+ }
+ // TODO: load more info like
+ // autohint
+ // hinting
+ // verticallayout
+
+ // load the face
+ error = FT_New_Face(
+ library,
+ (const char *) fc_file.u.s,
+ fc_index.u.i,
+ &(faces.faces[faces.length]) );
+ if (error == FT_Err_Unknown_File_Format) {
+ fprintf(stderr, "wrong file format");
+ continue;
+ } else if (error == FT_Err_Cannot_Open_Resource) {
+ fprintf(stderr, "could not open resource");
+ continue;
+ } else if (error) {
+ fprintf(stderr, "another sort of error");
+ continue;
+ }
+ if (faces.faces[faces.length] == NULL) {
+ fprintf(stderr, "face was empty");
+ continue;
+ }
+
+ result = FcPatternGet(patterns.patterns[i], FC_MATRIX, 0, &fc_matrix);
+ if (result == FcResultMatch) {
+ ft_matrix.xx = (FT_Fixed)(fc_matrix.u.m->xx * 0x10000L);
+ ft_matrix.xy = (FT_Fixed)(fc_matrix.u.m->xy * 0x10000L);
+ ft_matrix.yx = (FT_Fixed)(fc_matrix.u.m->yx * 0x10000L);
+ ft_matrix.yy = (FT_Fixed)(fc_matrix.u.m->yy * 0x10000L);
+
+ // apply the matrix
+ FT_Set_Transform(
+ faces.faces[faces.length],
+ &ft_matrix,
+ NULL);
+ }
+
+ result = FcPatternGet(patterns.patterns[i], FC_PIXEL_SIZE, 0, &fc_pixel_size);
+ if (result != FcResultMatch || fc_pixel_size.u.d == 0) {
+ fprintf(stderr, "font has no pixel size, using 12 by default");
+ fc_pixel_size.type = FcTypeInteger;
+ fc_pixel_size.u.d = 12;
+ }
+ //error = FT_Set_Pixel_Sizes(
+ // faces.faces[faces.length],
+ // 0, // width
+ // fc_pixel_size.u.d); // height
+
+ // pixel_size/ (dpi/72.0)
+ FT_Set_Char_Size(
+ faces.faces[faces.length], 0,
+ (fc_pixel_size.u.d/((double)dpi/72.0))*64,
+ dpi, dpi);
+ if (error != FT_Err_Ok) {
+ perror(NULL);
+ fprintf(stderr, "could not char size");
+ continue;
+ }
+
+ faces.length++;
+ }
+
+ faces.library = library;
+
+ return faces;
+}
+
+FcStrSet*
+xcbft_extract_fontsearch_list(char *string)
+{
+ FcStrSet *fontsearch = NULL;
+ FcChar8 *fontquery;
+ FcBool result = FcFalse;
+ char *r = strdup(string);
+ char *p_to_r = r;
+ char *token = NULL;
+
+ fontsearch = FcStrSetCreate();
+
+ token = strtok(r, ",");
+ while (token != NULL) {
+ fontquery = (FcChar8*)token;
+ result = FcStrSetAdd(fontsearch, fontquery);
+ if (result == FcFalse) {
+ fprintf(stderr,
+ "Couldn't add fontquery to fontsearch set");
+ }
+ token = strtok(NULL, ",");
+ }
+
+ free(p_to_r);
+
+ return fontsearch;
+}
+
+
+
+void
+xcbft_patterns_holder_destroy(struct xcbft_patterns_holder patterns)
+{
+ int i = 0;
+
+ for (; i < patterns.length; i++) {
+ FcPatternDestroy(patterns.patterns[i]);
+ }
+ free(patterns.patterns);
+ // FcFini(); // TODO: we can't leave that here, find a way for cleanup
+}
+
+void
+xcbft_face_holder_destroy(struct xcbft_face_holder faces)
+{
+ int i = 0;
+
+ for (; i < faces.length; i++) {
+ FT_Done_Face(faces.faces[i]);
+ }
+ if (faces.faces) {
+ free(faces.faces);
+ }
+ FT_Done_FreeType(faces.library);
+}
+
+FT_Vector
+xcbft_draw_text(
+ xcb_connection_t *c, // conn
+ xcb_drawable_t pmap, // win or pixmap
+ int16_t x, int16_t y, // x, y
+ struct utf_holder text, // text
+ xcb_render_color_t color,
+ struct xcbft_face_holder faces,
+ long dpi)
+{
+ xcb_void_cookie_t cookie;
+ uint32_t values[2];
+ xcb_generic_error_t *error;
+ xcb_render_picture_t picture;
+ xcb_render_pictforminfo_t *fmt;
+ const xcb_render_query_pict_formats_reply_t *fmt_rep =
+ xcb_render_util_query_formats(c);
+
+ fmt = xcb_render_util_find_standard_format(
+ fmt_rep,
+ XCB_PICT_STANDARD_RGB_24
+ );
+
+ // create the picture with its attribute and format
+ picture = xcb_generate_id(c);
+ values[0] = XCB_RENDER_POLY_MODE_IMPRECISE;
+ values[1] = XCB_RENDER_POLY_EDGE_SMOOTH;
+ cookie = xcb_render_create_picture_checked(c,
+ picture, // pid
+ pmap, // drawable from the user
+ fmt->id, // format
+ XCB_RENDER_CP_POLY_MODE|XCB_RENDER_CP_POLY_EDGE,
+ values); // make it smooth
+
+ error = xcb_request_check(c, cookie);
+ if (error) {
+ fprintf(stderr, "ERROR: %s : %d\n",
+ "could not create picture",
+ error->error_code);
+ }
+
+ // create a 1x1 pixel pen (on repeat mode) of a certain color
+ xcb_render_picture_t fg_pen = xcbft_create_pen(c, color);
+
+ // load all the glyphs in a glyphset
+ // TODO: maybe cache the xcb_render_glyphset_t
+ struct xcbft_glyphset_and_advance glyphset_advance =
+ xcbft_load_glyphset(c, faces, text, dpi);
+
+ // we now have a text stream - a bunch of glyphs basically
+ xcb_render_util_composite_text_stream_t *ts =
+ xcb_render_util_composite_text_stream(
+ glyphset_advance.glyphset,
+ text.length, 0);
+
+ // draw the text at a certain positions
+ xcb_render_util_glyphs_32(ts, x, y, text.length, text.str);
+
+ // finally render using the repeated pen color on the picture
+ // (which is related to the pixmap)
+ xcb_render_util_composite_text(
+ c, // connection
+ XCB_RENDER_PICT_OP_OVER, //op
+ fg_pen, // src
+ picture, // dst
+ 0, // fmt
+ 0, // src x
+ 0, // src y
+ ts); // txt stream
+
+ xcb_render_util_composite_text_free(ts);
+ xcb_render_free_picture(c, picture);
+ xcb_render_free_picture(c, fg_pen);
+ xcb_render_util_disconnect(c);
+
+ return glyphset_advance.advance;
+}
+
+xcb_render_picture_t
+xcbft_create_pen(xcb_connection_t *c, xcb_render_color_t color)
+{
+ xcb_render_pictforminfo_t *fmt;
+ const xcb_render_query_pict_formats_reply_t *fmt_rep =
+ xcb_render_util_query_formats(c);
+ // alpha can only be used with a picture containing a pixmap
+ fmt = xcb_render_util_find_standard_format(
+ fmt_rep,
+ XCB_PICT_STANDARD_ARGB_32
+ );
+
+ xcb_drawable_t root = xcb_setup_roots_iterator(
+ xcb_get_setup(c)
+ ).data->root;
+
+ xcb_pixmap_t pm = xcb_generate_id(c);
+ xcb_create_pixmap(c, 32, pm, root, 1, 1);
+
+ uint32_t values[1];
+ values[0] = XCB_RENDER_REPEAT_NORMAL;
+
+ xcb_render_picture_t picture = xcb_generate_id(c);
+
+ xcb_render_create_picture(c,
+ picture,
+ pm,
+ fmt->id,
+ XCB_RENDER_CP_REPEAT,
+ values);
+
+ xcb_rectangle_t rect = {
+ .x = 0,
+ .y = 0,
+ .width = 1,
+ .height = 1
+ };
+
+ xcb_render_fill_rectangles(c,
+ XCB_RENDER_PICT_OP_OVER,
+ picture,
+ color, 1, &rect);
+
+ xcb_free_pixmap(c, pm);
+ return picture;
+}
+
+struct xcbft_glyphset_and_advance
+xcbft_load_glyphset(
+ xcb_connection_t *c,
+ struct xcbft_face_holder faces,
+ struct utf_holder text,
+ long dpi)
+{
+ unsigned int i, j;
+ int glyph_index;
+ xcb_render_glyphset_t gs;
+ xcb_render_pictforminfo_t *fmt_a8;
+ struct xcbft_face_holder faces_for_unsupported;
+ const xcb_render_query_pict_formats_reply_t *fmt_rep =
+ xcb_render_util_query_formats(c);
+ FT_Vector total_advance, glyph_advance;
+ struct xcbft_glyphset_and_advance glyphset_advance;
+
+ total_advance.x = total_advance.y = 0;
+ glyph_index = 0;
+ faces_for_unsupported.length = 0;
+ // create a glyphset with a specific format
+ fmt_a8 = xcb_render_util_find_standard_format(
+ fmt_rep,
+ XCB_PICT_STANDARD_A_8
+ );
+ gs = xcb_generate_id(c);
+ xcb_render_create_glyph_set(c, gs, fmt_a8->id);
+
+ for (i = 0; i < text.length; i++) {
+ for (j = 0; j < faces.length; j++) {
+ glyph_index = FT_Get_Char_Index(
+ faces.faces[j],
+ text.str[i]);
+ if (glyph_index != 0) break;
+ }
+ // here use face at index j
+ if (glyph_index != 0) {
+ glyph_advance = xcbft_load_glyph(c, gs, faces.faces[j], text.str[i]);
+ total_advance.x += glyph_advance.x;
+ total_advance.y += glyph_advance.y;
+ } else {
+ // fallback
+ // TODO pass at least some of the query (font size, italic, etc..)
+
+ glyph_index = 0;
+ // check if we already loaded that face as fallback
+ if (faces_for_unsupported.length > 0) {
+ glyph_index = FT_Get_Char_Index(
+ faces_for_unsupported.faces[0],
+ text.str[i]);
+ }
+ if (glyph_index == 0) {
+ if (faces_for_unsupported.length > 0) {
+ xcbft_face_holder_destroy(faces_for_unsupported);
+ }
+ faces_for_unsupported = xcbft_query_by_char_support(
+ text.str[i],
+ NULL, dpi);
+ }
+ if (faces_for_unsupported.length == 0) {
+ fprintf(stderr,
+ "No faces found supporting character: %02x\n",
+ text.str[i]);
+ // draw a block using whatever font
+ glyph_advance = xcbft_load_glyph(c, gs, faces.faces[0], text.str[i]);
+ total_advance.x += glyph_advance.x;
+ total_advance.y += glyph_advance.y;
+ } else {
+ FT_Set_Char_Size(
+ faces_for_unsupported.faces[0],
+ 0, (faces.faces[0]->size->metrics.x_ppem/((double)dpi/72.0))*64,
+ dpi, dpi);
+
+ glyph_advance = xcbft_load_glyph(c, gs,
+ faces_for_unsupported.faces[0],
+ text.str[i]);
+ total_advance.x += glyph_advance.x;
+ total_advance.y += glyph_advance.y;
+ }
+ }
+ }
+ if (faces_for_unsupported.length > 0) {
+ xcbft_face_holder_destroy(faces_for_unsupported);
+ }
+
+ glyphset_advance.advance = total_advance;
+ glyphset_advance.glyphset = gs;
+ return glyphset_advance;
+}
+
+FT_Vector
+xcbft_load_glyph(
+ xcb_connection_t *c, xcb_render_glyphset_t gs, FT_Face face, int charcode)
+{
+ uint32_t gid;
+ int glyph_index;
+ FT_Vector glyph_advance;
+ xcb_render_glyphinfo_t ginfo;
+ FT_Bitmap *bitmap;
+
+ FT_Select_Charmap(face, ft_encoding_unicode);
+ glyph_index = FT_Get_Char_Index(face, charcode);
+
+ FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT);
+
+ bitmap = &face->glyph->bitmap;
+
+ ginfo.x = -face->glyph->bitmap_left;
+ ginfo.y = face->glyph->bitmap_top;
+ ginfo.width = bitmap->width;
+ ginfo.height = bitmap->rows;
+ glyph_advance.x = face->glyph->advance.x/64;
+ glyph_advance.y = face->glyph->advance.y/64;
+ ginfo.x_off = glyph_advance.x;
+ ginfo.y_off = glyph_advance.y;
+
+ // keep track of the max horiBearingY (yMax) and yMin
+ // 26.6 fractional pixel format
+ // yMax = face->glyph->metrics.horiBearingY/64; (yMax);
+ // yMin = -(face->glyph->metrics.height -
+ // face->glyph->metrics.horiBearingY)/64;
+
+ gid = charcode;
+
+ int stride = (ginfo.width+3)&~3;
+ uint8_t *tmpbitmap = calloc(sizeof(uint8_t),stride*ginfo.height);
+ int y;
+
+ for (y = 0; y < ginfo.height; y++)
+ memcpy(tmpbitmap+y*stride, bitmap->buffer+y*ginfo.width, ginfo.width);
+
+ xcb_render_add_glyphs_checked(c,
+ gs, 1, &gid, &ginfo, stride*ginfo.height, tmpbitmap);
+
+ free(tmpbitmap);
+
+ xcb_flush(c);
+ return glyph_advance;
+}
+
+long
+xcbft_get_dpi(xcb_connection_t *c)
+{
+ int i;
+ long dpi;
+ long xres;
+ xcb_xrm_database_t *xrm_db;
+ xcb_screen_iterator_t iter;
+
+ xrm_db = xcb_xrm_database_from_default(c);
+ if (xrm_db != NULL) {
+ i = xcb_xrm_resource_get_long(xrm_db, "Xft.dpi", NULL, &dpi);
+ xcb_xrm_database_free(xrm_db);
+ if (i < 0) {
+ fprintf(stderr,
+ "Could not fetch value of Xft.dpi from Xresources falling back to highest dpi found\n");
+ } else {
+ return dpi;
+ }
+ } else {
+ fprintf(stderr,
+ "Could not open Xresources database falling back to highest dpi found\n");
+ }
+
+ iter = xcb_setup_roots_iterator(xcb_get_setup(c));
+ dpi = 0;
+ for (; iter.rem; xcb_screen_next(&iter)) {
+ /*
+ * Inspired by xdpyinfo
+ *
+ * there are 2.54 centimeters to an inch; so
+ * there are 25.4 millimeters.
+ *
+ * dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
+ * = N pixels / (M inch / 25.4)
+ * = N * 25.4 pixels / M inch
+ */
+ if (iter.data != NULL) {
+ xres = ((((double) iter.data->width_in_pixels) * 25.4) /
+ ((double) iter.data->width_in_millimeters));
+
+ // ignore y resolution for now
+ //yres = ((((double) iter.data->height_in_pixels) * 25.4) /
+ // ((double) iter.data->height_in_millimeters));
+ if (xres > dpi) {
+ dpi = xres;
+ }
+ }
+ }
+
+ if (dpi == 0) {
+ // if everything fails use 96
+ fprintf(stderr,
+ "Could get highest dpi, using 96 as default\n");
+
+ dpi = 96;
+ }
+
+ return dpi;
+}
+
+#endif // _XCBFT
(DIR) diff --git a/xmenu.c b/xmenu.c
t@@ -10,6 +10,7 @@
#include "arg.h"
#include "config.h"
+#include "xcbft.h"
int verbose = 0;
xcb_connection_t *dpy;
t@@ -72,7 +73,12 @@ main(int argc, char *argv[])
int mask, val[4];
char *argv0;
- FT_Face face;
+ long dpi;
+ xcb_render_color_t color = { 0xffff, 0xffff, 0xffff, 0xffff };
+ FcStrSet *fontsearch;
+ struct xcbft_patterns_holder fontpattern;
+ struct xcbft_face_holder faces;
+ struct utf_holder text;
ARGBEGIN {
case 'h':
t@@ -88,13 +94,13 @@ main(int argc, char *argv[])
break;
} ARGEND;
+ if (argc < 1)
+ return 0;
+
dpy = xcb_connect(NULL, NULL);
if (xcb_connection_has_error(dpy))
return -1;
- if (setfont(font, &face) < 0)
- return -1;
-
screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
if (!screen)
return -1;
t@@ -114,6 +120,19 @@ main(int argc, char *argv[])
screen->root_visual, mask, val);
xcb_map_window(dpy, wid);
+
+ xcbft_init();
+ dpi = xcbft_get_dpi(dpy);
+ fontsearch = xcbft_extract_fontsearch_list(font);
+ fontpattern = xcbft_query_fontsearch_all(fontsearch);
+ faces = xcbft_load_faces(fontpattern, dpi);
+
+ FcStrSetDestroy(fontsearch);
+ xcbft_patterns_holder_destroy(fontpattern);
+
+ text = char_to_uint32(argv[0]);
+ xcbft_draw_text(dpy, wid, 16, 28, text, color, faces, dpi);
+
xcb_flush(dpy);
sleep(50);