Map, raise and activate the window selected in the window menu.
[gwm.git] / decorate-render.c
blob3ae193a24869a96503d118a27ec56b04222964ea
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( 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->cleared )
554 xcb_clear_area( c, FALSE, window->w, window->update.x, window->update.y,
555 window->update.width, window->update.height );
557 if( window->type == WINDOW_FRAME ) {
558 char *name = window->u.frame.child->u.managed.name;
560 render_text( window->w, window->screen, window == focus_frame ?
561 COL_TITLE_ACTIVE : COL_TITLE_INACTIVE,
562 button_size( window->u.frame.button, TRUE ) + 4,
563 TITLE_FONT_SIZE, name ? name : "(Untitled)",
564 STYLE_TITLE, &window->update );
565 } else if( window->type == WINDOW_MENUITEM ) {
566 struct gwm_window *menu = window->u.menuitem.menu;
567 int x = MENU_X_PAD;
569 if( window->u.menuitem.menu->u.menu.has_icons ) {
570 struct gwm_window *icon;
572 if( window->u.menuitem.icon &&
573 ( icon = lookup_window( window->u.menuitem.icon ) ) &&
574 icon->type == WINDOW_MANAGED && icon->u.managed.menu_icons )
575 render_picture( window->w, window->screen, 0, 0,
576 MENU_FONT_SIZE, MENU_FONT_SIZE, x, MENU_Y_PAD,
577 icon->u.managed.menu_icons[ window->screen ],
578 &window->update );
580 x += MENU_FONT_SIZE + MENU_X_PAD;
583 render_text( window->w, window->screen,
584 menu->u.menu.active_item >= 0 &&
585 menu->u.menu.items[ menu->u.menu.active_item ] == window ?
586 COL_MENU_ACTIVE_FORE : COL_MENU_INACTIVE_FORE,
587 x, MENU_FONT_SIZE, window->u.menuitem.label, STYLE_MENU,
588 &window->update );
589 } else if( window->type == WINDOW_FEEDBACK ) {
590 char text[ 32 ];
592 sprintf( text, "%dx%d", window->u.feedback.fb_width,
593 window->u.feedback.fb_height );
595 render_text( window->w, window->screen, COL_FEEDBACK_FORE,
596 ( FEEDBACK_WIDTH - text_width( STYLE_TITLE, text ) ) >> 1,
597 16, text, STYLE_TITLE, &window->update );
601 extern void render_window_size( struct gwm_window *window, int *width,
602 int *height ) {
604 switch( window->type ) {
605 case WINDOW_MENUITEM:
606 if( window->u.menuitem.label ) {
607 *width = text_width( STYLE_MENU, window->u.menuitem.label ) +
608 ( MENU_X_PAD << 1 );
609 *height = MENU_FONT_SIZE + ( MENU_Y_PAD << 1 );
611 if( window->u.menuitem.menu->u.menu.has_icons )
612 *width += MENU_FONT_SIZE + MENU_X_PAD;
613 } else {
614 *width = MENU_X_PAD << 1;
615 *height = MENU_Y_PAD << 1;
617 return;
619 case WINDOW_FEEDBACK:
620 *width = FEEDBACK_WIDTH;
621 *height = FEEDBACK_HEIGHT;
622 return;
624 default:
625 assert( FALSE );
629 static MALLOC uint8_t *assemble_image( int screen, int width, int height,
630 uint32_t *icon, int *len ) {
632 uint8_t *p, *linep, *out;
633 int x, y;
634 int bpp; /* bits per pixel */
635 int line; /* bytes per scanline */
637 bpp = render_screens[ screen ].pfmt->bits_per_pixel;
638 line = ( ( bpp * width +
639 render_screens[ screen ].pfmt->scanline_pad - 1 ) &
640 ~( render_screens[ screen ].pfmt->scanline_pad - 1 ) ) >> 3;
642 *len = line * height;
644 linep = out = xmalloc( *len );
646 for( y = 0; y < height; y++ ) {
647 p = linep;
648 for( x = 0; x < width; x++ ) {
649 uint32_t a = ( *icon >> 24 ) & 0xFF;
650 uint32_t r = ( *icon >> 16 ) & 0xFF;
651 uint32_t g = ( *icon >> 8 ) & 0xFF;
652 uint32_t b = *icon & 0xFF;
653 uint32_t pix;
654 int i;
656 if( gwm_screens[ screen ].root_visual->_class ==
657 XCB_VISUAL_CLASS_STATIC_GRAY ||
658 gwm_screens[ screen ].root_visual->_class ==
659 XCB_VISUAL_CLASS_GRAY_SCALE )
660 r = g = b = ( r * 0x4C8BU + g * 0x9646U + b * 0x1D2FU ) >> 16;
662 /* Premultiply alpha, and normalise to 32 bits. */
663 r = ( (unsigned int) r * a ) * ( 0x01010101 / 0xFF );
664 g = ( (unsigned int) g * a ) * ( 0x01010101 / 0xFF );
665 b = ( (unsigned int) b * a ) * ( 0x01010101 / 0xFF );
666 a *= 0x01010101;
668 pix = ( ( r >> ( 32 - render_screens[ screen ].rbits ) ) &
669 render_screens[ screen ].dfmt.red_mask ) <<
670 render_screens[ screen ].dfmt.red_shift;
671 pix |= ( ( g >> ( 32 - render_screens[ screen ].gbits ) ) &
672 render_screens[ screen ].dfmt.green_mask ) <<
673 render_screens[ screen ].dfmt.green_shift;
674 pix |= ( ( b >> ( 32 - render_screens[ screen ].bbits ) ) &
675 render_screens[ screen ].dfmt.blue_mask ) <<
676 render_screens[ screen ].dfmt.blue_shift;
677 pix |= ( ( a >> ( 32 - render_screens[ screen ].abits ) ) &
678 render_screens[ screen ].dfmt.alpha_mask ) <<
679 render_screens[ screen ].dfmt.alpha_shift;
681 if( image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST )
682 for( i = bpp; i; i -= 8 ) {
683 *p++ = ( pix & 0xFF );
684 pix >>= 8;
686 else
687 for( i = bpp; i; i -= 8 )
688 *p++ = ( ( pix >> ( i - 8 ) ) & 0xFF );
690 icon++;
692 linep += line;
695 return out;
698 extern void render_replace_icons( struct gwm_window *window, int num_icons,
699 int *widths, int *heights,
700 uint32_t **icons, xcb_pixmap_t *bitmaps ) {
702 int i;
704 if( window->u.managed.net_wm_icon && bitmaps )
705 /* Use old _NET_WM_ICON in preference to new WM_HINTS. */
706 return;
708 if( window->u.managed.full_icons ) {
709 for( i = 0; i < num_screens; i++ ) {
710 xcb_render_free_picture( c, window->u.managed.full_icons[ i ] );
711 xcb_render_free_picture( c, window->u.managed.menu_icons[ i ] );
714 free( window->u.managed.full_icons );
715 free( window->u.managed.menu_icons );
718 if( num_icons ) {
719 int i, len, best_full, full_size, best_menu, menu_size;
720 xcb_pixmap_t full_map, menu_map, temp_map;
721 xcb_render_picture_t temp;
722 xcb_render_transform_t t;
723 xcb_gcontext_t gc;
724 uint8_t *image;
726 best_full = best_menu = 0;
728 if( widths[ 0 ] > heights[ 0 ] )
729 full_size = menu_size = widths[ 0 ];
730 else
731 full_size = menu_size = heights[ 0 ];
733 for( i = 1; i < num_icons; i++ ) {
734 int s = widths[ i ] > heights[ i ] ? widths[ i ] : heights[ i ];
736 if( ( full_size < ICON_SIZE && s > full_size ) ||
737 ( full_size > ICON_SIZE && s >= ICON_SIZE && s < full_size ) )
738 best_full = i, full_size = s;
740 if( ( menu_size < MENU_FONT_SIZE && s > menu_size ) ||
741 ( menu_size > MENU_FONT_SIZE && s >= MENU_FONT_SIZE &&
742 s < menu_size ) )
743 best_menu = i, menu_size = s;
746 full_map = xcb_generate_id( c );
747 menu_map = xcb_generate_id( c );
748 temp_map = best_menu == best_full ? full_map : xcb_generate_id( c );
750 gc = xcb_generate_id( c );
752 window->u.managed.full_icons =
753 xmalloc( num_screens * sizeof *window->u.managed.full_icons );
754 window->u.managed.menu_icons =
755 xmalloc( num_screens * sizeof *window->u.managed.menu_icons );
757 for( i = 0; i < num_screens; i++ ) {
758 xcb_create_pixmap( c, render_screens[ i ].direct_depth,
759 full_map, screens[ i ]->root,
760 widths[ best_full ], heights[ best_full ] );
761 if( temp_map != full_map )
762 xcb_create_pixmap( c, render_screens[ i ].direct_depth,
763 temp_map, screens[ i ]->root,
764 widths[ best_menu ], heights[ best_menu ] );
766 xcb_create_pixmap( c, render_screens[ i ].direct_depth,
767 menu_map, screens[ i ]->root,
768 MENU_FONT_SIZE, MENU_FONT_SIZE );
770 if( icons ) {
771 xcb_create_gc( c, gc, full_map, 0, NULL );
773 image = assemble_image( i, widths[ best_full ],
774 heights[ best_full ],
775 icons[ best_full ], &len );
776 put_image( full_map, gc, widths[ best_full ],
777 heights[ best_full ], 0, 0, 0,
778 render_screens[ i ].direct_depth, len, image );
780 free( image );
782 if( temp_map != full_map ) {
783 image = assemble_image( i, widths[ best_menu ],
784 heights[ best_menu ],
785 icons[ best_menu ], &len );
786 put_image( temp_map, gc, widths[ best_menu ],
787 heights[ best_menu ], 0, 0, 0,
788 render_screens[ i ].direct_depth, len, image );
789 free( image );
791 } else {
792 uint32_t values[ 3 ];
794 values[ 0 ] = render_screens[ i ].dfmt.alpha_mask <<
795 render_screens[ i ].dfmt.alpha_shift;
796 values[ 1 ] = ( render_screens[ i ].dfmt.alpha_mask <<
797 render_screens[ i ].dfmt.alpha_shift ) |
798 ( render_screens[ i ].dfmt.red_mask <<
799 render_screens[ i ].dfmt.red_shift ) |
800 ( render_screens[ i ].dfmt.green_mask <<
801 render_screens[ i ].dfmt.green_shift ) |
802 ( render_screens[ i ].dfmt.blue_mask <<
803 render_screens[ i ].dfmt.blue_shift );
804 xcb_create_gc( c, gc, full_map, XCB_GC_FOREGROUND |
805 XCB_GC_BACKGROUND, values );
807 xcb_copy_plane( c, bitmaps[ best_full ], full_map, gc, 0, 0,
808 0, 0, widths[ best_full ],
809 heights[ best_full ], 1 );
811 if( bitmaps[ 1 ] ) {
812 /* We also have a mask bitmap: apply logical AND to turn
813 the appropriate pixels transparent. */
814 values[ 0 ] = XCB_GX_AND; /* function */
815 values[ 1 ] = 0xFFFFFFFF; /* foreground */
816 values[ 2 ] = 0; /* background */
817 xcb_change_gc( c, gc, XCB_GC_FUNCTION | XCB_GC_FOREGROUND |
818 XCB_GC_BACKGROUND, values );
820 xcb_copy_plane( c, bitmaps[ 1 ], full_map, gc, 0, 0,
821 0, 0, widths[ best_full ],
822 heights[ best_full ], 1 );
826 window->u.managed.full_icons[ i ] = xcb_generate_id( c );
827 window->u.managed.menu_icons[ i ] = xcb_generate_id( c );
829 xcb_render_create_picture( c, window->u.managed.full_icons[ i ],
830 full_map,
831 render_screens[ i ].direct_pictformat,
832 0, NULL );
833 xcb_render_create_picture( c, window->u.managed.menu_icons[ i ],
834 menu_map,
835 render_screens[ i ].direct_pictformat,
836 0, NULL );
838 if( temp_map == full_map )
839 temp = window->u.managed.full_icons[ i ];
840 else {
841 temp = xcb_generate_id( c );
842 xcb_render_create_picture(
843 c, temp, temp_map, render_screens[ i ].direct_pictformat,
844 0, NULL );
847 xcb_render_set_picture_filter( c, temp, 4, "best", 0, NULL );
848 t.matrix11 = 0x10000 * menu_size / MENU_FONT_SIZE;
849 t.matrix12 = 0;
850 t.matrix13 = 0x80000;
851 t.matrix21 = 0;
852 t.matrix22 = t.matrix11;
853 t.matrix23 = 0;
854 t.matrix31 = 0;
855 t.matrix32 = 0;
856 t.matrix33 = 0x10000;
857 if( widths[ best_menu ] < heights[ best_menu ] ) {
858 t.matrix12 = 0;
859 t.matrix13 = ( widths[ best_menu ] - heights[ best_menu ] )
860 << 15;
861 } else {
862 t.matrix12 = ( heights[ best_menu ] - widths[ best_menu ] )
863 << 15;
864 t.matrix13 = 0;
866 xcb_render_set_picture_transform( c, temp, t );
867 xcb_render_composite( c, XCB_RENDER_PICT_OP_SRC, temp, XCB_NONE,
868 window->u.managed.menu_icons[ i ], 0, 0,
869 0, 0, 0, 0, MENU_FONT_SIZE, MENU_FONT_SIZE );
871 xcb_render_set_picture_filter( c, window->u.managed.full_icons[ i ],
872 4, "good", 0, NULL );
873 t.matrix11 = t.matrix22 = 0x10000 * full_size / ICON_SIZE;
874 if( widths[ best_full ] < heights[ best_full ] ) {
875 t.matrix12 = 0;
876 t.matrix13 = ( widths[ best_full ] - heights[ best_full ] )
877 << 15;
878 } else {
879 t.matrix12 = ( heights[ best_full ] - widths[ best_full ] )
880 << 15;
881 t.matrix13 = 0;
883 xcb_render_set_picture_transform(
884 c, window->u.managed.full_icons[ i ], t );
886 xcb_free_pixmap( c, full_map );
887 xcb_free_pixmap( c, menu_map );
888 if( temp_map != full_map ) {
889 xcb_free_pixmap( c, temp_map );
890 xcb_render_free_picture( c, temp );
893 xcb_free_gc( c, gc );
896 window->u.managed.net_wm_icon = icons != NULL;
897 } else {
898 if( window->u.managed.net_wm_icon )
899 /* We've lost the _NET_WM_ICON property. If the window still
900 has a plain WM_HINTS icon, then fall back to that. */
901 async_get_property( window, PROP_WM_HINTS );
903 window->u.managed.full_icons = window->u.managed.menu_icons = NULL;
904 window->u.managed.net_wm_icon = FALSE;
908 static uint16_t luma( int col ) {
910 return ( decoration_cols[ col ][ 0 ] * 0x4C8BU +
911 decoration_cols[ col ][ 1 ] * 0x9646U +
912 decoration_cols[ col ][ 2 ] * 0x1D2FU ) >> 16;
915 static void handle_alloc_color( unsigned int sequence, void *reply,
916 xcb_generic_error_t *error,
917 union callback_param p ) {
919 xcb_alloc_color_reply_t *r = reply;
920 int screen = p.l >> 16, col = p.l & 0xFFFF;
922 if( error ) {
923 if( error->error_code != XCB_ALLOC )
924 show_error( error );
926 /* No standard RGB map available, and we couldn't allocate a shared
927 colour. Fall back to black or white. */
928 gwm_screens[ screen ].pixels[ col ] = luma( col ) >= 0x8000 ?
929 screens[ screen ]->white_pixel : screens[ screen ]->black_pixel;
931 free( error );
934 if( reply ) {
935 gwm_screens[ screen ].pixels[ col ] = r->pixel;
937 free( reply );
941 static void handle_get_default_map( unsigned int sequence, void *reply,
942 xcb_generic_error_t *error,
943 union callback_param p ) {
945 int col, got_cols = FALSE;
947 if( error ) {
948 show_error( error );
950 free( error );
953 if( reply ) {
954 xcb_get_property_reply_t *prop = reply;
955 struct std_cmap {
956 /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
957 xcb_colormap_t colormap;
958 uint32_t red_max, red_mult, green_max, green_mult, blue_max,
959 blue_mult, base_pixel;
960 xcb_visualid_t visual_id;
961 uint32_t kill_id;
962 } *std_cmaps = xcb_get_property_value( prop );
963 int num_cmaps = ( prop->value_len << 2 ) / sizeof *std_cmaps;
964 int i;
966 if( prop->format == 32 )
967 for( i = 0; i < num_cmaps; i++ )
968 if( std_cmaps[ i ].visual_id ==
969 gwm_screens[ p.l ].root_visual->visual_id ) {
971 for( col = 0; col < NUM_COLS; col++ ) {
972 int r0, g0, b0, r, g, b;
974 if( gwm_screens[ p.l ].root_visual->_class ==
975 XCB_VISUAL_CLASS_STATIC_GRAY ||
976 gwm_screens[ p.l ].root_visual->_class ==
977 XCB_VISUAL_CLASS_GRAY_SCALE ) {
978 r0 = luma( col );
979 g0 = b0 = 0;
980 } else {
981 r0 = decoration_cols[ col ][ 0 ];
982 g0 = decoration_cols[ col ][ 1 ];
983 b0 = decoration_cols[ col ][ 2 ];
986 r = ( r0 * std_cmaps[ i ].red_max + 0x8000 ) >> 16;
987 g = ( g0 * std_cmaps[ i ].green_max + 0x8000 ) >> 16;
988 b = ( b0 * std_cmaps[ i ].blue_max + 0x8000 ) >> 16;
990 gwm_screens[ p.l ].pixels[ col ] =
991 std_cmaps[ i ].base_pixel +
992 r * std_cmaps[ i ].red_mult +
993 g * std_cmaps[ i ].green_mult +
994 b * std_cmaps[ i ].blue_mult;
997 got_cols = TRUE;
998 break;
1001 free( reply );
1004 if( !got_cols )
1005 /* No useful default maps found; try allocating directly. */
1006 for( col = 0; col < NUM_COLS; col++ ) {
1007 int r, g, b;
1008 union callback_param cp;
1010 if( gwm_screens[ p.l ].root_visual->_class ==
1011 XCB_VISUAL_CLASS_STATIC_GRAY ||
1012 gwm_screens[ p.l ].root_visual->_class ==
1013 XCB_VISUAL_CLASS_GRAY_SCALE )
1014 r = g = b = luma( col );
1015 else {
1016 r = decoration_cols[ col ][ 0 ];
1017 g = decoration_cols[ col ][ 1 ];
1018 b = decoration_cols[ col ][ 2 ];
1021 cp.l = ( p.l << 16 ) | col;
1022 /* Ideally, we would like a guarantee that this operation will
1023 complete before the pixel values are ever looked up. In
1024 practice, it will, and it is too messy to insert a
1025 sync_with_callback() before every single pixel reference. */
1026 handle_async_reply( xcb_alloc_color(
1027 c, screens[ p.l ]->default_colormap,
1028 r, g, b ).sequence, handle_alloc_color,
1029 cp );
1033 static void decorate_compat_init( void ) {
1035 int i;
1036 const char *cursor_font_name = "cursor";
1037 xcb_font_t cursor_font;
1038 static const int cursor_glyphs[ NUM_CURSORS ] = {
1039 134, /* CURSOR_TL */
1040 138, /* CURSOR_T */
1041 136, /* CURSOR_TR */
1042 70, /* CURSOR_L */
1043 52, /* CURSOR_C */
1044 96, /* CURSOR_R */
1045 12, /* CURSOR_BL */
1046 16, /* CURSOR_B */
1047 14, /* CURSOR_BR */
1048 68, /* CURSOR_ARROW */
1049 88 /* CURSOR_DESTROY */
1052 cursor_font = xcb_generate_id( c );
1053 xcb_open_font( c, cursor_font, strlen( cursor_font_name ),
1054 cursor_font_name );
1055 for( i = 0; i < NUM_CURSORS; i++ ) {
1056 cursors[ i ] = xcb_generate_id( c );
1057 xcb_create_glyph_cursor( c, cursors[ i ], cursor_font, cursor_font,
1058 cursor_glyphs[ i ], cursor_glyphs[ i ] | 1,
1059 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
1061 xcb_close_font( c, cursor_font );
1063 /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
1064 for( i = 0; i < num_screens; i++ ) {
1065 union callback_param cp;
1067 cp.l = i;
1068 handle_async_reply( xcb_get_property( c, FALSE, screens[ i ]->root,
1069 RGB_DEFAULT_MAP, RGB_COLOR_MAP,
1070 0, 0x10000 ).sequence,
1071 handle_get_default_map, cp );
1075 extern int decorate_render_init( void ) {
1077 xcb_render_query_version_cookie_t render_cookie;
1078 xcb_render_query_pict_formats_cookie_t formats_cookie;
1079 xcb_render_query_version_reply_t *render_reply;
1080 xcb_render_query_pict_formats_reply_t *formats_reply;
1081 xcb_render_pictforminfo_t *formats;
1082 xcb_render_pictscreen_iterator_t siter;
1083 FT_Error err;
1084 FcFontSet *sets[ NUM_STYLES ];
1085 FcPattern *style_pats[ NUM_STYLES ];
1086 FcChar32 *hashes;
1087 FcResult r;
1088 int i, total;
1089 xcb_pixmap_t pixmap;
1090 uint32_t n;
1091 uint32_t icon_sizes[ 6 ];
1093 image_byte_order = xcb_get_setup( c )->image_byte_order;
1095 render_cookie = xcb_render_query_version( c, XCB_RENDER_MAJOR_VERSION,
1096 XCB_RENDER_MINOR_VERSION );
1098 formats_cookie = xcb_render_query_pict_formats( c );
1100 xcb_flush( c ); /* BLOCK */
1102 render_reply = xcb_render_query_version_reply( c, render_cookie, NULL );
1104 if( !render_reply )
1105 return -1;
1107 if( !render_reply->major_version && render_reply->minor_version < 1 ) {
1108 /* We need at least version 0.6, for SetPictureTransform support. */
1109 free( render_reply );
1111 return -1;
1114 free( render_reply );
1116 formats_reply = xcb_render_query_pict_formats_reply( c, formats_cookie,
1117 NULL );
1119 formats = xcb_render_query_pict_formats_formats( formats_reply );
1121 /* First of all, look for an 8 bit direct alpha PictFormat. */
1122 for( i = 0; i < formats_reply->num_formats; i++ )
1123 if( formats[ i ].type == XCB_RENDER_PICT_TYPE_DIRECT &&
1124 formats[ i ].direct.alpha_mask == 0xFF &&
1125 !formats[ i ].direct.red_mask &&
1126 !formats[ i ].direct.green_mask &&
1127 !formats[ i ].direct.blue_mask ) {
1128 fmt_a8 = formats[ i ].id;
1129 memcpy( &dfmt_a8, &formats[ i ].direct, sizeof dfmt_a8 );
1130 break;
1133 if( !fmt_a8 ) {
1134 /* The A8 PictFormat is guaranteed by the RENDER protocol
1135 (section 7). We can't cope without it, since that's what
1136 our glyphset uses. */
1137 free( formats_reply );
1139 return -1;
1142 render_screens = xmalloc( num_screens * sizeof *render_screens );
1144 /* Next, look for a PictFormat matching each root visual. */
1145 for( i = 0, siter = xcb_render_query_pict_formats_screens_iterator(
1146 formats_reply ); siter.rem;
1147 i++, xcb_render_pictscreen_next( &siter ) ) {
1148 xcb_render_pictdepth_iterator_t diter;
1150 render_screens[ i ].root_pictformat = 0;
1152 for( diter = xcb_render_pictscreen_depths_iterator( siter.data );
1153 diter.rem; xcb_render_pictdepth_next( &diter ) ) {
1154 xcb_render_pictvisual_iterator_t viter;
1156 for( viter = xcb_render_pictdepth_visuals_iterator(
1157 diter.data ); viter.rem;
1158 xcb_render_pictvisual_next( &viter ) )
1159 if( viter.data->visual ==
1160 gwm_screens[ i ].root_visual->visual_id ) {
1161 render_screens[ i ].root_pictformat = viter.data->format;
1162 goto next_screen;
1166 /* Oh dear. We found a screen with no PictFormat corresponding
1167 to its root visual, so we can't render to it. It's much too
1168 messy to try rendering on some screens and core graphics on
1169 others, so give up rendering entirely. */
1170 free( formats_reply );
1171 free( render_screens );
1173 return -1;
1175 next_screen:
1179 /* Next, look for the direct PictFormat most suitable for source
1180 pictures rendering to each root visual. */
1181 for( i = 0, siter = xcb_render_query_pict_formats_screens_iterator(
1182 formats_reply ); siter.rem;
1183 i++, xcb_render_pictscreen_next( &siter ) ) {
1184 xcb_render_pictdepth_iterator_t diter;
1185 int best_score = -1;
1186 int best_format;
1187 int red_mask, green_mask, blue_mask;
1189 if( gwm_screens[ i ].root_visual->_class ==
1190 XCB_VISUAL_CLASS_TRUE_COLOR ||
1191 gwm_screens[ i ].root_visual->_class ==
1192 XCB_VISUAL_CLASS_DIRECT_COLOR ) {
1193 for( red_mask = gwm_screens[ i ].root_visual->red_mask;
1194 !( red_mask & 1 ); red_mask >>= 1 )
1196 for( green_mask = gwm_screens[ i ].root_visual->green_mask;
1197 !( green_mask & 1 ); green_mask >>= 1 )
1199 for( blue_mask = gwm_screens[ i ].root_visual->blue_mask;
1200 !( blue_mask & 1 ); blue_mask >>= 1 )
1202 } else
1203 red_mask = green_mask = blue_mask = -1;
1205 for( diter = xcb_render_pictscreen_depths_iterator( siter.data );
1206 diter.rem; xcb_render_pictdepth_next( &diter ) ) {
1207 xcb_depth_iterator_t pixmap_depths;
1208 xcb_render_pictvisual_iterator_t viter;
1209 int format;
1211 for( pixmap_depths =
1212 xcb_screen_allowed_depths_iterator( screens[ i ] );
1213 pixmap_depths.rem; xcb_depth_next( &pixmap_depths ) )
1214 if( pixmap_depths.data->depth == diter.data->depth )
1215 break;
1217 if( !pixmap_depths.rem )
1218 /* We're not allowed to allocate pixmaps of this depth:
1219 don't bother with this PictFormat. */
1220 continue;
1222 for( viter = xcb_render_pictdepth_visuals_iterator(
1223 diter.data ); viter.rem;
1224 xcb_render_pictvisual_next( &viter ) ) {
1225 int score;
1227 for( format = 0; format < formats_reply->num_formats;
1228 format++ )
1229 if( formats[ format ].id == viter.data->format &&
1230 formats[ format ].type ==
1231 XCB_RENDER_PICT_TYPE_DIRECT &&
1232 formats[ format ].depth >= 8 )
1233 /* Insisting that the depth is at least 8 is somewhat
1234 arbitary, but since it's less work for us if we
1235 don't have to consider assembling non-byte-aligned
1236 images, and it's highly questionable whether
1237 alpha composition is particularly effective
1238 on 1- and 4-bit depths, we won't even try. */
1239 break;
1241 if( format == formats_reply->num_formats )
1242 continue;
1244 /* Aha! We've found a direct PictFormat that we can
1245 allocate pixmaps for. Decide how good it is... */
1246 score = 0;
1248 if( formats[ format ].direct.alpha_mask )
1249 /* Has an alpha component. Excellent. */
1250 score |= 0x80;
1252 if( formats[ format ].direct.red_mask == red_mask &&
1253 formats[ format ].direct.green_mask == green_mask &&
1254 formats[ format ].direct.blue_mask == blue_mask ) {
1255 /* The RGB component depths match the root. Good. */
1256 score |= 0x40;
1258 if( formats[ format ].direct.red_mask <<
1259 formats[ format ].direct.red_shift ==
1260 gwm_screens[ i ].root_visual->red_mask &&
1261 formats[ format ].direct.green_mask <<
1262 formats[ format ].direct.green_shift ==
1263 gwm_screens[ i ].root_visual->green_mask &&
1264 formats[ format ].direct.blue_mask <<
1265 formats[ format ].direct.blue_shift ==
1266 gwm_screens[ i ].root_visual->blue_mask )
1267 /* The RGB layouts match. Useful. */
1268 score |= 0x20;
1271 if( formats[ format ].depth == screens[ i ]->root_depth )
1272 /* The depth matches. Slightly helpful. */
1273 score |= 0x10;
1275 /* All else being equal, prefer deeper alpha channels. */
1276 if( formats[ format ].direct.alpha_mask >= 0xFFFF )
1277 score |= 0x0F;
1278 else {
1279 int n;
1281 for( n = formats[ format ].direct.alpha_mask >> 1; n;
1282 n >>= 1 )
1283 score++;
1286 if( score > best_score ) {
1287 best_score = score;
1288 best_format = format;
1293 if( best_score >= 0 ) {
1294 xcb_format_iterator_t fiter;
1295 int mask;
1297 render_screens[ i ].direct_pictformat = formats[ best_format ].id;
1298 render_screens[ i ].direct_depth = formats[ best_format ].depth;
1299 memcpy( &render_screens[ i ].dfmt, &formats[ best_format ].direct,
1300 sizeof render_screens[ i ].dfmt );
1302 for( fiter = xcb_setup_pixmap_formats_iterator(
1303 xcb_get_setup( c ) ); fiter.rem;
1304 xcb_format_next( &fiter ) )
1305 if( fiter.data->depth == formats[ best_format ].depth ) {
1306 render_screens[ i ].pfmt = fiter.data;
1307 break;
1310 assert( fiter.rem );
1312 for( render_screens[ i ].rbits = 0,
1313 mask = render_screens[ i ].dfmt.red_mask;
1314 mask; mask >>= 1, render_screens[ i ].rbits++ )
1317 for( render_screens[ i ].gbits = 0,
1318 mask = render_screens[ i ].dfmt.green_mask;
1319 mask; mask >>= 1, render_screens[ i ].gbits++ )
1322 for( render_screens[ i ].bbits = 0,
1323 mask = render_screens[ i ].dfmt.blue_mask;
1324 mask; mask >>= 1, render_screens[ i ].bbits++ )
1327 for( render_screens[ i ].abits = 0,
1328 mask = render_screens[ i ].dfmt.alpha_mask;
1329 mask; mask >>= 1, render_screens[ i ].abits++ )
1331 } else {
1332 /* Oh dear. We couldn't find any suitable direct PictFormat to
1333 use as a source rendering to the root visual. */
1334 free( formats_reply );
1335 free( render_screens );
1337 return -1;
1341 free( formats_reply );
1343 if( !FcInit() ) {
1344 warning( "font configuration error" );
1346 free( render_screens );
1348 return -1;
1351 if( ( err = FT_Init_FreeType( &ftl ) ) ) {
1352 warning( "typeface initialisation error %d", err );
1354 free( render_screens );
1355 FcFini();
1357 return -1;
1360 for( i = 0, total = 0; i < NUM_STYLES; i++ ) {
1361 if( !( style_pats[ i ] = FcNameParse( style_names[ i ] ) ) ) {
1362 warning( "could not parse font \"%s\"\n", style_names[ i ] );
1364 free( render_screens );
1365 FcFini();
1366 FT_Done_FreeType( ftl );
1368 return -1;
1371 FcConfigSubstitute( NULL, style_pats[ i ], FcMatchPattern );
1372 FcDefaultSubstitute( style_pats[ i ] );
1374 sets[ i ] = FcFontSort( NULL, style_pats[ i ], TRUE,
1375 &styles[ i ].charset, &r );
1377 total += sets[ i ]->nfont;
1380 fonts = xmalloc( total * sizeof *fonts );
1381 hashes = alloca( total * sizeof *hashes );
1383 for( i = 0; i < NUM_STYLES; i++ ) {
1384 int j;
1386 styles[ i ].fonts = xmalloc( sets[ i ]->nfont *
1387 sizeof (struct font *) );
1389 for( j = 0; j < sets[ i ]->nfont; j++ ) {
1390 FcPattern *pat;
1392 if( ( pat = FcFontRenderPrepare( NULL, style_pats[ i ],
1393 sets[ i ]->fonts[ j ] ) ) ) {
1394 FcChar32 hash = FcPatternHash( pat );
1395 int search;
1397 for( search = 0; search < num_fonts; search++ )
1398 if( hashes[ search ] == hash &&
1399 FcPatternEqual( fonts[ search ].pattern, pat ) ) {
1400 FcPatternDestroy( pat );
1401 break;
1404 if( search == num_fonts ) {
1405 hashes[ num_fonts ] = hash;
1406 fonts[ num_fonts ].pattern = pat;
1407 FcPatternGetCharSet( pat, FC_CHARSET, 0,
1408 &fonts[ num_fonts ].charset );
1409 fonts[ num_fonts ].face = NULL;
1410 num_fonts++;
1413 styles[ i ].fonts[ styles[ i ].num_fonts ] = search;
1414 styles[ i ].num_fonts++;
1418 styles[ i ].fonts = xrealloc( styles[ i ].fonts,
1419 styles[ i ].num_fonts *
1420 sizeof (struct font *) );
1422 FcFontSetDestroy( sets[ i ] );
1423 FcPatternDestroy( style_pats[ i ] );
1426 fonts = xrealloc( fonts, num_fonts * sizeof *fonts );
1428 glyphset = xcb_generate_id( c );
1429 xcb_render_create_glyph_set( c, glyphset, fmt_a8 );
1431 pixmap = xcb_generate_id( c );
1433 n = XCB_RENDER_REPEAT_NORMAL;
1435 /* A WM_ICON_SIZE property (see ICCCM 2.0, section 4.1.3.2). */
1436 icon_sizes[ 0 ] = MENU_FONT_SIZE; /* min_width */
1437 icon_sizes[ 1 ] = MENU_FONT_SIZE; /* min_height */
1438 icon_sizes[ 2 ] = ICON_SIZE; /* max_width */
1439 icon_sizes[ 3 ] = ICON_SIZE; /* max_height */
1440 icon_sizes[ 4 ] = 1; /* width_inc */
1441 icon_sizes[ 5 ] = 1; /* height_inc */
1442 for( i = 0; i < num_screens; i++ ) {
1443 xcb_create_pixmap( c, screens[ i ]->root_depth, pixmap,
1444 screens[ i ]->root, 1, 1 );
1446 render_screens[ i ].pic = xcb_generate_id( c );
1447 xcb_render_create_picture( c, render_screens[ i ].pic, pixmap,
1448 render_screens[ i ].root_pictformat,
1449 XCB_RENDER_CP_REPEAT, &n );
1450 render_screens[ i ].col = -1;
1452 xcb_free_pixmap( c, pixmap );
1454 xcb_change_property( c, XCB_PROP_MODE_REPLACE, screens[ i ]->root,
1455 WM_ICON_SIZE, WM_ICON_SIZE, 32,
1456 sizeof icon_sizes, icon_sizes );
1459 decorate_compat_init(); /* FIXME this is for pixel values (backgrounds)
1460 and cursors */
1462 return 0;
1465 extern void decorate_render_done( void ) {
1467 #if DEBUG
1468 int i;
1470 for( i = 0; i < NUM_STYLES; i++ ) {
1471 FcCharSetDestroy( styles[ i ].charset );
1472 free( styles[ i ].fonts );
1475 for( i = 0; i < num_fonts; i++ ) {
1476 FcPatternDestroy( fonts[ i ].pattern );
1477 FT_Done_Face( fonts[ i ].face );
1480 free( fonts );
1482 FcFini();
1484 FT_Done_FreeType( ftl );
1486 free( render_screens );
1487 #endif