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( !font
|| 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
->type
== WINDOW_CHILDLESS
)
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
;
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
],
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
,
592 } else if( window
->type
== WINDOW_FEEDBACK
) {
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
,
607 switch( window
->type
) {
608 case WINDOW_MENUITEM
:
609 if( window
->u
.menuitem
.label
) {
610 *width
= text_width( STYLE_MENU
, window
->u
.menuitem
.label
) +
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
;
617 *width
= MENU_X_PAD
<< 1;
618 *height
= MENU_Y_PAD
<< 1;
622 case WINDOW_FEEDBACK
:
623 *width
= FEEDBACK_WIDTH
;
624 *height
= FEEDBACK_HEIGHT
;
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
;
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
++ ) {
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;
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 );
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 );
690 for( i
= bpp
; i
; i
-= 8 )
691 *p
++ = ( ( pix
>> ( i
- 8 ) ) & 0xFF );
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
) {
707 if( window
->u
.managed
.net_wm_icon
&& bitmaps
)
708 /* Use old _NET_WM_ICON in preference to new WM_HINTS. */
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
);
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
;
729 best_full
= best_menu
= 0;
731 if( widths
[ 0 ] > heights
[ 0 ] )
732 full_size
= menu_size
= widths
[ 0 ];
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
&&
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
);
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
);
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
);
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 );
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
],
834 render_screens
[ i
].direct_pictformat
,
836 xcb_render_create_picture( c
, window
->u
.managed
.menu_icons
[ i
],
838 render_screens
[ i
].direct_pictformat
,
841 if( temp_map
== full_map
)
842 temp
= window
->u
.managed
.full_icons
[ i
];
844 temp
= xcb_generate_id( c
);
845 xcb_render_create_picture(
846 c
, temp
, temp_map
, render_screens
[ i
].direct_pictformat
,
850 xcb_render_set_picture_filter( c
, temp
, 4, "best", 0, NULL
);
851 t
.matrix11
= 0x10000 * menu_size
/ MENU_FONT_SIZE
;
853 t
.matrix13
= 0x80000;
855 t
.matrix22
= t
.matrix11
;
859 t
.matrix33
= 0x10000;
860 if( widths
[ best_menu
] < heights
[ best_menu
] ) {
862 t
.matrix13
= ( widths
[ best_menu
] - heights
[ best_menu
] )
865 t
.matrix12
= ( heights
[ best_menu
] - widths
[ best_menu
] )
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
] ) {
879 t
.matrix13
= ( widths
[ best_full
] - heights
[ best_full
] )
882 t
.matrix12
= ( heights
[ best_full
] - widths
[ best_full
] )
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
;
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;
926 if( error
->error_code
!= XCB_ALLOC
)
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
;
938 gwm_screens
[ screen
].pixels
[ col
] = r
->pixel
;
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
;
957 xcb_get_property_reply_t
*prop
= reply
;
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
;
965 } *std_cmaps
= xcb_get_property_value( prop
);
966 int num_cmaps
= ( prop
->value_len
<< 2 ) / sizeof *std_cmaps
;
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
) {
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
;
1008 /* No useful default maps found; try allocating directly. */
1009 for( col
= 0; col
< NUM_COLS
; col
++ ) {
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
);
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
,
1036 static INIT
void decorate_compat_init( void ) {
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 */
1044 136, /* CURSOR_TR */
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
),
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
;
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
;
1087 FcFontSet
*sets
[ NUM_STYLES
];
1088 FcPattern
*style_pats
[ NUM_STYLES
];
1092 xcb_pixmap_t pixmap
;
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
);
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
);
1117 free( render_reply
);
1119 formats_reply
= xcb_render_query_pict_formats_reply( c
, formats_cookie
,
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
);
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
);
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
;
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
);
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;
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 )
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
;
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
)
1220 if( !pixmap_depths
.rem
)
1221 /* We're not allowed to allocate pixmaps of this depth:
1222 don't bother with this PictFormat. */
1225 for( viter
= xcb_render_pictdepth_visuals_iterator(
1226 diter
.data
); viter
.rem
;
1227 xcb_render_pictvisual_next( &viter
) ) {
1230 for( format
= 0; format
< formats_reply
->num_formats
;
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. */
1244 if( format
== formats_reply
->num_formats
)
1247 /* Aha! We've found a direct PictFormat that we can
1248 allocate pixmaps for. Decide how good it is... */
1251 if( formats
[ format
].direct
.alpha_mask
)
1252 /* Has an alpha component. Excellent. */
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. */
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. */
1274 if( formats
[ format
].depth
== screens
[ i
]->root_depth
)
1275 /* The depth matches. Slightly helpful. */
1278 /* All else being equal, prefer deeper alpha channels. */
1279 if( formats
[ format
].direct
.alpha_mask
>= 0xFFFF )
1284 for( n
= formats
[ format
].direct
.alpha_mask
>> 1; n
;
1289 if( score
> best_score
) {
1291 best_format
= format
;
1296 if( best_score
>= 0 ) {
1297 xcb_format_iterator_t fiter
;
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
;
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
++ )
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
);
1344 free( formats_reply
);
1347 warning( "font configuration error" );
1349 free( render_screens
);
1354 if( ( err
= FT_Init_FreeType( &ftl
) ) ) {
1355 warning( "typeface initialisation error %d", err
);
1357 free( render_screens
);
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
);
1369 FT_Done_FreeType( ftl
);
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
++ ) {
1389 styles
[ i
].fonts
= xmalloc( sets
[ i
]->nfont
*
1390 sizeof (struct font
*) );
1392 for( j
= 0; j
< sets
[ i
]->nfont
; j
++ ) {
1395 if( ( pat
= FcFontRenderPrepare( NULL
, style_pats
[ i
],
1396 sets
[ i
]->fonts
[ j
] ) ) ) {
1397 FcChar32 hash
= FcPatternHash( pat
);
1400 for( search
= 0; search
< num_fonts
; search
++ )
1401 if( hashes
[ search
] == hash
&&
1402 FcPatternEqual( fonts
[ search
].pattern
, pat
) ) {
1403 FcPatternDestroy( pat
);
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
;
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,
1462 decorate_compat_init(); /* FIXME this is for pixel values (backgrounds)
1468 extern void decorate_render_done( void ) {
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
);
1487 FT_Done_FreeType( ftl
);
1489 free( render_screens
);