Add support for the zoomed state to managed windows.
[gwm.git] / decorate-core.c
blob0c9efa1c669d0a8aafa515f932dba46ccfddbd8f
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->type == WINDOW_CHILDLESS )
102 return;
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 ) {
127 char *p, text[ 32 ];
128 int width;
130 if( width_sequence ) {
131 sync_with_callback( width_sequence );
132 width_sequence = 0;
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' ];
141 else
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,
150 int *height ) {
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;
158 } else {
159 *width = 8;
160 *height = 4;
162 return;
164 case WINDOW_FEEDBACK:
165 *width = FEEDBACK_WIDTH;
166 *height = FEEDBACK_HEIGHT;
167 return;
169 default:
170 assert( FALSE );
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;
187 if( error ) {
188 show_error( error );
190 free( error );
193 if( reply ) {
194 digit_widths[ p.l ] = r->overall_width;
196 free( reply );
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;
230 if( error ) {
231 if( error->error_code != XCB_ALLOC )
232 show_error( error );
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;
239 free( error );
242 if( reply ) {
243 gwm_screens[ screen ].pixels[ col ] = r->pixel;
245 free( reply );
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;
255 if( error ) {
256 show_error( error );
258 free( error );
261 if( reply ) {
262 xcb_get_property_reply_t *prop = reply;
263 struct std_cmap {
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;
269 uint32_t kill_id;
270 } *std_cmaps = xcb_get_property_value( prop );
271 int num_cmaps = ( prop->value_len << 2 ) / sizeof *std_cmaps;
272 int i;
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 ) {
286 r0 = luma( col );
287 g0 = b0 = 0;
288 } else {
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;
305 got_cols = TRUE;
306 break;
309 free( reply );
312 if( !got_cols )
313 /* No useful default maps found; try allocating directly. */
314 for( col = 0; col < NUM_COLS; col++ ) {
315 int r, g, b;
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 );
323 else {
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,
333 cp );
337 extern INIT void decorate_core_init( void ) {
339 int i;
340 uint32_t n;
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";
344 xcb_char2b_t c2;
345 union callback_param p;
346 xcb_font_t cursor_font;
347 static INITD const int cursor_glyphs[ NUM_CURSORS ] = {
348 134, /* CURSOR_TL */
349 138, /* CURSOR_T */
350 136, /* CURSOR_TR */
351 70, /* CURSOR_L */
352 52, /* CURSOR_C */
353 96, /* CURSOR_R */
354 12, /* CURSOR_BL */
355 16, /* CURSOR_B */
356 14, /* CURSOR_BR */
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 );
368 n = font;
369 xcb_create_gc( c, gcs[ i ], screens[ i ]->root, XCB_GC_FONT, &n );
372 c2.byte1 = 0;
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 );
378 c2.byte2 = 'x';
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 ),
385 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;
398 cp.l = i;
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 ) {
409 #if DEBUG
410 free( gcs );
411 #endif