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>
39 #include "decorate-render.h"
41 /* FIXME make this configurable */
44 #define STRING(x) STRING2(x)
45 #define FONT_SIZE_STRING STRING( FONT_SIZE )
46 #define FONT_NAME "sans:pixelsize=" FONT_SIZE_STRING ":bold"
48 static FT_Library ftl
;
49 static FT_Face ftf
; /* FIXME allow multiple faces per fontconfig pattern */
51 static const uint16_t decoration_cols
[ NUM_COLS
][ 3 ] = {
52 { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */
53 { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */
54 { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */
55 { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */
56 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */
57 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */
58 { 0x3333, 0x3333, 0x3333 }, /* COL_TITLE_INACTIVE */
59 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */
60 { 0x0000, 0x0000, 0x0000 } /* COL_FEEDBACK_FORE */
63 #define METRIC_CACHE_SIZE_BITS 14
64 #define METRIC_CACHE_SIZE ( 1 << METRIC_CACHE_SIZE_BITS )
65 #define METRIC_CACHE_ASSOC_BITS 2
66 #define METRIC_CACHE_ASSOC ( 1 << METRIC_CACHE_ASSOC_BITS )
68 #define GLYPH_CACHE_SIZE_BITS 11
69 #define GLYPH_CACHE_SIZE ( 1 << GLYPH_CACHE_SIZE_BITS )
70 #define GLYPH_CACHE_ASSOC_BITS 3
71 #define GLYPH_CACHE_ASSOC ( 1 << GLYPH_CACHE_ASSOC_BITS )
73 #if GLYPH_CACHE_SIZE_BITS + GLYPH_CACHE_ASSOC_BITS > 16
74 #error "Glyph cache is too big for 16-bit indices."
77 static struct metric_cache_line
{
78 uint32_t c
; /* character code */
81 } metric_cache
[ METRIC_CACHE_SIZE
][ METRIC_CACHE_ASSOC
];
83 static struct glyph_cache_line
{
84 uint32_t c
; /* character code */
86 } glyph_cache
[ GLYPH_CACHE_SIZE
][ GLYPH_CACHE_ASSOC
];
88 static unsigned int current_time
;
90 static void query_metrics( uint32_t c
, int *x_off
, int *y_off
) {
92 int row
= c
& ( METRIC_CACHE_SIZE
- 1 );
95 for( i
= 0; i
< METRIC_CACHE_ASSOC
; i
++ )
96 if( metric_cache
[ row
][ i
].c
== c
) {
98 metric_cache
[ row
][ i
].time
= current_time
;
99 *x_off
= metric_cache
[ row
][ i
].x_off
;
100 *y_off
= metric_cache
[ row
][ i
].y_off
;
105 if( FT_Load_Char( ftf
, c
, FT_LOAD_DEFAULT
) ) {
106 /* Couldn't load metrics. Don't bother evicting anything. */
111 /* Search for a line to evict. */
112 for( i
= 1, max
= 0; i
< METRIC_CACHE_ASSOC
; i
++ )
113 if( current_time
- metric_cache
[ row
][ i
].time
>
114 current_time
- metric_cache
[ row
][ max
].time
)
117 metric_cache
[ row
][ max
].c
= c
;
118 metric_cache
[ row
][ max
].time
= current_time
;
119 *x_off
= metric_cache
[ row
][ max
].x_off
=
120 ( ftf
->glyph
->advance
.x
+ 0x20 ) >> 6;
121 *y_off
= metric_cache
[ row
][ max
].y_off
=
122 ( ftf
->glyph
->advance
.y
+ 0x20 ) >> 6;
125 static int query_glyph( uint32_t c
) {
127 int row
= c
& ( GLYPH_CACHE_SIZE
- 1 );
130 for( i
= 0; i
< GLYPH_CACHE_ASSOC
; i
++ )
131 if( glyph_cache
[ row
][ i
].c
== c
) {
133 glyph_cache
[ row
][ i
].time
= current_time
;
134 return ( row
<< GLYPH_CACHE_ASSOC_BITS
) | i
;
140 static int replace_glyph( uint32_t c
) {
142 int row
= c
& ( GLYPH_CACHE_SIZE
- 1 );
145 /* Search for a line to evict. */
146 for( i
= 1, max
= 0; i
< METRIC_CACHE_ASSOC
; i
++ ) {
147 assert( glyph_cache
[ row
][ i
].c
!= c
);
148 if( current_time
- glyph_cache
[ row
][ i
].time
>
149 current_time
- glyph_cache
[ row
][ max
].time
)
153 if( glyph_cache
[ row
][ max
].time
== current_time
)
154 /* Cache line set is full, and nothing is old enough to evict. */
157 glyph_cache
[ row
][ max
].c
= c
;
158 glyph_cache
[ row
][ max
].time
= current_time
;
160 return ( row
<< GLYPH_CACHE_ASSOC_BITS
) | max
;
163 static xcb_render_glyphset_t glyphset
;
164 static struct picture
{
165 xcb_render_picture_t pic
;
166 enum decoration_col col
;
167 } *pictures
; /* indexed by screen */
169 static xcb_void_cookie_t
render_text( xcb_drawable_t drawable
, int screen
,
170 int col
, int x
, int y
,
172 const xcb_rectangle_t
*clip
) {
174 xcb_render_picture_t src
, dest
;
175 xcb_rectangle_t rect
;
176 xcb_render_color_t rc
;
178 const unsigned char *p
;
181 if( !text
|| !*text
) {
182 xcb_void_cookie_t r
= { 0 };
188 p
= (const unsigned char *) text
;
193 else if( !( p
[ 0 ] & 0x80 ) ) {
194 /* Legal single byte character. */
197 } else if( *p
>= 0xC2 && *p
<= 0xDF &&
198 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF ) {
199 /* Legal two byte character. */
202 } else if( *p
>= 0xE0 && *p
<= 0xEF &&
203 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
204 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF &&
205 ( *p
> 0xE0 || p
[ 1 ] > 0x80 ) ) {
206 /* Legal three byte character. */
209 } else if( *p
>= 0xF0 && *p
<= 0xF4 &&
210 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
211 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF &&
212 p
[ 3 ] >= 0x80 && p
[ 3 ] <= 0xBF &&
213 ( *p
> 0xF0 || p
[ 1 ] > 0x80 ) ) {
214 /* Legal four byte character. */
218 /* Illegal character: ignore this byte and continue. */
222 xcb_void_cookie_t r
= { 0 };
227 buf
= alloca( len
* sizeof *buf
);
229 for( p
= (const unsigned char *) text
, i
= 0; i
< len
; i
++ ) {
233 if( !( p
[ 0 ] & 0x80 ) )
234 /* One byte character. */
236 else if( *p
>= 0xC2 && *p
<= 0xDF &&
237 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF ) {
238 /* Two byte character. */
239 buf
[ i
] = ( ( p
[ 0 ] & 0x1F ) << 6 ) | ( p
[ 1 ] & 0x3F );
241 } else if( *p
>= 0xE0 && *p
<= 0xEF &&
242 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
243 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF &&
244 ( *p
> 0xE0 || p
[ 1 ] > 0x80 ) ) {
245 /* Three byte character. */
246 buf
[ i
] = ( ( p
[ 0 ] & 0x0F ) << 12 ) |
247 ( ( p
[ 1 ] & 0x3F ) << 6 ) | ( p
[ 2 ] & 0x3F );
249 } else if( *p
>= 0xF0 && *p
<= 0xF4 &&
250 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
251 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF &&
252 p
[ 3 ] >= 0x80 && p
[ 3 ] <= 0xBF &&
253 ( *p
> 0xF0 || p
[ 1 ] > 0x80 ) ) {
254 /* Four byte character. */
255 buf
[ i
] = ( ( p
[ 0 ] & 0x07 ) << 18 ) |
256 ( ( p
[ 1 ] & 0x3F ) << 12 ) | ( ( p
[ 2 ] & 0x3F ) << 6 ) |
265 src
= pictures
[ screen
].pic
;
267 dest
= xcb_generate_id( c
);
268 xcb_render_create_picture( c
, dest
, drawable
,
269 gwm_screens
[ screen
].root_pictformat
, 0, NULL
);
272 xcb_render_set_picture_clip_rectangles( c
, dest
, 0, 0, 1, clip
);
274 if( pictures
[ screen
].col
!= col
) {
275 rc
.red
= decoration_cols
[ col
][ 0 ];
276 rc
.green
= decoration_cols
[ col
][ 1 ];
277 rc
.blue
= decoration_cols
[ col
][ 2 ];
283 xcb_render_fill_rectangles( c
, XCB_RENDER_PICT_OP_SRC
, src
, rc
, 1,
286 pictures
[ screen
].col
= col
;
291 uint32_t glyphids
[ 0x80 ];
292 xcb_render_glyphinfo_t glyphs
[ 0x80 ];
293 int num_glyphs
; /* number of glyphs in AddGlyphs request */
294 /* Buffer for "data" parameter of AddGlyphs request. We want
295 this to be big enough to hold at least one glyph, but no
296 bigger than the biggest request the server will accept (once
297 the other parameters are included). All servers must
298 accept requests of 16,384 bytes or smaller (see X Window
299 System Protocol, section 8), so this is safe. */
300 uint8_t data
[ 0x2000 ], *p
;
301 /* Buffer for "glyphcmds" parameter of CompositeGlyphs16
302 request. We always send exactly one command, which has an
303 8-byte header and at most a 254 glyph string. */
304 uint8_t param
[ 8 + ( 0xFE << 1 ) ], *out
;
305 int num_chars
; /* number of characters for CompositeGlyphs16 */
312 memset( param
, 0, 4 );
313 *( (uint16_t *) ( param
+ 4 ) ) = x
;
314 *( (uint16_t *) ( param
+ 6 ) ) = y
;
318 while( i
< len
&& num_chars
< 0xFF ) {
321 if( ( index
= query_glyph( buf
[ i
] ) ) < 0 ) {
326 if( ( index
= replace_glyph( buf
[ i
] ) ) < 0 )
327 /* Cache set full: spill the partial string to make
328 room for later glyphs. */
331 if( FT_Load_Char( ftf
, buf
[ i
], FT_LOAD_RENDER
) ||
332 ( bitmap_size
= ( ( ftf
->glyph
->bitmap
.width
+ 3 ) & ~3 ) *
333 ftf
->glyph
->bitmap
.rows
) > sizeof data
) {
334 /* We couldn't load the character, or it was so
335 huge that it won't fit in an empty AddGlyph
336 data buffer. We'll have to send the server
337 an empty glyph and carry on. */
343 if( ( p
- data
) + bitmap_size
> sizeof data
||
344 num_glyphs
== sizeof glyphids
/ sizeof *glyphids
) {
345 /* Can't fit this glyph into the existing request:
346 transmit what we have... */
347 xcb_render_add_glyphs( c
, glyphset
, num_glyphs
, glyphids
,
348 glyphs
, p
- data
, data
);
350 /* ...and start building another AddGlyph request. */
355 glyphids
[ num_glyphs
] = index
;
360 glyphs
[ num_glyphs
].width
= slot
->bitmap
.width
;
361 glyphs
[ num_glyphs
].height
= slot
->bitmap
.rows
;
362 glyphs
[ num_glyphs
].x
= -slot
->bitmap_left
;
363 glyphs
[ num_glyphs
].y
= slot
->bitmap_top
;
364 glyphs
[ num_glyphs
].x_off
=
365 ( slot
->advance
.x
+ 0x20 ) >> 6;
366 glyphs
[ num_glyphs
].y_off
=
367 ( slot
->advance
.y
+ 0x20 ) >> 6;
369 width_pad
= ( slot
->bitmap
.width
+ 3 ) & ~3;
371 for( y
= 0; y
< slot
->bitmap
.rows
; y
++ ) {
372 memcpy( p
, slot
->bitmap
.buffer
+ y
*
373 slot
->bitmap
.width
, slot
->bitmap
.width
);
377 glyphs
[ num_glyphs
].width
= 0;
378 glyphs
[ num_glyphs
].height
= 0;
379 glyphs
[ num_glyphs
].x
= 0;
380 glyphs
[ num_glyphs
].y
= 0;
381 glyphs
[ num_glyphs
].x_off
= 0;
382 glyphs
[ num_glyphs
].y_off
= 0;
388 *( (uint16_t *) out
) = index
;
391 query_metrics( buf
[ i
], &dx
, &dy
);
400 xcb_render_add_glyphs( c
, glyphset
, num_glyphs
, glyphids
, glyphs
,
407 param
[ 0 ] = num_chars
;
409 xcb_render_composite_glyphs_16( c
, XCB_RENDER_PICT_OP_OVER
, src
, dest
,
410 XCB_NONE
, glyphset
, 0, 0, out
- param
,
414 return xcb_render_free_picture( c
, dest
);
417 extern void render_update_window( struct gwm_window
*window
) {
419 if( !window
->cleared
)
420 xcb_clear_area( c
, FALSE
, window
->w
, window
->update
.x
, window
->update
.y
,
421 window
->update
.width
, window
->update
.height
);
423 if( window
->type
== WINDOW_FRAME
) {
424 char *name
= window
->u
.frame
.child
->u
.managed
.name
;
426 render_text( window
->w
, window
->screen
, window
== focus_frame
?
427 COL_TITLE_ACTIVE
: COL_TITLE_INACTIVE
,
428 FRAME_BUTTON_SIZE
+ FRAME_BORDER_WIDTH
* 3,
429 FONT_SIZE
, name
? name
: "(Untitled)",
431 } else if( window
->type
== WINDOW_FEEDBACK
) {
435 sprintf( text
, "%dx%d", window
->u
.feedback
.fb_width
,
436 window
->u
.feedback
.fb_height
);
438 for( width
= 0, p
= text
; *p
; p
++ ) {
441 query_metrics( *p
, &dx
, &dy
);
445 render_text( window
->w
, window
->screen
, COL_FEEDBACK_FORE
,
446 ( FEEDBACK_WIDTH
- width
) >> 1, 16, text
,
451 static uint16_t luma( int col
) {
453 return ( decoration_cols
[ col
][ 0 ] * 0x4C8BU
+
454 decoration_cols
[ col
][ 1 ] * 0x9646U
+
455 decoration_cols
[ col
][ 2 ] * 0x1D2FU
) >> 16;
458 static void handle_alloc_color( unsigned int sequence
, void *reply
,
459 xcb_generic_error_t
*error
,
460 union callback_param p
) {
462 xcb_alloc_color_reply_t
*r
= reply
;
463 int screen
= p
.l
>> 16, col
= p
.l
& 0xFFFF;
466 if( error
->error_code
!= XCB_ALLOC
)
469 /* No standard RGB map available, and we couldn't allocate a shared
470 colour. Fall back to black or white. */
471 gwm_screens
[ screen
].pixels
[ col
] = luma( col
) >= 0x8000 ?
472 screens
[ screen
]->white_pixel
: screens
[ screen
]->black_pixel
;
478 gwm_screens
[ screen
].pixels
[ col
] = r
->pixel
;
484 static void handle_get_default_map( unsigned int sequence
, void *reply
,
485 xcb_generic_error_t
*error
,
486 union callback_param p
) {
488 int col
, got_cols
= FALSE
;
497 xcb_get_property_reply_t
*prop
= reply
;
499 /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
500 xcb_colormap_t colormap
;
501 uint32_t red_max
, red_mult
, green_max
, green_mult
, blue_max
,
502 blue_mult
, base_pixel
;
503 xcb_visualid_t visual_id
;
505 } *std_cmaps
= xcb_get_property_value( prop
);
506 int num_cmaps
= ( xcb_get_property_value_length( prop
) << 2 ) /
510 if( prop
->format
== 32 )
511 for( i
= 0; i
< num_cmaps
; i
++ )
512 if( std_cmaps
[ i
].visual_id
==
513 gwm_screens
[ p
.l
].root_visual
->visual_id
) {
515 for( col
= 0; col
< NUM_COLS
; col
++ ) {
516 int r0
, g0
, b0
, r
, g
, b
;
518 if( gwm_screens
[ p
.l
].root_visual
->_class
==
519 XCB_VISUAL_CLASS_STATIC_GRAY
||
520 gwm_screens
[ p
.l
].root_visual
->_class
==
521 XCB_VISUAL_CLASS_GRAY_SCALE
) {
525 r0
= decoration_cols
[ col
][ 0 ];
526 g0
= decoration_cols
[ col
][ 1 ];
527 b0
= decoration_cols
[ col
][ 2 ];
530 r
= ( r0
* std_cmaps
[ i
].red_max
+ 0x8000 ) >> 16;
531 g
= ( g0
* std_cmaps
[ i
].green_max
+ 0x8000 ) >> 16;
532 b
= ( b0
* std_cmaps
[ i
].blue_max
+ 0x8000 ) >> 16;
534 gwm_screens
[ p
.l
].pixels
[ col
] =
535 std_cmaps
[ i
].base_pixel
+
536 r
* std_cmaps
[ i
].red_mult
+
537 g
* std_cmaps
[ i
].green_mult
+
538 b
* std_cmaps
[ i
].blue_mult
;
549 /* No useful default maps found; try allocating directly. */
550 for( col
= 0; col
< NUM_COLS
; col
++ ) {
552 union callback_param cp
;
554 if( gwm_screens
[ p
.l
].root_visual
->_class
==
555 XCB_VISUAL_CLASS_STATIC_GRAY
||
556 gwm_screens
[ p
.l
].root_visual
->_class
==
557 XCB_VISUAL_CLASS_GRAY_SCALE
)
558 r
= g
= b
= luma( col
);
560 r
= decoration_cols
[ col
][ 0 ];
561 g
= decoration_cols
[ col
][ 1 ];
562 b
= decoration_cols
[ col
][ 2 ];
565 cp
.l
= ( p
.l
<< 16 ) | col
;
566 /* Ideally, we would like a guarantee that this operation will
567 complete before the pixel values are ever looked up. In
568 practice, it will, and it is too messy to insert a
569 sync_with_callback() before every single pixel reference. */
570 handle_async_reply( xcb_alloc_color(
571 c
, screens
[ p
.l
]->default_colormap
,
572 r
, g
, b
).sequence
, handle_alloc_color
,
577 static void decorate_compat_init( void ) {
580 const char *cursor_font_name
= "cursor";
581 xcb_font_t cursor_font
;
582 static const int cursor_glyphs
[ NUM_CURSORS
] = {
583 68, /* CURSOR_ARROW */
584 88, /* CURSOR_DESTROY */
596 cursor_font
= xcb_generate_id( c
);
597 xcb_open_font( c
, cursor_font
, strlen( cursor_font_name
),
599 for( i
= 0; i
< NUM_CURSORS
; i
++ ) {
600 cursors
[ i
] = xcb_generate_id( c
);
601 xcb_create_glyph_cursor( c
, cursors
[ i
], cursor_font
, cursor_font
,
602 cursor_glyphs
[ i
], cursor_glyphs
[ i
] | 1,
603 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
605 xcb_close_font( c
, cursor_font
);
607 /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
608 for( i
= 0; i
< num_screens
; i
++ ) {
609 union callback_param cp
;
612 handle_async_reply( xcb_get_property( c
, FALSE
, screens
[ i
]->root
,
613 RGB_DEFAULT_MAP
, RGB_COLOR_MAP
,
614 0, 0x10000 ).sequence
,
615 handle_get_default_map
, cp
);
619 extern void decorate_render_init( void ) {
621 FcPattern
*pat
, *font
;
632 pat
= FcNameParse( (FcChar8
*) FONT_NAME
);
634 FcConfigSubstitute( NULL
, pat
, FcMatchPattern
);
636 FcDefaultSubstitute( pat
);
638 font
= FcFontMatch( NULL
, pat
, &r
);
640 FcPatternGet( font
, FC_FILE
, 0, &v
);
641 filename
= (char *) v
.u
.s
;
642 FcPatternGet( font
, FC_INDEX
, 0, &v
);
645 if( ( err
= FT_Init_FreeType( &ftl
) ) )
646 fatal( "typeface initialisation error %d", err
);
648 if( ( err
= FT_New_Face( ftl
, filename
, index
, &ftf
) ) )
649 fatal( "%s: could not load font (%d)", filename
, err
);
651 FcPatternDestroy( pat
);
652 FcPatternDestroy( font
);
656 if( ( err
= FT_Set_Char_Size( ftf
, 0, FONT_SIZE
<< 6, 0, 0 ) ) )
657 fatal( "%s: could not scale font (%d)", filename
, err
);
659 if( ( err
= FT_Select_Charmap( ftf
, FT_ENCODING_UNICODE
) ) )
660 fatal( "%s: could not select character map (%d)", filename
, err
);
662 glyphset
= xcb_generate_id( c
);
663 xcb_render_create_glyph_set( c
, glyphset
, fmt_a8
);
665 pixmap
= xcb_generate_id( c
);
667 pictures
= xmalloc( num_screens
* sizeof *pictures
);
669 n
= XCB_RENDER_REPEAT_NORMAL
;
670 for( i
= 0; i
< num_screens
; i
++ ) {
671 xcb_create_pixmap( c
, screens
[ i
]->root_depth
, pixmap
,
672 screens
[ i
]->root
, 1, 1 );
674 pictures
[ i
].pic
= xcb_generate_id( c
);
675 xcb_render_create_picture( c
, pictures
[ i
].pic
, pixmap
,
676 gwm_screens
[ i
].root_pictformat
,
677 XCB_RENDER_CP_REPEAT
, &n
);
678 pictures
[ i
].col
= -1;
680 xcb_free_pixmap( c
, pixmap
);
683 decorate_compat_init(); /* FIXME this is for pixel values (backgrounds)
687 extern void decorate_render_done( void ) {
692 FT_Done_FreeType( ftl
);