Use xmalloc() instead of malloc().
[gwm.git] / decorate-render.c
blobe8fb9d6732906e44b01406059d025994c7f4e2b2
1 /*
2 * decorate-render.c
4 * Part of gwm, the Gratuitous Window Manager,
5 * by Gary Wong, <gtw@gnu.org>.
7 * Copyright (C) 2009 Gary Wong
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of version 3 of the GNU General Public License as
11 * published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * $Id$
24 #include <config.h>
26 #include <assert.h>
27 #include <fontconfig/fontconfig.h>
28 #include <ft2build.h>
29 #include FT_FREETYPE_H
30 #include FT_CACHE_H
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <xcb/render.h>
35 #include <xcb/xcb.h>
37 #include "gwm.h"
39 #include "decorate-render.h"
41 /* FIXME make this configurable */
42 #define FONT_SIZE 12
43 #define STRING2(x) #x
44 #define STRING(x) STRING2(x)
45 #define FONT_SIZE_STRING STRING( FONT_SIZE )
46 #define FONT_NAME "sans:pixelsize=" FONT_SIZE_STRING ":bold"
48 static FT_Library ftl;
49 static FT_Face ftf; /* FIXME allow multiple faces per fontconfig pattern */
51 static const uint16_t decoration_cols[ NUM_COLS ][ 3 ] = {
52 { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */
53 { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */
54 { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */
55 { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */
56 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */
57 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */
58 { 0x3333, 0x3333, 0x3333 }, /* COL_TITLE_INACTIVE */
59 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */
60 { 0x0000, 0x0000, 0x0000 } /* COL_FEEDBACK_FORE */
63 #define METRIC_CACHE_SIZE_BITS 14
64 #define METRIC_CACHE_SIZE ( 1 << METRIC_CACHE_SIZE_BITS )
65 #define METRIC_CACHE_ASSOC_BITS 2
66 #define METRIC_CACHE_ASSOC ( 1 << METRIC_CACHE_ASSOC_BITS )
68 #define GLYPH_CACHE_SIZE_BITS 11
69 #define GLYPH_CACHE_SIZE ( 1 << GLYPH_CACHE_SIZE_BITS )
70 #define GLYPH_CACHE_ASSOC_BITS 3
71 #define GLYPH_CACHE_ASSOC ( 1 << GLYPH_CACHE_ASSOC_BITS )
73 #if GLYPH_CACHE_SIZE_BITS + GLYPH_CACHE_ASSOC_BITS > 16
74 #error "Glyph cache is too big for 16-bit indices."
75 #endif
77 static struct metric_cache_line {
78 uint32_t c; /* character code */
79 unsigned int time;
80 int x_off, y_off;
81 } metric_cache[ METRIC_CACHE_SIZE ][ METRIC_CACHE_ASSOC ];
83 static struct glyph_cache_line {
84 uint32_t c; /* character code */
85 unsigned int time;
86 } glyph_cache[ GLYPH_CACHE_SIZE ][ GLYPH_CACHE_ASSOC ];
88 static unsigned int current_time;
90 static void query_metrics( uint32_t c, int *x_off, int *y_off ) {
92 int row = c & ( METRIC_CACHE_SIZE - 1 );
93 int i, max;
95 for( i = 0; i < METRIC_CACHE_ASSOC; i++ )
96 if( metric_cache[ row ][ i ].c == c ) {
97 /* Cache hit. */
98 metric_cache[ row ][ i ].time = current_time;
99 *x_off = metric_cache[ row ][ i ].x_off;
100 *y_off = metric_cache[ row ][ i ].y_off;
101 return;
104 /* Cache miss. */
105 if( FT_Load_Char( ftf, c, FT_LOAD_DEFAULT ) ) {
106 /* Couldn't load metrics. Don't bother evicting anything. */
107 *x_off = *y_off = 0;
108 return;
111 /* Search for a line to evict. */
112 for( i = 1, max = 0; i < METRIC_CACHE_ASSOC; i++ )
113 if( current_time - metric_cache[ row ][ i ].time >
114 current_time - metric_cache[ row ][ max ].time )
115 max = i;
117 metric_cache[ row ][ max ].c = c;
118 metric_cache[ row ][ max ].time = current_time;
119 *x_off = metric_cache[ row ][ max ].x_off =
120 ( ftf->glyph->advance.x + 0x20 ) >> 6;
121 *y_off = metric_cache[ row ][ max ].y_off =
122 ( ftf->glyph->advance.y + 0x20 ) >> 6;
125 static int query_glyph( uint32_t c ) {
127 int row = c & ( GLYPH_CACHE_SIZE - 1 );
128 int i;
130 for( i = 0; i < GLYPH_CACHE_ASSOC; i++ )
131 if( glyph_cache[ row ][ i ].c == c ) {
132 /* Cache hit. */
133 glyph_cache[ row ][ i ].time = current_time;
134 return ( row << GLYPH_CACHE_ASSOC_BITS ) | i;
137 return -1;
140 static int replace_glyph( uint32_t c ) {
142 int row = c & ( GLYPH_CACHE_SIZE - 1 );
143 int i, max;
145 /* Search for a line to evict. */
146 for( i = 1, max = 0; i < METRIC_CACHE_ASSOC; i++ ) {
147 assert( glyph_cache[ row ][ i ].c != c );
148 if( current_time - glyph_cache[ row ][ i ].time >
149 current_time - glyph_cache[ row ][ max ].time )
150 max = i;
153 if( glyph_cache[ row ][ max ].time == current_time )
154 /* Cache line set is full, and nothing is old enough to evict. */
155 return -1;
157 glyph_cache[ row ][ max ].c = c;
158 glyph_cache[ row ][ max ].time = current_time;
160 return ( row << GLYPH_CACHE_ASSOC_BITS ) | max;
163 static xcb_render_glyphset_t glyphset;
164 static struct picture {
165 xcb_render_picture_t pic;
166 enum decoration_col col;
167 } *pictures; /* indexed by screen */
169 static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen,
170 int col, int x, int y,
171 const char *text,
172 const xcb_rectangle_t *clip ) {
174 xcb_render_picture_t src, dest;
175 xcb_rectangle_t rect;
176 xcb_render_color_t rc;
177 int i, len;
178 const unsigned char *p;
179 uint32_t *buf;
181 if( !text || !*text ) {
182 xcb_void_cookie_t r = { 0 };
184 return r;
187 len = 0;
188 p = (const unsigned char *) text;
189 for(;;)
190 if( !p[ 0 ] )
191 /* End of string. */
192 break;
193 else if( !( p[ 0 ] & 0x80 ) ) {
194 /* Legal single byte character. */
195 len++;
196 p++;
197 } else if( *p >= 0xC2 && *p <= 0xDF &&
198 p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF ) {
199 /* Legal two byte character. */
200 len++;
201 p += 2;
202 } else if( *p >= 0xE0 && *p <= 0xEF &&
203 p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF &&
204 p[ 2 ] >= 0x80 && p[ 2 ] <= 0xBF &&
205 ( *p > 0xE0 || p[ 1 ] > 0x80 ) ) {
206 /* Legal three byte character. */
207 len++;
208 p += 3;
209 } else if( *p >= 0xF0 && *p <= 0xF4 &&
210 p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF &&
211 p[ 2 ] >= 0x80 && p[ 2 ] <= 0xBF &&
212 p[ 3 ] >= 0x80 && p[ 3 ] <= 0xBF &&
213 ( *p > 0xF0 || p[ 1 ] > 0x80 ) ) {
214 /* Legal four byte character. */
215 len++;
216 p += 4;
217 } else
218 /* Illegal character: ignore this byte and continue. */
219 p++;
221 if( !len ) {
222 xcb_void_cookie_t r = { 0 };
224 return r;
227 buf = alloca( len * sizeof *buf );
229 for( p = (const unsigned char *) text, i = 0; i < len; i++ ) {
230 retry:
231 assert( *p );
233 if( !( p[ 0 ] & 0x80 ) )
234 /* One byte character. */
235 buf[ i ] = *p++;
236 else if( *p >= 0xC2 && *p <= 0xDF &&
237 p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF ) {
238 /* Two byte character. */
239 buf[ i ] = ( ( p[ 0 ] & 0x1F ) << 6 ) | ( p[ 1 ] & 0x3F );
240 p += 2;
241 } else if( *p >= 0xE0 && *p <= 0xEF &&
242 p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF &&
243 p[ 2 ] >= 0x80 && p[ 2 ] <= 0xBF &&
244 ( *p > 0xE0 || p[ 1 ] > 0x80 ) ) {
245 /* Three byte character. */
246 buf[ i ] = ( ( p[ 0 ] & 0x0F ) << 12 ) |
247 ( ( p[ 1 ] & 0x3F ) << 6 ) | ( p[ 2 ] & 0x3F );
248 p += 3;
249 } else if( *p >= 0xF0 && *p <= 0xF4 &&
250 p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF &&
251 p[ 2 ] >= 0x80 && p[ 2 ] <= 0xBF &&
252 p[ 3 ] >= 0x80 && p[ 3 ] <= 0xBF &&
253 ( *p > 0xF0 || p[ 1 ] > 0x80 ) ) {
254 /* Four byte character. */
255 buf[ i ] = ( ( p[ 0 ] & 0x07 ) << 18 ) |
256 ( ( p[ 1 ] & 0x3F ) << 12 ) | ( ( p[ 2 ] & 0x3F ) << 6 ) |
257 ( p[ 3 ] & 0x3F );
258 p += 4;
259 } else {
260 p++;
261 goto retry;
265 src = pictures[ screen ].pic;
267 dest = xcb_generate_id( c );
268 xcb_render_create_picture( c, dest, drawable,
269 gwm_screens[ screen ].root_pictformat, 0, NULL );
271 if( clip )
272 xcb_render_set_picture_clip_rectangles( c, dest, 0, 0, 1, clip );
274 if( pictures[ screen ].col != col ) {
275 rc.red = decoration_cols[ col ][ 0 ];
276 rc.green = decoration_cols[ col ][ 1 ];
277 rc.blue = decoration_cols[ col ][ 2 ];
278 rc.alpha = 0xFFFF;
279 rect.x = 0;
280 rect.y = 0;
281 rect.width = 1;
282 rect.height = 1;
283 xcb_render_fill_rectangles( c, XCB_RENDER_PICT_OP_SRC, src, rc, 1,
284 &rect );
286 pictures[ screen ].col = col;
289 i = 0;
290 while( i < len ) {
291 uint32_t glyphids[ 0x80 ];
292 xcb_render_glyphinfo_t glyphs[ 0x80 ];
293 int num_glyphs; /* number of glyphs in AddGlyphs request */
294 /* Buffer for "data" parameter of AddGlyphs request. We want
295 this to be big enough to hold at least one glyph, but no
296 bigger than the biggest request the server will accept (once
297 the other parameters are included). All servers must
298 accept requests of 16,384 bytes or smaller (see X Window
299 System Protocol, section 8), so this is safe. */
300 uint8_t data[ 0x2000 ], *p;
301 /* Buffer for "glyphcmds" parameter of CompositeGlyphs16
302 request. We always send exactly one command, which has an
303 8-byte header and at most a 254 glyph string. */
304 uint8_t param[ 8 + ( 0xFE << 1 ) ], *out;
305 int num_chars; /* number of characters for CompositeGlyphs16 */
307 current_time++;
309 p = data;
310 num_glyphs = 0;
312 memset( param, 0, 4 );
313 *( (uint16_t *) ( param + 4 ) ) = x;
314 *( (uint16_t *) ( param + 6 ) ) = y;
315 out = param + 8;
316 num_chars = 0;
318 while( i < len && num_chars < 0xFF ) {
319 int dx, dy, index;
321 if( ( index = query_glyph( buf[ i ] ) ) < 0 ) {
322 /* Cache miss. */
323 int bitmap_size;
324 FT_GlyphSlot slot;
326 if( ( index = replace_glyph( buf[ i ] ) ) < 0 )
327 /* Cache set full: spill the partial string to make
328 room for later glyphs. */
329 break;
331 if( FT_Load_Char( ftf, buf[ i ], FT_LOAD_RENDER ) ||
332 ( bitmap_size = ( ( ftf->glyph->bitmap.width + 3 ) & ~3 ) *
333 ftf->glyph->bitmap.rows ) > sizeof data ) {
334 /* We couldn't load the character, or it was so
335 huge that it won't fit in an empty AddGlyph
336 data buffer. We'll have to send the server
337 an empty glyph and carry on. */
338 slot = NULL;
339 bitmap_size = 0;
340 } else
341 slot = ftf->glyph;
343 if( ( p - data ) + bitmap_size > sizeof data ||
344 num_glyphs == sizeof glyphids / sizeof *glyphids ) {
345 /* Can't fit this glyph into the existing request:
346 transmit what we have... */
347 xcb_render_add_glyphs( c, glyphset, num_glyphs, glyphids,
348 glyphs, p - data, data );
350 /* ...and start building another AddGlyph request. */
351 p = data;
352 num_glyphs = 0;
355 glyphids[ num_glyphs ] = index;
357 if( slot ) {
358 int y, width_pad;
360 glyphs[ num_glyphs ].width = slot->bitmap.width;
361 glyphs[ num_glyphs ].height = slot->bitmap.rows;
362 glyphs[ num_glyphs ].x = -slot->bitmap_left;
363 glyphs[ num_glyphs ].y = slot->bitmap_top;
364 glyphs[ num_glyphs ].x_off =
365 ( slot->advance.x + 0x20 ) >> 6;
366 glyphs[ num_glyphs ].y_off =
367 ( slot->advance.y + 0x20 ) >> 6;
369 width_pad = ( slot->bitmap.width + 3 ) & ~3;
371 for( y = 0; y < slot->bitmap.rows; y++ ) {
372 memcpy( p, slot->bitmap.buffer + y *
373 slot->bitmap.width, slot->bitmap.width );
374 p += width_pad;
376 } else {
377 glyphs[ num_glyphs ].width = 0;
378 glyphs[ num_glyphs ].height = 0;
379 glyphs[ num_glyphs ].x = 0;
380 glyphs[ num_glyphs ].y = 0;
381 glyphs[ num_glyphs ].x_off = 0;
382 glyphs[ num_glyphs ].y_off = 0;
385 num_glyphs++;
388 *( (uint16_t *) out ) = index;
389 out += 2;
391 query_metrics( buf[ i ], &dx, &dy );
392 x += dx;
393 y += dy;
395 num_chars++;
396 i++;
399 if( num_glyphs ) {
400 xcb_render_add_glyphs( c, glyphset, num_glyphs, glyphids, glyphs,
401 p - data, data );
403 p = data;
404 num_glyphs = 0;
407 param[ 0 ] = num_chars;
409 xcb_render_composite_glyphs_16( c, XCB_RENDER_PICT_OP_OVER, src, dest,
410 XCB_NONE, glyphset, 0, 0, out - param,
411 param );
414 return xcb_render_free_picture( c, dest );
417 extern void render_update_window( struct gwm_window *window ) {
419 if( !window->cleared )
420 xcb_clear_area( c, FALSE, window->w, window->update.x, window->update.y,
421 window->update.width, window->update.height );
423 if( window->type == WINDOW_FRAME ) {
424 char *name = window->u.frame.child->u.managed.name;
426 render_text( window->w, window->screen, window == focus_frame ?
427 COL_TITLE_ACTIVE : COL_TITLE_INACTIVE,
428 FRAME_BUTTON_SIZE + FRAME_BORDER_WIDTH * 3,
429 FONT_SIZE, name ? name : "(Untitled)",
430 &window->update );
431 } else if( window->type == WINDOW_FEEDBACK ) {
432 char *p, text[ 32 ];
433 int width;
435 sprintf( text, "%dx%d", window->u.feedback.fb_width,
436 window->u.feedback.fb_height );
438 for( width = 0, p = text; *p; p++ ) {
439 int dx, dy;
441 query_metrics( *p, &dx, &dy );
442 width += dx;
445 render_text( window->w, window->screen, COL_FEEDBACK_FORE,
446 ( FEEDBACK_WIDTH - width ) >> 1, 16, text,
447 &window->update );
451 static uint16_t luma( int col ) {
453 return ( decoration_cols[ col ][ 0 ] * 0x4C8BU +
454 decoration_cols[ col ][ 1 ] * 0x9646U +
455 decoration_cols[ col ][ 2 ] * 0x1D2FU ) >> 16;
458 static void handle_alloc_color( unsigned int sequence, void *reply,
459 xcb_generic_error_t *error,
460 union callback_param p ) {
462 xcb_alloc_color_reply_t *r = reply;
463 int screen = p.l >> 16, col = p.l & 0xFFFF;
465 if( error ) {
466 if( error->error_code != XCB_ALLOC )
467 show_error( error );
469 /* No standard RGB map available, and we couldn't allocate a shared
470 colour. Fall back to black or white. */
471 gwm_screens[ screen ].pixels[ col ] = luma( col ) >= 0x8000 ?
472 screens[ screen ]->white_pixel : screens[ screen ]->black_pixel;
474 free( error );
477 if( reply ) {
478 gwm_screens[ screen ].pixels[ col ] = r->pixel;
480 free( reply );
484 static void handle_get_default_map( unsigned int sequence, void *reply,
485 xcb_generic_error_t *error,
486 union callback_param p ) {
488 int col, got_cols = FALSE;
490 if( error ) {
491 show_error( error );
493 free( error );
496 if( reply ) {
497 xcb_get_property_reply_t *prop = reply;
498 struct std_cmap {
499 /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
500 xcb_colormap_t colormap;
501 uint32_t red_max, red_mult, green_max, green_mult, blue_max,
502 blue_mult, base_pixel;
503 xcb_visualid_t visual_id;
504 uint32_t kill_id;
505 } *std_cmaps = xcb_get_property_value( prop );
506 int num_cmaps = ( xcb_get_property_value_length( prop ) << 2 ) /
507 sizeof *std_cmaps;
508 int i;
510 if( prop->format == 32 )
511 for( i = 0; i < num_cmaps; i++ )
512 if( std_cmaps[ i ].visual_id ==
513 gwm_screens[ p.l ].root_visual->visual_id ) {
515 for( col = 0; col < NUM_COLS; col++ ) {
516 int r0, g0, b0, r, g, b;
518 if( gwm_screens[ p.l ].root_visual->_class ==
519 XCB_VISUAL_CLASS_STATIC_GRAY ||
520 gwm_screens[ p.l ].root_visual->_class ==
521 XCB_VISUAL_CLASS_GRAY_SCALE ) {
522 r0 = luma( col );
523 g0 = b0 = 0;
524 } else {
525 r0 = decoration_cols[ col ][ 0 ];
526 g0 = decoration_cols[ col ][ 1 ];
527 b0 = decoration_cols[ col ][ 2 ];
530 r = ( r0 * std_cmaps[ i ].red_max + 0x8000 ) >> 16;
531 g = ( g0 * std_cmaps[ i ].green_max + 0x8000 ) >> 16;
532 b = ( b0 * std_cmaps[ i ].blue_max + 0x8000 ) >> 16;
534 gwm_screens[ p.l ].pixels[ col ] =
535 std_cmaps[ i ].base_pixel +
536 r * std_cmaps[ i ].red_mult +
537 g * std_cmaps[ i ].green_mult +
538 b * std_cmaps[ i ].blue_mult;
541 got_cols = TRUE;
542 break;
545 free( reply );
548 if( !got_cols )
549 /* No useful default maps found; try allocating directly. */
550 for( col = 0; col < NUM_COLS; col++ ) {
551 int r, g, b;
552 union callback_param cp;
554 if( gwm_screens[ p.l ].root_visual->_class ==
555 XCB_VISUAL_CLASS_STATIC_GRAY ||
556 gwm_screens[ p.l ].root_visual->_class ==
557 XCB_VISUAL_CLASS_GRAY_SCALE )
558 r = g = b = luma( col );
559 else {
560 r = decoration_cols[ col ][ 0 ];
561 g = decoration_cols[ col ][ 1 ];
562 b = decoration_cols[ col ][ 2 ];
565 cp.l = ( p.l << 16 ) | col;
566 /* Ideally, we would like a guarantee that this operation will
567 complete before the pixel values are ever looked up. In
568 practice, it will, and it is too messy to insert a
569 sync_with_callback() before every single pixel reference. */
570 handle_async_reply( xcb_alloc_color(
571 c, screens[ p.l ]->default_colormap,
572 r, g, b ).sequence, handle_alloc_color,
573 cp );
577 static void decorate_compat_init( void ) {
579 int i;
580 const char *cursor_font_name = "cursor";
581 xcb_font_t cursor_font;
582 static const int cursor_glyphs[ NUM_CURSORS ] = {
583 68, /* CURSOR_ARROW */
584 88, /* CURSOR_DESTROY */
585 134, /* CURSOR_TL */
586 138, /* CURSOR_T */
587 136, /* CURSOR_TR */
588 70, /* CURSOR_L */
589 52, /* CURSOR_C */
590 96, /* CURSOR_R */
591 12, /* CURSOR_BL */
592 16, /* CURSOR_B */
593 14 /* CURSOR_BR */
596 cursor_font = xcb_generate_id( c );
597 xcb_open_font( c, cursor_font, strlen( cursor_font_name ),
598 cursor_font_name );
599 for( i = 0; i < NUM_CURSORS; i++ ) {
600 cursors[ i ] = xcb_generate_id( c );
601 xcb_create_glyph_cursor( c, cursors[ i ], cursor_font, cursor_font,
602 cursor_glyphs[ i ], cursor_glyphs[ i ] | 1,
603 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
605 xcb_close_font( c, cursor_font );
607 /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
608 for( i = 0; i < num_screens; i++ ) {
609 union callback_param cp;
611 cp.l = i;
612 handle_async_reply( xcb_get_property( c, FALSE, screens[ i ]->root,
613 RGB_DEFAULT_MAP, RGB_COLOR_MAP,
614 0, 0x10000 ).sequence,
615 handle_get_default_map, cp );
619 extern void decorate_render_init( void ) {
621 FcPattern *pat, *font;
622 FcResult r;
623 FcValue v;
624 FT_Error err;
625 char *filename;
626 int i, index;
627 xcb_pixmap_t pixmap;
628 uint32_t n;
630 FcInit();
632 pat = FcNameParse( (FcChar8 *) FONT_NAME );
634 FcConfigSubstitute( NULL, pat, FcMatchPattern );
636 FcDefaultSubstitute( pat );
638 font = FcFontMatch( NULL, pat, &r );
640 FcPatternGet( font, FC_FILE, 0, &v );
641 filename = (char *) v.u.s;
642 FcPatternGet( font, FC_INDEX, 0, &v );
643 index = v.u.i;
645 if( ( err = FT_Init_FreeType( &ftl ) ) )
646 fatal( "typeface initialisation error %d", err );
648 if( ( err = FT_New_Face( ftl, filename, index, &ftf ) ) )
649 fatal( "%s: could not load font (%d)", filename, err );
651 FcPatternDestroy( pat );
652 FcPatternDestroy( font );
654 FcFini();
656 if( ( err = FT_Set_Char_Size( ftf, 0, FONT_SIZE << 6, 0, 0 ) ) )
657 fatal( "%s: could not scale font (%d)", filename, err );
659 if( ( err = FT_Select_Charmap( ftf, FT_ENCODING_UNICODE ) ) )
660 fatal( "%s: could not select character map (%d)", filename, err );
662 glyphset = xcb_generate_id( c );
663 xcb_render_create_glyph_set( c, glyphset, fmt_a8 );
665 pixmap = xcb_generate_id( c );
667 pictures = xmalloc( num_screens * sizeof *pictures );
669 n = XCB_RENDER_REPEAT_NORMAL;
670 for( i = 0; i < num_screens; i++ ) {
671 xcb_create_pixmap( c, screens[ i ]->root_depth, pixmap,
672 screens[ i ]->root, 1, 1 );
674 pictures[ i ].pic = xcb_generate_id( c );
675 xcb_render_create_picture( c, pictures[ i ].pic, pixmap,
676 gwm_screens[ i ].root_pictformat,
677 XCB_RENDER_CP_REPEAT, &n );
678 pictures[ i ].col = -1;
680 xcb_free_pixmap( c, pixmap );
683 decorate_compat_init(); /* FIXME this is for pixel values (backgrounds)
684 and cursors */
687 extern void decorate_render_done( void ) {
689 #if DEBUG
690 free( pictures );
691 FT_Done_Face( ftf );
692 FT_Done_FreeType( ftl );
693 #endif