Add a menu for selecting a window to map and raise.
[gwm.git] / decorate-render.c
bloba04a3fc12d2e302ba4ed78539a89123e8b32aa8e
1 /*
2 * decorate-render.c
4 * Part of gwm, the Gratuitous Window Manager,
5 * by Gary Wong, <gtw@gnu.org>.
7 * Copyright (C) 2009 Gary Wong
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of version 3 of the GNU General Public License as
11 * published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * $Id$
24 #include <config.h>
26 #include <assert.h>
27 #include <fontconfig/fontconfig.h>
28 #include <ft2build.h>
29 #include FT_FREETYPE_H
30 #include FT_BITMAP_H
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <xcb/render.h>
35 #include <xcb/xcb.h>
37 #include "gwm.h"
39 #include "button.h"
40 #include "decorate-render.h"
41 #include "frame.h"
43 enum style_id {
44 STYLE_TITLE, STYLE_MENU, NUM_STYLES
47 #define TITLE_FONT_SIZE 12
48 #define MENU_FONT_SIZE 14
50 #define STRING2(x) #x
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 )
55 #define MENU_X_PAD 4
56 #define MENU_Y_PAD 2
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
67 static struct font {
68 FcPattern *pattern;
69 FcCharSet *charset;
70 FT_Face face;
71 FT_Int32 load_flags;
72 } *fonts;
73 static int num_fonts;
75 static struct style {
76 int *fonts; /* indices into font table above */
77 int num_fonts;
78 FcCharSet *charset;
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."
111 #endif
113 static struct metric_cache_line {
114 enum style_id style;
115 uint32_t c; /* character code */
116 unsigned int time;
117 int x_off, y_off;
118 } metric_cache[ METRIC_CACHE_SIZE ][ METRIC_CACHE_ASSOC ];
120 static struct glyph_cache_line {
121 enum style_id style;
122 uint32_t c; /* character code */
123 unsigned int time;
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 ) {
130 int i;
132 if( !FcCharSetHasChar( styles[ style ].charset, c ) )
133 return NULL;
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 ) ) {
139 if( !font->face ) {
140 FcChar8 *filename;
141 FcMatrix *matrix = NULL;
142 FT_Matrix ft_matrix;
143 int index = 0, hintstyle = FC_HINT_MEDIUM;
144 double size;
145 FcBool hinting = TRUE, autohint = FALSE, globaladvance = TRUE,
146 embeddedbitmap = TRUE;
148 if( FcPatternGetString( font->pattern, FC_FILE, 0,
149 &filename ) ||
150 FcPatternGetDouble( font->pattern, FC_PIXEL_SIZE, 0,
151 &size ) )
152 continue;
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 ) )
158 continue;
160 FT_Set_Pixel_Sizes( font->face, 0, (int) ( size + 0.5 ) );
162 if( matrix ) {
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,
174 &hintstyle );
175 FcPatternGetBool( font->pattern, FC_AUTOHINT, 0, &autohint );
176 FcPatternGetBool( font->pattern, FC_GLOBAL_ADVANCE, 0,
177 &globaladvance );
178 FcPatternGetBool( font->pattern, FC_EMBEDDED_BITMAP, 0,
179 &embeddedbitmap );
181 font->load_flags = FT_LOAD_DEFAULT;
183 if( !hinting )
184 font->load_flags |= FT_LOAD_NO_HINTING;
186 switch( hintstyle ) {
187 case FC_HINT_NONE:
188 font->load_flags |= FT_LOAD_NO_HINTING;
189 break;
191 case FC_HINT_SLIGHT:
192 font->load_flags |= FT_LOAD_TARGET_LIGHT;
193 break;
195 case FC_HINT_MEDIUM:
196 default:
197 font->load_flags |= FT_LOAD_TARGET_NORMAL;
198 break;
200 case FC_HINT_FULL:
201 font->load_flags |= FT_LOAD_TARGET_MONO;
202 break;
205 if( autohint )
206 font->load_flags |= FT_LOAD_FORCE_AUTOHINT;
208 if( !globaladvance )
209 font->load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
211 if( !embeddedbitmap )
212 font->load_flags |= FT_LOAD_NO_BITMAP;
215 return font;
219 return NULL;
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 );
226 int i, max;
227 struct font *font;
229 for( i = 0; i < METRIC_CACHE_ASSOC; i++ )
230 if( metric_cache[ row ][ i ].style == style &&
231 metric_cache[ row ][ i ].c == c ) {
232 /* Cache hit. */
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;
236 return;
239 font = lookup_font( style, c );
241 /* Cache miss. */
242 if( FT_Load_Char( font->face, c, font->load_flags ) ) {
243 /* Couldn't load metrics. Don't bother evicting anything. */
244 *x_off = *y_off = 0;
245 return;
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 )
252 max = i;
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 );
266 int i;
268 for( i = 0; i < GLYPH_CACHE_ASSOC; i++ )
269 if( glyph_cache[ row ][ i ].style == style &&
270 glyph_cache[ row ][ i ].c == c ) {
271 /* Cache hit. */
272 glyph_cache[ row ][ i ].time = current_time;
273 return ( row << GLYPH_CACHE_ASSOC_BITS ) | i;
276 return -1;
279 static int replace_glyph( enum style_id style, uint32_t c ) {
281 int row = c & ( GLYPH_CACHE_SIZE - 1 );
282 int i, max;
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 )
290 max = i;
293 if( glyph_cache[ row ][ max ].time == current_time )
294 /* Cache line set is full, and nothing is old enough to evict. */
295 return -1;
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;
318 int i, len;
319 const unsigned char *p;
320 uint32_t *buf;
322 if( !text || !*text ) {
323 xcb_void_cookie_t r = { 0 };
325 return r;
328 len = 0;
329 p = (const unsigned char *) text;
330 for(;;)
331 if( !p[ 0 ] )
332 /* End of string. */
333 break;
334 else if( !( p[ 0 ] & 0x80 ) ) {
335 /* Legal single byte character. */
336 len++;
337 p++;
338 } else if( *p >= 0xC2 && *p <= 0xDF &&
339 p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF ) {
340 /* Legal two byte character. */
341 len++;
342 p += 2;
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. */
348 len++;
349 p += 3;
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. */
356 len++;
357 p += 4;
358 } else
359 /* Illegal character: ignore this byte and continue. */
360 p++;
362 if( !len ) {
363 xcb_void_cookie_t r = { 0 };
365 return r;
368 buf = alloca( len * sizeof *buf );
370 for( p = (const unsigned char *) text, i = 0; i < len; i++ ) {
371 retry:
372 assert( *p );
374 if( !( p[ 0 ] & 0x80 ) )
375 /* One byte character. */
376 buf[ i ] = *p++;
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 );
381 p += 2;
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 );
389 p += 3;
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 ) |
398 ( p[ 3 ] & 0x3F );
399 p += 4;
400 } else {
401 p++;
402 goto retry;
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 );
412 if( clip )
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 ];
419 rc.alpha = 0xFFFF;
420 rect.x = 0;
421 rect.y = 0;
422 rect.width = 1;
423 rect.height = 1;
424 xcb_render_fill_rectangles( c, XCB_RENDER_PICT_OP_SRC, src, rc, 1,
425 &rect );
427 pictures[ screen ].col = col;
430 i = 0;
431 while( i < len ) {
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 */
448 current_time++;
450 p = data;
451 num_glyphs = 0;
453 memset( param, 0, 4 );
454 *( (uint16_t *) ( param + 4 ) ) = x;
455 *( (uint16_t *) ( param + 6 ) ) = y;
456 out = param + 8;
457 num_chars = 0;
459 while( i < len && num_chars < 0xFF ) {
460 int dx, dy, index;
462 if( ( index = query_glyph( style, buf[ i ] ) ) < 0 ) {
463 /* Cache miss. */
464 int bitmap_size;
465 struct font *font;
466 FT_GlyphSlot slot;
468 if( ( index = replace_glyph( style, buf[ i ] ) ) < 0 )
469 /* Cache set full: spill the partial string to make
470 room for later glyphs. */
471 break;
473 if( !( font = lookup_font( style, buf[ i ] ) ) ||
474 FT_Load_Char( font->face, buf[ i ], font->load_flags |
475 FT_LOAD_RENDER ) ||
476 ( bitmap_size = ( ( font->face->glyph->bitmap.width +
477 3 ) & ~3 ) *
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. */
483 slot = NULL;
484 bitmap_size = 0;
485 } else
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. */
496 p = data;
497 num_glyphs = 0;
500 glyphids[ num_glyphs ] = index;
502 if( slot ) {
503 FT_Bitmap bitmap;
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 ) {
520 unsigned char *c,
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 );
531 } else {
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;
540 num_glyphs++;
543 *( (uint16_t *) out ) = index;
544 out += 2;
546 query_metrics( style, buf[ i ], &dx, &dy );
547 x += dx;
548 y += dy;
550 num_chars++;
551 i++;
554 if( num_glyphs ) {
555 xcb_render_add_glyphs( c, glyphset, num_glyphs, glyphids, glyphs,
556 p - data, data );
558 p = data;
559 num_glyphs = 0;
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,
566 param );
569 return xcb_render_free_picture( c, dest );
572 static int text_width( enum style_id style, const char *text ) {
574 const char *p;
575 int width;
577 for( width = 0, p = text; *p; p++ ) {
578 int dx, dy;
580 query_metrics( style, *p, &dx, &dy );
581 width += dx;
584 return width;
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 ) {
611 char text[ 32 ];
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,
623 int *height ) {
625 switch( window->type ) {
626 case WINDOW_MENUITEM:
627 if( window->u.menuitem.label ) {
628 *width = text_width( STYLE_MENU, window->u.menuitem.label ) +
629 ( MENU_X_PAD << 1 );
630 *height = MENU_FONT_SIZE + ( MENU_Y_PAD << 1 );
631 } else {
632 *width = MENU_X_PAD << 1;
633 *height = MENU_Y_PAD << 1;
635 return;
637 case WINDOW_FEEDBACK:
638 *width = FEEDBACK_WIDTH;
639 *height = FEEDBACK_HEIGHT;
640 return;
642 default:
643 assert( FALSE );
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;
661 if( error ) {
662 if( error->error_code != XCB_ALLOC )
663 show_error( error );
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;
670 free( error );
673 if( reply ) {
674 gwm_screens[ screen ].pixels[ col ] = r->pixel;
676 free( reply );
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;
686 if( error ) {
687 show_error( error );
689 free( error );
692 if( reply ) {
693 xcb_get_property_reply_t *prop = reply;
694 struct std_cmap {
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;
700 uint32_t kill_id;
701 } *std_cmaps = xcb_get_property_value( prop );
702 int num_cmaps = ( xcb_get_property_value_length( prop ) << 2 ) /
703 sizeof *std_cmaps;
704 int i;
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 ) {
718 r0 = luma( col );
719 g0 = b0 = 0;
720 } else {
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;
737 got_cols = TRUE;
738 break;
741 free( reply );
744 if( !got_cols )
745 /* No useful default maps found; try allocating directly. */
746 for( col = 0; col < NUM_COLS; col++ ) {
747 int r, g, b;
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 );
755 else {
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,
769 cp );
773 static void decorate_compat_init( void ) {
775 int i;
776 const char *cursor_font_name = "cursor";
777 xcb_font_t cursor_font;
778 static const int cursor_glyphs[ NUM_CURSORS ] = {
779 134, /* CURSOR_TL */
780 138, /* CURSOR_T */
781 136, /* CURSOR_TR */
782 70, /* CURSOR_L */
783 52, /* CURSOR_C */
784 96, /* CURSOR_R */
785 12, /* CURSOR_BL */
786 16, /* CURSOR_B */
787 14, /* CURSOR_BR */
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 ),
794 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;
807 cp.l = i;
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 ) {
817 FT_Error err;
818 FcFontSet *sets[ NUM_STYLES ];
819 FcPattern *style_pats[ NUM_STYLES ];
820 FcChar32 *hashes;
821 FcResult r;
822 int i, total;
823 xcb_pixmap_t pixmap;
824 uint32_t n;
826 if( !FcInit() )
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++ ) {
849 int j;
851 styles[ i ].fonts = xmalloc( sets[ i ]->nfont *
852 sizeof (struct font *) );
854 for( j = 0; j < sets[ i ]->nfont; j++ ) {
855 FcPattern *pat;
857 if( ( pat = FcFontRenderPrepare( NULL, style_pats[ i ],
858 sets[ i ]->fonts[ j ] ) ) ) {
859 FcChar32 hash = FcPatternHash( pat );
860 int search;
862 for( search = 0; search < num_fonts; search++ )
863 if( hashes[ search ] == hash &&
864 FcPatternEqual( fonts[ search ].pattern, pat ) ) {
865 FcPatternDestroy( pat );
866 break;
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;
875 num_fonts++;
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)
915 and cursors */
918 extern void decorate_render_done( void ) {
920 #if DEBUG
921 int i;
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 );
933 free( fonts );
935 FcFini();
937 FT_Done_FreeType( ftl );
939 free( pictures );
940 #endif