Specify property size in 32-bit units, not bytes.
[gwm.git] / decorate-render.c
blob75f1a12d037a5f3cbd8258adc192a7ab579abe53
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 "image.h"
43 #include "managed.h"
44 #include "utf8.h"
45 #include "window-table.h"
47 enum style_id {
48 STYLE_TITLE, STYLE_MENU, NUM_STYLES
51 #define TITLE_FONT_SIZE 12
52 #define MENU_FONT_SIZE 14
54 #define STRING2(x) #x
55 #define STRING(x) STRING2(x)
56 #define TITLE_FONT_SIZE_STRING STRING( TITLE_FONT_SIZE )
57 #define MENU_FONT_SIZE_STRING STRING( MENU_FONT_SIZE )
59 #define MENU_X_PAD 4
60 #define MENU_Y_PAD 2
62 #define FEEDBACK_WIDTH 96 /* width of size feedback window */
63 #define FEEDBACK_HEIGHT 24 /* height of size feedback window */
65 #define ICON_SIZE 256
67 static const FcChar8 *const style_names[ NUM_STYLES ] = {
68 /* FIXME make this configurable */
69 (FcChar8 *) "sans:pixelsize=" TITLE_FONT_SIZE_STRING ":bold",
70 (FcChar8 *) "sans:pixelsize=" MENU_FONT_SIZE_STRING
73 static struct font {
74 FcPattern *pattern;
75 FcCharSet *charset;
76 FT_Face face;
77 FT_Int32 load_flags;
78 } *fonts;
79 static int num_fonts;
81 static struct style {
82 int *fonts; /* indices into font table above */
83 int num_fonts;
84 FcCharSet *charset;
85 } styles[ NUM_STYLES ];
87 static FT_Library ftl;
89 static const uint16_t decoration_cols[ NUM_COLS ][ 3 ] = {
90 { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */
91 { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */
92 { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */
93 { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */
94 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */
95 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */
96 { 0x3333, 0x3333, 0x3333 }, /* COL_TITLE_INACTIVE */
97 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */
98 { 0x0000, 0x0000, 0x0000 }, /* COL_FEEDBACK_FORE */
99 { 0x2222, 0x3333, 0xEEEE }, /* COL_MENU_ACTIVE_BACK */
100 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MENU_ACTIVE_FORE */
101 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_MENU_INACTIVE_BACK */
102 { 0x0000, 0x0000, 0x0000 } /* COL_MENU_INACTIVE_FORE */
105 #define METRIC_CACHE_SIZE_BITS 14
106 #define METRIC_CACHE_SIZE ( 1 << METRIC_CACHE_SIZE_BITS )
107 #define METRIC_CACHE_ASSOC_BITS 2
108 #define METRIC_CACHE_ASSOC ( 1 << METRIC_CACHE_ASSOC_BITS )
110 #define GLYPH_CACHE_SIZE_BITS 11
111 #define GLYPH_CACHE_SIZE ( 1 << GLYPH_CACHE_SIZE_BITS )
112 #define GLYPH_CACHE_ASSOC_BITS 3
113 #define GLYPH_CACHE_ASSOC ( 1 << GLYPH_CACHE_ASSOC_BITS )
115 #if GLYPH_CACHE_SIZE_BITS + GLYPH_CACHE_ASSOC_BITS > 16
116 #error "Glyph cache is too big for 16-bit indices."
117 #endif
119 static struct metric_cache_line {
120 enum style_id style;
121 uint32_t c; /* character code */
122 unsigned int time;
123 int x_off, y_off;
124 } metric_cache[ METRIC_CACHE_SIZE ][ METRIC_CACHE_ASSOC ];
126 static struct glyph_cache_line {
127 enum style_id style;
128 uint32_t c; /* character code */
129 unsigned int time;
130 } glyph_cache[ GLYPH_CACHE_SIZE ][ GLYPH_CACHE_ASSOC ];
132 static unsigned int current_time;
134 static struct font *lookup_font( enum style_id style, uint32_t c ) {
136 int i;
138 if( !FcCharSetHasChar( styles[ style ].charset, c ) )
139 return NULL;
141 for( i = 0; i < styles[ style ].num_fonts; i++ ) {
142 struct font *font = fonts + styles[ style ].fonts[ i ];
144 if( FcCharSetHasChar( font->charset, c ) ) {
145 if( !font->face ) {
146 FcChar8 *filename;
147 FcMatrix *matrix = NULL;
148 FT_Matrix ft_matrix;
149 int index = 0, hintstyle = FC_HINT_MEDIUM;
150 double size;
151 FcBool hinting = TRUE, autohint = FALSE, globaladvance = TRUE,
152 embeddedbitmap = TRUE;
154 if( FcPatternGetString( font->pattern, FC_FILE, 0,
155 &filename ) ||
156 FcPatternGetDouble( font->pattern, FC_PIXEL_SIZE, 0,
157 &size ) )
158 continue;
160 FcPatternGetInteger( font->pattern, FC_INDEX, 0, &index );
161 FcPatternGetMatrix( font->pattern, FC_MATRIX, 0, &matrix );
163 if( FT_New_Face( ftl, (char *) filename, index, &font->face ) )
164 continue;
166 FT_Set_Pixel_Sizes( font->face, 0, (int) ( size + 0.5 ) );
168 if( matrix ) {
169 ft_matrix.xx = matrix->xx * 64.0;
170 ft_matrix.xy = matrix->xy * 64.0;
171 ft_matrix.yx = matrix->yx * 64.0;
172 ft_matrix.yy = matrix->yy * 64.0;
173 FT_Set_Transform( font->face, &ft_matrix, NULL );
176 FT_Select_Charmap( font->face, FT_ENCODING_UNICODE );
178 FcPatternGetBool( font->pattern, FC_HINTING, 0, &hinting );
179 FcPatternGetInteger( font->pattern, FC_HINT_STYLE, 0,
180 &hintstyle );
181 FcPatternGetBool( font->pattern, FC_AUTOHINT, 0, &autohint );
182 FcPatternGetBool( font->pattern, FC_GLOBAL_ADVANCE, 0,
183 &globaladvance );
184 FcPatternGetBool( font->pattern, FC_EMBEDDED_BITMAP, 0,
185 &embeddedbitmap );
187 font->load_flags = FT_LOAD_DEFAULT;
189 if( !hinting )
190 font->load_flags |= FT_LOAD_NO_HINTING;
192 switch( hintstyle ) {
193 case FC_HINT_NONE:
194 font->load_flags |= FT_LOAD_NO_HINTING;
195 break;
197 case FC_HINT_SLIGHT:
198 font->load_flags |= FT_LOAD_TARGET_LIGHT;
199 break;
201 case FC_HINT_MEDIUM:
202 default:
203 font->load_flags |= FT_LOAD_TARGET_NORMAL;
204 break;
206 case FC_HINT_FULL:
207 font->load_flags |= FT_LOAD_TARGET_MONO;
208 break;
211 if( autohint )
212 font->load_flags |= FT_LOAD_FORCE_AUTOHINT;
214 if( !globaladvance )
215 font->load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
217 if( !embeddedbitmap )
218 font->load_flags |= FT_LOAD_NO_BITMAP;
221 return font;
225 return NULL;
228 static void query_metrics( enum style_id style, uint32_t c,
229 int *x_off, int *y_off ) {
231 int row = c & ( METRIC_CACHE_SIZE - 1 );
232 int i, max;
233 struct font *font;
235 for( i = 0; i < METRIC_CACHE_ASSOC; i++ )
236 if( metric_cache[ row ][ i ].style == style &&
237 metric_cache[ row ][ i ].c == c ) {
238 /* Cache hit. */
239 metric_cache[ row ][ i ].time = current_time;
240 *x_off = metric_cache[ row ][ i ].x_off;
241 *y_off = metric_cache[ row ][ i ].y_off;
242 return;
245 font = lookup_font( style, c );
247 /* Cache miss. */
248 if( !font || FT_Load_Char( font->face, c, font->load_flags ) ) {
249 /* Couldn't load metrics. Don't bother evicting anything. */
250 *x_off = *y_off = 0;
251 return;
254 /* Search for a line to evict. */
255 for( i = 1, max = 0; i < METRIC_CACHE_ASSOC; i++ )
256 if( current_time - metric_cache[ row ][ i ].time >
257 current_time - metric_cache[ row ][ max ].time )
258 max = i;
260 metric_cache[ row ][ max ].style = style;
261 metric_cache[ row ][ max ].c = c;
262 metric_cache[ row ][ max ].time = current_time;
263 *x_off = metric_cache[ row ][ max ].x_off =
264 ( font->face->glyph->advance.x + 0x20 ) >> 6;
265 *y_off = metric_cache[ row ][ max ].y_off =
266 ( font->face->glyph->advance.y + 0x20 ) >> 6;
269 static int query_glyph( enum style_id style, uint32_t c ) {
271 int row = c & ( GLYPH_CACHE_SIZE - 1 );
272 int i;
274 for( i = 0; i < GLYPH_CACHE_ASSOC; i++ )
275 if( glyph_cache[ row ][ i ].style == style &&
276 glyph_cache[ row ][ i ].c == c ) {
277 /* Cache hit. */
278 glyph_cache[ row ][ i ].time = current_time;
279 return ( row << GLYPH_CACHE_ASSOC_BITS ) | i;
282 return -1;
285 static int replace_glyph( enum style_id style, uint32_t c ) {
287 int row = c & ( GLYPH_CACHE_SIZE - 1 );
288 int i, max;
290 /* Search for a line to evict. */
291 for( i = 1, max = 0; i < METRIC_CACHE_ASSOC; i++ ) {
292 assert( glyph_cache[ row ][ i ].style != style ||
293 glyph_cache[ row ][ i ].c != c);
294 if( current_time - glyph_cache[ row ][ i ].time >
295 current_time - glyph_cache[ row ][ max ].time )
296 max = i;
299 if( glyph_cache[ row ][ max ].time == current_time )
300 /* Cache line set is full, and nothing is old enough to evict. */
301 return -1;
303 glyph_cache[ row ][ max ].style = style;
304 glyph_cache[ row ][ max ].c = c;
305 glyph_cache[ row ][ max ].time = current_time;
307 return ( row << GLYPH_CACHE_ASSOC_BITS ) | max;
310 static enum xcb_image_order_t image_byte_order;
311 static xcb_render_pictformat_t fmt_a8;
312 static xcb_render_directformat_t dfmt_a8;
313 static xcb_render_glyphset_t glyphset;
314 static struct render_screen {
315 xcb_render_pictformat_t root_pictformat, direct_pictformat;
316 int direct_depth;
317 xcb_render_directformat_t dfmt;
318 xcb_format_t *pfmt;
319 int rbits, gbits, bbits, abits;
320 xcb_render_picture_t pic;
321 enum decoration_col col;
322 } *render_screens;
324 static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen,
325 int col, int x, int y,
326 const char *text, enum style_id style,
327 const xcb_rectangle_t *clip ) {
329 xcb_render_picture_t src, dest;
330 xcb_rectangle_t rect;
331 xcb_render_color_t rc;
332 int i, len;
333 const unsigned char *p;
334 uint32_t *buf;
336 if( !text || !( len = utf8_length( (const unsigned char *) text ) ) ) {
337 xcb_void_cookie_t r = { 0 };
339 return r;
342 buf = alloca( len * sizeof *buf );
344 for( i = 0, p = (const unsigned char *) text; i < len; i++ )
345 buf[ i ] = utf8_next( &p );
347 src = render_screens[ screen ].pic;
349 dest = xcb_generate_id( c );
350 xcb_render_create_picture( c, dest, drawable,
351 render_screens[ screen ].root_pictformat,
352 0, NULL );
354 if( clip )
355 xcb_render_set_picture_clip_rectangles( c, dest, 0, 0, 1, clip );
357 if( render_screens[ screen ].col != col ) {
358 rc.red = decoration_cols[ col ][ 0 ];
359 rc.green = decoration_cols[ col ][ 1 ];
360 rc.blue = decoration_cols[ col ][ 2 ];
361 rc.alpha = 0xFFFF;
362 rect.x = 0;
363 rect.y = 0;
364 rect.width = 1;
365 rect.height = 1;
366 xcb_render_fill_rectangles( c, XCB_RENDER_PICT_OP_SRC, src, rc, 1,
367 &rect );
369 render_screens[ screen ].col = col;
372 i = 0;
373 while( i < len ) {
374 uint32_t glyphids[ 0x80 ];
375 xcb_render_glyphinfo_t glyphs[ 0x80 ];
376 int num_glyphs; /* number of glyphs in AddGlyphs request */
377 /* Buffer for "data" parameter of AddGlyphs request. We want
378 this to be big enough to hold at least one glyph, but no
379 bigger than the biggest request the server will accept (once
380 the other parameters are included). All servers must
381 accept requests of 16,384 bytes or smaller (see X Window
382 System Protocol, section 8), so this is safe. */
383 uint8_t data[ 0x2000 ], *p;
384 /* Buffer for "glyphcmds" parameter of CompositeGlyphs16
385 request. We always send exactly one command, which has an
386 8-byte header and at most a 254 glyph string. */
387 uint8_t param[ 8 + ( 0xFE << 1 ) ], *out;
388 int num_chars; /* number of characters for CompositeGlyphs16 */
390 current_time++;
392 p = data;
393 num_glyphs = 0;
395 memset( param, 0, 4 );
396 *( (uint16_t *) ( param + 4 ) ) = x;
397 *( (uint16_t *) ( param + 6 ) ) = y;
398 out = param + 8;
399 num_chars = 0;
401 while( i < len && num_chars < 0xFF ) {
402 int dx, dy, index;
404 if( ( index = query_glyph( style, buf[ i ] ) ) < 0 ) {
405 /* Cache miss. */
406 int bitmap_size;
407 struct font *font;
408 FT_GlyphSlot slot;
410 if( ( index = replace_glyph( style, buf[ i ] ) ) < 0 )
411 /* Cache set full: spill the partial string to make
412 room for later glyphs. */
413 break;
415 if( !( font = lookup_font( style, buf[ i ] ) ) ||
416 FT_Load_Char( font->face, buf[ i ], font->load_flags |
417 FT_LOAD_RENDER ) ||
418 ( bitmap_size = ( ( font->face->glyph->bitmap.width +
419 3 ) & ~3 ) *
420 font->face->glyph->bitmap.rows ) > sizeof data ) {
421 /* We couldn't load the character, or it was so
422 huge that it won't fit in an empty AddGlyph
423 data buffer. We'll have to send the server
424 an empty glyph and carry on. */
425 slot = NULL;
426 bitmap_size = 0;
427 } else
428 slot = font->face->glyph;
430 if( ( p - data ) + bitmap_size > sizeof data ||
431 num_glyphs == sizeof glyphids / sizeof *glyphids ) {
432 /* Can't fit this glyph into the existing request:
433 transmit what we have... */
434 xcb_render_add_glyphs( c, glyphset, num_glyphs, glyphids,
435 glyphs, p - data, data );
437 /* ...and start building another AddGlyph request. */
438 p = data;
439 num_glyphs = 0;
442 glyphids[ num_glyphs ] = index;
444 if( slot ) {
445 FT_Bitmap bitmap;
447 FT_Bitmap_New( &bitmap );
448 FT_Bitmap_Convert( ftl, &slot->bitmap, &bitmap, 4 );
450 glyphs[ num_glyphs ].width = bitmap.width;
451 glyphs[ num_glyphs ].height = bitmap.rows;
452 glyphs[ num_glyphs ].x = -slot->bitmap_left;
453 glyphs[ num_glyphs ].y = slot->bitmap_top;
454 glyphs[ num_glyphs ].x_off =
455 ( slot->advance.x + 0x20 ) >> 6;
456 glyphs[ num_glyphs ].y_off =
457 ( slot->advance.y + 0x20 ) >> 6;
459 memcpy( p, bitmap.buffer, bitmap.pitch * bitmap.rows );
461 if( bitmap.num_grays != 0x100 ) {
462 unsigned char *c,
463 *end = p + bitmap.pitch * bitmap.rows;
465 for( c = p; c < end; c++ )
466 *c = ( (unsigned int) *c * 0xFF /
467 ( bitmap.num_grays - 1 ) );
470 p += bitmap.pitch * bitmap.rows;
472 FT_Bitmap_Done( ftl, &bitmap );
473 } else {
474 glyphs[ num_glyphs ].width = 0;
475 glyphs[ num_glyphs ].height = 0;
476 glyphs[ num_glyphs ].x = 0;
477 glyphs[ num_glyphs ].y = 0;
478 glyphs[ num_glyphs ].x_off = 0;
479 glyphs[ num_glyphs ].y_off = 0;
482 num_glyphs++;
485 *( (uint16_t *) out ) = index;
486 out += 2;
488 query_metrics( style, buf[ i ], &dx, &dy );
489 x += dx;
490 y += dy;
492 num_chars++;
493 i++;
496 if( num_glyphs ) {
497 xcb_render_add_glyphs( c, glyphset, num_glyphs, glyphids, glyphs,
498 p - data, data );
500 p = data;
501 num_glyphs = 0;
504 param[ 0 ] = num_chars;
506 xcb_render_composite_glyphs_16( c, XCB_RENDER_PICT_OP_OVER, src, dest,
507 XCB_NONE, glyphset, 0, 0, out - param,
508 param );
511 return xcb_render_free_picture( c, dest );
514 static xcb_void_cookie_t render_picture( xcb_drawable_t drawable, int screen,
515 int sx, int sy, int width, int height,
516 int dx, int dy,
517 xcb_render_picture_t src,
518 const xcb_rectangle_t *clip ) {
520 xcb_render_picture_t dest;
522 dest = xcb_generate_id( c );
523 xcb_render_create_picture( c, dest, drawable,
524 render_screens[ screen ].root_pictformat,
525 0, NULL );
527 if( clip )
528 xcb_render_set_picture_clip_rectangles( c, dest, 0, 0, 1, clip );
530 xcb_render_composite( c, XCB_RENDER_PICT_OP_OVER, src, XCB_NONE, dest,
531 sx, sy, 0, 0, dx, dy, width, height );
533 return xcb_render_free_picture( c, dest );
536 static int text_width( enum style_id style, const char *text ) {
538 const unsigned char *p = (const unsigned char *) text;
539 int width = 0;
541 while( *p ) {
542 int dx, dy;
544 query_metrics( style, utf8_next( &p ), &dx, &dy );
545 width += dx;
548 return width;
551 extern void render_update_window( struct gwm_window *window ) {
553 if( window->type == WINDOW_CHILDLESS )
554 return;
556 if( !window->cleared )
557 xcb_clear_area( c, FALSE, window->w, window->update.x, window->update.y,
558 window->update.width, window->update.height );
560 if( window->type == WINDOW_FRAME ) {
561 char *name = window->u.frame.child->u.managed.name;
563 render_text( window->w, window->screen, window == focus_frame ?
564 COL_TITLE_ACTIVE : COL_TITLE_INACTIVE,
565 button_size( window->u.frame.button, TRUE ) + 4,
566 TITLE_FONT_SIZE, name ? name : "(Untitled)",
567 STYLE_TITLE, &window->update );
568 } else if( window->type == WINDOW_MENUITEM ) {
569 struct gwm_window *menu = window->u.menuitem.menu;
570 int x = MENU_X_PAD;
572 if( window->u.menuitem.menu->u.menu.has_icons ) {
573 struct gwm_window *icon;
575 if( window->u.menuitem.icon &&
576 ( icon = lookup_window( window->u.menuitem.icon ) ) &&
577 icon->type == WINDOW_MANAGED && icon->u.managed.menu_icons )
578 render_picture( window->w, window->screen, 0, 0,
579 MENU_FONT_SIZE, MENU_FONT_SIZE, x, MENU_Y_PAD,
580 icon->u.managed.menu_icons[ window->screen ],
581 &window->update );
583 x += MENU_FONT_SIZE + MENU_X_PAD;
586 render_text( window->w, window->screen,
587 menu->u.menu.active_item >= 0 &&
588 menu->u.menu.items[ menu->u.menu.active_item ] == window ?
589 COL_MENU_ACTIVE_FORE : COL_MENU_INACTIVE_FORE,
590 x, MENU_FONT_SIZE, window->u.menuitem.label, STYLE_MENU,
591 &window->update );
592 } else if( window->type == WINDOW_FEEDBACK ) {
593 char text[ 32 ];
595 sprintf( text, "%dx%d", window->u.feedback.fb_width,
596 window->u.feedback.fb_height );
598 render_text( window->w, window->screen, COL_FEEDBACK_FORE,
599 ( FEEDBACK_WIDTH - text_width( STYLE_TITLE, text ) ) >> 1,
600 16, text, STYLE_TITLE, &window->update );
604 extern void render_window_size( struct gwm_window *window, int *width,
605 int *height ) {
607 switch( window->type ) {
608 case WINDOW_MENUITEM:
609 if( window->u.menuitem.label ) {
610 *width = text_width( STYLE_MENU, window->u.menuitem.label ) +
611 ( MENU_X_PAD << 1 );
612 *height = MENU_FONT_SIZE + ( MENU_Y_PAD << 1 );
614 if( window->u.menuitem.menu->u.menu.has_icons )
615 *width += MENU_FONT_SIZE + MENU_X_PAD;
616 } else {
617 *width = MENU_X_PAD << 1;
618 *height = MENU_Y_PAD << 1;
620 return;
622 case WINDOW_FEEDBACK:
623 *width = FEEDBACK_WIDTH;
624 *height = FEEDBACK_HEIGHT;
625 return;
627 default:
628 assert( FALSE );
632 static MALLOC uint8_t *assemble_image( int screen, int width, int height,
633 uint32_t *icon, int *len ) {
635 uint8_t *p, *linep, *out;
636 int x, y;
637 int bpp; /* bits per pixel */
638 int line; /* bytes per scanline */
640 bpp = render_screens[ screen ].pfmt->bits_per_pixel;
641 line = ( ( bpp * width +
642 render_screens[ screen ].pfmt->scanline_pad - 1 ) &
643 ~( render_screens[ screen ].pfmt->scanline_pad - 1 ) ) >> 3;
645 *len = line * height;
647 linep = out = xmalloc( *len );
649 for( y = 0; y < height; y++ ) {
650 p = linep;
651 for( x = 0; x < width; x++ ) {
652 uint32_t a = ( *icon >> 24 ) & 0xFF;
653 uint32_t r = ( *icon >> 16 ) & 0xFF;
654 uint32_t g = ( *icon >> 8 ) & 0xFF;
655 uint32_t b = *icon & 0xFF;
656 uint32_t pix;
657 int i;
659 if( gwm_screens[ screen ].root_visual->_class ==
660 XCB_VISUAL_CLASS_STATIC_GRAY ||
661 gwm_screens[ screen ].root_visual->_class ==
662 XCB_VISUAL_CLASS_GRAY_SCALE )
663 r = g = b = ( r * 0x4C8BU + g * 0x9646U + b * 0x1D2FU ) >> 16;
665 /* Premultiply alpha, and normalise to 32 bits. */
666 r = ( (unsigned int) r * a ) * ( 0x01010101 / 0xFF );
667 g = ( (unsigned int) g * a ) * ( 0x01010101 / 0xFF );
668 b = ( (unsigned int) b * a ) * ( 0x01010101 / 0xFF );
669 a *= 0x01010101;
671 pix = ( ( r >> ( 32 - render_screens[ screen ].rbits ) ) &
672 render_screens[ screen ].dfmt.red_mask ) <<
673 render_screens[ screen ].dfmt.red_shift;
674 pix |= ( ( g >> ( 32 - render_screens[ screen ].gbits ) ) &
675 render_screens[ screen ].dfmt.green_mask ) <<
676 render_screens[ screen ].dfmt.green_shift;
677 pix |= ( ( b >> ( 32 - render_screens[ screen ].bbits ) ) &
678 render_screens[ screen ].dfmt.blue_mask ) <<
679 render_screens[ screen ].dfmt.blue_shift;
680 pix |= ( ( a >> ( 32 - render_screens[ screen ].abits ) ) &
681 render_screens[ screen ].dfmt.alpha_mask ) <<
682 render_screens[ screen ].dfmt.alpha_shift;
684 if( image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST )
685 for( i = bpp; i; i -= 8 ) {
686 *p++ = ( pix & 0xFF );
687 pix >>= 8;
689 else
690 for( i = bpp; i; i -= 8 )
691 *p++ = ( ( pix >> ( i - 8 ) ) & 0xFF );
693 icon++;
695 linep += line;
698 return out;
701 extern void render_replace_icons( struct gwm_window *window, int num_icons,
702 int *widths, int *heights,
703 uint32_t **icons, xcb_pixmap_t *bitmaps ) {
705 int i;
707 if( window->u.managed.net_wm_icon && bitmaps )
708 /* Use old _NET_WM_ICON in preference to new WM_HINTS. */
709 return;
711 if( window->u.managed.full_icons ) {
712 for( i = 0; i < num_screens; i++ ) {
713 xcb_render_free_picture( c, window->u.managed.full_icons[ i ] );
714 xcb_render_free_picture( c, window->u.managed.menu_icons[ i ] );
717 free( window->u.managed.full_icons );
718 free( window->u.managed.menu_icons );
721 if( num_icons ) {
722 int i, len, best_full, full_size, best_menu, menu_size;
723 xcb_pixmap_t full_map, menu_map, temp_map;
724 xcb_render_picture_t temp;
725 xcb_render_transform_t t;
726 xcb_gcontext_t gc;
727 uint8_t *image;
729 best_full = best_menu = 0;
731 if( widths[ 0 ] > heights[ 0 ] )
732 full_size = menu_size = widths[ 0 ];
733 else
734 full_size = menu_size = heights[ 0 ];
736 for( i = 1; i < num_icons; i++ ) {
737 int s = widths[ i ] > heights[ i ] ? widths[ i ] : heights[ i ];
739 if( ( full_size < ICON_SIZE && s > full_size ) ||
740 ( full_size > ICON_SIZE && s >= ICON_SIZE && s < full_size ) )
741 best_full = i, full_size = s;
743 if( ( menu_size < MENU_FONT_SIZE && s > menu_size ) ||
744 ( menu_size > MENU_FONT_SIZE && s >= MENU_FONT_SIZE &&
745 s < menu_size ) )
746 best_menu = i, menu_size = s;
749 full_map = xcb_generate_id( c );
750 menu_map = xcb_generate_id( c );
751 temp_map = best_menu == best_full ? full_map : xcb_generate_id( c );
753 gc = xcb_generate_id( c );
755 window->u.managed.full_icons =
756 xmalloc( num_screens * sizeof *window->u.managed.full_icons );
757 window->u.managed.menu_icons =
758 xmalloc( num_screens * sizeof *window->u.managed.menu_icons );
760 for( i = 0; i < num_screens; i++ ) {
761 xcb_create_pixmap( c, render_screens[ i ].direct_depth,
762 full_map, screens[ i ]->root,
763 widths[ best_full ], heights[ best_full ] );
764 if( temp_map != full_map )
765 xcb_create_pixmap( c, render_screens[ i ].direct_depth,
766 temp_map, screens[ i ]->root,
767 widths[ best_menu ], heights[ best_menu ] );
769 xcb_create_pixmap( c, render_screens[ i ].direct_depth,
770 menu_map, screens[ i ]->root,
771 MENU_FONT_SIZE, MENU_FONT_SIZE );
773 if( icons ) {
774 xcb_create_gc( c, gc, full_map, 0, NULL );
776 image = assemble_image( i, widths[ best_full ],
777 heights[ best_full ],
778 icons[ best_full ], &len );
779 put_image( full_map, gc, widths[ best_full ],
780 heights[ best_full ], 0, 0, 0,
781 render_screens[ i ].direct_depth, len, image );
783 free( image );
785 if( temp_map != full_map ) {
786 image = assemble_image( i, widths[ best_menu ],
787 heights[ best_menu ],
788 icons[ best_menu ], &len );
789 put_image( temp_map, gc, widths[ best_menu ],
790 heights[ best_menu ], 0, 0, 0,
791 render_screens[ i ].direct_depth, len, image );
792 free( image );
794 } else {
795 uint32_t values[ 3 ];
797 values[ 0 ] = render_screens[ i ].dfmt.alpha_mask <<
798 render_screens[ i ].dfmt.alpha_shift;
799 values[ 1 ] = ( render_screens[ i ].dfmt.alpha_mask <<
800 render_screens[ i ].dfmt.alpha_shift ) |
801 ( render_screens[ i ].dfmt.red_mask <<
802 render_screens[ i ].dfmt.red_shift ) |
803 ( render_screens[ i ].dfmt.green_mask <<
804 render_screens[ i ].dfmt.green_shift ) |
805 ( render_screens[ i ].dfmt.blue_mask <<
806 render_screens[ i ].dfmt.blue_shift );
807 xcb_create_gc( c, gc, full_map, XCB_GC_FOREGROUND |
808 XCB_GC_BACKGROUND, values );
810 xcb_copy_plane( c, bitmaps[ best_full ], full_map, gc, 0, 0,
811 0, 0, widths[ best_full ],
812 heights[ best_full ], 1 );
814 if( bitmaps[ 1 ] ) {
815 /* We also have a mask bitmap: apply logical AND to turn
816 the appropriate pixels transparent. */
817 values[ 0 ] = XCB_GX_AND; /* function */
818 values[ 1 ] = 0xFFFFFFFF; /* foreground */
819 values[ 2 ] = 0; /* background */
820 xcb_change_gc( c, gc, XCB_GC_FUNCTION | XCB_GC_FOREGROUND |
821 XCB_GC_BACKGROUND, values );
823 xcb_copy_plane( c, bitmaps[ 1 ], full_map, gc, 0, 0,
824 0, 0, widths[ best_full ],
825 heights[ best_full ], 1 );
829 window->u.managed.full_icons[ i ] = xcb_generate_id( c );
830 window->u.managed.menu_icons[ i ] = xcb_generate_id( c );
832 xcb_render_create_picture( c, window->u.managed.full_icons[ i ],
833 full_map,
834 render_screens[ i ].direct_pictformat,
835 0, NULL );
836 xcb_render_create_picture( c, window->u.managed.menu_icons[ i ],
837 menu_map,
838 render_screens[ i ].direct_pictformat,
839 0, NULL );
841 if( temp_map == full_map )
842 temp = window->u.managed.full_icons[ i ];
843 else {
844 temp = xcb_generate_id( c );
845 xcb_render_create_picture(
846 c, temp, temp_map, render_screens[ i ].direct_pictformat,
847 0, NULL );
850 xcb_render_set_picture_filter( c, temp, 4, "best", 0, NULL );
851 t.matrix11 = 0x10000 * menu_size / MENU_FONT_SIZE;
852 t.matrix12 = 0;
853 t.matrix13 = 0x80000;
854 t.matrix21 = 0;
855 t.matrix22 = t.matrix11;
856 t.matrix23 = 0;
857 t.matrix31 = 0;
858 t.matrix32 = 0;
859 t.matrix33 = 0x10000;
860 if( widths[ best_menu ] < heights[ best_menu ] ) {
861 t.matrix12 = 0;
862 t.matrix13 = ( widths[ best_menu ] - heights[ best_menu ] )
863 << 15;
864 } else {
865 t.matrix12 = ( heights[ best_menu ] - widths[ best_menu ] )
866 << 15;
867 t.matrix13 = 0;
869 xcb_render_set_picture_transform( c, temp, t );
870 xcb_render_composite( c, XCB_RENDER_PICT_OP_SRC, temp, XCB_NONE,
871 window->u.managed.menu_icons[ i ], 0, 0,
872 0, 0, 0, 0, MENU_FONT_SIZE, MENU_FONT_SIZE );
874 xcb_render_set_picture_filter( c, window->u.managed.full_icons[ i ],
875 4, "good", 0, NULL );
876 t.matrix11 = t.matrix22 = 0x10000 * full_size / ICON_SIZE;
877 if( widths[ best_full ] < heights[ best_full ] ) {
878 t.matrix12 = 0;
879 t.matrix13 = ( widths[ best_full ] - heights[ best_full ] )
880 << 15;
881 } else {
882 t.matrix12 = ( heights[ best_full ] - widths[ best_full ] )
883 << 15;
884 t.matrix13 = 0;
886 xcb_render_set_picture_transform(
887 c, window->u.managed.full_icons[ i ], t );
889 xcb_free_pixmap( c, full_map );
890 xcb_free_pixmap( c, menu_map );
891 if( temp_map != full_map ) {
892 xcb_free_pixmap( c, temp_map );
893 xcb_render_free_picture( c, temp );
896 xcb_free_gc( c, gc );
899 window->u.managed.net_wm_icon = icons != NULL;
900 } else {
901 if( window->u.managed.net_wm_icon )
902 /* We've lost the _NET_WM_ICON property. If the window still
903 has a plain WM_HINTS icon, then fall back to that. */
904 async_get_property( window, PROP_WM_HINTS );
906 window->u.managed.full_icons = window->u.managed.menu_icons = NULL;
907 window->u.managed.net_wm_icon = FALSE;
911 static uint16_t luma( int col ) {
913 return ( decoration_cols[ col ][ 0 ] * 0x4C8BU +
914 decoration_cols[ col ][ 1 ] * 0x9646U +
915 decoration_cols[ col ][ 2 ] * 0x1D2FU ) >> 16;
918 static void handle_alloc_color( unsigned int sequence, void *reply,
919 xcb_generic_error_t *error,
920 union callback_param p ) {
922 xcb_alloc_color_reply_t *r = reply;
923 int screen = p.l >> 16, col = p.l & 0xFFFF;
925 if( error ) {
926 if( error->error_code != XCB_ALLOC )
927 show_error( error );
929 /* No standard RGB map available, and we couldn't allocate a shared
930 colour. Fall back to black or white. */
931 gwm_screens[ screen ].pixels[ col ] = luma( col ) >= 0x8000 ?
932 screens[ screen ]->white_pixel : screens[ screen ]->black_pixel;
934 free( error );
937 if( reply ) {
938 gwm_screens[ screen ].pixels[ col ] = r->pixel;
940 free( reply );
944 static void handle_get_default_map( unsigned int sequence, void *reply,
945 xcb_generic_error_t *error,
946 union callback_param p ) {
948 int col, got_cols = FALSE;
950 if( error ) {
951 show_error( error );
953 free( error );
956 if( reply ) {
957 xcb_get_property_reply_t *prop = reply;
958 struct std_cmap {
959 /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
960 xcb_colormap_t colormap;
961 uint32_t red_max, red_mult, green_max, green_mult, blue_max,
962 blue_mult, base_pixel;
963 xcb_visualid_t visual_id;
964 uint32_t kill_id;
965 } *std_cmaps = xcb_get_property_value( prop );
966 int num_cmaps = ( prop->value_len << 2 ) / sizeof *std_cmaps;
967 int i;
969 if( prop->format == 32 )
970 for( i = 0; i < num_cmaps; i++ )
971 if( std_cmaps[ i ].visual_id ==
972 gwm_screens[ p.l ].root_visual->visual_id ) {
974 for( col = 0; col < NUM_COLS; col++ ) {
975 int r0, g0, b0, r, g, b;
977 if( gwm_screens[ p.l ].root_visual->_class ==
978 XCB_VISUAL_CLASS_STATIC_GRAY ||
979 gwm_screens[ p.l ].root_visual->_class ==
980 XCB_VISUAL_CLASS_GRAY_SCALE ) {
981 r0 = luma( col );
982 g0 = b0 = 0;
983 } else {
984 r0 = decoration_cols[ col ][ 0 ];
985 g0 = decoration_cols[ col ][ 1 ];
986 b0 = decoration_cols[ col ][ 2 ];
989 r = ( r0 * std_cmaps[ i ].red_max + 0x8000 ) >> 16;
990 g = ( g0 * std_cmaps[ i ].green_max + 0x8000 ) >> 16;
991 b = ( b0 * std_cmaps[ i ].blue_max + 0x8000 ) >> 16;
993 gwm_screens[ p.l ].pixels[ col ] =
994 std_cmaps[ i ].base_pixel +
995 r * std_cmaps[ i ].red_mult +
996 g * std_cmaps[ i ].green_mult +
997 b * std_cmaps[ i ].blue_mult;
1000 got_cols = TRUE;
1001 break;
1004 free( reply );
1007 if( !got_cols )
1008 /* No useful default maps found; try allocating directly. */
1009 for( col = 0; col < NUM_COLS; col++ ) {
1010 int r, g, b;
1011 union callback_param cp;
1013 if( gwm_screens[ p.l ].root_visual->_class ==
1014 XCB_VISUAL_CLASS_STATIC_GRAY ||
1015 gwm_screens[ p.l ].root_visual->_class ==
1016 XCB_VISUAL_CLASS_GRAY_SCALE )
1017 r = g = b = luma( col );
1018 else {
1019 r = decoration_cols[ col ][ 0 ];
1020 g = decoration_cols[ col ][ 1 ];
1021 b = decoration_cols[ col ][ 2 ];
1024 cp.l = ( p.l << 16 ) | col;
1025 /* Ideally, we would like a guarantee that this operation will
1026 complete before the pixel values are ever looked up. In
1027 practice, it will, and it is too messy to insert a
1028 sync_with_callback() before every single pixel reference. */
1029 handle_async_reply( xcb_alloc_color(
1030 c, screens[ p.l ]->default_colormap,
1031 r, g, b ).sequence, handle_alloc_color,
1032 cp );
1036 static INIT void decorate_compat_init( void ) {
1038 int i;
1039 const char *cursor_font_name = "cursor";
1040 xcb_font_t cursor_font;
1041 static INITD const int cursor_glyphs[ NUM_CURSORS ] = {
1042 134, /* CURSOR_TL */
1043 138, /* CURSOR_T */
1044 136, /* CURSOR_TR */
1045 70, /* CURSOR_L */
1046 52, /* CURSOR_C */
1047 96, /* CURSOR_R */
1048 12, /* CURSOR_BL */
1049 16, /* CURSOR_B */
1050 14, /* CURSOR_BR */
1051 68, /* CURSOR_ARROW */
1052 88 /* CURSOR_DESTROY */
1055 cursor_font = xcb_generate_id( c );
1056 xcb_open_font( c, cursor_font, strlen( cursor_font_name ),
1057 cursor_font_name );
1058 for( i = 0; i < NUM_CURSORS; i++ ) {
1059 cursors[ i ] = xcb_generate_id( c );
1060 xcb_create_glyph_cursor( c, cursors[ i ], cursor_font, cursor_font,
1061 cursor_glyphs[ i ], cursor_glyphs[ i ] | 1,
1062 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
1064 xcb_close_font( c, cursor_font );
1066 /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
1067 for( i = 0; i < num_screens; i++ ) {
1068 union callback_param cp;
1070 cp.l = i;
1071 handle_async_reply( xcb_get_property( c, FALSE, screens[ i ]->root,
1072 RGB_DEFAULT_MAP, RGB_COLOR_MAP,
1073 0, 0x10000 ).sequence,
1074 handle_get_default_map, cp );
1078 extern INIT int decorate_render_init( void ) {
1080 xcb_render_query_version_cookie_t render_cookie;
1081 xcb_render_query_pict_formats_cookie_t formats_cookie;
1082 xcb_render_query_version_reply_t *render_reply;
1083 xcb_render_query_pict_formats_reply_t *formats_reply;
1084 xcb_render_pictforminfo_t *formats;
1085 xcb_render_pictscreen_iterator_t siter;
1086 FT_Error err;
1087 FcFontSet *sets[ NUM_STYLES ];
1088 FcPattern *style_pats[ NUM_STYLES ];
1089 FcChar32 *hashes;
1090 FcResult r;
1091 int i, total;
1092 xcb_pixmap_t pixmap;
1093 uint32_t n;
1094 uint32_t icon_sizes[ 6 ];
1096 image_byte_order = xcb_get_setup( c )->image_byte_order;
1098 render_cookie = xcb_render_query_version( c, XCB_RENDER_MAJOR_VERSION,
1099 XCB_RENDER_MINOR_VERSION );
1101 formats_cookie = xcb_render_query_pict_formats( c );
1103 xcb_flush( c ); /* BLOCK */
1105 render_reply = xcb_render_query_version_reply( c, render_cookie, NULL );
1107 if( !render_reply )
1108 return -1;
1110 if( !render_reply->major_version && render_reply->minor_version < 1 ) {
1111 /* We need at least version 0.6, for SetPictureTransform support. */
1112 free( render_reply );
1114 return -1;
1117 free( render_reply );
1119 formats_reply = xcb_render_query_pict_formats_reply( c, formats_cookie,
1120 NULL );
1122 formats = xcb_render_query_pict_formats_formats( formats_reply );
1124 /* First of all, look for an 8 bit direct alpha PictFormat. */
1125 for( i = 0; i < formats_reply->num_formats; i++ )
1126 if( formats[ i ].type == XCB_RENDER_PICT_TYPE_DIRECT &&
1127 formats[ i ].direct.alpha_mask == 0xFF &&
1128 !formats[ i ].direct.red_mask &&
1129 !formats[ i ].direct.green_mask &&
1130 !formats[ i ].direct.blue_mask ) {
1131 fmt_a8 = formats[ i ].id;
1132 memcpy( &dfmt_a8, &formats[ i ].direct, sizeof dfmt_a8 );
1133 break;
1136 if( !fmt_a8 ) {
1137 /* The A8 PictFormat is guaranteed by the RENDER protocol
1138 (section 7). We can't cope without it, since that's what
1139 our glyphset uses. */
1140 free( formats_reply );
1142 return -1;
1145 render_screens = xmalloc( num_screens * sizeof *render_screens );
1147 /* Next, look for a PictFormat matching each root visual. */
1148 for( i = 0, siter = xcb_render_query_pict_formats_screens_iterator(
1149 formats_reply ); siter.rem;
1150 i++, xcb_render_pictscreen_next( &siter ) ) {
1151 xcb_render_pictdepth_iterator_t diter;
1153 render_screens[ i ].root_pictformat = 0;
1155 for( diter = xcb_render_pictscreen_depths_iterator( siter.data );
1156 diter.rem; xcb_render_pictdepth_next( &diter ) ) {
1157 xcb_render_pictvisual_iterator_t viter;
1159 for( viter = xcb_render_pictdepth_visuals_iterator(
1160 diter.data ); viter.rem;
1161 xcb_render_pictvisual_next( &viter ) )
1162 if( viter.data->visual ==
1163 gwm_screens[ i ].root_visual->visual_id ) {
1164 render_screens[ i ].root_pictformat = viter.data->format;
1165 goto next_screen;
1169 /* Oh dear. We found a screen with no PictFormat corresponding
1170 to its root visual, so we can't render to it. It's much too
1171 messy to try rendering on some screens and core graphics on
1172 others, so give up rendering entirely. */
1173 free( formats_reply );
1174 free( render_screens );
1176 return -1;
1178 next_screen:
1182 /* Next, look for the direct PictFormat most suitable for source
1183 pictures rendering to each root visual. */
1184 for( i = 0, siter = xcb_render_query_pict_formats_screens_iterator(
1185 formats_reply ); siter.rem;
1186 i++, xcb_render_pictscreen_next( &siter ) ) {
1187 xcb_render_pictdepth_iterator_t diter;
1188 int best_score = -1;
1189 int best_format;
1190 int red_mask, green_mask, blue_mask;
1192 if( gwm_screens[ i ].root_visual->_class ==
1193 XCB_VISUAL_CLASS_TRUE_COLOR ||
1194 gwm_screens[ i ].root_visual->_class ==
1195 XCB_VISUAL_CLASS_DIRECT_COLOR ) {
1196 for( red_mask = gwm_screens[ i ].root_visual->red_mask;
1197 !( red_mask & 1 ); red_mask >>= 1 )
1199 for( green_mask = gwm_screens[ i ].root_visual->green_mask;
1200 !( green_mask & 1 ); green_mask >>= 1 )
1202 for( blue_mask = gwm_screens[ i ].root_visual->blue_mask;
1203 !( blue_mask & 1 ); blue_mask >>= 1 )
1205 } else
1206 red_mask = green_mask = blue_mask = -1;
1208 for( diter = xcb_render_pictscreen_depths_iterator( siter.data );
1209 diter.rem; xcb_render_pictdepth_next( &diter ) ) {
1210 xcb_depth_iterator_t pixmap_depths;
1211 xcb_render_pictvisual_iterator_t viter;
1212 int format;
1214 for( pixmap_depths =
1215 xcb_screen_allowed_depths_iterator( screens[ i ] );
1216 pixmap_depths.rem; xcb_depth_next( &pixmap_depths ) )
1217 if( pixmap_depths.data->depth == diter.data->depth )
1218 break;
1220 if( !pixmap_depths.rem )
1221 /* We're not allowed to allocate pixmaps of this depth:
1222 don't bother with this PictFormat. */
1223 continue;
1225 for( viter = xcb_render_pictdepth_visuals_iterator(
1226 diter.data ); viter.rem;
1227 xcb_render_pictvisual_next( &viter ) ) {
1228 int score;
1230 for( format = 0; format < formats_reply->num_formats;
1231 format++ )
1232 if( formats[ format ].id == viter.data->format &&
1233 formats[ format ].type ==
1234 XCB_RENDER_PICT_TYPE_DIRECT &&
1235 formats[ format ].depth >= 8 )
1236 /* Insisting that the depth is at least 8 is somewhat
1237 arbitary, but since it's less work for us if we
1238 don't have to consider assembling non-byte-aligned
1239 images, and it's highly questionable whether
1240 alpha composition is particularly effective
1241 on 1- and 4-bit depths, we won't even try. */
1242 break;
1244 if( format == formats_reply->num_formats )
1245 continue;
1247 /* Aha! We've found a direct PictFormat that we can
1248 allocate pixmaps for. Decide how good it is... */
1249 score = 0;
1251 if( formats[ format ].direct.alpha_mask )
1252 /* Has an alpha component. Excellent. */
1253 score |= 0x80;
1255 if( formats[ format ].direct.red_mask == red_mask &&
1256 formats[ format ].direct.green_mask == green_mask &&
1257 formats[ format ].direct.blue_mask == blue_mask ) {
1258 /* The RGB component depths match the root. Good. */
1259 score |= 0x40;
1261 if( formats[ format ].direct.red_mask <<
1262 formats[ format ].direct.red_shift ==
1263 gwm_screens[ i ].root_visual->red_mask &&
1264 formats[ format ].direct.green_mask <<
1265 formats[ format ].direct.green_shift ==
1266 gwm_screens[ i ].root_visual->green_mask &&
1267 formats[ format ].direct.blue_mask <<
1268 formats[ format ].direct.blue_shift ==
1269 gwm_screens[ i ].root_visual->blue_mask )
1270 /* The RGB layouts match. Useful. */
1271 score |= 0x20;
1274 if( formats[ format ].depth == screens[ i ]->root_depth )
1275 /* The depth matches. Slightly helpful. */
1276 score |= 0x10;
1278 /* All else being equal, prefer deeper alpha channels. */
1279 if( formats[ format ].direct.alpha_mask >= 0xFFFF )
1280 score |= 0x0F;
1281 else {
1282 int n;
1284 for( n = formats[ format ].direct.alpha_mask >> 1; n;
1285 n >>= 1 )
1286 score++;
1289 if( score > best_score ) {
1290 best_score = score;
1291 best_format = format;
1296 if( best_score >= 0 ) {
1297 xcb_format_iterator_t fiter;
1298 int mask;
1300 render_screens[ i ].direct_pictformat = formats[ best_format ].id;
1301 render_screens[ i ].direct_depth = formats[ best_format ].depth;
1302 memcpy( &render_screens[ i ].dfmt, &formats[ best_format ].direct,
1303 sizeof render_screens[ i ].dfmt );
1305 for( fiter = xcb_setup_pixmap_formats_iterator(
1306 xcb_get_setup( c ) ); fiter.rem;
1307 xcb_format_next( &fiter ) )
1308 if( fiter.data->depth == formats[ best_format ].depth ) {
1309 render_screens[ i ].pfmt = fiter.data;
1310 break;
1313 assert( fiter.rem );
1315 for( render_screens[ i ].rbits = 0,
1316 mask = render_screens[ i ].dfmt.red_mask;
1317 mask; mask >>= 1, render_screens[ i ].rbits++ )
1320 for( render_screens[ i ].gbits = 0,
1321 mask = render_screens[ i ].dfmt.green_mask;
1322 mask; mask >>= 1, render_screens[ i ].gbits++ )
1325 for( render_screens[ i ].bbits = 0,
1326 mask = render_screens[ i ].dfmt.blue_mask;
1327 mask; mask >>= 1, render_screens[ i ].bbits++ )
1330 for( render_screens[ i ].abits = 0,
1331 mask = render_screens[ i ].dfmt.alpha_mask;
1332 mask; mask >>= 1, render_screens[ i ].abits++ )
1334 } else {
1335 /* Oh dear. We couldn't find any suitable direct PictFormat to
1336 use as a source rendering to the root visual. */
1337 free( formats_reply );
1338 free( render_screens );
1340 return -1;
1344 free( formats_reply );
1346 if( !FcInit() ) {
1347 warning( "font configuration error" );
1349 free( render_screens );
1351 return -1;
1354 if( ( err = FT_Init_FreeType( &ftl ) ) ) {
1355 warning( "typeface initialisation error %d", err );
1357 free( render_screens );
1358 FcFini();
1360 return -1;
1363 for( i = 0, total = 0; i < NUM_STYLES; i++ ) {
1364 if( !( style_pats[ i ] = FcNameParse( style_names[ i ] ) ) ) {
1365 warning( "could not parse font \"%s\"\n", style_names[ i ] );
1367 free( render_screens );
1368 FcFini();
1369 FT_Done_FreeType( ftl );
1371 return -1;
1374 FcConfigSubstitute( NULL, style_pats[ i ], FcMatchPattern );
1375 FcDefaultSubstitute( style_pats[ i ] );
1377 sets[ i ] = FcFontSort( NULL, style_pats[ i ], TRUE,
1378 &styles[ i ].charset, &r );
1380 total += sets[ i ]->nfont;
1383 fonts = xmalloc( total * sizeof *fonts );
1384 hashes = alloca( total * sizeof *hashes );
1386 for( i = 0; i < NUM_STYLES; i++ ) {
1387 int j;
1389 styles[ i ].fonts = xmalloc( sets[ i ]->nfont *
1390 sizeof (struct font *) );
1392 for( j = 0; j < sets[ i ]->nfont; j++ ) {
1393 FcPattern *pat;
1395 if( ( pat = FcFontRenderPrepare( NULL, style_pats[ i ],
1396 sets[ i ]->fonts[ j ] ) ) ) {
1397 FcChar32 hash = FcPatternHash( pat );
1398 int search;
1400 for( search = 0; search < num_fonts; search++ )
1401 if( hashes[ search ] == hash &&
1402 FcPatternEqual( fonts[ search ].pattern, pat ) ) {
1403 FcPatternDestroy( pat );
1404 break;
1407 if( search == num_fonts ) {
1408 hashes[ num_fonts ] = hash;
1409 fonts[ num_fonts ].pattern = pat;
1410 FcPatternGetCharSet( pat, FC_CHARSET, 0,
1411 &fonts[ num_fonts ].charset );
1412 fonts[ num_fonts ].face = NULL;
1413 num_fonts++;
1416 styles[ i ].fonts[ styles[ i ].num_fonts ] = search;
1417 styles[ i ].num_fonts++;
1421 styles[ i ].fonts = xrealloc( styles[ i ].fonts,
1422 styles[ i ].num_fonts *
1423 sizeof (struct font *) );
1425 FcFontSetDestroy( sets[ i ] );
1426 FcPatternDestroy( style_pats[ i ] );
1429 fonts = xrealloc( fonts, num_fonts * sizeof *fonts );
1431 glyphset = xcb_generate_id( c );
1432 xcb_render_create_glyph_set( c, glyphset, fmt_a8 );
1434 pixmap = xcb_generate_id( c );
1436 n = XCB_RENDER_REPEAT_NORMAL;
1438 /* A WM_ICON_SIZE property (see ICCCM 2.0, section 4.1.3.2). */
1439 icon_sizes[ 0 ] = MENU_FONT_SIZE; /* min_width */
1440 icon_sizes[ 1 ] = MENU_FONT_SIZE; /* min_height */
1441 icon_sizes[ 2 ] = ICON_SIZE; /* max_width */
1442 icon_sizes[ 3 ] = ICON_SIZE; /* max_height */
1443 icon_sizes[ 4 ] = 1; /* width_inc */
1444 icon_sizes[ 5 ] = 1; /* height_inc */
1445 for( i = 0; i < num_screens; i++ ) {
1446 xcb_create_pixmap( c, screens[ i ]->root_depth, pixmap,
1447 screens[ i ]->root, 1, 1 );
1449 render_screens[ i ].pic = xcb_generate_id( c );
1450 xcb_render_create_picture( c, render_screens[ i ].pic, pixmap,
1451 render_screens[ i ].root_pictformat,
1452 XCB_RENDER_CP_REPEAT, &n );
1453 render_screens[ i ].col = -1;
1455 xcb_free_pixmap( c, pixmap );
1457 xcb_change_property( c, XCB_PROP_MODE_REPLACE, screens[ i ]->root,
1458 WM_ICON_SIZE, WM_ICON_SIZE, 32, 6,
1459 icon_sizes );
1462 decorate_compat_init(); /* FIXME this is for pixel values (backgrounds)
1463 and cursors */
1465 return 0;
1468 extern void decorate_render_done( void ) {
1470 #if DEBUG
1471 int i;
1473 for( i = 0; i < NUM_STYLES; i++ ) {
1474 FcCharSetDestroy( styles[ i ].charset );
1475 free( styles[ i ].fonts );
1478 for( i = 0; i < num_fonts; i++ ) {
1479 FcPatternDestroy( fonts[ i ].pattern );
1480 FT_Done_Face( fonts[ i ].face );
1483 free( fonts );
1485 FcFini();
1487 FT_Done_FreeType( ftl );
1489 free( render_screens );
1490 #endif