tfont.c - xmenu - drop-down menu for X11
(HTM) git clone git://git.z3bra.org/xmenu.git
(DIR) Log
(DIR) Files
(DIR) Refs
---
tfont.c (6255B)
---
1 #include "font.h"
2
3 /* internal variable holding last loaded font */
4 static FT_Face face;
5 static FT_Library ft;
6
7 double
8 xft_forcedpi(xcb_connection_t *dpy)
9 {
10 xcb_xrm_database_t *db;
11 char *r;
12
13 /* try to set DPI from "Xft.dpi" resource */
14 db = xcb_xrm_database_from_default(dpy);
15 if (db) {
16 xcb_xrm_resource_get_string(db, "Xft.dpi", NULL, &r);
17 if (r)
18 return strtod(r, NULL);
19 }
20
21 return -1;
22 }
23
24 double
25 xft_getxdpi(xcb_screen_t *screen)
26 {
27 /* return actual screen DPI */
28 return 25.4 * screen->width_in_pixels/screen->width_in_millimeters;
29 }
30
31 double
32 xft_getydpi(xcb_screen_t *screen)
33 {
34 /* return actual screen DPI */
35 return 25.4 * screen->height_in_pixels/screen->height_in_millimeters;
36 }
37
38
39 int
40 xft_loadfont(xcb_connection_t *dpy, xcb_screen_t *screen, char *query)
41 {
42 double dpi[2];
43 FcResult result;
44 FcPattern *pattern, *match;
45 FcValue index, file, pxsz;
46
47 if (FcInit() != FcTrue)
48 return -1;
49
50 /* match font pattern */
51 pattern = FcNameParse((FcChar8 *)query);
52 if (!pattern)
53 return -1;
54
55 FcDefaultSubstitute(pattern);
56 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
57 match = FcFontMatch(NULL, pattern, &result);
58 if (result != FcResultMatch)
59 return -1;
60
61 FcPatternDestroy(pattern);
62
63 /* get file associated with the font matched */
64 if (FcPatternGet(match, FC_FILE, 0, &file) != FcResultMatch) {
65 fprintf(stderr, "No font matched\n");
66 return -1;
67 }
68
69 /* use first font if no index matched */
70 if (FcPatternGet(match, FC_INDEX, 0, &index) != FcResultMatch) {
71 index.type = FcTypeInteger;
72 index.u.i = 0;
73 }
74
75 /* set pixel size to 12 by default */
76 if (FcPatternGet(match, FC_PIXEL_SIZE, 0, &pxsz) != FcResultMatch) {
77 pxsz.type = FcTypeInteger;
78 pxsz.u.i = 12;
79 }
80
81 FT_Init_FreeType(&ft);
82
83 if (FT_New_Face(ft, (const char *)file.u.s, index.u.i, &face))
84 return -1;
85
86 dpi[0] = dpi[1] = xft_forcedpi(dpy);
87 if (dpi[0] < 0) {
88 dpi[0] = xft_getxdpi(screen);
89 dpi[1] = xft_getydpi(screen);
90 }
91 FT_Set_Char_Size(face, 0, 64 * pxsz.u.d, dpi[0], dpi[1]);
92
93 FcPatternDestroy(match);
94
95 return 0;
96 }
97
98 static xcb_render_picture_t
99 xr_create_canvas(xcb_connection_t *dpy, xcb_drawable_t xid)
100 {
101 int val[2];
102 xcb_render_picture_t pic;
103 xcb_render_pictforminfo_t *fmt;
104 const xcb_render_query_pict_formats_reply_t *r;
105
106 /* create a canvas to draw text on */
107 pic = xcb_generate_id(dpy);
108
109 val[0] = XCB_RENDER_POLY_MODE_IMPRECISE;
110 val[1] = XCB_RENDER_POLY_EDGE_SMOOTH;
111
112 r = xcb_render_util_query_formats(dpy);
113 fmt = xcb_render_util_find_standard_format(r,
114 XCB_PICT_STANDARD_RGB_24);
115
116 xcb_render_create_picture(dpy, pic, xid, fmt->id,
117 XCB_RENDER_CP_POLY_MODE | XCB_RENDER_CP_POLY_EDGE, val);
118
119 return pic;
120 }
121
122 static xcb_render_picture_t
123 xr_create_pen(xcb_connection_t *dpy, xcb_render_color_t color)
124 {
125 int val[1];
126 xcb_screen_t *screen;
127 xcb_pixmap_t px;
128 xcb_rectangle_t rect = { 0, 0, 1, 1 };
129 xcb_render_picture_t pen;
130 xcb_render_pictforminfo_t *fmt;
131 const xcb_render_query_pict_formats_reply_t *r;
132
133 screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
134
135 px = xcb_generate_id(dpy);
136 pen = xcb_generate_id(dpy);
137
138 r = xcb_render_util_query_formats(dpy);
139 fmt = xcb_render_util_find_standard_format(r, XCB_PICT_STANDARD_ARGB_32);
140
141 val[0] = XCB_RENDER_REPEAT_NORMAL;
142 xcb_create_pixmap(dpy, 32, px, screen->root, 1, 1);
143 xcb_render_create_picture(dpy, pen, px, fmt->id, XCB_RENDER_CP_REPEAT, val);
144
145 xcb_render_fill_rectangles(dpy, XCB_RENDER_PICT_OP_OVER, pen, color, 1, &rect);
146 xcb_free_pixmap(dpy, px);
147
148 return pen;
149 }
150
151 int
152 xr_loadglyph(char c, xcb_render_glyphinfo_t *glyph)
153 {
154 FT_Select_Charmap(face, FT_ENCODING_UNICODE);
155 if (FT_Load_Char(face, c, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT))
156 return -1;
157
158 glyph->x = -face->glyph->bitmap_left;
159 glyph->y = face->glyph->bitmap_top;
160 glyph->width = face->glyph->bitmap.width;
161 glyph->height = face->glyph->bitmap.rows;
162 glyph->x_off = face->glyph->advance.x / 64;
163 glyph->y_off = face->glyph->advance.y / 64;
164
165 return 0;
166 }
167
168 size_t
169 xft_txtw(char *text)
170 {
171 size_t i, w;
172 xcb_render_glyphinfo_t g;
173
174 for (i = w = 0; i < strlen(text); i++) {
175 if (xr_loadglyph(text[i], &g) < 0)
176 return -1;
177
178 w += g.x_off;
179 }
180
181 return w;
182 }
183
184 size_t
185 xft_txth(char *text)
186 {
187 size_t i, h;
188 xcb_render_glyphinfo_t g;
189
190 for (i = h = 0; i < strlen(text); i++) {
191 if (xr_loadglyph(text[i], &g) < 0)
192 return -1;
193
194 h = g.height > h ? g.height : h;
195 }
196
197 return h;
198 }
199
200 int
201 xft_drawtext(xcb_connection_t *dpy, xcb_drawable_t xid, int x, int y, int color, char *text)
202 {
203 xcb_render_color_t argb;
204 xcb_render_glyphset_t gs;
205 xcb_render_picture_t pic, pen;
206 xcb_render_pictforminfo_t *fmt;
207 const xcb_render_query_pict_formats_reply_t *r;
208 xcb_render_util_composite_text_stream_t *ts;
209
210 argb.red = 0x00ff | (((color >> 16) & 0xff) << 8);
211 argb.green = 0x00ff | (((color >> 8 ) & 0xff) << 8);
212 argb.blue = 0x00ff | (((color >> 0 ) & 0xff) << 8);
213 argb.alpha = 0xffff;
214
215 pic = xr_create_canvas(dpy, xid);
216 pen = xr_create_pen(dpy, argb);
217
218 /* preload all our glyphs */
219 r = xcb_render_util_query_formats(dpy);
220 fmt = xcb_render_util_find_standard_format(r, XCB_PICT_STANDARD_A_8);
221 gs = xcb_generate_id(dpy);
222 xcb_render_create_glyph_set(dpy, gs, fmt->id);
223
224 for (size_t i = 0; i < strlen(text); i++) {
225 uint8_t *bmp;
226 uint32_t gid;
227 xcb_render_glyphinfo_t g;
228 xr_loadglyph(text[i], &g);
229
230 int stride = (g.width + 3) & ~3;
231 bmp = calloc(sizeof(*bmp), stride * g.height);
232
233 for (int y = 0; y < g.height; y++)
234 memcpy(bmp+y*stride, face->glyph->bitmap.buffer+y*g.width, g.width);
235
236 gid = text[i];
237 xcb_render_add_glyphs(dpy, gs, 1, &gid, &g, stride * g.height, bmp);
238 free(bmp);
239 }
240
241 ts = xcb_render_util_composite_text_stream(gs, strlen(text), 0);
242
243 /*
244 * X coordinates are expressed from top-left downwards, while
245 * font is rendered from bottom-left upwards, so we must add
246 * the text height to the Y coordinates to be more consistent with
247 * what X expects
248 */
249 xcb_render_util_glyphs_8(ts, x, y + xft_txth(text), strlen(text), (uint8_t *)text);
250 xcb_render_util_composite_text(dpy, XCB_RENDER_PICT_OP_OVER, pen, pic, 0, 0, 0, ts);
251
252 xcb_render_util_composite_text_free(ts);
253 xcb_render_free_picture(dpy, pen);
254 xcb_render_free_picture(dpy, pic);
255 xcb_render_util_disconnect(dpy);
256
257 return 0;
258 }
259
260 void
261 xft_unload(void)
262 {
263 FcFini();
264 FT_Done_FreeType(ft);
265 }