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
->type
== WINDOW_CHILDLESS
)
104 if( !window
->cleared
)
105 xcb_clear_area( c
, FALSE
, window
->w
, window
->update
.x
, window
->update
.y
,
106 window
->update
.width
, window
->update
.height
);
108 xcb_set_clip_rectangles( c
, XCB_CLIP_ORDERING_YX_BANDED
,
109 gcs
[ window
->screen
], 0, 0, 1, &window
->update
);
111 if( window
->type
== WINDOW_FRAME
) {
112 char *name
= window
->u
.frame
.child
->u
.managed
.name
;
114 core_text( window
->w
, window
->screen
, window
== focus_frame
?
115 COL_TITLE_ACTIVE
: COL_TITLE_INACTIVE
,
116 button_size( window
->u
.frame
.button
, TRUE
) + 4,
117 FONT_SIZE
, name
? name
: "(Untitled)" );
118 } else if( window
->type
== WINDOW_MENUITEM
) {
119 struct gwm_window
*menu
= window
->u
.menuitem
.menu
;
121 core_text( window
->w
, window
->screen
,
122 menu
->u
.menu
.active_item
>= 0 &&
123 menu
->u
.menu
.items
[ menu
->u
.menu
.active_item
] == window
?
124 COL_MENU_ACTIVE_FORE
: COL_MENU_INACTIVE_FORE
,
125 4, FONT_SIZE
, window
->u
.menuitem
.label
);
126 } else if( window
->type
== WINDOW_FEEDBACK
) {
130 if( width_sequence
) {
131 sync_with_callback( width_sequence
);
135 sprintf( text
, "%dx%d", window
->u
.feedback
.fb_width
,
136 window
->u
.feedback
.fb_height
);
138 for( width
= 0, p
= text
; *p
; p
++ )
139 if( *p
>= '0' && *p
<= '9' )
140 width
+= digit_widths
[ *p
- '0' ];
142 width
+= digit_widths
[ 10 ];
144 core_text( window
->w
, window
->screen
, COL_FEEDBACK_FORE
,
145 ( FEEDBACK_WIDTH
- width
) >> 1, 16, text
);
149 extern void core_window_size( struct gwm_window
*window
, int *width
,
152 switch( window
->type
) {
153 case WINDOW_MENUITEM
:
154 if( window
->u
.menuitem
.label
) {
155 *width
= FONT_SIZE
* utf8_length( (const unsigned char *)
156 window
->u
.menuitem
.label
);
157 *height
= FONT_SIZE
+ 4;
164 case WINDOW_FEEDBACK
:
165 *width
= FEEDBACK_WIDTH
;
166 *height
= FEEDBACK_HEIGHT
;
174 extern void core_replace_icons( struct gwm_window
*window
, int num_icons
,
175 int *widths
, int *heights
, uint32_t **icons
,
176 xcb_pixmap_t
*bitmaps
) {
178 /* FIXME Implement core icons... or maybe not. */
181 static void query_text_callback( unsigned int sequence
, void *reply
,
182 xcb_generic_error_t
*error
,
183 union callback_param p
) {
185 xcb_query_text_extents_reply_t
*r
= reply
;
194 digit_widths
[ p
.l
] = r
->overall_width
;
200 static const uint16_t decoration_cols
[ NUM_COLS
][ 3 ] = {
201 { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */
202 { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */
203 { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */
204 { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */
205 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */
206 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */
207 { 0x0000, 0x0000, 0x0000 }, /* COL_TITLE_INACTIVE */
208 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */
209 { 0x0000, 0x0000, 0x0000 }, /* COL_FEEDBACK_FORE */
210 { 0x2222, 0x3333, 0xEEEE }, /* COL_MENU_ACTIVE_BACK */
211 { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MENU_ACTIVE_FORE */
212 { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_MENU_INACTIVE_BACK */
213 { 0x0000, 0x0000, 0x0000 } /* COL_MENU_INACTIVE_FORE */
216 static uint16_t luma( int col
) {
218 return ( decoration_cols
[ col
][ 0 ] * 0x4C8BU
+
219 decoration_cols
[ col
][ 1 ] * 0x9646U
+
220 decoration_cols
[ col
][ 2 ] * 0x1D2FU
) >> 16;
223 static void handle_alloc_color( unsigned int sequence
, void *reply
,
224 xcb_generic_error_t
*error
,
225 union callback_param p
) {
227 xcb_alloc_color_reply_t
*r
= reply
;
228 int screen
= p
.l
>> 16, col
= p
.l
& 0xFFFF;
231 if( error
->error_code
!= XCB_ALLOC
)
234 /* No standard RGB map available, and we couldn't allocate a shared
235 colour. Fall back to black or white. */
236 gwm_screens
[ screen
].pixels
[ col
] = luma( col
) >= 0x8000 ?
237 screens
[ screen
]->white_pixel
: screens
[ screen
]->black_pixel
;
243 gwm_screens
[ screen
].pixels
[ col
] = r
->pixel
;
249 static void handle_get_default_map( unsigned int sequence
, void *reply
,
250 xcb_generic_error_t
*error
,
251 union callback_param p
) {
253 int col
, got_cols
= FALSE
;
262 xcb_get_property_reply_t
*prop
= reply
;
264 /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */
265 xcb_colormap_t colormap
;
266 uint32_t red_max
, red_mult
, green_max
, green_mult
, blue_max
,
267 blue_mult
, base_pixel
;
268 xcb_visualid_t visual_id
;
270 } *std_cmaps
= xcb_get_property_value( prop
);
271 int num_cmaps
= ( prop
->value_len
<< 2 ) / sizeof *std_cmaps
;
274 if( prop
->format
== 32 )
275 for( i
= 0; i
< num_cmaps
; i
++ )
276 if( std_cmaps
[ i
].visual_id
==
277 gwm_screens
[ p
.l
].root_visual
->visual_id
) {
279 for( col
= 0; col
< NUM_COLS
; col
++ ) {
280 int r0
, g0
, b0
, r
, g
, b
;
282 if( gwm_screens
[ p
.l
].root_visual
->_class
==
283 XCB_VISUAL_CLASS_STATIC_GRAY
||
284 gwm_screens
[ p
.l
].root_visual
->_class
==
285 XCB_VISUAL_CLASS_GRAY_SCALE
) {
289 r0
= decoration_cols
[ col
][ 0 ];
290 g0
= decoration_cols
[ col
][ 1 ];
291 b0
= decoration_cols
[ col
][ 2 ];
294 r
= ( r0
* std_cmaps
[ i
].red_max
+ 0x8000 ) >> 16;
295 g
= ( g0
* std_cmaps
[ i
].green_max
+ 0x8000 ) >> 16;
296 b
= ( b0
* std_cmaps
[ i
].blue_max
+ 0x8000 ) >> 16;
298 gwm_screens
[ p
.l
].pixels
[ col
] =
299 std_cmaps
[ i
].base_pixel
+
300 r
* std_cmaps
[ i
].red_mult
+
301 g
* std_cmaps
[ i
].green_mult
+
302 b
* std_cmaps
[ i
].blue_mult
;
313 /* No useful default maps found; try allocating directly. */
314 for( col
= 0; col
< NUM_COLS
; col
++ ) {
316 union callback_param cp
;
318 if( gwm_screens
[ p
.l
].root_visual
->_class
==
319 XCB_VISUAL_CLASS_STATIC_GRAY
||
320 gwm_screens
[ p
.l
].root_visual
->_class
==
321 XCB_VISUAL_CLASS_GRAY_SCALE
)
322 r
= g
= b
= luma( col
);
324 r
= decoration_cols
[ col
][ 0 ];
325 g
= decoration_cols
[ col
][ 1 ];
326 b
= decoration_cols
[ col
][ 2 ];
329 cp
.l
= ( p
.l
<< 16 ) | col
;
330 handle_async_reply( cmap_sequence
= xcb_alloc_color(
331 c
, screens
[ p
.l
]->default_colormap
,
332 r
, g
, b
).sequence
, handle_alloc_color
,
337 extern INIT
void decorate_core_init( void ) {
341 const char *font_name
= "-*-lucida-bold-r-normal-sans-"
342 FONT_SIZE_STRING
"-*-*-*-p-*-iso10646-1", /* FIXME allow selection */
343 *cursor_font_name
= "cursor";
345 union callback_param p
;
346 xcb_font_t cursor_font
;
347 static INITD
const int cursor_glyphs
[ NUM_CURSORS
] = {
357 68, /* CURSOR_ARROW */
358 88 /* CURSOR_DESTROY */
361 font
= xcb_generate_id( c
);
362 xcb_open_font( c
, font
, strlen( font_name
), font_name
);
364 gcs
= xmalloc( num_screens
* sizeof *gcs
);
366 for( i
= 0; i
< num_screens
; i
++ ) {
367 gcs
[ i
] = xcb_generate_id( c
);
369 xcb_create_gc( c
, gcs
[ i
], screens
[ i
]->root
, XCB_GC_FONT
, &n
);
373 for( p
.l
= 0; p
.l
<= 9; p
.l
++ ) {
374 c2
.byte2
= p
.l
+ '0';
375 handle_async_reply( xcb_query_text_extents( c
, font
, 1, &c2
).sequence
,
376 query_text_callback
, p
);
379 handle_async_reply( width_sequence
=
380 xcb_query_text_extents( c
, font
, 1, &c2
).sequence
,
381 query_text_callback
, p
);
383 cursor_font
= xcb_generate_id( c
);
384 xcb_open_font( c
, cursor_font
, strlen( cursor_font_name
),
386 for( i
= 0; i
< NUM_CURSORS
; i
++ ) {
387 cursors
[ i
] = xcb_generate_id( c
);
388 xcb_create_glyph_cursor( c
, cursors
[ i
], cursor_font
, cursor_font
,
389 cursor_glyphs
[ i
], cursor_glyphs
[ i
] | 1,
390 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF );
392 xcb_close_font( c
, cursor_font
);
394 /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */
395 for( i
= 0; i
< num_screens
; i
++ ) {
396 union callback_param cp
;
399 handle_async_reply( cmap_sequence
=
400 xcb_get_property( c
, FALSE
, screens
[ i
]->root
,
401 RGB_DEFAULT_MAP
, RGB_COLOR_MAP
,
402 0, 0x10000 ).sequence
,
403 handle_get_default_map
, cp
);
407 extern void decorate_core_done( void ) {