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/>.
35 #include "decorate-core.h"
41 #define STRING(x) STRING2(x)
42 #define FONT_SIZE_STRING STRING( FONT_SIZE )
44 #define FEEDBACK_WIDTH 96 /* width of size feedback window */
45 #define FEEDBACK_HEIGHT 24 /* height of size feedback window */
47 static xcb_font_t font
;
48 static xcb_gcontext_t
*gcs
;
50 static int digit_widths
[ 11 ]; /* widths of characters '0' to '9', and 'x' */
51 static unsigned int width_sequence
, cmap_sequence
;
53 static xcb_void_cookie_t
core_text( xcb_drawable_t drawable
, int screen
,
54 int col
, int x
, int y
, const char *text
) {
57 const unsigned char *p
;
63 p
= (const unsigned char *) text
;
64 while( ( g
= utf8_next( &p
) ) )
70 n
= gwm_screens
[ screen
].pixels
[ col
];
71 xcb_change_gc( c
, gcs
[ screen
], XCB_GC_FOREGROUND
, &n
);
73 out
= param
= alloca( ( len
+ ( len
>> 7 ) + 1 ) << 1 );
75 for( p
= (const unsigned char *) text
, i
= 0; i
< len
;
78 out
[ 0 ] = len
- i
> 0x7F ? 0x80 : len
- i
; /* length */
79 out
[ 1 ] = 0; /* delta */
83 while( assert( *p
), ( g
= utf8_next( &p
) ) > 0xFFFF )
90 return xcb_poly_text_16( c
, drawable
, gcs
[ screen
], x
, y
, out
- param
,
93 xcb_void_cookie_t r
= { 0 };
99 extern void core_update_window( struct gwm_window
*window
) {
101 if( !window
->cleared
)
102 xcb_clear_area( c
, FALSE
, window
->w
, window
->update
.x
, window
->update
.y
,
103 window
->update
.width
, window
->update
.height
);
105 xcb_set_clip_rectangles( c
, XCB_CLIP_ORDERING_YX_BANDED
,
106 gcs
[ window
->screen
], 0, 0, 1, &window
->update
);
108 if( window
->type
== WINDOW_FRAME
) {
109 char *name
= window
->u
.frame
.child
->u
.managed
.name
;
111 core_text( window
->w
, window
->screen
, window
== focus_frame
?
112 COL_TITLE_ACTIVE
: COL_TITLE_INACTIVE
,
113 button_size( window
->u
.frame
.button
, TRUE
) + 4,
114 FONT_SIZE
, name
? name
: "(Untitled)" );
115 } else if( window
->type
== WINDOW_MENUITEM
) {
116 struct gwm_window
*menu
= window
->u
.menuitem
.menu
;
118 core_text( window
->w
, window
->screen
,
119 menu
->u
.menu
.active_item
>= 0 &&
120 menu
->u
.menu
.items
[ menu
->u
.menu
.active_item
] == window
?
121 COL_MENU_ACTIVE_FORE
: COL_MENU_INACTIVE_FORE
,
122 4, FONT_SIZE
, window
->u
.menuitem
.label
);
123 } else if( window
->type
== WINDOW_FEEDBACK
) {
127 if( width_sequence
) {
128 sync_with_callback( width_sequence
);
132 sprintf( text
, "%dx%d", window
->u
.feedback
.fb_width
,
133 window
->u
.feedback
.fb_height
);
135 for( width
= 0, p
= text
; *p
; p
++ )
136 if( *p
>= '0' && *p
<= '9' )
137 width
+= digit_widths
[ *p
- '0' ];
139 width
+= digit_widths
[ 10 ];
141 core_text( window
->w
, window
->screen
, COL_FEEDBACK_FORE
,
142 ( FEEDBACK_WIDTH
- width
) >> 1, 16, text
);
146 extern void core_window_size( struct gwm_window
*window
, int *width
,
149 switch( window
->type
) {
150 case WINDOW_MENUITEM
:
151 if( window
->u
.menuitem
.label
) {
152 *width
= FONT_SIZE
* utf8_length( (const unsigned char *)
153 window
->u
.menuitem
.label
);
154 *height
= FONT_SIZE
+ 4;
161 case WINDOW_FEEDBACK
:
162 *width
= FEEDBACK_WIDTH
;
163 *height
= FEEDBACK_HEIGHT
;
171 extern void core_replace_icons( struct gwm_window
*window
, int num_icons
,
172 int *widths
, int *heights
, uint32_t **icons
,
173 xcb_pixmap_t
*bitmaps
) {
175 /* FIXME Implement core icons... or maybe not. */
178 static void query_text_callback( unsigned int sequence
, void *reply
,
179 xcb_generic_error_t
*error
,
180 union callback_param p
) {
182 xcb_query_text_extents_reply_t
*r
= reply
;
191 digit_widths
[ p
.l
] = r
->overall_width
;
197 static const uint16_t decoration_cols
[ NUM_COLS
][ 3 ] = {
198 { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */
199 { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */
200 { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */
201 { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */
202 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */
203 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */
204 { 0x0000, 0x0000, 0x0000 }, /* COL_TITLE_INACTIVE */
205 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */
206 { 0x0000, 0x0000, 0x0000 }, /* COL_FEEDBACK_FORE */
207 { 0x2222, 0x3333, 0xEEEE }, /* COL_MENU_ACTIVE_BACK */
208 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MENU_ACTIVE_FORE */
209 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_MENU_INACTIVE_BACK */
210 { 0x0000, 0x0000, 0x0000 } /* COL_MENU_INACTIVE_FORE */
213 static uint16_t luma( int col
) {
215 return ( decoration_cols
[ col
][ 0 ] * 0x4C8BU
+
216 decoration_cols
[ col
][ 1 ] * 0x9646U
+
217 decoration_cols
[ col
][ 2 ] * 0x1D2FU
) >> 16;
220 static void handle_alloc_color( unsigned int sequence
, void *reply
,
221 xcb_generic_error_t
*error
,
222 union callback_param p
) {
224 xcb_alloc_color_reply_t
*r
= reply
;
225 int screen
= p
.l
>> 16, col
= p
.l
& 0xFFFF;
228 if( error
->error_code
!= XCB_ALLOC
)
231 /* No standard RGB map available, and we couldn't allocate a shared
232 colour. Fall back to black or white. */
233 gwm_screens
[ screen
].pixels
[ col
] = luma( col
) >= 0x8000 ?
234 screens
[ screen
]->white_pixel
: screens
[ screen
]->black_pixel
;
240 gwm_screens
[ screen
].pixels
[ col
] = r
->pixel
;
246 static void handle_get_default_map( unsigned int sequence
, void *reply
,
247 xcb_generic_error_t
*error
,
248 union callback_param p
) {
250 int col
, got_cols
= FALSE
;
259 xcb_get_property_reply_t
*prop
= reply
;
261 /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
262 xcb_colormap_t colormap
;
263 uint32_t red_max
, red_mult
, green_max
, green_mult
, blue_max
,
264 blue_mult
, base_pixel
;
265 xcb_visualid_t visual_id
;
267 } *std_cmaps
= xcb_get_property_value( prop
);
268 int num_cmaps
= ( prop
->value_len
<< 2 ) / sizeof *std_cmaps
;
271 if( prop
->format
== 32 )
272 for( i
= 0; i
< num_cmaps
; i
++ )
273 if( std_cmaps
[ i
].visual_id
==
274 gwm_screens
[ p
.l
].root_visual
->visual_id
) {
276 for( col
= 0; col
< NUM_COLS
; col
++ ) {
277 int r0
, g0
, b0
, r
, g
, b
;
279 if( gwm_screens
[ p
.l
].root_visual
->_class
==
280 XCB_VISUAL_CLASS_STATIC_GRAY
||
281 gwm_screens
[ p
.l
].root_visual
->_class
==
282 XCB_VISUAL_CLASS_GRAY_SCALE
) {
286 r0
= decoration_cols
[ col
][ 0 ];
287 g0
= decoration_cols
[ col
][ 1 ];
288 b0
= decoration_cols
[ col
][ 2 ];
291 r
= ( r0
* std_cmaps
[ i
].red_max
+ 0x8000 ) >> 16;
292 g
= ( g0
* std_cmaps
[ i
].green_max
+ 0x8000 ) >> 16;
293 b
= ( b0
* std_cmaps
[ i
].blue_max
+ 0x8000 ) >> 16;
295 gwm_screens
[ p
.l
].pixels
[ col
] =
296 std_cmaps
[ i
].base_pixel
+
297 r
* std_cmaps
[ i
].red_mult
+
298 g
* std_cmaps
[ i
].green_mult
+
299 b
* std_cmaps
[ i
].blue_mult
;
310 /* No useful default maps found; try allocating directly. */
311 for( col
= 0; col
< NUM_COLS
; col
++ ) {
313 union callback_param cp
;
315 if( gwm_screens
[ p
.l
].root_visual
->_class
==
316 XCB_VISUAL_CLASS_STATIC_GRAY
||
317 gwm_screens
[ p
.l
].root_visual
->_class
==
318 XCB_VISUAL_CLASS_GRAY_SCALE
)
319 r
= g
= b
= luma( col
);
321 r
= decoration_cols
[ col
][ 0 ];
322 g
= decoration_cols
[ col
][ 1 ];
323 b
= decoration_cols
[ col
][ 2 ];
326 cp
.l
= ( p
.l
<< 16 ) | col
;
327 handle_async_reply( cmap_sequence
= xcb_alloc_color(
328 c
, screens
[ p
.l
]->default_colormap
,
329 r
, g
, b
).sequence
, handle_alloc_color
,
334 extern INIT
void decorate_core_init( void ) {
338 const char *font_name
= "-*-lucida-bold-r-normal-sans-"
339 FONT_SIZE_STRING
"-*-*-*-p-*-iso10646-1", /* FIXME allow selection */
340 *cursor_font_name
= "cursor";
342 union callback_param p
;
343 xcb_font_t cursor_font
;
344 static INITD
const int cursor_glyphs
[ NUM_CURSORS
] = {
354 68, /* CURSOR_ARROW */
355 88 /* CURSOR_DESTROY */
358 font
= xcb_generate_id( c
);
359 xcb_open_font( c
, font
, strlen( font_name
), font_name
);
361 gcs
= xmalloc( num_screens
* sizeof *gcs
);
363 for( i
= 0; i
< num_screens
; i
++ ) {
364 gcs
[ i
] = xcb_generate_id( c
);
366 xcb_create_gc( c
, gcs
[ i
], screens
[ i
]->root
, XCB_GC_FONT
, &n
);
370 for( p
.l
= 0; p
.l
<= 9; p
.l
++ ) {
371 c2
.byte2
= p
.l
+ '0';
372 handle_async_reply( xcb_query_text_extents( c
, font
, 1, &c2
).sequence
,
373 query_text_callback
, p
);
376 handle_async_reply( width_sequence
=
377 xcb_query_text_extents( c
, font
, 1, &c2
).sequence
,
378 query_text_callback
, p
);
380 cursor_font
= xcb_generate_id( c
);
381 xcb_open_font( c
, cursor_font
, strlen( cursor_font_name
),
383 for( i
= 0; i
< NUM_CURSORS
; i
++ ) {
384 cursors
[ i
] = xcb_generate_id( c
);
385 xcb_create_glyph_cursor( c
, cursors
[ i
], cursor_font
, cursor_font
,
386 cursor_glyphs
[ i
], cursor_glyphs
[ i
] | 1,
387 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
389 xcb_close_font( c
, cursor_font
);
391 /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
392 for( i
= 0; i
< num_screens
; i
++ ) {
393 union callback_param cp
;
396 handle_async_reply( cmap_sequence
=
397 xcb_get_property( c
, FALSE
, screens
[ i
]->root
,
398 RGB_DEFAULT_MAP
, RGB_COLOR_MAP
,
399 0, 0x10000 ).sequence
,
400 handle_get_default_map
, cp
);
404 extern void decorate_core_done( void ) {