Clean up UTF-8 processing for core text, too.
[gwm.git] / decorate-render.c
blob01e535c006dd0e5ab590552d244e9548826feeef
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_BITMAP_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 "button.h"
40 #include "decorate-render.h"
41 #include "frame.h"
42 #include "utf8.h"
44 enum style_id {
45 STYLE_TITLE, STYLE_MENU, NUM_STYLES
48 #define TITLE_FONT_SIZE 12
49 #define MENU_FONT_SIZE 14
51 #define STRING2(x) #x
52 #define STRING(x) STRING2(x)
53 #define TITLE_FONT_SIZE_STRING STRING( TITLE_FONT_SIZE )
54 #define MENU_FONT_SIZE_STRING STRING( MENU_FONT_SIZE )
56 #define MENU_X_PAD 4
57 #define MENU_Y_PAD 2
59 #define FEEDBACK_WIDTH 96 /* width of size feedback window */
60 #define FEEDBACK_HEIGHT 24 /* height of size feedback window */
62 static const FcChar8 *const style_names[ NUM_STYLES ] = {
63 /* FIXME make this configurable */
64 (FcChar8 *) "sans:pixelsize=" TITLE_FONT_SIZE_STRING ":bold",
65 (FcChar8 *) "sans:pixelsize=" MENU_FONT_SIZE_STRING
68 static struct font {
69 FcPattern *pattern;
70 FcCharSet *charset;
71 FT_Face face;
72 FT_Int32 load_flags;
73 } *fonts;
74 static int num_fonts;
76 static struct style {
77 int *fonts; /* indices into font table above */
78 int num_fonts;
79 FcCharSet *charset;
80 } styles[ NUM_STYLES ];
82 static FT_Library ftl;
84 static const uint16_t decoration_cols[ NUM_COLS ][ 3 ] = {
85 { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */
86 { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */
87 { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */
88 { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */
89 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */
90 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */
91 { 0x3333, 0x3333, 0x3333 }, /* COL_TITLE_INACTIVE */
92 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */
93 { 0x0000, 0x0000, 0x0000 }, /* COL_FEEDBACK_FORE */
94 { 0x2222, 0x3333, 0xEEEE }, /* COL_MENU_ACTIVE_BACK */
95 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MENU_ACTIVE_FORE */
96 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_MENU_INACTIVE_BACK */
97 { 0x0000, 0x0000, 0x0000 } /* COL_MENU_INACTIVE_FORE */
100 #define METRIC_CACHE_SIZE_BITS 14
101 #define METRIC_CACHE_SIZE ( 1 << METRIC_CACHE_SIZE_BITS )
102 #define METRIC_CACHE_ASSOC_BITS 2
103 #define METRIC_CACHE_ASSOC ( 1 << METRIC_CACHE_ASSOC_BITS )
105 #define GLYPH_CACHE_SIZE_BITS 11
106 #define GLYPH_CACHE_SIZE ( 1 << GLYPH_CACHE_SIZE_BITS )
107 #define GLYPH_CACHE_ASSOC_BITS 3
108 #define GLYPH_CACHE_ASSOC ( 1 << GLYPH_CACHE_ASSOC_BITS )
110 #if GLYPH_CACHE_SIZE_BITS + GLYPH_CACHE_ASSOC_BITS > 16
111 #error "Glyph cache is too big for 16-bit indices."
112 #endif
114 static struct metric_cache_line {
115 enum style_id style;
116 uint32_t c; /* character code */
117 unsigned int time;
118 int x_off, y_off;
119 } metric_cache[ METRIC_CACHE_SIZE ][ METRIC_CACHE_ASSOC ];
121 static struct glyph_cache_line {
122 enum style_id style;
123 uint32_t c; /* character code */
124 unsigned int time;
125 } glyph_cache[ GLYPH_CACHE_SIZE ][ GLYPH_CACHE_ASSOC ];
127 static unsigned int current_time;
129 static struct font *lookup_font( enum style_id style, uint32_t c ) {
131 int i;
133 if( !FcCharSetHasChar( styles[ style ].charset, c ) )
134 return NULL;
136 for( i = 0; i < styles[ style ].num_fonts; i++ ) {
137 struct font *font = fonts + styles[ style ].fonts[ i ];
139 if( FcCharSetHasChar( font->charset, c ) ) {
140 if( !font->face ) {
141 FcChar8 *filename;
142 FcMatrix *matrix = NULL;
143 FT_Matrix ft_matrix;
144 int index = 0, hintstyle = FC_HINT_MEDIUM;
145 double size;
146 FcBool hinting = TRUE, autohint = FALSE, globaladvance = TRUE,
147 embeddedbitmap = TRUE;
149 if( FcPatternGetString( font->pattern, FC_FILE, 0,
150 &filename ) ||
151 FcPatternGetDouble( font->pattern, FC_PIXEL_SIZE, 0,
152 &size ) )
153 continue;
155 FcPatternGetInteger( font->pattern, FC_INDEX, 0, &index );
156 FcPatternGetMatrix( font->pattern, FC_MATRIX, 0, &matrix );
158 if( FT_New_Face( ftl, (char *) filename, index, &font->face ) )
159 continue;
161 FT_Set_Pixel_Sizes( font->face, 0, (int) ( size + 0.5 ) );
163 if( matrix ) {
164 ft_matrix.xx = matrix->xx * 64.0;
165 ft_matrix.xy = matrix->xy * 64.0;
166 ft_matrix.yx = matrix->yx * 64.0;
167 ft_matrix.yy = matrix->yy * 64.0;
168 FT_Set_Transform( font->face, &ft_matrix, NULL );
171 FT_Select_Charmap( font->face, FT_ENCODING_UNICODE );
173 FcPatternGetBool( font->pattern, FC_HINTING, 0, &hinting );
174 FcPatternGetInteger( font->pattern, FC_HINT_STYLE, 0,
175 &hintstyle );
176 FcPatternGetBool( font->pattern, FC_AUTOHINT, 0, &autohint );
177 FcPatternGetBool( font->pattern, FC_GLOBAL_ADVANCE, 0,
178 &globaladvance );
179 FcPatternGetBool( font->pattern, FC_EMBEDDED_BITMAP, 0,
180 &embeddedbitmap );
182 font->load_flags = FT_LOAD_DEFAULT;
184 if( !hinting )
185 font->load_flags |= FT_LOAD_NO_HINTING;
187 switch( hintstyle ) {
188 case FC_HINT_NONE:
189 font->load_flags |= FT_LOAD_NO_HINTING;
190 break;
192 case FC_HINT_SLIGHT:
193 font->load_flags |= FT_LOAD_TARGET_LIGHT;
194 break;
196 case FC_HINT_MEDIUM:
197 default:
198 font->load_flags |= FT_LOAD_TARGET_NORMAL;
199 break;
201 case FC_HINT_FULL:
202 font->load_flags |= FT_LOAD_TARGET_MONO;
203 break;
206 if( autohint )
207 font->load_flags |= FT_LOAD_FORCE_AUTOHINT;
209 if( !globaladvance )
210 font->load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
212 if( !embeddedbitmap )
213 font->load_flags |= FT_LOAD_NO_BITMAP;
216 return font;
220 return NULL;
223 static void query_metrics( enum style_id style, uint32_t c,
224 int *x_off, int *y_off ) {
226 int row = c & ( METRIC_CACHE_SIZE - 1 );
227 int i, max;
228 struct font *font;
230 for( i = 0; i < METRIC_CACHE_ASSOC; i++ )
231 if( metric_cache[ row ][ i ].style == style &&
232 metric_cache[ row ][ i ].c == c ) {
233 /* Cache hit. */
234 metric_cache[ row ][ i ].time = current_time;
235 *x_off = metric_cache[ row ][ i ].x_off;
236 *y_off = metric_cache[ row ][ i ].y_off;
237 return;
240 font = lookup_font( style, c );
242 /* Cache miss. */
243 if( FT_Load_Char( font->face, c, font->load_flags ) ) {
244 /* Couldn't load metrics. Don't bother evicting anything. */
245 *x_off = *y_off = 0;
246 return;
249 /* Search for a line to evict. */
250 for( i = 1, max = 0; i < METRIC_CACHE_ASSOC; i++ )
251 if( current_time - metric_cache[ row ][ i ].time >
252 current_time - metric_cache[ row ][ max ].time )
253 max = i;
255 metric_cache[ row ][ max ].style = style;
256 metric_cache[ row ][ max ].c = c;
257 metric_cache[ row ][ max ].time = current_time;
258 *x_off = metric_cache[ row ][ max ].x_off =
259 ( font->face->glyph->advance.x + 0x20 ) >> 6;
260 *y_off = metric_cache[ row ][ max ].y_off =
261 ( font->face->glyph->advance.y + 0x20 ) >> 6;
264 static int query_glyph( enum style_id style, uint32_t c ) {
266 int row = c & ( GLYPH_CACHE_SIZE - 1 );
267 int i;
269 for( i = 0; i < GLYPH_CACHE_ASSOC; i++ )
270 if( glyph_cache[ row ][ i ].style == style &&
271 glyph_cache[ row ][ i ].c == c ) {
272 /* Cache hit. */
273 glyph_cache[ row ][ i ].time = current_time;
274 return ( row << GLYPH_CACHE_ASSOC_BITS ) | i;
277 return -1;
280 static int replace_glyph( enum style_id style, uint32_t c ) {
282 int row = c & ( GLYPH_CACHE_SIZE - 1 );
283 int i, max;
285 /* Search for a line to evict. */
286 for( i = 1, max = 0; i < METRIC_CACHE_ASSOC; i++ ) {
287 assert( glyph_cache[ row ][ i ].style != style ||
288 glyph_cache[ row ][ i ].c != c);
289 if( current_time - glyph_cache[ row ][ i ].time >
290 current_time - glyph_cache[ row ][ max ].time )
291 max = i;
294 if( glyph_cache[ row ][ max ].time == current_time )
295 /* Cache line set is full, and nothing is old enough to evict. */
296 return -1;
298 glyph_cache[ row ][ max ].style = style;
299 glyph_cache[ row ][ max ].c = c;
300 glyph_cache[ row ][ max ].time = current_time;
302 return ( row << GLYPH_CACHE_ASSOC_BITS ) | max;
305 static xcb_render_glyphset_t glyphset;
306 static struct picture {
307 xcb_render_picture_t pic;
308 enum decoration_col col;
309 } *pictures; /* indexed by screen */
311 static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen,
312 int col, int x, int y,
313 const char *text, enum style_id style,
314 const xcb_rectangle_t *clip ) {
316 xcb_render_picture_t src, dest;
317 xcb_rectangle_t rect;
318 xcb_render_color_t rc;
319 int i, len;
320 const unsigned char *p;
321 uint32_t *buf;
323 if( !text || !( len = utf8_length( (const unsigned char *) text ) ) ) {
324 xcb_void_cookie_t r = { 0 };
326 return r;
329 buf = alloca( len * sizeof *buf );
331 for( i = 0, p = (const unsigned char *) text; i < len; i++ )
332 buf[ i ] = utf8_next( &p );
334 src = pictures[ screen ].pic;
336 dest = xcb_generate_id( c );
337 xcb_render_create_picture( c, dest, drawable,
338 gwm_screens[ screen ].root_pictformat, 0, NULL );
340 if( clip )
341 xcb_render_set_picture_clip_rectangles( c, dest, 0, 0, 1, clip );
343 if( pictures[ screen ].col != col ) {
344 rc.red = decoration_cols[ col ][ 0 ];
345 rc.green = decoration_cols[ col ][ 1 ];
346 rc.blue = decoration_cols[ col ][ 2 ];
347 rc.alpha = 0xFFFF;
348 rect.x = 0;
349 rect.y = 0;
350 rect.width = 1;
351 rect.height = 1;
352 xcb_render_fill_rectangles( c, XCB_RENDER_PICT_OP_SRC, src, rc, 1,
353 &rect );
355 pictures[ screen ].col = col;
358 i = 0;
359 while( i < len ) {
360 uint32_t glyphids[ 0x80 ];
361 xcb_render_glyphinfo_t glyphs[ 0x80 ];
362 int num_glyphs; /* number of glyphs in AddGlyphs request */
363 /* Buffer for "data" parameter of AddGlyphs request. We want
364 this to be big enough to hold at least one glyph, but no
365 bigger than the biggest request the server will accept (once
366 the other parameters are included). All servers must
367 accept requests of 16,384 bytes or smaller (see X Window
368 System Protocol, section 8), so this is safe. */
369 uint8_t data[ 0x2000 ], *p;
370 /* Buffer for "glyphcmds" parameter of CompositeGlyphs16
371 request. We always send exactly one command, which has an
372 8-byte header and at most a 254 glyph string. */
373 uint8_t param[ 8 + ( 0xFE << 1 ) ], *out;
374 int num_chars; /* number of characters for CompositeGlyphs16 */
376 current_time++;
378 p = data;
379 num_glyphs = 0;
381 memset( param, 0, 4 );
382 *( (uint16_t *) ( param + 4 ) ) = x;
383 *( (uint16_t *) ( param + 6 ) ) = y;
384 out = param + 8;
385 num_chars = 0;
387 while( i < len && num_chars < 0xFF ) {
388 int dx, dy, index;
390 if( ( index = query_glyph( style, buf[ i ] ) ) < 0 ) {
391 /* Cache miss. */
392 int bitmap_size;
393 struct font *font;
394 FT_GlyphSlot slot;
396 if( ( index = replace_glyph( style, buf[ i ] ) ) < 0 )
397 /* Cache set full: spill the partial string to make
398 room for later glyphs. */
399 break;
401 if( !( font = lookup_font( style, buf[ i ] ) ) ||
402 FT_Load_Char( font->face, buf[ i ], font->load_flags |
403 FT_LOAD_RENDER ) ||
404 ( bitmap_size = ( ( font->face->glyph->bitmap.width +
405 3 ) & ~3 ) *
406 font->face->glyph->bitmap.rows ) > sizeof data ) {
407 /* We couldn't load the character, or it was so
408 huge that it won't fit in an empty AddGlyph
409 data buffer. We'll have to send the server
410 an empty glyph and carry on. */
411 slot = NULL;
412 bitmap_size = 0;
413 } else
414 slot = font->face->glyph;
416 if( ( p - data ) + bitmap_size > sizeof data ||
417 num_glyphs == sizeof glyphids / sizeof *glyphids ) {
418 /* Can't fit this glyph into the existing request:
419 transmit what we have... */
420 xcb_render_add_glyphs( c, glyphset, num_glyphs, glyphids,
421 glyphs, p - data, data );
423 /* ...and start building another AddGlyph request. */
424 p = data;
425 num_glyphs = 0;
428 glyphids[ num_glyphs ] = index;
430 if( slot ) {
431 FT_Bitmap bitmap;
433 FT_Bitmap_New( &bitmap );
434 FT_Bitmap_Convert( ftl, &slot->bitmap, &bitmap, 4 );
436 glyphs[ num_glyphs ].width = bitmap.width;
437 glyphs[ num_glyphs ].height = bitmap.rows;
438 glyphs[ num_glyphs ].x = -slot->bitmap_left;
439 glyphs[ num_glyphs ].y = slot->bitmap_top;
440 glyphs[ num_glyphs ].x_off =
441 ( slot->advance.x + 0x20 ) >> 6;
442 glyphs[ num_glyphs ].y_off =
443 ( slot->advance.y + 0x20 ) >> 6;
445 memcpy( p, bitmap.buffer, bitmap.pitch * bitmap.rows );
447 if( bitmap.num_grays != 0x100 ) {
448 unsigned char *c,
449 *end = p + bitmap.pitch * bitmap.rows;
451 for( c = p; c < end; c++ )
452 *c = ( (unsigned int) *c * 0xFF /
453 ( bitmap.num_grays - 1 ) );
456 p += bitmap.pitch * bitmap.rows;
458 FT_Bitmap_Done( ftl, &bitmap );
459 } else {
460 glyphs[ num_glyphs ].width = 0;
461 glyphs[ num_glyphs ].height = 0;
462 glyphs[ num_glyphs ].x = 0;
463 glyphs[ num_glyphs ].y = 0;
464 glyphs[ num_glyphs ].x_off = 0;
465 glyphs[ num_glyphs ].y_off = 0;
468 num_glyphs++;
471 *( (uint16_t *) out ) = index;
472 out += 2;
474 query_metrics( style, buf[ i ], &dx, &dy );
475 x += dx;
476 y += dy;
478 num_chars++;
479 i++;
482 if( num_glyphs ) {
483 xcb_render_add_glyphs( c, glyphset, num_glyphs, glyphids, glyphs,
484 p - data, data );
486 p = data;
487 num_glyphs = 0;
490 param[ 0 ] = num_chars;
492 xcb_render_composite_glyphs_16( c, XCB_RENDER_PICT_OP_OVER, src, dest,
493 XCB_NONE, glyphset, 0, 0, out - param,
494 param );
497 return xcb_render_free_picture( c, dest );
500 static int text_width( enum style_id style, const char *text ) {
502 const unsigned char *p = (const unsigned char *) text;
503 int width = 0;
505 while( *p ) {
506 int dx, dy;
508 query_metrics( style, utf8_next( &p ), &dx, &dy );
509 width += dx;
512 return width;
515 extern void render_update_window( struct gwm_window *window ) {
517 if( !window->cleared )
518 xcb_clear_area( c, FALSE, window->w, window->update.x, window->update.y,
519 window->update.width, window->update.height );
521 if( window->type == WINDOW_FRAME ) {
522 char *name = window->u.frame.child->u.managed.name;
524 render_text( window->w, window->screen, window == focus_frame ?
525 COL_TITLE_ACTIVE : COL_TITLE_INACTIVE,
526 button_size( window->u.frame.button, TRUE ) + 4,
527 TITLE_FONT_SIZE, name ? name : "(Untitled)",
528 STYLE_TITLE, &window->update );
529 } else if( window->type == WINDOW_MENUITEM ) {
530 struct gwm_window *menu = window->u.menuitem.menu;
532 render_text( window->w, window->screen,
533 menu->u.menu.active_item >= 0 &&
534 menu->u.menu.items[ menu->u.menu.active_item ] == window ?
535 COL_MENU_ACTIVE_FORE : COL_MENU_INACTIVE_FORE,
536 MENU_X_PAD, MENU_FONT_SIZE,
537 window->u.menuitem.label, STYLE_MENU, &window->update );
538 } else if( window->type == WINDOW_FEEDBACK ) {
539 char text[ 32 ];
541 sprintf( text, "%dx%d", window->u.feedback.fb_width,
542 window->u.feedback.fb_height );
544 render_text( window->w, window->screen, COL_FEEDBACK_FORE,
545 ( FEEDBACK_WIDTH - text_width( STYLE_TITLE, text ) ) >> 1,
546 16, text, STYLE_TITLE, &window->update );
550 extern void render_window_size( struct gwm_window *window, int *width,
551 int *height ) {
553 switch( window->type ) {
554 case WINDOW_MENUITEM:
555 if( window->u.menuitem.label ) {
556 *width = text_width( STYLE_MENU, window->u.menuitem.label ) +
557 ( MENU_X_PAD << 1 );
558 *height = MENU_FONT_SIZE + ( MENU_Y_PAD << 1 );
559 } else {
560 *width = MENU_X_PAD << 1;
561 *height = MENU_Y_PAD << 1;
563 return;
565 case WINDOW_FEEDBACK:
566 *width = FEEDBACK_WIDTH;
567 *height = FEEDBACK_HEIGHT;
568 return;
570 default:
571 assert( FALSE );
575 static uint16_t luma( int col ) {
577 return ( decoration_cols[ col ][ 0 ] * 0x4C8BU +
578 decoration_cols[ col ][ 1 ] * 0x9646U +
579 decoration_cols[ col ][ 2 ] * 0x1D2FU ) >> 16;
582 static void handle_alloc_color( unsigned int sequence, void *reply,
583 xcb_generic_error_t *error,
584 union callback_param p ) {
586 xcb_alloc_color_reply_t *r = reply;
587 int screen = p.l >> 16, col = p.l & 0xFFFF;
589 if( error ) {
590 if( error->error_code != XCB_ALLOC )
591 show_error( error );
593 /* No standard RGB map available, and we couldn't allocate a shared
594 colour. Fall back to black or white. */
595 gwm_screens[ screen ].pixels[ col ] = luma( col ) >= 0x8000 ?
596 screens[ screen ]->white_pixel : screens[ screen ]->black_pixel;
598 free( error );
601 if( reply ) {
602 gwm_screens[ screen ].pixels[ col ] = r->pixel;
604 free( reply );
608 static void handle_get_default_map( unsigned int sequence, void *reply,
609 xcb_generic_error_t *error,
610 union callback_param p ) {
612 int col, got_cols = FALSE;
614 if( error ) {
615 show_error( error );
617 free( error );
620 if( reply ) {
621 xcb_get_property_reply_t *prop = reply;
622 struct std_cmap {
623 /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
624 xcb_colormap_t colormap;
625 uint32_t red_max, red_mult, green_max, green_mult, blue_max,
626 blue_mult, base_pixel;
627 xcb_visualid_t visual_id;
628 uint32_t kill_id;
629 } *std_cmaps = xcb_get_property_value( prop );
630 int num_cmaps = ( xcb_get_property_value_length( prop ) << 2 ) /
631 sizeof *std_cmaps;
632 int i;
634 if( prop->format == 32 )
635 for( i = 0; i < num_cmaps; i++ )
636 if( std_cmaps[ i ].visual_id ==
637 gwm_screens[ p.l ].root_visual->visual_id ) {
639 for( col = 0; col < NUM_COLS; col++ ) {
640 int r0, g0, b0, r, g, b;
642 if( gwm_screens[ p.l ].root_visual->_class ==
643 XCB_VISUAL_CLASS_STATIC_GRAY ||
644 gwm_screens[ p.l ].root_visual->_class ==
645 XCB_VISUAL_CLASS_GRAY_SCALE ) {
646 r0 = luma( col );
647 g0 = b0 = 0;
648 } else {
649 r0 = decoration_cols[ col ][ 0 ];
650 g0 = decoration_cols[ col ][ 1 ];
651 b0 = decoration_cols[ col ][ 2 ];
654 r = ( r0 * std_cmaps[ i ].red_max + 0x8000 ) >> 16;
655 g = ( g0 * std_cmaps[ i ].green_max + 0x8000 ) >> 16;
656 b = ( b0 * std_cmaps[ i ].blue_max + 0x8000 ) >> 16;
658 gwm_screens[ p.l ].pixels[ col ] =
659 std_cmaps[ i ].base_pixel +
660 r * std_cmaps[ i ].red_mult +
661 g * std_cmaps[ i ].green_mult +
662 b * std_cmaps[ i ].blue_mult;
665 got_cols = TRUE;
666 break;
669 free( reply );
672 if( !got_cols )
673 /* No useful default maps found; try allocating directly. */
674 for( col = 0; col < NUM_COLS; col++ ) {
675 int r, g, b;
676 union callback_param cp;
678 if( gwm_screens[ p.l ].root_visual->_class ==
679 XCB_VISUAL_CLASS_STATIC_GRAY ||
680 gwm_screens[ p.l ].root_visual->_class ==
681 XCB_VISUAL_CLASS_GRAY_SCALE )
682 r = g = b = luma( col );
683 else {
684 r = decoration_cols[ col ][ 0 ];
685 g = decoration_cols[ col ][ 1 ];
686 b = decoration_cols[ col ][ 2 ];
689 cp.l = ( p.l << 16 ) | col;
690 /* Ideally, we would like a guarantee that this operation will
691 complete before the pixel values are ever looked up. In
692 practice, it will, and it is too messy to insert a
693 sync_with_callback() before every single pixel reference. */
694 handle_async_reply( xcb_alloc_color(
695 c, screens[ p.l ]->default_colormap,
696 r, g, b ).sequence, handle_alloc_color,
697 cp );
701 static void decorate_compat_init( void ) {
703 int i;
704 const char *cursor_font_name = "cursor";
705 xcb_font_t cursor_font;
706 static const int cursor_glyphs[ NUM_CURSORS ] = {
707 134, /* CURSOR_TL */
708 138, /* CURSOR_T */
709 136, /* CURSOR_TR */
710 70, /* CURSOR_L */
711 52, /* CURSOR_C */
712 96, /* CURSOR_R */
713 12, /* CURSOR_BL */
714 16, /* CURSOR_B */
715 14, /* CURSOR_BR */
716 68, /* CURSOR_ARROW */
717 88 /* CURSOR_DESTROY */
720 cursor_font = xcb_generate_id( c );
721 xcb_open_font( c, cursor_font, strlen( cursor_font_name ),
722 cursor_font_name );
723 for( i = 0; i < NUM_CURSORS; i++ ) {
724 cursors[ i ] = xcb_generate_id( c );
725 xcb_create_glyph_cursor( c, cursors[ i ], cursor_font, cursor_font,
726 cursor_glyphs[ i ], cursor_glyphs[ i ] | 1,
727 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
729 xcb_close_font( c, cursor_font );
731 /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
732 for( i = 0; i < num_screens; i++ ) {
733 union callback_param cp;
735 cp.l = i;
736 handle_async_reply( xcb_get_property( c, FALSE, screens[ i ]->root,
737 RGB_DEFAULT_MAP, RGB_COLOR_MAP,
738 0, 0x10000 ).sequence,
739 handle_get_default_map, cp );
743 extern void decorate_render_init( void ) {
745 FT_Error err;
746 FcFontSet *sets[ NUM_STYLES ];
747 FcPattern *style_pats[ NUM_STYLES ];
748 FcChar32 *hashes;
749 FcResult r;
750 int i, total;
751 xcb_pixmap_t pixmap;
752 uint32_t n;
754 if( !FcInit() )
755 fatal( "font configuration error" );
757 if( ( err = FT_Init_FreeType( &ftl ) ) )
758 fatal( "typeface initialisation error %d", err );
760 for( i = 0, total = 0; i < NUM_STYLES; i++ ) {
761 if( !( style_pats[ i ] = FcNameParse( style_names[ i ] ) ) )
762 fatal( "could not parse font \"%s\"\n", style_names[ i ] );
764 FcConfigSubstitute( NULL, style_pats[ i ], FcMatchPattern );
765 FcDefaultSubstitute( style_pats[ i ] );
767 sets[ i ] = FcFontSort( NULL, style_pats[ i ], TRUE,
768 &styles[ i ].charset, &r );
770 total += sets[ i ]->nfont;
773 fonts = xmalloc( total * sizeof *fonts );
774 hashes = alloca( total * sizeof *hashes );
776 for( i = 0; i < NUM_STYLES; i++ ) {
777 int j;
779 styles[ i ].fonts = xmalloc( sets[ i ]->nfont *
780 sizeof (struct font *) );
782 for( j = 0; j < sets[ i ]->nfont; j++ ) {
783 FcPattern *pat;
785 if( ( pat = FcFontRenderPrepare( NULL, style_pats[ i ],
786 sets[ i ]->fonts[ j ] ) ) ) {
787 FcChar32 hash = FcPatternHash( pat );
788 int search;
790 for( search = 0; search < num_fonts; search++ )
791 if( hashes[ search ] == hash &&
792 FcPatternEqual( fonts[ search ].pattern, pat ) ) {
793 FcPatternDestroy( pat );
794 break;
797 if( search == num_fonts ) {
798 hashes[ num_fonts ] = hash;
799 fonts[ num_fonts ].pattern = pat;
800 FcPatternGetCharSet( pat, FC_CHARSET, 0,
801 &fonts[ num_fonts ].charset );
802 fonts[ num_fonts ].face = NULL;
803 num_fonts++;
806 styles[ i ].fonts[ styles[ i ].num_fonts ] = search;
807 styles[ i ].num_fonts++;
811 styles[ i ].fonts = xrealloc( styles[ i ].fonts,
812 styles[ i ].num_fonts *
813 sizeof (struct font *) );
815 FcFontSetDestroy( sets[ i ] );
816 FcPatternDestroy( style_pats[ i ] );
819 fonts = xrealloc( fonts, num_fonts * sizeof *fonts );
821 glyphset = xcb_generate_id( c );
822 xcb_render_create_glyph_set( c, glyphset, fmt_a8 );
824 pixmap = xcb_generate_id( c );
826 pictures = xmalloc( num_screens * sizeof *pictures );
828 n = XCB_RENDER_REPEAT_NORMAL;
829 for( i = 0; i < num_screens; i++ ) {
830 xcb_create_pixmap( c, screens[ i ]->root_depth, pixmap,
831 screens[ i ]->root, 1, 1 );
833 pictures[ i ].pic = xcb_generate_id( c );
834 xcb_render_create_picture( c, pictures[ i ].pic, pixmap,
835 gwm_screens[ i ].root_pictformat,
836 XCB_RENDER_CP_REPEAT, &n );
837 pictures[ i ].col = -1;
839 xcb_free_pixmap( c, pixmap );
842 decorate_compat_init(); /* FIXME this is for pixel values (backgrounds)
843 and cursors */
846 extern void decorate_render_done( void ) {
848 #if DEBUG
849 int i;
851 for( i = 0; i < NUM_STYLES; i++ ) {
852 FcCharSetDestroy( styles[ i ].charset );
853 free( styles[ i ].fonts );
856 for( i = 0; i < num_fonts; i++ ) {
857 FcPatternDestroy( fonts[ i ].pattern );
858 FT_Done_Face( fonts[ i ].face );
861 free( fonts );
863 FcFini();
865 FT_Done_FreeType( ftl );
867 free( pictures );
868 #endif