/* * decorate-core.c * * Part of gwm, the Gratuitous Window Manager, * by Gary Wong, . * * Copyright (C) 2009 Gary Wong * * This program is free software: you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * $Id$ */ #include #include #include #include #include #include #include "gwm.h" #include "button.h" #include "decorate-core.h" #include "frame.h" #include "utf8.h" #define FONT_SIZE 12 #define STRING2(x) #x #define STRING(x) STRING2(x) #define FONT_SIZE_STRING STRING( FONT_SIZE ) #define FEEDBACK_WIDTH 96 /* width of size feedback window */ #define FEEDBACK_HEIGHT 24 /* height of size feedback window */ static xcb_font_t font; static xcb_gcontext_t *gcs; static int digit_widths[ 11 ]; /* widths of characters '0' to '9', and 'x' */ static unsigned int width_sequence, cmap_sequence; static xcb_void_cookie_t core_text( xcb_drawable_t drawable, int screen, int col, int x, int y, const char *text ) { int i, len; const unsigned char *p; uint8_t *param, *out; uint32_t g, n; len = 0; if( text ) { p = (const unsigned char *) text; while( ( g = utf8_next( &p ) ) ) if( g < 0x10000 ) len++; } if( len ) { n = gwm_screens[ screen ].pixels[ col ]; xcb_change_gc( c, gcs[ screen ], XCB_GC_FOREGROUND, &n ); out = param = alloca( ( len + ( len >> 7 ) + 1 ) << 1 ); for( p = (const unsigned char *) text, i = 0; i < len; i++, out += 2 ) { if( !( i & 0x7F ) ) { out[ 0 ] = len - i > 0x7F ? 0x80 : len - i; /* length */ out[ 1 ] = 0; /* delta */ out += 2; } while( assert( *p ), ( g = utf8_next( &p ) ) > 0xFFFF ) ; out[ 0 ] = g >> 8; out[ 1 ] = g & 0xFF; } return xcb_poly_text_16( c, drawable, gcs[ screen ], x, y, out - param, param ); } else { xcb_void_cookie_t r = { 0 }; return r; } } extern void core_update_window( struct gwm_window *window ) { if( window->type == WINDOW_CHILDLESS ) return; if( !window->cleared ) xcb_clear_area( c, FALSE, window->w, window->update.x, window->update.y, window->update.width, window->update.height ); xcb_set_clip_rectangles( c, XCB_CLIP_ORDERING_YX_BANDED, gcs[ window->screen ], 0, 0, 1, &window->update ); if( window->type == WINDOW_FRAME ) { char *name = window->u.frame.child->u.managed.name; core_text( window->w, window->screen, window == focus_frame ? COL_TITLE_ACTIVE : COL_TITLE_INACTIVE, button_size( window->u.frame.button, TRUE ) + 4, FONT_SIZE, name ? name : "(Untitled)" ); } else if( window->type == WINDOW_MENUITEM ) { struct gwm_window *menu = window->u.menuitem.menu; core_text( window->w, window->screen, menu->u.menu.active_item >= 0 && menu->u.menu.items[ menu->u.menu.active_item ] == window ? COL_MENU_ACTIVE_FORE : COL_MENU_INACTIVE_FORE, 4, FONT_SIZE, window->u.menuitem.label ); } else if( window->type == WINDOW_FEEDBACK ) { char *p, text[ 32 ]; int width; if( width_sequence ) { sync_with_callback( width_sequence ); width_sequence = 0; } sprintf( text, "%dx%d", window->u.feedback.fb_width, window->u.feedback.fb_height ); for( width = 0, p = text; *p; p++ ) if( *p >= '0' && *p <= '9' ) width += digit_widths[ *p - '0' ]; else width += digit_widths[ 10 ]; core_text( window->w, window->screen, COL_FEEDBACK_FORE, ( FEEDBACK_WIDTH - width ) >> 1, 16, text ); } } extern void core_window_size( struct gwm_window *window, int *width, int *height ) { switch( window->type ) { case WINDOW_MENUITEM: if( window->u.menuitem.label ) { *width = FONT_SIZE * utf8_length( (const unsigned char *) window->u.menuitem.label ); *height = FONT_SIZE + 4; } else { *width = 8; *height = 4; } return; case WINDOW_FEEDBACK: *width = FEEDBACK_WIDTH; *height = FEEDBACK_HEIGHT; return; default: assert( FALSE ); } } extern void core_replace_icons( struct gwm_window *window, int num_icons, int *widths, int *heights, uint32_t **icons, xcb_pixmap_t *bitmaps ) { /* FIXME Implement core icons... or maybe not. */ } static void query_text_callback( unsigned int sequence, void *reply, xcb_generic_error_t *error, union callback_param p ) { xcb_query_text_extents_reply_t *r = reply; if( error ) { show_error( error ); free( error ); } if( reply ) { digit_widths[ p.l ] = r->overall_width; free( reply ); } } static const uint16_t decoration_cols[ NUM_COLS ][ 3 ] = { { 0x2222, 0x3333, 0xEEEE }, /* COL_FRAME_ACTIVE */ { 0xAAAA, 0xAAAA, 0xAAAA }, /* COL_FRAME_INACTIVE */ { 0x0000, 0x0000, 0x0000 }, /* COL_BORDER */ { 0xFFFF, 0x0000, 0x0000 }, /* COL_BUTTON_ACTIVE */ { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_BUTTON_INACTIVE */ { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_TITLE_ACTIVE */ { 0x0000, 0x0000, 0x0000 }, /* COL_TITLE_INACTIVE */ { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_FEEDBACK_BACK */ { 0x0000, 0x0000, 0x0000 }, /* COL_FEEDBACK_FORE */ { 0x2222, 0x3333, 0xEEEE }, /* COL_MENU_ACTIVE_BACK */ { 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MENU_ACTIVE_FORE */ { 0xCCCC, 0xCCCC, 0xCCCC }, /* COL_MENU_INACTIVE_BACK */ { 0x0000, 0x0000, 0x0000 } /* COL_MENU_INACTIVE_FORE */ }; static uint16_t luma( int col ) { return ( decoration_cols[ col ][ 0 ] * 0x4C8BU + decoration_cols[ col ][ 1 ] * 0x9646U + decoration_cols[ col ][ 2 ] * 0x1D2FU ) >> 16; } static void handle_alloc_color( unsigned int sequence, void *reply, xcb_generic_error_t *error, union callback_param p ) { xcb_alloc_color_reply_t *r = reply; int screen = p.l >> 16, col = p.l & 0xFFFF; if( error ) { if( error->error_code != XCB_ALLOC ) show_error( error ); /* No standard RGB map available, and we couldn't allocate a shared colour. Fall back to black or white. */ gwm_screens[ screen ].pixels[ col ] = luma( col ) >= 0x8000 ? screens[ screen ]->white_pixel : screens[ screen ]->black_pixel; free( error ); } if( reply ) { gwm_screens[ screen ].pixels[ col ] = r->pixel; free( reply ); } } static void handle_get_default_map( unsigned int sequence, void *reply, xcb_generic_error_t *error, union callback_param p ) { int col, got_cols = FALSE; if( error ) { show_error( error ); free( error ); } if( reply ) { xcb_get_property_reply_t *prop = reply; struct std_cmap { /* RGB_COLOR_MAP property -- see ICCCM 2.0, section 6.4. */ xcb_colormap_t colormap; uint32_t red_max, red_mult, green_max, green_mult, blue_max, blue_mult, base_pixel; xcb_visualid_t visual_id; uint32_t kill_id; } *std_cmaps = xcb_get_property_value( prop ); int num_cmaps = ( prop->value_len << 2 ) / sizeof *std_cmaps; int i; if( prop->format == 32 ) for( i = 0; i < num_cmaps; i++ ) if( std_cmaps[ i ].visual_id == gwm_screens[ p.l ].root_visual->visual_id ) { for( col = 0; col < NUM_COLS; col++ ) { int r0, g0, b0, r, g, b; if( gwm_screens[ p.l ].root_visual->_class == XCB_VISUAL_CLASS_STATIC_GRAY || gwm_screens[ p.l ].root_visual->_class == XCB_VISUAL_CLASS_GRAY_SCALE ) { r0 = luma( col ); g0 = b0 = 0; } else { r0 = decoration_cols[ col ][ 0 ]; g0 = decoration_cols[ col ][ 1 ]; b0 = decoration_cols[ col ][ 2 ]; } r = ( r0 * std_cmaps[ i ].red_max + 0x8000 ) >> 16; g = ( g0 * std_cmaps[ i ].green_max + 0x8000 ) >> 16; b = ( b0 * std_cmaps[ i ].blue_max + 0x8000 ) >> 16; gwm_screens[ p.l ].pixels[ col ] = std_cmaps[ i ].base_pixel + r * std_cmaps[ i ].red_mult + g * std_cmaps[ i ].green_mult + b * std_cmaps[ i ].blue_mult; } got_cols = TRUE; break; } free( reply ); } if( !got_cols ) /* No useful default maps found; try allocating directly. */ for( col = 0; col < NUM_COLS; col++ ) { int r, g, b; union callback_param cp; if( gwm_screens[ p.l ].root_visual->_class == XCB_VISUAL_CLASS_STATIC_GRAY || gwm_screens[ p.l ].root_visual->_class == XCB_VISUAL_CLASS_GRAY_SCALE ) r = g = b = luma( col ); else { r = decoration_cols[ col ][ 0 ]; g = decoration_cols[ col ][ 1 ]; b = decoration_cols[ col ][ 2 ]; } cp.l = ( p.l << 16 ) | col; handle_async_reply( cmap_sequence = xcb_alloc_color( c, screens[ p.l ]->default_colormap, r, g, b ).sequence, handle_alloc_color, cp ); } } extern INIT void decorate_core_init( void ) { int i; uint32_t n; const char *font_name = "-*-lucida-bold-r-normal-sans-" FONT_SIZE_STRING "-*-*-*-p-*-iso10646-1", /* FIXME allow selection */ *cursor_font_name = "cursor"; xcb_char2b_t c2; union callback_param p; xcb_font_t cursor_font; static INITD const int cursor_glyphs[ NUM_CURSORS ] = { 134, /* CURSOR_TL */ 138, /* CURSOR_T */ 136, /* CURSOR_TR */ 70, /* CURSOR_L */ 52, /* CURSOR_C */ 96, /* CURSOR_R */ 12, /* CURSOR_BL */ 16, /* CURSOR_B */ 14, /* CURSOR_BR */ 68, /* CURSOR_ARROW */ 88 /* CURSOR_DESTROY */ }; font = xcb_generate_id( c ); xcb_open_font( c, font, strlen( font_name ), font_name ); gcs = xmalloc( num_screens * sizeof *gcs ); for( i = 0; i < num_screens; i++ ) { gcs[ i ] = xcb_generate_id( c ); n = font; xcb_create_gc( c, gcs[ i ], screens[ i ]->root, XCB_GC_FONT, &n ); } c2.byte1 = 0; for( p.l = 0; p.l <= 9; p.l++ ) { c2.byte2 = p.l + '0'; handle_async_reply( xcb_query_text_extents( c, font, 1, &c2 ).sequence, query_text_callback, p ); } c2.byte2 = 'x'; handle_async_reply( width_sequence = xcb_query_text_extents( c, font, 1, &c2 ).sequence, query_text_callback, p ); cursor_font = xcb_generate_id( c ); xcb_open_font( c, cursor_font, strlen( cursor_font_name ), cursor_font_name ); for( i = 0; i < NUM_CURSORS; i++ ) { cursors[ i ] = xcb_generate_id( c ); xcb_create_glyph_cursor( c, cursors[ i ], cursor_font, cursor_font, cursor_glyphs[ i ], cursor_glyphs[ i ] | 1, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF ); } xcb_close_font( c, cursor_font ); /* Retrieve any RGB_DEFAULT_MAP properties on the roots. */ for( i = 0; i < num_screens; i++ ) { union callback_param cp; cp.l = i; handle_async_reply( cmap_sequence = xcb_get_property( c, FALSE, screens[ i ]->root, RGB_DEFAULT_MAP, RGB_COLOR_MAP, 0, 0x10000 ).sequence, handle_get_default_map, cp ); } } extern void decorate_core_done( void ) { #if DEBUG free( gcs ); #endif }