Move initialisation code into its own section, if supported by the linker.
[gwm.git] / decorate-core.c
blob9e5d6b10bc7696c39a1016e3e5f53f577984d95c
1 /*
2 * decorate-core.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 <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <xcb/xcb.h>
32 #include "gwm.h"
34 #include "button.h"
35 #include "decorate-core.h"
36 #include "frame.h"
37 #include "utf8.h"
39 #define FONT_SIZE 12
40 #define STRING2(x) #x
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 ) {
56 int i, len;
57 const unsigned char *p;
58 uint8_t *param, *out;
59 uint32_t g, n;
61 len = 0;
62 if( text ) {
63 p = (const unsigned char *) text;
64 while( ( g = utf8_next( &p ) ) )
65 if( g < 0x10000 )
66 len++;
69 if( len ) {
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;
76 i++, out += 2 ) {
77 if( !( i & 0x7F ) ) {
78 out[ 0 ] = len - i > 0x7F ? 0x80 : len - i; /* length */
79 out[ 1 ] = 0; /* delta */
80 out += 2;
83 while( assert( *p ), ( g = utf8_next( &p ) ) > 0xFFFF )
86 out[ 0 ] = g >> 8;
87 out[ 1 ] = g & 0xFF;
90 return xcb_poly_text_16( c, drawable, gcs[ screen ], x, y, out - param,
91 param );
92 } else {
93 xcb_void_cookie_t r = { 0 };
95 return r;
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 ) {
124 char *p, text[ 32 ];
125 int width;
127 if( width_sequence ) {
128 sync_with_callback( width_sequence );
129 width_sequence = 0;
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' ];
138 else
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,
147 int *height ) {
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;
155 } else {
156 *width = 8;
157 *height = 4;
159 return;
161 case WINDOW_FEEDBACK:
162 *width = FEEDBACK_WIDTH;
163 *height = FEEDBACK_HEIGHT;
164 return;
166 default:
167 assert( FALSE );
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;
184 if( error ) {
185 show_error( error );
187 free( error );
190 if( reply ) {
191 digit_widths[ p.l ] = r->overall_width;
193 free( reply );
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;
227 if( error ) {
228 if( error->error_code != XCB_ALLOC )
229 show_error( error );
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;
236 free( error );
239 if( reply ) {
240 gwm_screens[ screen ].pixels[ col ] = r->pixel;
242 free( reply );
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;
252 if( error ) {
253 show_error( error );
255 free( error );
258 if( reply ) {
259 xcb_get_property_reply_t *prop = reply;
260 struct std_cmap {
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;
266 uint32_t kill_id;
267 } *std_cmaps = xcb_get_property_value( prop );
268 int num_cmaps = ( prop->value_len << 2 ) / sizeof *std_cmaps;
269 int i;
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 ) {
283 r0 = luma( col );
284 g0 = b0 = 0;
285 } else {
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;
302 got_cols = TRUE;
303 break;
306 free( reply );
309 if( !got_cols )
310 /* No useful default maps found; try allocating directly. */
311 for( col = 0; col < NUM_COLS; col++ ) {
312 int r, g, b;
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 );
320 else {
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,
330 cp );
334 extern INIT void decorate_core_init( void ) {
336 int i;
337 uint32_t n;
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";
341 xcb_char2b_t c2;
342 union callback_param p;
343 xcb_font_t cursor_font;
344 static INITD const int cursor_glyphs[ NUM_CURSORS ] = {
345 134, /* CURSOR_TL */
346 138, /* CURSOR_T */
347 136, /* CURSOR_TR */
348 70, /* CURSOR_L */
349 52, /* CURSOR_C */
350 96, /* CURSOR_R */
351 12, /* CURSOR_BL */
352 16, /* CURSOR_B */
353 14, /* CURSOR_BR */
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 );
365 n = font;
366 xcb_create_gc( c, gcs[ i ], screens[ i ]->root, XCB_GC_FONT, &n );
369 c2.byte1 = 0;
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 );
375 c2.byte2 = 'x';
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 ),
382 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;
395 cp.l = i;
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 ) {
406 #if DEBUG
407 free( gcs );
408 #endif