Respond to ShapeNotify events for bounding region modifications only.
[gwm.git] / decorate-core.c
blob69765379c1405b958b97df6e0fb8e85a7586ff38
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 "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 ) {
45 int i, len;
46 const unsigned char *p;
47 uint8_t *param, *out;
48 uint32_t n;
50 if( !text || !*text ) {
51 xcb_void_cookie_t r = { 0 };
53 return r;
56 n = gwm_screens[ screen ].pixels[ col ];
57 xcb_change_gc( c, gcs[ screen ], XCB_GC_FOREGROUND, &n );
59 len = 0;
60 p = (const unsigned char *) text;
61 for(;;)
62 if( !p[ 0 ] )
63 /* End of string. */
64 break;
65 else if( !( p[ 0 ] & 0x80 ) ) {
66 /* Legal single byte character. */
67 len++;
68 p++;
69 } else if( *p >= 0xC2 && *p <= 0xDF &&
70 p[ 1 ] >= 0x80 && p[ 1 ] <= 0xBF ) {
71 /* Legal two byte character. */
72 len++;
73 p += 2;
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. */
78 len++;
79 p += 3;
80 } else
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. */
84 p++;
86 out = param = alloca( ( len + ( len >> 7 ) + 1 ) << 1 );
88 for( p = (const unsigned char *) text, i = 0; i < len; i++, out += 2 ) {
89 if( !( i & 0x7F ) ) {
90 out[ 0 ] = len - i > 0x7F ? 0x80 : len - i; /* length */
91 out[ 1 ] = 0; /* delta */
92 out += 2;
95 retry:
96 assert( *p );
98 if( !( p[ 0 ] & 0x80 ) ) {
99 /* One byte character. */
100 out[ 0 ] = 0;
101 out[ 1 ] = *p++;
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 );
107 p += 2;
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 );
114 p += 3;
115 } else {
116 p++;
117 goto retry;
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 ) {
140 char *p, text[ 32 ];
141 int width;
143 if( width_sequence ) {
144 sync_with_callback( width_sequence );
145 width_sequence = 0;
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' ];
154 else
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;
168 if( error ) {
169 show_error( error );
171 free( error );
174 if( reply ) {
175 digit_widths[ p.l ] = r->overall_width;
177 free( reply );
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;
207 if( error ) {
208 if( error->error_code != XCB_ALLOC )
209 show_error( error );
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;
216 free( error );
219 if( reply ) {
220 gwm_screens[ screen ].pixels[ col ] = r->pixel;
222 free( reply );
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;
232 if( error ) {
233 show_error( error );
235 free( error );
238 if( reply ) {
239 xcb_get_property_reply_t *prop = reply;
240 struct std_cmap {
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;
246 uint32_t kill_id;
247 } *std_cmaps = xcb_get_property_value( prop );
248 int num_cmaps = ( xcb_get_property_value_length( prop ) << 2 ) /
249 sizeof *std_cmaps;
250 int i;
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 ) {
264 r0 = luma( col );
265 g0 = b0 = 0;
266 } else {
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;
283 got_cols = TRUE;
284 break;
287 free( reply );
290 if( !got_cols )
291 /* No useful default maps found; try allocating directly. */
292 for( col = 0; col < NUM_COLS; col++ ) {
293 int r, g, b;
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 );
301 else {
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,
311 cp );
315 extern void decorate_core_init( void ) {
317 int i;
318 uint32_t n;
319 const char *font_name = "-*-lucida-bold-r-normal-sans-12-"
320 "*-*-*-p-*-iso10646-1", /* FIXME allow font selection */
321 *cursor_font_name = "cursor";
322 xcb_char2b_t c2;
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 */
328 134, /* CURSOR_TL */
329 138, /* CURSOR_T */
330 136, /* CURSOR_TR */
331 70, /* CURSOR_L */
332 52, /* CURSOR_C */
333 96, /* CURSOR_R */
334 12, /* CURSOR_BL */
335 16, /* CURSOR_B */
336 14 /* CURSOR_BR */
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 );
346 n = font;
347 xcb_create_gc( c, gcs[ i ], screens[ i ]->root, XCB_GC_FONT, &n );
350 c2.byte1 = 0;
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 );
356 c2.byte2 = 'x';
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 ),
363 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;
376 cp.l = i;
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 ) {
387 free( gcs );