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/>.
34 #include "decorate-core.h"
36 static xcb_font_t font
;
37 static xcb_gcontext_t
*gcs
;
39 static int digit_widths
[ 11 ]; /* widths of characters '0' to '9', and 'x' */
40 static unsigned int width_sequence
, cmap_sequence
;
42 static xcb_void_cookie_t
core_text( xcb_drawable_t drawable
, int screen
,
43 int col
, int x
, int y
, const char *text
) {
46 const unsigned char *p
;
50 if( !text
|| !*text
) {
51 xcb_void_cookie_t r
= { 0 };
56 n
= gwm_screens
[ screen
].pixels
[ col
];
57 xcb_change_gc( c
, gcs
[ screen
], XCB_GC_FOREGROUND
, &n
);
60 p
= (const unsigned char *) text
;
65 else if( !( p
[ 0 ] & 0x80 ) ) {
66 /* Legal single byte character. */
69 } else if( *p
>= 0xC2 && *p
<= 0xDF &&
70 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF ) {
71 /* Legal two byte character. */
74 } else if( *p
>= 0xE0 && *p
<= 0xEF &&
75 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
76 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF ) {
77 /* Legal three byte character. */
81 /* Illegal character, or a four byte character which lies outside
82 the BMP and so won't be included in a core font. Either way,
83 ignore this byte and continue. */
86 out
= param
= alloca( ( len
+ ( len
>> 7 ) + 1 ) << 1 );
88 for( p
= (const unsigned char *) text
, i
= 0; i
< len
; i
++, out
+= 2 ) {
90 out
[ 0 ] = len
- i
> 0x7F ? 0x80 : len
- i
; /* length */
91 out
[ 1 ] = 0; /* delta */
98 if( !( p
[ 0 ] & 0x80 ) ) {
99 /* One byte character. */
102 } else if( *p
>= 0xC2 && *p
<= 0xDF &&
103 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF ) {
104 /* Two byte character. */
105 out
[ 0 ] = ( p
[ 0 ] >> 2 ) & 0x07;
106 out
[ 1 ] = ( ( p
[ 0 ] & 3 ) << 6 ) | ( p
[ 1 ] & 0x3F );
108 } else if( *p
>= 0xE0 && *p
<= 0xEF &&
109 p
[ 1 ] >= 0x80 && p
[ 1 ] <= 0xBF &&
110 p
[ 2 ] >= 0x80 && p
[ 2 ] <= 0xBF ) {
111 /* Three byte character. */
112 out
[ 0 ] = ( ( p
[ 0 ] & 0x0F ) << 4 ) | ( ( p
[ 1 ] & 0x3C ) >> 2 );
113 out
[ 1 ] = ( ( p
[ 1 ] & 3 ) << 6 ) | ( p
[ 2 ] & 0x3F );
121 return xcb_poly_text_16( c
, drawable
, gcs
[ screen
], x
, y
,
122 out
- param
, param
);
125 extern void core_update_window( struct gwm_window
*window
) {
127 /* FIXME SetClipRectangles? */
128 if( !window
->cleared
)
129 xcb_clear_area( c
, FALSE
, window
->w
, window
->update
.x
, window
->update
.y
,
130 window
->update
.width
, window
->update
.height
);
132 if( window
->type
== WINDOW_FRAME
) {
133 char *name
= window
->u
.frame
.child
->u
.managed
.name
;
135 /* FIXME Define constants for position. */
136 core_text( window
->w
, window
->screen
, window
== focus_frame
?
137 COL_TITLE_ACTIVE
: COL_TITLE_INACTIVE
, 18, 12,
138 name
? name
: "(Untitled)" );
139 } else if( window
->type
== WINDOW_FEEDBACK
) {
143 if( width_sequence
) {
144 sync_with_callback( width_sequence
);
148 sprintf( text
, "%dx%d", window
->u
.feedback
.fb_width
,
149 window
->u
.feedback
.fb_height
);
151 for( width
= 0, p
= text
; *p
; p
++ )
152 if( *p
>= '0' && *p
<= '9' )
153 width
+= digit_widths
[ *p
- '0' ];
155 width
+= digit_widths
[ 10 ];
157 core_text( window
->w
, window
->screen
, COL_FEEDBACK_FORE
,
158 ( FEEDBACK_WIDTH
- width
) >> 1, 16, text
);
162 static void query_text_callback( unsigned int sequence
, void *reply
,
163 xcb_generic_error_t
*error
,
164 union callback_param p
) {
166 xcb_query_text_extents_reply_t
*r
= reply
;
175 digit_widths
[ p
.l
] = r
->overall_width
;
181 static const uint16_t decoration_cols
[ NUM_COLS
][ 3 ] = {
182 { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */
183 { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */
184 { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */
185 { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */
186 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */
187 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */
188 { 0x0000, 0x0000, 0x0000 }, /* COL_TITLE_INACTIVE */
189 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */
190 { 0x0000, 0x0000, 0x0000 } /* COL_FEEDBACK_FORE */
193 static uint16_t luma( int col
) {
195 return ( decoration_cols
[ col
][ 0 ] * 0x4C8BU
+
196 decoration_cols
[ col
][ 1 ] * 0x9646U
+
197 decoration_cols
[ col
][ 2 ] * 0x1D2FU
) >> 16;
200 static void handle_alloc_color( unsigned int sequence
, void *reply
,
201 xcb_generic_error_t
*error
,
202 union callback_param p
) {
204 xcb_alloc_color_reply_t
*r
= reply
;
205 int screen
= p
.l
>> 16, col
= p
.l
& 0xFFFF;
208 if( error
->error_code
!= XCB_ALLOC
)
211 /* No standard RGB map available, and we couldn't allocate a shared
212 colour. Fall back to black or white. */
213 gwm_screens
[ screen
].pixels
[ col
] = luma( col
) >= 0x8000 ?
214 screens
[ screen
]->white_pixel
: screens
[ screen
]->black_pixel
;
220 gwm_screens
[ screen
].pixels
[ col
] = r
->pixel
;
226 static void handle_get_default_map( unsigned int sequence
, void *reply
,
227 xcb_generic_error_t
*error
,
228 union callback_param p
) {
230 int col
, got_cols
= FALSE
;
239 xcb_get_property_reply_t
*prop
= reply
;
241 /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
242 xcb_colormap_t colormap
;
243 uint32_t red_max
, red_mult
, green_max
, green_mult
, blue_max
,
244 blue_mult
, base_pixel
;
245 xcb_visualid_t visual_id
;
247 } *std_cmaps
= xcb_get_property_value( prop
);
248 int num_cmaps
= ( xcb_get_property_value_length( prop
) << 2 ) /
252 if( prop
->format
== 32 )
253 for( i
= 0; i
< num_cmaps
; i
++ )
254 if( std_cmaps
[ i
].visual_id
==
255 gwm_screens
[ p
.l
].root_visual
->visual_id
) {
257 for( col
= 0; col
< NUM_COLS
; col
++ ) {
258 int r0
, g0
, b0
, r
, g
, b
;
260 if( gwm_screens
[ p
.l
].root_visual
->_class
==
261 XCB_VISUAL_CLASS_STATIC_GRAY
||
262 gwm_screens
[ p
.l
].root_visual
->_class
==
263 XCB_VISUAL_CLASS_GRAY_SCALE
) {
267 r0
= decoration_cols
[ col
][ 0 ];
268 g0
= decoration_cols
[ col
][ 1 ];
269 b0
= decoration_cols
[ col
][ 2 ];
272 r
= ( r0
* std_cmaps
[ i
].red_max
+ 0x8000 ) >> 16;
273 g
= ( g0
* std_cmaps
[ i
].green_max
+ 0x8000 ) >> 16;
274 b
= ( b0
* std_cmaps
[ i
].blue_max
+ 0x8000 ) >> 16;
276 gwm_screens
[ p
.l
].pixels
[ col
] =
277 std_cmaps
[ i
].base_pixel
+
278 r
* std_cmaps
[ i
].red_mult
+
279 g
* std_cmaps
[ i
].green_mult
+
280 b
* std_cmaps
[ i
].blue_mult
;
291 /* No useful default maps found; try allocating directly. */
292 for( col
= 0; col
< NUM_COLS
; col
++ ) {
294 union callback_param cp
;
296 if( gwm_screens
[ p
.l
].root_visual
->_class
==
297 XCB_VISUAL_CLASS_STATIC_GRAY
||
298 gwm_screens
[ p
.l
].root_visual
->_class
==
299 XCB_VISUAL_CLASS_GRAY_SCALE
)
300 r
= g
= b
= luma( col
);
302 r
= decoration_cols
[ col
][ 0 ];
303 g
= decoration_cols
[ col
][ 1 ];
304 b
= decoration_cols
[ col
][ 2 ];
307 cp
.l
= ( p
.l
<< 16 ) | col
;
308 handle_async_reply( cmap_sequence
= xcb_alloc_color(
309 c
, screens
[ p
.l
]->default_colormap
,
310 r
, g
, b
).sequence
, handle_alloc_color
,
315 extern void decorate_core_init( void ) {
319 const char *font_name
= "-*-lucida-bold-r-normal-sans-12-"
320 "*-*-*-p-*-iso10646-1", /* FIXME allow font selection */
321 *cursor_font_name
= "cursor";
323 union callback_param p
;
324 xcb_font_t cursor_font
;
325 static const int cursor_glyphs
[ NUM_CURSORS
] = {
326 68, /* CURSOR_ARROW */
327 88, /* CURSOR_DESTROY */
339 font
= xcb_generate_id( c
);
340 xcb_open_font( c
, font
, strlen( font_name
), font_name
);
342 gcs
= xmalloc( num_screens
* sizeof *gcs
);
344 for( i
= 0; i
< num_screens
; i
++ ) {
345 gcs
[ i
] = xcb_generate_id( c
);
347 xcb_create_gc( c
, gcs
[ i
], screens
[ i
]->root
, XCB_GC_FONT
, &n
);
351 for( p
.l
= 0; p
.l
<= 9; p
.l
++ ) {
352 c2
.byte2
= p
.l
+ '0';
353 handle_async_reply( xcb_query_text_extents( c
, font
, 1, &c2
).sequence
,
354 query_text_callback
, p
);
357 handle_async_reply( width_sequence
=
358 xcb_query_text_extents( c
, font
, 1, &c2
).sequence
,
359 query_text_callback
, p
);
361 cursor_font
= xcb_generate_id( c
);
362 xcb_open_font( c
, cursor_font
, strlen( cursor_font_name
),
364 for( i
= 0; i
< NUM_CURSORS
; i
++ ) {
365 cursors
[ i
] = xcb_generate_id( c
);
366 xcb_create_glyph_cursor( c
, cursors
[ i
], cursor_font
, cursor_font
,
367 cursor_glyphs
[ i
], cursor_glyphs
[ i
] | 1,
368 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
370 xcb_close_font( c
, cursor_font
);
372 /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
373 for( i
= 0; i
< num_screens
; i
++ ) {
374 union callback_param cp
;
377 handle_async_reply( cmap_sequence
=
378 xcb_get_property( c
, FALSE
, screens
[ i
]->root
,
379 RGB_DEFAULT_MAP
, RGB_COLOR_MAP
,
380 0, 0x10000 ).sequence
,
381 handle_get_default_map
, cp
);
385 extern void decorate_core_done( void ) {