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"
44 STYLE_TITLE
, STYLE_MENU
, NUM_STYLES
47 #define TITLE_FONT_SIZE 12
48 #define MENU_FONT_SIZE 14
51 #define STRING(x) STRING2(x)
52 #define TITLE_FONT_SIZE_STRING STRING( TITLE_FONT_SIZE )
53 #define MENU_FONT_SIZE_STRING STRING( MENU_FONT_SIZE )
58 #define FEEDBACK_WIDTH 96 /* width of size feedback window */
59 #define FEEDBACK_HEIGHT 24 /* height of size feedback window */
61 static const FcChar8
*const style_names
[ NUM_STYLES
] = {
62 /* FIXME make this configurable */
63 (FcChar8
*) "sans:pixelsize=" TITLE_FONT_SIZE_STRING
":bold",
64 (FcChar8
*) "sans:pixelsize=" MENU_FONT_SIZE_STRING
76 int *fonts
; /* indices into font table above */
79 } styles
[ NUM_STYLES
];
81 static FT_Library ftl
;
83 static const uint16_t decoration_cols
[ NUM_COLS
][ 3 ] = {
84 { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */
85 { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */
86 { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */
87 { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */
88 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */
89 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */
90 { 0x3333, 0x3333, 0x3333 }, /* COL_TITLE_INACTIVE */
91 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */
92 { 0x0000, 0x0000, 0x0000 }, /* COL_FEEDBACK_FORE */
93 { 0x2222, 0x3333, 0xEEEE }, /* COL_MENU_ACTIVE_BACK */
94 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MENU_ACTIVE_FORE */
95 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_MENU_INACTIVE_BACK */
96 { 0x0000, 0x0000, 0x0000 } /* COL_MENU_INACTIVE_FORE */
99 #define METRIC_CACHE_SIZE_BITS 14
100 #define METRIC_CACHE_SIZE ( 1 << METRIC_CACHE_SIZE_BITS )
101 #define METRIC_CACHE_ASSOC_BITS 2
102 #define METRIC_CACHE_ASSOC ( 1 << METRIC_CACHE_ASSOC_BITS )
104 #define GLYPH_CACHE_SIZE_BITS 11
105 #define GLYPH_CACHE_SIZE ( 1 << GLYPH_CACHE_SIZE_BITS )
106 #define GLYPH_CACHE_ASSOC_BITS 3
107 #define GLYPH_CACHE_ASSOC ( 1 << GLYPH_CACHE_ASSOC_BITS )
109 #if GLYPH_CACHE_SIZE_BITS + GLYPH_CACHE_ASSOC_BITS > 16
110 #error "Glyph cache is too big for 16-bit indices."
113 static struct metric_cache_line
{
115 uint32_t c
; /* character code */
118 } metric_cache
[ METRIC_CACHE_SIZE
][ METRIC_CACHE_ASSOC
];
120 static struct glyph_cache_line
{
122 uint32_t c
; /* character code */
124 } glyph_cache
[ GLYPH_CACHE_SIZE
][ GLYPH_CACHE_ASSOC
];
126 static unsigned int current_time
;
128 static struct font
*lookup_font( enum style_id style
, uint32_t c
) {
132 if( !FcCharSetHasChar( styles
[ style
].charset
, c
) )
135 for( i
= 0; i
< styles
[ style
].num_fonts
; i
++ ) {
136 struct font
*font
= fonts
+ styles
[ style
].fonts
[ i
];
138 if( FcCharSetHasChar( font
->charset
, c
) ) {
141 FcMatrix
*matrix
= NULL
;
143 int index
= 0, hintstyle
= FC_HINT_MEDIUM
;
145 FcBool hinting
= TRUE
, autohint
= FALSE
, globaladvance
= TRUE
,
146 embeddedbitmap
= TRUE
;
148 if( FcPatternGetString( font
->pattern
, FC_FILE
, 0,
150 FcPatternGetDouble( font
->pattern
, FC_PIXEL_SIZE
, 0,
154 FcPatternGetInteger( font
->pattern
, FC_INDEX
, 0, &index
);
155 FcPatternGetMatrix( font
->pattern
, FC_MATRIX
, 0, &matrix
);
157 if( FT_New_Face( ftl
, (char *) filename
, index
, &font
->face
) )
160 FT_Set_Pixel_Sizes( font
->face
, 0, (int) ( size
+ 0.5 ) );
163 ft_matrix
.xx
= matrix
->xx
* 64.0;
164 ft_matrix
.xy
= matrix
->xy
* 64.0;
165 ft_matrix
.yx
= matrix
->yx
* 64.0;
166 ft_matrix
.yy
= matrix
->yy
* 64.0;
167 FT_Set_Transform( font
->face
, &ft_matrix
, NULL
);
170 FT_Select_Charmap( font
->face
, FT_ENCODING_UNICODE
);
172 FcPatternGetBool( font
->pattern
, FC_HINTING
, 0, &hinting
);
173 FcPatternGetInteger( font
->pattern
, FC_HINT_STYLE
, 0,
175 FcPatternGetBool( font
->pattern
, FC_AUTOHINT
, 0, &autohint
);
176 FcPatternGetBool( font
->pattern
, FC_GLOBAL_ADVANCE
, 0,
178 FcPatternGetBool( font
->pattern
, FC_EMBEDDED_BITMAP
, 0,
181 font
->load_flags
= FT_LOAD_DEFAULT
;
184 font
->load_flags
|= FT_LOAD_NO_HINTING
;
186 switch( hintstyle
) {
188 font
->load_flags
|= FT_LOAD_NO_HINTING
;
192 font
->load_flags
|= FT_LOAD_TARGET_LIGHT
;
197 font
->load_flags
|= FT_LOAD_TARGET_NORMAL
;
201 font
->load_flags
|= FT_LOAD_TARGET_MONO
;
206 font
->load_flags
|= FT_LOAD_FORCE_AUTOHINT
;
209 font
->load_flags
|= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
;
211 if( !embeddedbitmap
)
212 font
->load_flags
|= FT_LOAD_NO_BITMAP
;
222 static void query_metrics( enum style_id style
, uint32_t c
,
223 int *x_off
, int *y_off
) {
225 int row
= c
& ( METRIC_CACHE_SIZE
- 1 );
229 for( i
= 0; i
< METRIC_CACHE_ASSOC
; i
++ )
230 if( metric_cache
[ row
][ i
].style
== style
&&
231 metric_cache
[ row
][ i
].c
== c
) {
233 metric_cache
[ row
][ i
].time
= current_time
;
234 *x_off
= metric_cache
[ row
][ i
].x_off
;
235 *y_off
= metric_cache
[ row
][ i
].y_off
;
239 font
= lookup_font( style
, c
);
242 if( FT_Load_Char( font
->face
, c
, font
->load_flags
) ) {
243 /* Couldn't load metrics. Don't bother evicting anything. */
248 /* Search for a line to evict. */
249 for( i
= 1, max
= 0; i
< METRIC_CACHE_ASSOC
; i
++ )
250 if( current_time
- metric_cache
[ row
][ i
].time
>
251 current_time
- metric_cache
[ row
][ max
].time
)
254 metric_cache
[ row
][ max
].style
= style
;
255 metric_cache
[ row
][ max
].c
= c
;
256 metric_cache
[ row
][ max
].time
= current_time
;
257 *x_off
= metric_cache
[ row
][ max
].x_off
=
258 ( font
->face
->glyph
->advance
.x
+ 0x20 ) >> 6;
259 *y_off
= metric_cache
[ row
][ max
].y_off
=
260 ( font
->face
->glyph
->advance
.y
+ 0x20 ) >> 6;
263 static int query_glyph( enum style_id style
, uint32_t c
) {
265 int row
= c
& ( GLYPH_CACHE_SIZE
- 1 );
268 for( i
= 0; i
< GLYPH_CACHE_ASSOC
; i
++ )
269 if( glyph_cache
[ row
][ i
].style
== style
&&
270 glyph_cache
[ row
][ i
].c
== c
) {
272 glyph_cache
[ row
][ i
].time
= current_time
;
273 return ( row
<< GLYPH_CACHE_ASSOC_BITS
) | i
;
279 static int replace_glyph( enum style_id style
, uint32_t c
) {
281 int row
= c
& ( GLYPH_CACHE_SIZE
- 1 );
284 /* Search for a line to evict. */
285 for( i
= 1, max
= 0; i
< METRIC_CACHE_ASSOC
; i
++ ) {
286 assert( glyph_cache
[ row
][ i
].style
!= style
||
287 glyph_cache
[ row
][ i
].c
!= c
);
288 if( current_time
- glyph_cache
[ row
][ i
].time
>
289 current_time
- glyph_cache
[ row
][ max
].time
)
293 if( glyph_cache
[ row
][ max
].time
== current_time
)
294 /* Cache line set is full, and nothing is old enough to evict. */
297 glyph_cache
[ row
][ max
].style
= style
;
298 glyph_cache
[ row
][ max
].c
= c
;
299 glyph_cache
[ row
][ max
].time
= current_time
;
301 return ( row
<< GLYPH_CACHE_ASSOC_BITS
) | max
;
304 static xcb_render_glyphset_t glyphset
;
305 static struct picture
{
306 xcb_render_picture_t pic
;
307 enum decoration_col col
;
308 } *pictures
; /* indexed by screen */
310 static xcb_void_cookie_t
render_text( xcb_drawable_t drawable
, int screen
,
311 int col
, int x
, int y
,
312 const char *text
, enum style_id style
,
313 const xcb_rectangle_t
*clip
) {
315 xcb_render_picture_t src
, dest
;
316 xcb_rectangle_t rect
;
317 xcb_render_color_t rc
;
319 const unsigned char *p
;
322 if( !text
|| !*text
) {
323 xcb_void_cookie_t r
= { 0 };
329 p
= (const unsigned char *) text
;
334 else if( !( p
[ 0 ] & 0x80 ) ) {
335 /* Legal single byte character. */
338 } else if( *p
>= 0xC2 && *p
<= 0xDF &&
339 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF ) {
340 /* Legal two byte character. */
343 } else if( *p
>= 0xE0 && *p
<= 0xEF &&
344 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
345 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF &&
346 ( *p
> 0xE0 || p
[ 1 ] > 0x80 ) ) {
347 /* Legal three byte character. */
350 } else if( *p
>= 0xF0 && *p
<= 0xF4 &&
351 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
352 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF &&
353 p
[ 3 ] >= 0x80 && p
[ 3 ] <= 0xBF &&
354 ( *p
> 0xF0 || p
[ 1 ] > 0x80 ) ) {
355 /* Legal four byte character. */
359 /* Illegal character: ignore this byte and continue. */
363 xcb_void_cookie_t r
= { 0 };
368 buf
= alloca( len
* sizeof *buf
);
370 for( p
= (const unsigned char *) text
, i
= 0; i
< len
; i
++ ) {
374 if( !( p
[ 0 ] & 0x80 ) )
375 /* One byte character. */
377 else if( *p
>= 0xC2 && *p
<= 0xDF &&
378 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF ) {
379 /* Two byte character. */
380 buf
[ i
] = ( ( p
[ 0 ] & 0x1F ) << 6 ) | ( p
[ 1 ] & 0x3F );
382 } else if( *p
>= 0xE0 && *p
<= 0xEF &&
383 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
384 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF &&
385 ( *p
> 0xE0 || p
[ 1 ] > 0x80 ) ) {
386 /* Three byte character. */
387 buf
[ i
] = ( ( p
[ 0 ] & 0x0F ) << 12 ) |
388 ( ( p
[ 1 ] & 0x3F ) << 6 ) | ( p
[ 2 ] & 0x3F );
390 } else if( *p
>= 0xF0 && *p
<= 0xF4 &&
391 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
392 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF &&
393 p
[ 3 ] >= 0x80 && p
[ 3 ] <= 0xBF &&
394 ( *p
> 0xF0 || p
[ 1 ] > 0x80 ) ) {
395 /* Four byte character. */
396 buf
[ i
] = ( ( p
[ 0 ] & 0x07 ) << 18 ) |
397 ( ( p
[ 1 ] & 0x3F ) << 12 ) | ( ( p
[ 2 ] & 0x3F ) << 6 ) |
406 src
= pictures
[ screen
].pic
;
408 dest
= xcb_generate_id( c
);
409 xcb_render_create_picture( c
, dest
, drawable
,
410 gwm_screens
[ screen
].root_pictformat
, 0, NULL
);
413 xcb_render_set_picture_clip_rectangles( c
, dest
, 0, 0, 1, clip
);
415 if( pictures
[ screen
].col
!= col
) {
416 rc
.red
= decoration_cols
[ col
][ 0 ];
417 rc
.green
= decoration_cols
[ col
][ 1 ];
418 rc
.blue
= decoration_cols
[ col
][ 2 ];
424 xcb_render_fill_rectangles( c
, XCB_RENDER_PICT_OP_SRC
, src
, rc
, 1,
427 pictures
[ screen
].col
= col
;
432 uint32_t glyphids
[ 0x80 ];
433 xcb_render_glyphinfo_t glyphs
[ 0x80 ];
434 int num_glyphs
; /* number of glyphs in AddGlyphs request */
435 /* Buffer for "data" parameter of AddGlyphs request. We want
436 this to be big enough to hold at least one glyph, but no
437 bigger than the biggest request the server will accept (once
438 the other parameters are included). All servers must
439 accept requests of 16,384 bytes or smaller (see X Window
440 System Protocol, section 8), so this is safe. */
441 uint8_t data
[ 0x2000 ], *p
;
442 /* Buffer for "glyphcmds" parameter of CompositeGlyphs16
443 request. We always send exactly one command, which has an
444 8-byte header and at most a 254 glyph string. */
445 uint8_t param
[ 8 + ( 0xFE << 1 ) ], *out
;
446 int num_chars
; /* number of characters for CompositeGlyphs16 */
453 memset( param
, 0, 4 );
454 *( (uint16_t *) ( param
+ 4 ) ) = x
;
455 *( (uint16_t *) ( param
+ 6 ) ) = y
;
459 while( i
< len
&& num_chars
< 0xFF ) {
462 if( ( index
= query_glyph( style
, buf
[ i
] ) ) < 0 ) {
468 if( ( index
= replace_glyph( style
, buf
[ i
] ) ) < 0 )
469 /* Cache set full: spill the partial string to make
470 room for later glyphs. */
473 if( !( font
= lookup_font( style
, buf
[ i
] ) ) ||
474 FT_Load_Char( font
->face
, buf
[ i
], font
->load_flags
|
476 ( bitmap_size
= ( ( font
->face
->glyph
->bitmap
.width
+
478 font
->face
->glyph
->bitmap
.rows
) > sizeof data
) {
479 /* We couldn't load the character, or it was so
480 huge that it won't fit in an empty AddGlyph
481 data buffer. We'll have to send the server
482 an empty glyph and carry on. */
486 slot
= font
->face
->glyph
;
488 if( ( p
- data
) + bitmap_size
> sizeof data
||
489 num_glyphs
== sizeof glyphids
/ sizeof *glyphids
) {
490 /* Can't fit this glyph into the existing request:
491 transmit what we have... */
492 xcb_render_add_glyphs( c
, glyphset
, num_glyphs
, glyphids
,
493 glyphs
, p
- data
, data
);
495 /* ...and start building another AddGlyph request. */
500 glyphids
[ num_glyphs
] = index
;
505 FT_Bitmap_New( &bitmap
);
506 FT_Bitmap_Convert( ftl
, &slot
->bitmap
, &bitmap
, 4 );
508 glyphs
[ num_glyphs
].width
= bitmap
.width
;
509 glyphs
[ num_glyphs
].height
= bitmap
.rows
;
510 glyphs
[ num_glyphs
].x
= -slot
->bitmap_left
;
511 glyphs
[ num_glyphs
].y
= slot
->bitmap_top
;
512 glyphs
[ num_glyphs
].x_off
=
513 ( slot
->advance
.x
+ 0x20 ) >> 6;
514 glyphs
[ num_glyphs
].y_off
=
515 ( slot
->advance
.y
+ 0x20 ) >> 6;
517 memcpy( p
, bitmap
.buffer
, bitmap
.pitch
* bitmap
.rows
);
519 if( bitmap
.num_grays
!= 0x100 ) {
521 *end
= p
+ bitmap
.pitch
* bitmap
.rows
;
523 for( c
= p
; c
< end
; c
++ )
524 *c
= ( (unsigned int) *c
* 0xFF /
525 ( bitmap
.num_grays
- 1 ) );
528 p
+= bitmap
.pitch
* bitmap
.rows
;
530 FT_Bitmap_Done( ftl
, &bitmap
);
532 glyphs
[ num_glyphs
].width
= 0;
533 glyphs
[ num_glyphs
].height
= 0;
534 glyphs
[ num_glyphs
].x
= 0;
535 glyphs
[ num_glyphs
].y
= 0;
536 glyphs
[ num_glyphs
].x_off
= 0;
537 glyphs
[ num_glyphs
].y_off
= 0;
543 *( (uint16_t *) out
) = index
;
546 query_metrics( style
, buf
[ i
], &dx
, &dy
);
555 xcb_render_add_glyphs( c
, glyphset
, num_glyphs
, glyphids
, glyphs
,
562 param
[ 0 ] = num_chars
;
564 xcb_render_composite_glyphs_16( c
, XCB_RENDER_PICT_OP_OVER
, src
, dest
,
565 XCB_NONE
, glyphset
, 0, 0, out
- param
,
569 return xcb_render_free_picture( c
, dest
);
572 static int text_width( enum style_id style
, const char *text
) {
577 for( width
= 0, p
= text
; *p
; p
++ ) {
580 query_metrics( style
, *p
, &dx
, &dy
);
587 extern void render_update_window( struct gwm_window
*window
) {
589 if( !window
->cleared
)
590 xcb_clear_area( c
, FALSE
, window
->w
, window
->update
.x
, window
->update
.y
,
591 window
->update
.width
, window
->update
.height
);
593 if( window
->type
== WINDOW_FRAME
) {
594 char *name
= window
->u
.frame
.child
->u
.managed
.name
;
596 render_text( window
->w
, window
->screen
, window
== focus_frame
?
597 COL_TITLE_ACTIVE
: COL_TITLE_INACTIVE
,
598 button_size( window
->u
.frame
.button
, TRUE
) + 4,
599 TITLE_FONT_SIZE
, name
? name
: "(Untitled)",
600 STYLE_TITLE
, &window
->update
);
601 } else if( window
->type
== WINDOW_MENUITEM
) {
602 struct gwm_window
*menu
= window
->u
.menuitem
.menu
;
604 render_text( window
->w
, window
->screen
,
605 menu
->u
.menu
.active_item
>= 0 &&
606 menu
->u
.menu
.items
[ menu
->u
.menu
.active_item
] == window
?
607 COL_MENU_ACTIVE_FORE
: COL_MENU_INACTIVE_FORE
,
608 MENU_X_PAD
, MENU_FONT_SIZE
,
609 window
->u
.menuitem
.label
, STYLE_MENU
, &window
->update
);
610 } else if( window
->type
== WINDOW_FEEDBACK
) {
613 sprintf( text
, "%dx%d", window
->u
.feedback
.fb_width
,
614 window
->u
.feedback
.fb_height
);
616 render_text( window
->w
, window
->screen
, COL_FEEDBACK_FORE
,
617 ( FEEDBACK_WIDTH
- text_width( STYLE_TITLE
, text
) ) >> 1,
618 16, text
, STYLE_TITLE
, &window
->update
);
622 extern void render_window_size( struct gwm_window
*window
, int *width
,
625 switch( window
->type
) {
626 case WINDOW_MENUITEM
:
627 if( window
->u
.menuitem
.label
) {
628 *width
= text_width( STYLE_MENU
, window
->u
.menuitem
.label
) +
630 *height
= MENU_FONT_SIZE
+ ( MENU_Y_PAD
<< 1 );
632 *width
= MENU_X_PAD
<< 1;
633 *height
= MENU_Y_PAD
<< 1;
637 case WINDOW_FEEDBACK
:
638 *width
= FEEDBACK_WIDTH
;
639 *height
= FEEDBACK_HEIGHT
;
647 static uint16_t luma( int col
) {
649 return ( decoration_cols
[ col
][ 0 ] * 0x4C8BU
+
650 decoration_cols
[ col
][ 1 ] * 0x9646U
+
651 decoration_cols
[ col
][ 2 ] * 0x1D2FU
) >> 16;
654 static void handle_alloc_color( unsigned int sequence
, void *reply
,
655 xcb_generic_error_t
*error
,
656 union callback_param p
) {
658 xcb_alloc_color_reply_t
*r
= reply
;
659 int screen
= p
.l
>> 16, col
= p
.l
& 0xFFFF;
662 if( error
->error_code
!= XCB_ALLOC
)
665 /* No standard RGB map available, and we couldn't allocate a shared
666 colour. Fall back to black or white. */
667 gwm_screens
[ screen
].pixels
[ col
] = luma( col
) >= 0x8000 ?
668 screens
[ screen
]->white_pixel
: screens
[ screen
]->black_pixel
;
674 gwm_screens
[ screen
].pixels
[ col
] = r
->pixel
;
680 static void handle_get_default_map( unsigned int sequence
, void *reply
,
681 xcb_generic_error_t
*error
,
682 union callback_param p
) {
684 int col
, got_cols
= FALSE
;
693 xcb_get_property_reply_t
*prop
= reply
;
695 /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
696 xcb_colormap_t colormap
;
697 uint32_t red_max
, red_mult
, green_max
, green_mult
, blue_max
,
698 blue_mult
, base_pixel
;
699 xcb_visualid_t visual_id
;
701 } *std_cmaps
= xcb_get_property_value( prop
);
702 int num_cmaps
= ( xcb_get_property_value_length( prop
) << 2 ) /
706 if( prop
->format
== 32 )
707 for( i
= 0; i
< num_cmaps
; i
++ )
708 if( std_cmaps
[ i
].visual_id
==
709 gwm_screens
[ p
.l
].root_visual
->visual_id
) {
711 for( col
= 0; col
< NUM_COLS
; col
++ ) {
712 int r0
, g0
, b0
, r
, g
, b
;
714 if( gwm_screens
[ p
.l
].root_visual
->_class
==
715 XCB_VISUAL_CLASS_STATIC_GRAY
||
716 gwm_screens
[ p
.l
].root_visual
->_class
==
717 XCB_VISUAL_CLASS_GRAY_SCALE
) {
721 r0
= decoration_cols
[ col
][ 0 ];
722 g0
= decoration_cols
[ col
][ 1 ];
723 b0
= decoration_cols
[ col
][ 2 ];
726 r
= ( r0
* std_cmaps
[ i
].red_max
+ 0x8000 ) >> 16;
727 g
= ( g0
* std_cmaps
[ i
].green_max
+ 0x8000 ) >> 16;
728 b
= ( b0
* std_cmaps
[ i
].blue_max
+ 0x8000 ) >> 16;
730 gwm_screens
[ p
.l
].pixels
[ col
] =
731 std_cmaps
[ i
].base_pixel
+
732 r
* std_cmaps
[ i
].red_mult
+
733 g
* std_cmaps
[ i
].green_mult
+
734 b
* std_cmaps
[ i
].blue_mult
;
745 /* No useful default maps found; try allocating directly. */
746 for( col
= 0; col
< NUM_COLS
; col
++ ) {
748 union callback_param cp
;
750 if( gwm_screens
[ p
.l
].root_visual
->_class
==
751 XCB_VISUAL_CLASS_STATIC_GRAY
||
752 gwm_screens
[ p
.l
].root_visual
->_class
==
753 XCB_VISUAL_CLASS_GRAY_SCALE
)
754 r
= g
= b
= luma( col
);
756 r
= decoration_cols
[ col
][ 0 ];
757 g
= decoration_cols
[ col
][ 1 ];
758 b
= decoration_cols
[ col
][ 2 ];
761 cp
.l
= ( p
.l
<< 16 ) | col
;
762 /* Ideally, we would like a guarantee that this operation will
763 complete before the pixel values are ever looked up. In
764 practice, it will, and it is too messy to insert a
765 sync_with_callback() before every single pixel reference. */
766 handle_async_reply( xcb_alloc_color(
767 c
, screens
[ p
.l
]->default_colormap
,
768 r
, g
, b
).sequence
, handle_alloc_color
,
773 static void decorate_compat_init( void ) {
776 const char *cursor_font_name
= "cursor";
777 xcb_font_t cursor_font
;
778 static const int cursor_glyphs
[ NUM_CURSORS
] = {
788 68, /* CURSOR_ARROW */
789 88 /* CURSOR_DESTROY */
792 cursor_font
= xcb_generate_id( c
);
793 xcb_open_font( c
, cursor_font
, strlen( cursor_font_name
),
795 for( i
= 0; i
< NUM_CURSORS
; i
++ ) {
796 cursors
[ i
] = xcb_generate_id( c
);
797 xcb_create_glyph_cursor( c
, cursors
[ i
], cursor_font
, cursor_font
,
798 cursor_glyphs
[ i
], cursor_glyphs
[ i
] | 1,
799 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
801 xcb_close_font( c
, cursor_font
);
803 /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
804 for( i
= 0; i
< num_screens
; i
++ ) {
805 union callback_param cp
;
808 handle_async_reply( xcb_get_property( c
, FALSE
, screens
[ i
]->root
,
809 RGB_DEFAULT_MAP
, RGB_COLOR_MAP
,
810 0, 0x10000 ).sequence
,
811 handle_get_default_map
, cp
);
815 extern void decorate_render_init( void ) {
818 FcFontSet
*sets
[ NUM_STYLES
];
819 FcPattern
*style_pats
[ NUM_STYLES
];
827 fatal( "font configuration error" );
829 if( ( err
= FT_Init_FreeType( &ftl
) ) )
830 fatal( "typeface initialisation error %d", err
);
832 for( i
= 0, total
= 0; i
< NUM_STYLES
; i
++ ) {
833 if( !( style_pats
[ i
] = FcNameParse( style_names
[ i
] ) ) )
834 fatal( "could not parse font \"%s\"\n", style_names
[ i
] );
836 FcConfigSubstitute( NULL
, style_pats
[ i
], FcMatchPattern
);
837 FcDefaultSubstitute( style_pats
[ i
] );
839 sets
[ i
] = FcFontSort( NULL
, style_pats
[ i
], TRUE
,
840 &styles
[ i
].charset
, &r
);
842 total
+= sets
[ i
]->nfont
;
845 fonts
= xmalloc( total
* sizeof *fonts
);
846 hashes
= alloca( total
* sizeof *hashes
);
848 for( i
= 0; i
< NUM_STYLES
; i
++ ) {
851 styles
[ i
].fonts
= xmalloc( sets
[ i
]->nfont
*
852 sizeof (struct font
*) );
854 for( j
= 0; j
< sets
[ i
]->nfont
; j
++ ) {
857 if( ( pat
= FcFontRenderPrepare( NULL
, style_pats
[ i
],
858 sets
[ i
]->fonts
[ j
] ) ) ) {
859 FcChar32 hash
= FcPatternHash( pat
);
862 for( search
= 0; search
< num_fonts
; search
++ )
863 if( hashes
[ search
] == hash
&&
864 FcPatternEqual( fonts
[ search
].pattern
, pat
) ) {
865 FcPatternDestroy( pat
);
869 if( search
== num_fonts
) {
870 hashes
[ num_fonts
] = hash
;
871 fonts
[ num_fonts
].pattern
= pat
;
872 FcPatternGetCharSet( pat
, FC_CHARSET
, 0,
873 &fonts
[ num_fonts
].charset
);
874 fonts
[ num_fonts
].face
= NULL
;
878 styles
[ i
].fonts
[ styles
[ i
].num_fonts
] = search
;
879 styles
[ i
].num_fonts
++;
883 styles
[ i
].fonts
= xrealloc( styles
[ i
].fonts
,
884 styles
[ i
].num_fonts
*
885 sizeof (struct font
*) );
887 FcFontSetDestroy( sets
[ i
] );
888 FcPatternDestroy( style_pats
[ i
] );
891 fonts
= xrealloc( fonts
, num_fonts
* sizeof *fonts
);
893 glyphset
= xcb_generate_id( c
);
894 xcb_render_create_glyph_set( c
, glyphset
, fmt_a8
);
896 pixmap
= xcb_generate_id( c
);
898 pictures
= xmalloc( num_screens
* sizeof *pictures
);
900 n
= XCB_RENDER_REPEAT_NORMAL
;
901 for( i
= 0; i
< num_screens
; i
++ ) {
902 xcb_create_pixmap( c
, screens
[ i
]->root_depth
, pixmap
,
903 screens
[ i
]->root
, 1, 1 );
905 pictures
[ i
].pic
= xcb_generate_id( c
);
906 xcb_render_create_picture( c
, pictures
[ i
].pic
, pixmap
,
907 gwm_screens
[ i
].root_pictformat
,
908 XCB_RENDER_CP_REPEAT
, &n
);
909 pictures
[ i
].col
= -1;
911 xcb_free_pixmap( c
, pixmap
);
914 decorate_compat_init(); /* FIXME this is for pixel values (backgrounds)
918 extern void decorate_render_done( void ) {
923 for( i
= 0; i
< NUM_STYLES
; i
++ ) {
924 FcCharSetDestroy( styles
[ i
].charset
);
925 free( styles
[ i
].fonts
);
928 for( i
= 0; i
< num_fonts
; i
++ ) {
929 FcPatternDestroy( fonts
[ i
].pattern
);
930 FT_Done_Face( fonts
[ i
].face
);
937 FT_Done_FreeType( ftl
);