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/>.
27 #include <fontconfig/fontconfig.h>
29 #include FT_FREETYPE_H
34 #include <xcb/render.h>
40 #include "decorate-render.h"
45 #include "window-table.h"
48 STYLE_TITLE
, STYLE_MENU
, NUM_STYLES
51 #define TITLE_FONT_SIZE 12
52 #define MENU_FONT_SIZE 14
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 )
62 #define FEEDBACK_WIDTH 96 /* width of size feedback window */
63 #define FEEDBACK_HEIGHT 24 /* height of size feedback window */
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
82 int *fonts
; /* indices into font table above */
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."
119 static struct metric_cache_line
{
121 uint32_t c
; /* character code */
124 } metric_cache
[ METRIC_CACHE_SIZE
][ METRIC_CACHE_ASSOC
];
126 static struct glyph_cache_line
{
128 uint32_t c
; /* character code */
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
) {
138 if( !FcCharSetHasChar( styles
[ style
].charset
, c
) )
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
) ) {
147 FcMatrix
*matrix
= NULL
;
149 int index
= 0, hintstyle
= FC_HINT_MEDIUM
;
151 FcBool hinting
= TRUE
, autohint
= FALSE
, globaladvance
= TRUE
,
152 embeddedbitmap
= TRUE
;
154 if( FcPatternGetString( font
->pattern
, FC_FILE
, 0,
156 FcPatternGetDouble( font
->pattern
, FC_PIXEL_SIZE
, 0,
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
) )
166 FT_Set_Pixel_Sizes( font
->face
, 0, (int) ( size
+ 0.5 ) );
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,
181 FcPatternGetBool( font
->pattern
, FC_AUTOHINT
, 0, &autohint
);
182 FcPatternGetBool( font
->pattern
, FC_GLOBAL_ADVANCE
, 0,
184 FcPatternGetBool( font
->pattern
, FC_EMBEDDED_BITMAP
, 0,
187 font
->load_flags
= FT_LOAD_DEFAULT
;
190 font
->load_flags
|= FT_LOAD_NO_HINTING
;
192 switch( hintstyle
) {
194 font
->load_flags
|= FT_LOAD_NO_HINTING
;
198 font
->load_flags
|= FT_LOAD_TARGET_LIGHT
;
203 font
->load_flags
|= FT_LOAD_TARGET_NORMAL
;
207 font
->load_flags
|= FT_LOAD_TARGET_MONO
;
212 font
->load_flags
|= FT_LOAD_FORCE_AUTOHINT
;
215 font
->load_flags
|= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
;
217 if( !embeddedbitmap
)
218 font
->load_flags
|= FT_LOAD_NO_BITMAP
;
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 );
235 for( i
= 0; i
< METRIC_CACHE_ASSOC
; i
++ )
236 if( metric_cache
[ row
][ i
].style
== style
&&
237 metric_cache
[ row
][ i
].c
== c
) {
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
;
245 font
= lookup_font( style
, c
);
248 if( FT_Load_Char( font
->face
, c
, font
->load_flags
) ) {
249 /* Couldn't load metrics. Don't bother evicting anything. */
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
)
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 );
274 for( i
= 0; i
< GLYPH_CACHE_ASSOC
; i
++ )
275 if( glyph_cache
[ row
][ i
].style
== style
&&
276 glyph_cache
[ row
][ i
].c
== c
) {
278 glyph_cache
[ row
][ i
].time
= current_time
;
279 return ( row
<< GLYPH_CACHE_ASSOC_BITS
) | i
;
285 static int replace_glyph( enum style_id style
, uint32_t c
) {
287 int row
= c
& ( GLYPH_CACHE_SIZE
- 1 );
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
)
299 if( glyph_cache
[ row
][ max
].time
== current_time
)
300 /* Cache line set is full, and nothing is old enough to evict. */
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
;
317 xcb_render_directformat_t dfmt
;
319 int rbits
, gbits
, bbits
, abits
;
320 xcb_render_picture_t pic
;
321 enum decoration_col col
;
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
;
333 const unsigned char *p
;
336 if( !text
|| !( len
= utf8_length( (const unsigned char *) text
) ) ) {
337 xcb_void_cookie_t r
= { 0 };
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
,
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 ];
366 xcb_render_fill_rectangles( c
, XCB_RENDER_PICT_OP_SRC
, src
, rc
, 1,
369 render_screens
[ screen
].col
= col
;
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 */
395 memset( param
, 0, 4 );
396 *( (uint16_t *) ( param
+ 4 ) ) = x
;
397 *( (uint16_t *) ( param
+ 6 ) ) = y
;
401 while( i
< len
&& num_chars
< 0xFF ) {
404 if( ( index
= query_glyph( style
, buf
[ i
] ) ) < 0 ) {
410 if( ( index
= replace_glyph( style
, buf
[ i
] ) ) < 0 )
411 /* Cache set full: spill the partial string to make
412 room for later glyphs. */
415 if( !( font
= lookup_font( style
, buf
[ i
] ) ) ||
416 FT_Load_Char( font
->face
, buf
[ i
], font
->load_flags
|
418 ( bitmap_size
= ( ( font
->face
->glyph
->bitmap
.width
+
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. */
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. */
442 glyphids
[ num_glyphs
] = index
;
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 ) {
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
);
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;
485 *( (uint16_t *) out
) = index
;
488 query_metrics( style
, buf
[ i
], &dx
, &dy
);
497 xcb_render_add_glyphs( c
, glyphset
, num_glyphs
, glyphids
, glyphs
,
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
,
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
,
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
,
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
;
544 query_metrics( style
, utf8_next( &p
), &dx
, &dy
);
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
;
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
],
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
,
589 } else if( window
->type
== WINDOW_FEEDBACK
) {
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
,
604 switch( window
->type
) {
605 case WINDOW_MENUITEM
:
606 if( window
->u
.menuitem
.label
) {
607 *width
= text_width( STYLE_MENU
, window
->u
.menuitem
.label
) +
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
;
614 *width
= MENU_X_PAD
<< 1;
615 *height
= MENU_Y_PAD
<< 1;
619 case WINDOW_FEEDBACK
:
620 *width
= FEEDBACK_WIDTH
;
621 *height
= FEEDBACK_HEIGHT
;
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
;
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
++ ) {
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;
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 );
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 );
687 for( i
= bpp
; i
; i
-= 8 )
688 *p
++ = ( ( pix
>> ( i
- 8 ) ) & 0xFF );
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
) {
704 if( window
->u
.managed
.net_wm_icon
&& bitmaps
)
705 /* Use old _NET_WM_ICON in preference to new WM_HINTS. */
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
);
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
;
726 best_full
= best_menu
= 0;
728 if( widths
[ 0 ] > heights
[ 0 ] )
729 full_size
= menu_size
= widths
[ 0 ];
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
&&
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
);
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
);
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
);
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 );
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
],
831 render_screens
[ i
].direct_pictformat
,
833 xcb_render_create_picture( c
, window
->u
.managed
.menu_icons
[ i
],
835 render_screens
[ i
].direct_pictformat
,
838 if( temp_map
== full_map
)
839 temp
= window
->u
.managed
.full_icons
[ i
];
841 temp
= xcb_generate_id( c
);
842 xcb_render_create_picture(
843 c
, temp
, temp_map
, render_screens
[ i
].direct_pictformat
,
847 xcb_render_set_picture_filter( c
, temp
, 4, "best", 0, NULL
);
848 t
.matrix11
= 0x10000 * menu_size
/ MENU_FONT_SIZE
;
850 t
.matrix13
= 0x80000;
852 t
.matrix22
= t
.matrix11
;
856 t
.matrix33
= 0x10000;
857 if( widths
[ best_menu
] < heights
[ best_menu
] ) {
859 t
.matrix13
= ( widths
[ best_menu
] - heights
[ best_menu
] )
862 t
.matrix12
= ( heights
[ best_menu
] - widths
[ best_menu
] )
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
] ) {
876 t
.matrix13
= ( widths
[ best_full
] - heights
[ best_full
] )
879 t
.matrix12
= ( heights
[ best_full
] - widths
[ best_full
] )
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
;
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;
923 if( error
->error_code
!= XCB_ALLOC
)
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
;
935 gwm_screens
[ screen
].pixels
[ col
] = r
->pixel
;
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
;
954 xcb_get_property_reply_t
*prop
= reply
;
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
;
962 } *std_cmaps
= xcb_get_property_value( prop
);
963 int num_cmaps
= ( prop
->value_len
<< 2 ) / sizeof *std_cmaps
;
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
) {
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
;
1005 /* No useful default maps found; try allocating directly. */
1006 for( col
= 0; col
< NUM_COLS
; col
++ ) {
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
);
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
,
1033 static void decorate_compat_init( void ) {
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 */
1041 136, /* CURSOR_TR */
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
),
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
;
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
;
1084 FcFontSet
*sets
[ NUM_STYLES
];
1085 FcPattern
*style_pats
[ NUM_STYLES
];
1089 xcb_pixmap_t pixmap
;
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
);
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
);
1114 free( render_reply
);
1116 formats_reply
= xcb_render_query_pict_formats_reply( c
, formats_cookie
,
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
);
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
);
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
;
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
);
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;
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 )
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
;
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
)
1217 if( !pixmap_depths
.rem
)
1218 /* We're not allowed to allocate pixmaps of this depth:
1219 don't bother with this PictFormat. */
1222 for( viter
= xcb_render_pictdepth_visuals_iterator(
1223 diter
.data
); viter
.rem
;
1224 xcb_render_pictvisual_next( &viter
) ) {
1227 for( format
= 0; format
< formats_reply
->num_formats
;
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. */
1241 if( format
== formats_reply
->num_formats
)
1244 /* Aha! We've found a direct PictFormat that we can
1245 allocate pixmaps for. Decide how good it is... */
1248 if( formats
[ format
].direct
.alpha_mask
)
1249 /* Has an alpha component. Excellent. */
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. */
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. */
1271 if( formats
[ format
].depth
== screens
[ i
]->root_depth
)
1272 /* The depth matches. Slightly helpful. */
1275 /* All else being equal, prefer deeper alpha channels. */
1276 if( formats
[ format
].direct
.alpha_mask
>= 0xFFFF )
1281 for( n
= formats
[ format
].direct
.alpha_mask
>> 1; n
;
1286 if( score
> best_score
) {
1288 best_format
= format
;
1293 if( best_score
>= 0 ) {
1294 xcb_format_iterator_t fiter
;
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
;
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
++ )
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
);
1341 free( formats_reply
);
1344 warning( "font configuration error" );
1346 free( render_screens
);
1351 if( ( err
= FT_Init_FreeType( &ftl
) ) ) {
1352 warning( "typeface initialisation error %d", err
);
1354 free( render_screens
);
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
);
1366 FT_Done_FreeType( ftl
);
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
++ ) {
1386 styles
[ i
].fonts
= xmalloc( sets
[ i
]->nfont
*
1387 sizeof (struct font
*) );
1389 for( j
= 0; j
< sets
[ i
]->nfont
; j
++ ) {
1392 if( ( pat
= FcFontRenderPrepare( NULL
, style_pats
[ i
],
1393 sets
[ i
]->fonts
[ j
] ) ) ) {
1394 FcChar32 hash
= FcPatternHash( pat
);
1397 for( search
= 0; search
< num_fonts
; search
++ )
1398 if( hashes
[ search
] == hash
&&
1399 FcPatternEqual( fonts
[ search
].pattern
, pat
) ) {
1400 FcPatternDestroy( pat
);
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
;
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)
1465 extern void decorate_render_done( void ) {
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
);
1484 FT_Done_FreeType( ftl
);
1486 free( render_screens
);