From 273f014b05bdb206718bf5488bde7971cdbd1ce7 Mon Sep 17 00:00:00 2001 From: Gary Wong Date: Sun, 6 Sep 2009 12:29:38 -0600 Subject: [PATCH] Added basic icon support. --- ChangeLog | 19 ++ Makefile.am | 1 + actions.c | 1 + decorate-core.c | 7 + decorate-core.h | 4 + decorate-render.c | 666 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- decorate-render.h | 6 +- gwm.c | 121 ++-------- gwm.h | 25 +- image.c | 64 ++++++ image.h | 12 + managed.c | 151 +++++++++++-- managed.h | 2 + menu.c | 16 +- menu.h | 3 +- 15 files changed, 935 insertions(+), 163 deletions(-) create mode 100644 image.c create mode 100644 image.h diff --git a/ChangeLog b/ChangeLog index 93cb66c..e183958 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2009-09-06 Gary Wong + + * decorate-core.c (core_replace_icons): New function. + * decorate-render.c (render_picture, assemble_image) + (render_replace_icons): New functions. + (render_update_window): Draw icons in menus where requested. + (decorate_render_init): Look for suitable PictFormats. + * gwm.c (start_managing_window): Initialise icons. + (unmanage_window): Deallocate icons. + (setup_display): Add _NET_WM_ICON property. + * managed.c (handle_managed_get_property, async_get_property): Use + the window resource ID instead of a pointer to identify the window, + to detect if it disappears asynchronously. + (handle_get_geometry): New function. + (managed_property_change): Handle _NET_WM_ICON, and the icon + parameters of WM_HINTS. + * menu.c (popup_menu): Allow icons in menus. + * actions.c (action_window_list_menu): Add icons to menu. + 2009-09-03 Gary Wong * decorate-core.c (core_text): Use utf8_next(). diff --git a/Makefile.am b/Makefile.am index ed07c20..7c2f588 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,6 +8,7 @@ gwm_SOURCES = \ decorate-core.c decorate-core.h \ frame.c frame.h \ gwm.c gwm.h \ + image.c image.h \ keyboard.c keyboard.h \ managed.c managed.h \ menu.c menu.h \ diff --git a/actions.c b/actions.c index 3e65c09..69897fa 100644 --- a/actions.c +++ b/actions.c @@ -229,6 +229,7 @@ extern void action_window_list_menu( struct gwm_window *window, items[ num_items ].label = name; items[ num_items ].action = window_list_activate; items[ num_items ].cp.l = managed->w; + items[ num_items ].icon = managed; num_items++; } diff --git a/decorate-core.c b/decorate-core.c index 56ba206..4e9d611 100644 --- a/decorate-core.c +++ b/decorate-core.c @@ -168,6 +168,13 @@ extern void core_window_size( struct gwm_window *window, int *width, } } +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 ) { diff --git a/decorate-core.h b/decorate-core.h index 6e39a36..cac4a37 100644 --- a/decorate-core.h +++ b/decorate-core.h @@ -4,6 +4,10 @@ extern void core_update_window( struct gwm_window *window ); extern void core_window_size( struct gwm_window *window, int *width, int *height ); +extern void core_replace_icons( struct gwm_window *window, int num_icons, + int *widths, int *heights, uint32_t **icons, + xcb_pixmap_t *bitmaps ); + extern void decorate_core_init( void ); extern void decorate_core_done( void ); diff --git a/decorate-render.c b/decorate-render.c index 01e535c..2a0b9e7 100644 --- a/decorate-render.c +++ b/decorate-render.c @@ -39,6 +39,8 @@ #include "button.h" #include "decorate-render.h" #include "frame.h" +#include "image.h" +#include "managed.h" #include "utf8.h" enum style_id { @@ -59,6 +61,8 @@ enum style_id { #define FEEDBACK_WIDTH 96 /* width of size feedback window */ #define FEEDBACK_HEIGHT 24 /* height of size feedback window */ +#define ICON_SIZE 256 + static const FcChar8 *const style_names[ NUM_STYLES ] = { /* FIXME make this configurable */ (FcChar8 *) "sans:pixelsize=" TITLE_FONT_SIZE_STRING ":bold", @@ -302,11 +306,19 @@ static int replace_glyph( enum style_id style, uint32_t c ) { return ( row << GLYPH_CACHE_ASSOC_BITS ) | max; } -static xcb_render_glyphset_t glyphset; -static struct picture { +static enum xcb_image_order_t image_byte_order; +static xcb_render_pictformat_t fmt_a8; +static xcb_render_directformat_t dfmt_a8; +static xcb_render_glyphset_t glyphset; +static struct render_screen { + xcb_render_pictformat_t root_pictformat, direct_pictformat; + int direct_depth; + xcb_render_directformat_t dfmt; + xcb_format_t *pfmt; + int rbits, gbits, bbits, abits; xcb_render_picture_t pic; enum decoration_col col; -} *pictures; /* indexed by screen */ +} *render_screens; static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, int col, int x, int y, @@ -331,16 +343,17 @@ static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, for( i = 0, p = (const unsigned char *) text; i < len; i++ ) buf[ i ] = utf8_next( &p ); - src = pictures[ screen ].pic; + src = render_screens[ screen ].pic; dest = xcb_generate_id( c ); xcb_render_create_picture( c, dest, drawable, - gwm_screens[ screen ].root_pictformat, 0, NULL ); + render_screens[ screen ].root_pictformat, + 0, NULL ); if( clip ) xcb_render_set_picture_clip_rectangles( c, dest, 0, 0, 1, clip ); - if( pictures[ screen ].col != col ) { + if( render_screens[ screen ].col != col ) { rc.red = decoration_cols[ col ][ 0 ]; rc.green = decoration_cols[ col ][ 1 ]; rc.blue = decoration_cols[ col ][ 2 ]; @@ -352,7 +365,7 @@ static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, xcb_render_fill_rectangles( c, XCB_RENDER_PICT_OP_SRC, src, rc, 1, &rect ); - pictures[ screen ].col = col; + render_screens[ screen ].col = col; } i = 0; @@ -497,6 +510,28 @@ static xcb_void_cookie_t render_text( xcb_drawable_t drawable, int screen, return xcb_render_free_picture( c, dest ); } +static xcb_void_cookie_t render_picture( xcb_drawable_t drawable, int screen, + int sx, int sy, int width, int height, + int dx, int dy, + xcb_render_picture_t src, + const xcb_rectangle_t *clip ) { + + xcb_render_picture_t dest; + + dest = xcb_generate_id( c ); + xcb_render_create_picture( c, dest, drawable, + render_screens[ screen ].root_pictformat, + 0, NULL ); + + if( clip ) + xcb_render_set_picture_clip_rectangles( c, dest, 0, 0, 1, clip ); + + xcb_render_composite( c, XCB_RENDER_PICT_OP_OVER, src, XCB_NONE, dest, + sx, sy, 0, 0, dx, dy, width, height ); + + return xcb_render_free_picture( c, dest ); +} + static int text_width( enum style_id style, const char *text ) { const unsigned char *p = (const unsigned char *) text; @@ -528,13 +563,27 @@ extern void render_update_window( struct gwm_window *window ) { STYLE_TITLE, &window->update ); } else if( window->type == WINDOW_MENUITEM ) { struct gwm_window *menu = window->u.menuitem.menu; + int x = MENU_X_PAD; + + if( window->u.menuitem.menu->u.menu.has_icons ) { + if( window->u.menuitem.icon && + window->u.menuitem.icon->type == WINDOW_MANAGED && + window->u.menuitem.icon->u.managed.menu_icons ) + render_picture( window->w, window->screen, 0, 0, + MENU_FONT_SIZE, MENU_FONT_SIZE, x, MENU_Y_PAD, + window->u.menuitem.icon-> + u.managed.menu_icons[ window->screen ], + &window->update ); + + x += MENU_FONT_SIZE + MENU_X_PAD; + } render_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, - MENU_X_PAD, MENU_FONT_SIZE, - window->u.menuitem.label, STYLE_MENU, &window->update ); + x, MENU_FONT_SIZE, window->u.menuitem.label, STYLE_MENU, + &window->update ); } else if( window->type == WINDOW_FEEDBACK ) { char text[ 32 ]; @@ -556,6 +605,9 @@ extern void render_window_size( struct gwm_window *window, int *width, *width = text_width( STYLE_MENU, window->u.menuitem.label ) + ( MENU_X_PAD << 1 ); *height = MENU_FONT_SIZE + ( MENU_Y_PAD << 1 ); + + if( window->u.menuitem.menu->u.menu.has_icons ) + *width += MENU_FONT_SIZE + MENU_X_PAD; } else { *width = MENU_X_PAD << 1; *height = MENU_Y_PAD << 1; @@ -572,6 +624,285 @@ extern void render_window_size( struct gwm_window *window, int *width, } } +static MALLOC uint8_t *assemble_image( int screen, int width, int height, + uint32_t *icon, int *len ) { + + uint8_t *p, *linep, *out; + int x, y; + int bpp; /* bits per pixel */ + int line; /* bytes per scanline */ + + bpp = render_screens[ screen ].pfmt->bits_per_pixel; + line = ( ( bpp * width + + render_screens[ screen ].pfmt->scanline_pad - 1 ) & + ~( render_screens[ screen ].pfmt->scanline_pad - 1 ) ) >> 3; + + *len = line * height; + + linep = out = xmalloc( *len ); + + for( y = 0; y < height; y++ ) { + p = linep; + for( x = 0; x < width; x++ ) { + uint32_t a = ( *icon >> 24 ) & 0xFF; + uint32_t r = ( *icon >> 16 ) & 0xFF; + uint32_t g = ( *icon >> 8 ) & 0xFF; + uint32_t b = *icon & 0xFF; + uint32_t pix; + int i; + + if( gwm_screens[ screen ].root_visual->_class == + XCB_VISUAL_CLASS_STATIC_GRAY || + gwm_screens[ screen ].root_visual->_class == + XCB_VISUAL_CLASS_GRAY_SCALE ) + r = g = b = ( r * 0x4C8BU + g * 0x9646U + b * 0x1D2FU ) >> 16; + + /* Premultiply alpha, and normalise to 32 bits. */ + r = ( (unsigned int) r * a ) * ( 0x01010101 / 0xFF ); + g = ( (unsigned int) g * a ) * ( 0x01010101 / 0xFF ); + b = ( (unsigned int) b * a ) * ( 0x01010101 / 0xFF ); + a *= 0x01010101; + + pix = ( ( r >> ( 32 - render_screens[ screen ].rbits ) ) & + render_screens[ screen ].dfmt.red_mask ) << + render_screens[ screen ].dfmt.red_shift; + pix |= ( ( g >> ( 32 - render_screens[ screen ].gbits ) ) & + render_screens[ screen ].dfmt.green_mask ) << + render_screens[ screen ].dfmt.green_shift; + pix |= ( ( b >> ( 32 - render_screens[ screen ].bbits ) ) & + render_screens[ screen ].dfmt.blue_mask ) << + render_screens[ screen ].dfmt.blue_shift; + pix |= ( ( a >> ( 32 - render_screens[ screen ].abits ) ) & + render_screens[ screen ].dfmt.alpha_mask ) << + render_screens[ screen ].dfmt.alpha_shift; + + if( image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ) + for( i = bpp; i; i -= 8 ) { + *p++ = ( pix & 0xFF ); + pix >>= 8; + } + else + for( i = bpp; i; i -= 8 ) + *p++ = ( ( pix >> ( i - 8 ) ) & 0xFF ); + + icon++; + } + linep += line; + } + + return out; +} + +extern void render_replace_icons( struct gwm_window *window, int num_icons, + int *widths, int *heights, + uint32_t **icons, xcb_pixmap_t *bitmaps ) { + + int i; + + if( window->u.managed.net_wm_icon && bitmaps ) + /* Use old _NET_WM_ICON in preference to new WM_HINTS. */ + return; + + if( window->u.managed.full_icons ) { + for( i = 0; i < num_screens; i++ ) { + xcb_render_free_picture( c, window->u.managed.full_icons[ i ] ); + xcb_render_free_picture( c, window->u.managed.menu_icons[ i ] ); + } + + free( window->u.managed.full_icons ); + free( window->u.managed.menu_icons ); + } + + if( num_icons ) { + int i, len, best_full, full_size, best_menu, menu_size; + xcb_pixmap_t full_map, menu_map, temp_map; + xcb_render_picture_t temp; + xcb_render_transform_t t; + xcb_gcontext_t gc; + uint8_t *image; + + best_full = best_menu = 0; + + if( widths[ 0 ] > heights[ 0 ] ) + full_size = menu_size = widths[ 0 ]; + else + full_size = menu_size = heights[ 0 ]; + + for( i = 1; i < num_icons; i++ ) { + int s = widths[ i ] > heights[ i ] ? widths[ i ] : heights[ i ]; + + if( ( full_size < ICON_SIZE && s > full_size ) || + ( full_size > ICON_SIZE && s >= ICON_SIZE && s < full_size ) ) + best_full = i, full_size = s; + + if( ( menu_size < MENU_FONT_SIZE && s > menu_size ) || + ( menu_size > MENU_FONT_SIZE && s >= MENU_FONT_SIZE && + s < menu_size ) ) + best_menu = i, menu_size = s; + } + + full_map = xcb_generate_id( c ); + menu_map = xcb_generate_id( c ); + temp_map = best_menu == best_full ? full_map : xcb_generate_id( c ); + + gc = xcb_generate_id( c ); + + window->u.managed.full_icons = + xmalloc( num_screens * sizeof *window->u.managed.full_icons ); + window->u.managed.menu_icons = + xmalloc( num_screens * sizeof *window->u.managed.menu_icons ); + + for( i = 0; i < num_screens; i++ ) { + xcb_create_pixmap( c, render_screens[ i ].direct_depth, + full_map, screens[ i ]->root, + widths[ best_full ], heights[ best_full ] ); + if( temp_map != full_map ) + xcb_create_pixmap( c, render_screens[ i ].direct_depth, + temp_map, screens[ i ]->root, + widths[ best_menu ], heights[ best_menu ] ); + + xcb_create_pixmap( c, render_screens[ i ].direct_depth, + menu_map, screens[ i ]->root, + MENU_FONT_SIZE, MENU_FONT_SIZE ); + + if( icons ) { + xcb_create_gc( c, gc, full_map, 0, NULL ); + + image = assemble_image( i, widths[ best_full ], + heights[ best_full ], + icons[ best_full ], &len ); + put_image( full_map, gc, widths[ best_full ], + heights[ best_full ], 0, 0, 0, + render_screens[ i ].direct_depth, len, image ); + + free( image ); + + if( temp_map != full_map ) { + image = assemble_image( i, widths[ best_menu ], + heights[ best_menu ], + icons[ best_menu ], &len ); + put_image( temp_map, gc, widths[ best_menu ], + heights[ best_menu ], 0, 0, 0, + render_screens[ i ].direct_depth, len, image ); + free( image ); + } + } else { + uint32_t values[ 3 ]; + + values[ 0 ] = render_screens[ i ].dfmt.alpha_mask << + render_screens[ i ].dfmt.alpha_shift; + values[ 1 ] = ( render_screens[ i ].dfmt.alpha_mask << + render_screens[ i ].dfmt.alpha_shift ) | + ( render_screens[ i ].dfmt.red_mask << + render_screens[ i ].dfmt.red_shift ) | + ( render_screens[ i ].dfmt.green_mask << + render_screens[ i ].dfmt.green_shift ) | + ( render_screens[ i ].dfmt.blue_mask << + render_screens[ i ].dfmt.blue_shift ); + xcb_create_gc( c, gc, full_map, XCB_GC_FOREGROUND | + XCB_GC_BACKGROUND, values ); + + xcb_copy_plane( c, bitmaps[ best_full ], full_map, gc, 0, 0, + 0, 0, widths[ best_full ], + heights[ best_full ], 1 ); + + if( bitmaps[ 1 ] ) { + /* We also have a mask bitmap: apply logical AND to turn + the appropriate pixels transparent. */ + values[ 0 ] = XCB_GX_AND; /* function */ + values[ 1 ] = 0xFFFFFFFF; /* foreground */ + values[ 2 ] = 0; /* background */ + xcb_change_gc( c, gc, XCB_GC_FUNCTION | XCB_GC_FOREGROUND | + XCB_GC_BACKGROUND, values ); + + xcb_copy_plane( c, bitmaps[ 1 ], full_map, gc, 0, 0, + 0, 0, widths[ best_full ], + heights[ best_full ], 1 ); + } + } + + window->u.managed.full_icons[ i ] = xcb_generate_id( c ); + window->u.managed.menu_icons[ i ] = xcb_generate_id( c ); + + xcb_render_create_picture( c, window->u.managed.full_icons[ i ], + full_map, + render_screens[ i ].direct_pictformat, + 0, NULL ); + xcb_render_create_picture( c, window->u.managed.menu_icons[ i ], + menu_map, + render_screens[ i ].direct_pictformat, + 0, NULL ); + + if( temp_map == full_map ) + temp = window->u.managed.full_icons[ i ]; + else { + temp = xcb_generate_id( c ); + xcb_render_create_picture( + c, temp, temp_map, render_screens[ i ].direct_pictformat, + 0, NULL ); + } + + xcb_render_set_picture_filter( c, temp, 4, "best", 0, NULL ); + t.matrix11 = 0x10000 * menu_size / MENU_FONT_SIZE; + t.matrix12 = 0; + t.matrix13 = 0x80000; + t.matrix21 = 0; + t.matrix22 = t.matrix11; + t.matrix23 = 0; + t.matrix31 = 0; + t.matrix32 = 0; + t.matrix33 = 0x10000; + if( widths[ best_menu ] < heights[ best_menu ] ) { + t.matrix12 = 0; + t.matrix13 = ( widths[ best_menu ] - heights[ best_menu ] ) + << 15; + } else { + t.matrix12 = ( heights[ best_menu ] - widths[ best_menu ] ) + << 15; + t.matrix13 = 0; + } + xcb_render_set_picture_transform( c, temp, t ); + xcb_render_composite( c, XCB_RENDER_PICT_OP_SRC, temp, XCB_NONE, + window->u.managed.menu_icons[ i ], 0, 0, + 0, 0, 0, 0, MENU_FONT_SIZE, MENU_FONT_SIZE ); + + xcb_render_set_picture_filter( c, window->u.managed.full_icons[ i ], + 4, "good", 0, NULL ); + t.matrix11 = t.matrix22 = 0x10000 * full_size / ICON_SIZE; + if( widths[ best_full ] < heights[ best_full ] ) { + t.matrix12 = 0; + t.matrix13 = ( widths[ best_full ] - heights[ best_full ] ) + << 15; + } else { + t.matrix12 = ( heights[ best_full ] - widths[ best_full ] ) + << 15; + t.matrix13 = 0; + } + xcb_render_set_picture_transform( + c, window->u.managed.full_icons[ i ], t ); + + xcb_free_pixmap( c, full_map ); + xcb_free_pixmap( c, menu_map ); + if( temp_map != full_map ) { + xcb_free_pixmap( c, temp_map ); + xcb_render_free_picture( c, temp ); + } + + xcb_free_gc( c, gc ); + } + + window->u.managed.net_wm_icon = icons != NULL; + } else { + if( window->u.managed.net_wm_icon ) + /* We've lost the _NET_WM_ICON property. If the window still + has a plain WM_HINTS icon, then fall back to that. */ + async_get_property( window, PROP_WM_HINTS ); + + window->u.managed.full_icons = window->u.managed.menu_icons = NULL; + window->u.managed.net_wm_icon = FALSE; + } +} + static uint16_t luma( int col ) { return ( decoration_cols[ col ][ 0 ] * 0x4C8BU + @@ -740,8 +1071,14 @@ static void decorate_compat_init( void ) { } } -extern void decorate_render_init( void ) { +extern int decorate_render_init( void ) { + xcb_render_query_version_cookie_t render_cookie; + xcb_render_query_pict_formats_cookie_t formats_cookie; + xcb_render_query_version_reply_t *render_reply; + xcb_render_query_pict_formats_reply_t *formats_reply; + xcb_render_pictforminfo_t *formats; + xcb_render_pictscreen_iterator_t siter; FT_Error err; FcFontSet *sets[ NUM_STYLES ]; FcPattern *style_pats[ NUM_STYLES ]; @@ -750,16 +1087,285 @@ extern void decorate_render_init( void ) { int i, total; xcb_pixmap_t pixmap; uint32_t n; + uint32_t icon_sizes[ 6 ]; - if( !FcInit() ) - fatal( "font configuration error" ); + image_byte_order = xcb_get_setup( c )->image_byte_order; + + render_cookie = xcb_render_query_version( c, XCB_RENDER_MAJOR_VERSION, + XCB_RENDER_MINOR_VERSION ); + + formats_cookie = xcb_render_query_pict_formats( c ); + + xcb_flush( c ); /* BLOCK */ + + render_reply = xcb_render_query_version_reply( c, render_cookie, NULL ); - if( ( err = FT_Init_FreeType( &ftl ) ) ) - fatal( "typeface initialisation error %d", err ); + if( !render_reply ) + return -1; + + if( !render_reply->major_version && render_reply->minor_version < 1 ) { + /* We need at least version 0.6, for SetPictureTransform support. */ + free( render_reply ); + + return -1; + } + + free( render_reply ); + + formats_reply = xcb_render_query_pict_formats_reply( c, formats_cookie, + NULL ); + + formats = xcb_render_query_pict_formats_formats( formats_reply ); + + /* First of all, look for an 8 bit direct alpha PictFormat. */ + for( i = 0; i < formats_reply->num_formats; i++ ) + if( formats[ i ].type == XCB_RENDER_PICT_TYPE_DIRECT && + formats[ i ].direct.alpha_mask == 0xFF && + !formats[ i ].direct.red_mask && + !formats[ i ].direct.green_mask && + !formats[ i ].direct.blue_mask ) { + fmt_a8 = formats[ i ].id; + memcpy( &dfmt_a8, &formats[ i ].direct, sizeof dfmt_a8 ); + break; + } + + if( !fmt_a8 ) { + /* The A8 PictFormat is guaranteed by the RENDER protocol + (section 7). We can't cope without it, since that's what + our glyphset uses. */ + free( formats_reply ); + + return -1; + } + + render_screens = xmalloc( num_screens * sizeof *render_screens ); + + /* Next, look for a PictFormat matching each root visual. */ + for( i = 0, siter = xcb_render_query_pict_formats_screens_iterator( + formats_reply ); siter.rem; + i++, xcb_render_pictscreen_next( &siter ) ) { + xcb_render_pictdepth_iterator_t diter; + + render_screens[ i ].root_pictformat = 0; + + for( diter = xcb_render_pictscreen_depths_iterator( siter.data ); + diter.rem; xcb_render_pictdepth_next( &diter ) ) { + xcb_render_pictvisual_iterator_t viter; + + for( viter = xcb_render_pictdepth_visuals_iterator( + diter.data ); viter.rem; + xcb_render_pictvisual_next( &viter ) ) + if( viter.data->visual == + gwm_screens[ i ].root_visual->visual_id ) { + render_screens[ i ].root_pictformat = viter.data->format; + goto next_screen; + } + } + + /* Oh dear. We found a screen with no PictFormat corresponding + to its root visual, so we can't render to it. It's much too + messy to try rendering on some screens and core graphics on + others, so give up rendering entirely. */ + free( formats_reply ); + free( render_screens ); + + return -1; + + next_screen: + ; + } + + /* Next, look for the direct PictFormat most suitable for source + pictures rendering to each root visual. */ + for( i = 0, siter = xcb_render_query_pict_formats_screens_iterator( + formats_reply ); siter.rem; + i++, xcb_render_pictscreen_next( &siter ) ) { + xcb_render_pictdepth_iterator_t diter; + int best_score = -1; + int best_format; + int red_mask, green_mask, blue_mask; + + if( gwm_screens[ i ].root_visual->_class == + XCB_VISUAL_CLASS_TRUE_COLOR || + gwm_screens[ i ].root_visual->_class == + XCB_VISUAL_CLASS_DIRECT_COLOR ) { + for( red_mask = gwm_screens[ i ].root_visual->red_mask; + !( red_mask & 1 ); red_mask >>= 1 ) + ; + for( green_mask = gwm_screens[ i ].root_visual->green_mask; + !( green_mask & 1 ); green_mask >>= 1 ) + ; + for( blue_mask = gwm_screens[ i ].root_visual->blue_mask; + !( blue_mask & 1 ); blue_mask >>= 1 ) + ; + } else + red_mask = green_mask = blue_mask = -1; + + for( diter = xcb_render_pictscreen_depths_iterator( siter.data ); + diter.rem; xcb_render_pictdepth_next( &diter ) ) { + xcb_depth_iterator_t pixmap_depths; + xcb_render_pictvisual_iterator_t viter; + int format; + + for( pixmap_depths = + xcb_screen_allowed_depths_iterator( screens[ i ] ); + pixmap_depths.rem; xcb_depth_next( &pixmap_depths ) ) + if( pixmap_depths.data->depth == diter.data->depth ) + break; + + if( !pixmap_depths.rem ) + /* We're not allowed to allocate pixmaps of this depth: + don't bother with this PictFormat. */ + continue; + + for( viter = xcb_render_pictdepth_visuals_iterator( + diter.data ); viter.rem; + xcb_render_pictvisual_next( &viter ) ) { + int score; + + for( format = 0; format < formats_reply->num_formats; + format++ ) + if( formats[ format ].id == viter.data->format && + formats[ format ].type == + XCB_RENDER_PICT_TYPE_DIRECT && + formats[ format ].depth >= 8 ) + /* Insisting that the depth is at least 8 is somewhat + arbitary, but since it's less work for us if we + don't have to consider assembling non-byte-aligned + images, and it's highly questionable whether + alpha composition is particularly effective + on 1- and 4-bit depths, we won't even try. */ + break; + + if( format == formats_reply->num_formats ) + continue; + + /* Aha! We've found a direct PictFormat that we can + allocate pixmaps for. Decide how good it is... */ + score = 0; + + if( formats[ format ].direct.alpha_mask ) + /* Has an alpha component. Excellent. */ + score |= 0x80; + + if( formats[ format ].direct.red_mask == red_mask && + formats[ format ].direct.green_mask == green_mask && + formats[ format ].direct.blue_mask == blue_mask ) { + /* The RGB component depths match the root. Good. */ + score |= 0x40; + + if( formats[ format ].direct.red_mask << + formats[ format ].direct.red_shift == + gwm_screens[ i ].root_visual->red_mask && + formats[ format ].direct.green_mask << + formats[ format ].direct.green_shift == + gwm_screens[ i ].root_visual->green_mask && + formats[ format ].direct.blue_mask << + formats[ format ].direct.blue_shift == + gwm_screens[ i ].root_visual->blue_mask ) + /* The RGB layouts match. Useful. */ + score |= 0x20; + } + + if( formats[ format ].depth == screens[ i ]->root_depth ) + /* The depth matches. Slightly helpful. */ + score |= 0x10; + + /* All else being equal, prefer deeper alpha channels. */ + if( formats[ format ].direct.alpha_mask >= 0xFFFF ) + score |= 0x0F; + else { + int n; + + for( n = formats[ format ].direct.alpha_mask >> 1; n; + n >>= 1 ) + score++; + } + + if( score > best_score ) { + best_score = score; + best_format = format; + } + } + } + + if( best_score >= 0 ) { + xcb_format_iterator_t fiter; + int mask; + + render_screens[ i ].direct_pictformat = formats[ best_format ].id; + render_screens[ i ].direct_depth = formats[ best_format ].depth; + memcpy( &render_screens[ i ].dfmt, &formats[ best_format ].direct, + sizeof render_screens[ i ].dfmt ); + + for( fiter = xcb_setup_pixmap_formats_iterator( + xcb_get_setup( c ) ); fiter.rem; + xcb_format_next( &fiter ) ) + if( fiter.data->depth == formats[ best_format ].depth ) { + render_screens[ i ].pfmt = fiter.data; + break; + } + + assert( fiter.rem ); + + for( render_screens[ i ].rbits = 0, + mask = render_screens[ i ].dfmt.red_mask; + mask; mask >>= 1, render_screens[ i ].rbits++ ) + ; + + for( render_screens[ i ].gbits = 0, + mask = render_screens[ i ].dfmt.green_mask; + mask; mask >>= 1, render_screens[ i ].gbits++ ) + ; + + for( render_screens[ i ].bbits = 0, + mask = render_screens[ i ].dfmt.blue_mask; + mask; mask >>= 1, render_screens[ i ].bbits++ ) + ; + + for( render_screens[ i ].abits = 0, + mask = render_screens[ i ].dfmt.alpha_mask; + mask; mask >>= 1, render_screens[ i ].abits++ ) + ; + } else { + /* Oh dear. We couldn't find any suitable direct PictFormat to + use as a source rendering to the root visual. */ + free( formats_reply ); + free( render_screens ); + + return -1; + } + } + + free( formats_reply ); + + if( !FcInit() ) { + warning( "font configuration error" ); + + free( render_screens ); + + return -1; + } + + if( ( err = FT_Init_FreeType( &ftl ) ) ) { + warning( "typeface initialisation error %d", err ); + + free( render_screens ); + FcFini(); + + return -1; + } for( i = 0, total = 0; i < NUM_STYLES; i++ ) { - if( !( style_pats[ i ] = FcNameParse( style_names[ i ] ) ) ) - fatal( "could not parse font \"%s\"\n", style_names[ i ] ); + if( !( style_pats[ i ] = FcNameParse( style_names[ i ] ) ) ) { + warning( "could not parse font \"%s\"\n", style_names[ i ] ); + + free( render_screens ); + FcFini(); + FT_Done_FreeType( ftl ); + + return -1; + } FcConfigSubstitute( NULL, style_pats[ i ], FcMatchPattern ); FcDefaultSubstitute( style_pats[ i ] ); @@ -820,27 +1426,39 @@ extern void decorate_render_init( void ) { glyphset = xcb_generate_id( c ); xcb_render_create_glyph_set( c, glyphset, fmt_a8 ); - + pixmap = xcb_generate_id( c ); - pictures = xmalloc( num_screens * sizeof *pictures ); - n = XCB_RENDER_REPEAT_NORMAL; + + /* A WM_ICON_SIZE property (see ICCCM 2.0, section 4.1.3.2). */ + icon_sizes[ 0 ] = MENU_FONT_SIZE; /* min_width */ + icon_sizes[ 1 ] = MENU_FONT_SIZE; /* min_height */ + icon_sizes[ 2 ] = ICON_SIZE; /* max_width */ + icon_sizes[ 3 ] = ICON_SIZE; /* max_height */ + icon_sizes[ 4 ] = 1; /* width_inc */ + icon_sizes[ 5 ] = 1; /* height_inc */ for( i = 0; i < num_screens; i++ ) { xcb_create_pixmap( c, screens[ i ]->root_depth, pixmap, screens[ i ]->root, 1, 1 ); - pictures[ i ].pic = xcb_generate_id( c ); - xcb_render_create_picture( c, pictures[ i ].pic, pixmap, - gwm_screens[ i ].root_pictformat, + render_screens[ i ].pic = xcb_generate_id( c ); + xcb_render_create_picture( c, render_screens[ i ].pic, pixmap, + render_screens[ i ].root_pictformat, XCB_RENDER_CP_REPEAT, &n ); - pictures[ i ].col = -1; + render_screens[ i ].col = -1; xcb_free_pixmap( c, pixmap ); + + xcb_change_property( c, XCB_PROP_MODE_REPLACE, screens[ i ]->root, + WM_ICON_SIZE, WM_ICON_SIZE, 32, + sizeof icon_sizes, icon_sizes ); } decorate_compat_init(); /* FIXME this is for pixel values (backgrounds) and cursors */ + + return 0; } extern void decorate_render_done( void ) { @@ -864,6 +1482,6 @@ extern void decorate_render_done( void ) { FT_Done_FreeType( ftl ); - free( pictures ); + free( render_screens ); #endif } diff --git a/decorate-render.h b/decorate-render.h index bd60d3d..e7c46bd 100644 --- a/decorate-render.h +++ b/decorate-render.h @@ -4,7 +4,11 @@ extern void render_update_window( struct gwm_window *window ); extern void render_window_size( struct gwm_window *window, int *width, int *height ); -extern void decorate_render_init( void ); +extern void render_replace_icons( struct gwm_window *window, int num_icons, + int *widths, int *heights, uint32_t **icons, + xcb_pixmap_t *bitmaps ); + +extern int decorate_render_init( void ); extern void decorate_render_done( void ); #endif diff --git a/gwm.c b/gwm.c index 604e8ae..e43d827 100644 --- a/gwm.c +++ b/gwm.c @@ -94,6 +94,7 @@ static const char *const atom_names[ NUM_ATOMS ] = { "COMPOUND_TEXT", "MANAGER", "_MOTIF_WM_HINTS", + "_NET_WM_ICON", "_NET_WM_NAME", "UTF8_STRING", "VERSION", @@ -135,11 +136,6 @@ int num_screens; xcb_screen_t **screens; struct gwm_screen *gwm_screens; -#if USE_RENDER -xcb_render_pictformat_t fmt_rgba8, fmt_a8; -xcb_render_directformat_t dfmt_rgba8, dfmt_a8; -#endif - xcb_cursor_t cursors[ NUM_CURSORS ]; struct gwm_window *fake_window; @@ -154,6 +150,9 @@ volatile int signal_caught; static void ( *update_window )( struct gwm_window *window ); void ( *window_size )( struct gwm_window *window, int *width, int *height ); +void ( *replace_icons )( struct gwm_window *window, int num_icons, + int *widths, int *heights, uint32_t **icons, + xcb_pixmap_t *bitmaps ); extern FORMAT( printf, 1, 2 ) void warning( char *format, ... ) { @@ -1068,8 +1067,12 @@ static void start_managing_window( struct gwm_window *window, window->u.managed.border_width = geom->border_width; window->u.managed.cmap = attr->colormap; window->u.managed.hints = 0; +#if USE_RENDER + window->u.managed.full_icons = window->u.managed.menu_icons = NULL; + window->u.managed.net_wm_icon = FALSE; +#endif window->u.managed.name = NULL; - window->u.managed.net_wm_name = 0; + window->u.managed.net_wm_name = FALSE; window->u.managed.state = STATE_WITHDRAWN; #if USE_SHAPE window->u.managed.shaped = have_extension[ EXT_SHAPE ] && shape && @@ -1419,6 +1422,8 @@ extern void unmanage_window( struct gwm_window *window ) { if( window->u.managed.name ) free( window->u.managed.name ); + + replace_icons( window, 0, NULL, NULL, NULL, NULL ); forget_window( window ); forget_window( frame->u.frame.button ); @@ -1683,8 +1688,9 @@ static void setup_display( void ) { free( r ); } - /* FIXME Also monitor _NET_WM_ICON and set _NET_FRAME_EXTENTS. */ + /* FIXME Also set _NET_FRAME_EXTENTS. */ prop_atoms[ PROP__MOTIF_WM_HINTS ] = atoms[ ATOM__MOTIF_WM_HINTS ]; + prop_atoms[ PROP__NET_WM_ICON ] = atoms[ ATOM__NET_WM_ICON ]; prop_atoms[ PROP__NET_WM_NAME ] = atoms[ ATOM__NET_WM_NAME ]; prop_atoms[ PROP_WM_COLORMAP_WINDOWS ] = atoms[ ATOM_WM_COLORMAP_WINDOWS ]; prop_atoms[ PROP_WM_HINTS ] = WM_HINTS; @@ -1693,6 +1699,7 @@ static void setup_display( void ) { prop_atoms[ PROP_WM_PROTOCOLS ] = atoms[ ATOM_WM_PROTOCOLS ]; prop_types[ PROP__MOTIF_WM_HINTS ] = atoms[ ATOM__MOTIF_WM_HINTS ]; + prop_types[ PROP__NET_WM_ICON ] = CARDINAL; prop_types[ PROP__NET_WM_NAME ] = atoms[ ATOM_UTF8_STRING ]; prop_types[ PROP_WM_COLORMAP_WINDOWS ] = WINDOW; prop_types[ PROP_WM_HINTS ] = WM_HINTS; @@ -1709,110 +1716,18 @@ static void setup_display( void ) { } #if USE_RENDER - if( have_extension[ EXT_RENDER ] ) { - xcb_render_query_version_cookie_t render_cookie; - xcb_render_query_pict_formats_cookie_t formats_cookie; - xcb_render_query_version_reply_t *render_reply; - xcb_render_query_pict_formats_reply_t *formats_reply; - xcb_render_pictforminfo_t *formats; - int i; - xcb_render_pictscreen_iterator_t siter; - - render_cookie = xcb_render_query_version( c, XCB_RENDER_MAJOR_VERSION, - XCB_RENDER_MINOR_VERSION ); - - formats_cookie = xcb_render_query_pict_formats( c ); - - xcb_flush( c ); /* BLOCK */ - - render_reply = xcb_render_query_version_reply( c, render_cookie, NULL ); - - if( !render_reply ) { - have_extension[ EXT_RENDER ] = FALSE; - goto no_render; - } - - if( !render_reply->major_version && render_reply->minor_version < 1 ) { - /* We need at least version 0.1, for FillRectangles support. It's - too inconvenient to initialise pictures to a constant colour - without it (which seems to have been an oversight in the - original specification of the protocol). */ - have_extension[ EXT_RENDER ] = FALSE; - - free( render_reply ); - - goto no_render; - } - - free( render_reply ); - - formats_reply = xcb_render_query_pict_formats_reply( c, formats_cookie, - NULL ); - - formats = xcb_render_query_pict_formats_formats( formats_reply ); - - for( i = 0; i < formats_reply->num_formats; i++ ) - if( formats[ i ].type == XCB_RENDER_PICT_TYPE_DIRECT && - formats[ i ].direct.alpha_mask == 0xFF ) { - if( !formats[ i ].direct.red_mask && - !formats[ i ].direct.green_mask && - !formats[ i ].direct.blue_mask ) { - fmt_a8 = formats[ i ].id; - memcpy( &dfmt_a8, &formats[ i ].direct, sizeof dfmt_a8 ); - } else if( formats[ i ].direct.red_mask == 0xFF && - formats[ i ].direct.green_mask == 0xFF && - formats[ i ].direct.blue_mask == 0xFF ) { - fmt_rgba8 = formats[ i ].id; - memcpy( &dfmt_rgba8, &formats[ i ].direct, - sizeof dfmt_rgba8 ); - } - } - - if( !fmt_a8 || !fmt_rgba8 ) - /* These two pictformats are guaranteed by the RENDER protocol - (section 7). We can't cope without them. */ - have_extension[ EXT_RENDER ] = FALSE; - - for( i = 0, siter = xcb_render_query_pict_formats_screens_iterator( - formats_reply ); siter.rem; - i++, xcb_render_pictscreen_next( &siter ) ) { - xcb_render_pictdepth_iterator_t diter; - - for( diter = xcb_render_pictscreen_depths_iterator( siter.data ); - diter.rem; xcb_render_pictdepth_next( &diter ) ) { - xcb_render_pictvisual_iterator_t viter; - - for( viter = xcb_render_pictdepth_visuals_iterator( - diter.data ); viter.rem; - xcb_render_pictvisual_next( &viter ) ) - if( viter.data->visual == - gwm_screens[ i ].root_visual->visual_id ) { - gwm_screens[ i ].root_pictformat = viter.data->format; - goto next_screen; - } - } - next_screen: - ; - } - - free( formats_reply ); - - no_render: - ; - } -#endif - -#if USE_RENDER - if( have_extension[ EXT_RENDER ] ) { - decorate_render_init(); + if( have_extension[ EXT_RENDER ] && + ( have_extension[ EXT_RENDER ] == !decorate_render_init() ) ) { update_window = render_update_window; window_size = render_window_size; + replace_icons = render_replace_icons; } else #endif { decorate_core_init(); update_window = core_update_window; window_size = core_window_size; + replace_icons = core_replace_icons; } /* Listen for a selection timestamp. */ diff --git a/gwm.h b/gwm.h index c3bb95e..8ba17ce 100644 --- a/gwm.h +++ b/gwm.h @@ -50,6 +50,7 @@ extern xcb_timestamp_t latest_timestamp; enum x_atom { /* See X Window System Protocol (version 11, release 6.7), Appendix B. */ ATOM = 4, + CARDINAL = 6, INTEGER = 19, RGB_COLOR_MAP = 24, RGB_DEFAULT_MAP = 27, @@ -57,6 +58,7 @@ enum x_atom { WINDOW = 33, WM_HINTS = 35, WM_ICON_NAME = 37, + WM_ICON_SIZE = 38, WM_NAME = 39, WM_NORMAL_HINTS = 40, WM_SIZE_HINTS = 41 @@ -66,6 +68,7 @@ enum gwm_atom { ATOM_COMPOUND_TEXT, ATOM_MANAGER, ATOM__MOTIF_WM_HINTS, + ATOM__NET_WM_ICON, ATOM__NET_WM_NAME, ATOM_UTF8_STRING, ATOM_VERSION, @@ -83,6 +86,7 @@ extern xcb_atom_t atoms[ NUM_ATOMS ]; enum gwm_property_type { PROP__MOTIF_WM_HINTS, + PROP__NET_WM_ICON, PROP__NET_WM_NAME, PROP_WM_COLORMAP_WINDOWS, PROP_WM_HINTS, @@ -92,7 +96,7 @@ enum gwm_property_type { NUM_PROPS }; -#define PROP_SIZE 64 /* maximum number of 32-bit words to retrieve */ +#define PROP_SIZE 262144 /* maximum number of 32-bit words to retrieve */ extern xcb_atom_t prop_atoms[ NUM_PROPS ]; extern xcb_atom_t prop_types[ NUM_PROPS ]; @@ -126,11 +130,6 @@ extern int have_extension[ EXTENSIONS_SIZE ]; extern uint8_t extension_event[ EXTENSIONS_SIZE ], extension_error[ EXTENSIONS_SIZE ]; -#if USE_RENDER && defined( XCB_RENDER_MAJOR_VERSION ) -extern xcb_render_pictformat_t fmt_rgba8, fmt_a8; -extern xcb_render_directformat_t dfmt_rgba8, dfmt_a8; -#endif - enum decoration_col { COL_FRAME_ACTIVE, COL_FRAME_INACTIVE, @@ -157,9 +156,6 @@ struct gwm_screen { xcb_colormap_t cmap; /* the most recently installed colormap */ xcb_timestamp_t cmap_time; /* the time at which it was installed */ uint32_t pixels[ NUM_COLS ]; /* pixel values in the default cmap */ -#if USE_RENDER - uint32_t root_pictformat; -#endif }; extern struct gwm_screen *gwm_screens; @@ -235,6 +231,11 @@ struct gwm_window { xcb_window_t cmap_window; /* from WM_HINTS: */ int hints; /* see HINT_* above */ + /* from _NET_WM_ICON: */ +#if USE_RENDER + uint32_t *full_icons, *menu_icons; /* pictures, screen indexed */ + int net_wm_icon; +#endif /* from WM_NAME and _NET_WM_NAME: */ char *name; /* legal UTF-8; must be free()d */ int net_wm_name; @@ -260,10 +261,11 @@ struct gwm_window { xcb_window_t window_param; int num_items, active_item; int width; + int has_icons; struct gwm_window **items; /* must be free()d */ } menu; struct _gwm_menuitem { - struct gwm_window *menu; + struct gwm_window *menu, *icon; char *label; /* must be free()d */ void ( *action )( struct gwm_window *window, xcb_generic_event_t *ev, @@ -356,5 +358,8 @@ extern xcb_generic_event_t *wait_for_event( void ); extern void ( *window_size )( struct gwm_window *window, int *width, int *height ); +extern void ( *replace_icons )( struct gwm_window *window, int num_icons, + int *widths, int *heights, uint32_t **icons, + xcb_pixmap_t *pixmaps ); #endif diff --git a/image.c b/image.c new file mode 100644 index 0000000..4059d30 --- /dev/null +++ b/image.c @@ -0,0 +1,64 @@ +/* + * image.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 "gwm.h" + +#include "image.h" + +extern xcb_void_cookie_t put_image( xcb_drawable_t drawable, xcb_gcontext_t gc, + uint16_t width, uint16_t height, int16_t x, + int16_t y, uint8_t left_pad, uint8_t depth, + uint32_t len, const uint8_t *data ) { + + static uint16_t max; + + if( !max ) + max = xcb_get_setup( c )->maximum_request_length << 2; + + if( len + sizeof (xcb_put_image_request_t ) < max ) + return xcb_put_image( c, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable, + gc, width, height, x, y, left_pad, + depth, len, data ); + else { + int linesize = len / height; + int lines = ( max - sizeof (xcb_put_image_request_t) ) / linesize; + + while( height > lines ) { + xcb_put_image( c, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable, + gc, width, lines, x, y, left_pad, + depth, lines * linesize, data ); + y += lines; + height -= lines; + len -= lines * linesize; + data += lines * linesize; + } + + return xcb_put_image( c, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable, + gc, width, height, x, y, left_pad, + depth, len, data ); + } +} diff --git a/image.h b/image.h new file mode 100644 index 0000000..cf41235 --- /dev/null +++ b/image.h @@ -0,0 +1,12 @@ +#ifndef IMAGE_H +#define IMAGE_H + +/* Like xcb_put_image (with XCB_IMAGE_FORMAT_Z_PIXMAP), but capable of + breaking the data into multiple requests if it's too big for the + server to handle in one. */ +extern xcb_void_cookie_t put_image( xcb_drawable_t drawable, xcb_gcontext_t gc, + uint16_t width, uint16_t height, int16_t x, + int16_t y, uint8_t left_pad, uint8_t depth, + uint32_t len, const uint8_t *data ); + +#endif diff --git a/managed.c b/managed.c index 7a74b60..6b88ccc 100644 --- a/managed.c +++ b/managed.c @@ -129,7 +129,7 @@ static void managed_reparent_notify( struct gwm_window *window, } struct managed_get_property { - struct gwm_window *window; + xcb_window_t w; enum gwm_property_type prop; }; @@ -149,7 +149,12 @@ static void handle_managed_get_property( unsigned int sequence, void *reply, } if( reply ) { - managed_property_change( p->window, p->prop, reply ); + struct gwm_window *window = lookup_window( p->w ); + + /* Don't assume the window is valid: it might have been destroyed + or forgotten asynchronously. */ + if( window && window->type == WINDOW_MANAGED ) + managed_property_change( window, p->prop, reply ); free( reply ); } @@ -157,13 +162,13 @@ static void handle_managed_get_property( unsigned int sequence, void *reply, free( p ); } -static void async_get_property( struct gwm_window *window, +extern void async_get_property( struct gwm_window *window, enum gwm_property_type prop ) { struct managed_get_property *p = xmalloc( sizeof *p ); union callback_param cp; - p->window = window; + p->w = window->w; p->prop = prop; cp.p = p; @@ -174,6 +179,45 @@ static void async_get_property( struct gwm_window *window, handle_managed_get_property, cp ); } +struct managed_get_geometry { + struct gwm_window *window; + xcb_pixmap_t icon, mask; +}; + +static void handle_get_geometry( unsigned int sequence, void *reply, + xcb_generic_error_t *error, + union callback_param cp ) { + + struct managed_get_geometry *p = cp.p; + + if( error ) { + /* Ignore Drawable errors, since it was specified by the client. */ + if( error->error_code != XCB_DRAWABLE ) + show_error( error ); + + free( error ); + + if( !reply ) + replace_icons( p->window, 0, NULL, NULL, NULL, &p->icon ); + } + + if( reply ) { + xcb_get_geometry_reply_t *r = reply; + int width, height; + xcb_pixmap_t icons[ 2 ]; + + width = r->width; + height = r->height; + icons[ 0 ] = p->icon; + icons[ 1 ] = p->mask; + replace_icons( p->window, 1, &width, &height, NULL, icons ); + + free( reply ); + } + + free( p ); +} + #define MOTIF_WM_HINTS_DECORATIONS 0x2 #define MOTIF_WM_HINTS_FLAGS_OFF 0 @@ -184,13 +228,16 @@ static void async_get_property( struct gwm_window *window, #define MOTIF_WM_HINTS_DEC_BORDER 0x2 #define MOTIF_WM_HINTS_DEC_TITLE 0x8 -#define WM_HINTS_INPUT 0x1 -#define WM_HINTS_STATE 0x2 +#define WM_HINTS_INPUT 0x01 +#define WM_HINTS_STATE 0x02 +#define WM_HINTS_ICON 0x04 +#define WM_HINTS_ICON_MASK 0x20 #define WM_HINTS_FLAGS_OFF 0 #define WM_HINTS_INPUT_OFF 1 #define WM_HINTS_STATE_OFF 2 -#define WM_HINTS_MIN_SIZE 3 /* ignore hint properties smaller than this */ +#define WM_HINTS_ICON_OFF 3 +#define WM_HINTS_MASK_OFF 7 #define WM_HINTS_STATE_NORMAL 1 #define WM_HINTS_STATE_ICONIC 3 @@ -220,6 +267,8 @@ extern void managed_property_change( struct gwm_window *window, int prop, struct xcb_screen_t *screen; int value_len; int old_decoration; + int num_icons; + int len; assert( window->type == WINDOW_MANAGED ); @@ -330,16 +379,50 @@ extern void managed_property_change( struct gwm_window *window, int prop, } break; + + case PROP__NET_WM_ICON: + /* _NET_WM_ICON property (see EWMH "Application Window Properties". */ + p32 = xcb_get_property_value( p ); + len = p->format == 32 ? p->value_len : 0; + + i = num_icons = 0; + while( len > i + 2 && len >= i + 2 + p32[ i ] * p32[ i + 1 ] ) { + i += p32[ i ] * p32[ i + 1 ] + 2; + num_icons++; + } + + if( num_icons ) { + int *widths, *heights; + uint32_t **icons; + + widths = alloca( num_icons * sizeof *widths ); + heights = alloca( num_icons * sizeof *heights ); + icons = alloca( num_icons * sizeof *icons ); + + i = num_icons = 0; + while( len > i + 2 && + len >= i + 2 + ( widths[ num_icons ] = p32[ i ] ) * + ( heights[ num_icons ] = p32[ i + 1 ] ) ) { + icons[ num_icons ] = p32 + i + 2; + i += widths[ num_icons ] * heights[ num_icons ] + 2; + num_icons++; + } + + replace_icons( window, num_icons, widths, heights, icons, NULL ); + } else + replace_icons( window, 0, NULL, NULL, NULL, NULL ); + + break; case PROP__NET_WM_NAME: - /* _NET_WM_NAME property (see EMWH "Application Window Properties". */ + /* _NET_WM_NAME property (see EWMH "Application Window Properties". */ if( window->u.managed.name ) free( window->u.managed.name ); if( p->value_len && p->format == 8 ) { window->u.managed.name = (char *) utf8_dup_valid_len( xcb_get_property_value( p ), p->value_len ); - window->u.managed.net_wm_name = 1; + window->u.managed.net_wm_name = TRUE; if( window->u.managed.state == STATE_NORMAL && ( window->u.managed.frame->u.frame.decoration & DEC_TITLE ) ) @@ -349,7 +432,7 @@ extern void managed_property_change( struct gwm_window *window, int prop, FALSE ); } else { window->u.managed.name = NULL; - window->u.managed.net_wm_name = 0; + window->u.managed.net_wm_name = FALSE; /* We've lost the _NET_WM_NAME property. If the window still has a plain WM_NAME, then fall back to that. */ async_get_property( window, PROP_WM_NAME ); @@ -386,19 +469,43 @@ extern void managed_property_change( struct gwm_window *window, int prop, /* WM_HINTS property (see ICCCM 2.0, section 4.1.2.4). */ window->u.managed.hints &= ~HINT_ICONIC; window->u.managed.hints |= HINT_INPUT; - - if( p->value_len < WM_HINTS_MIN_SIZE || p->format != 32 ) - break; - - p32 = xcb_get_property_value( p ); - if( ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_INPUT ) && - !p32[ WM_HINTS_INPUT_OFF ] ) - window->u.managed.hints &= ~HINT_INPUT; - - if( ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_STATE ) && - p32[ WM_HINTS_STATE_OFF ] == WM_HINTS_STATE_ICONIC ) - window->u.managed.hints |= HINT_ICONIC; + if( p->format == 32 ) { + xcb_pixmap_t icon = XCB_NONE, mask = XCB_NONE; + + p32 = xcb_get_property_value( p ); + + if( p->value_len > WM_HINTS_INPUT_OFF && + ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_INPUT ) && + !p32[ WM_HINTS_INPUT_OFF ] ) + window->u.managed.hints &= ~HINT_INPUT; + + if( p->value_len > WM_HINTS_STATE_OFF && + ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_STATE ) && + p32[ WM_HINTS_STATE_OFF ] == WM_HINTS_STATE_ICONIC ) + window->u.managed.hints |= HINT_ICONIC; + + if( p->value_len > WM_HINTS_ICON_OFF && + ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_ICON ) ) + icon = p32[ WM_HINTS_ICON_OFF ]; + + if( p->value_len > WM_HINTS_MASK_OFF && + ( p32[ WM_HINTS_FLAGS_OFF ] & WM_HINTS_ICON_MASK ) ) + mask = p32[ WM_HINTS_MASK_OFF ]; + + if( icon ) { + struct managed_get_geometry *p = xmalloc( sizeof *p ); + union callback_param cp; + + p->window = window; + p->icon = icon; + p->mask = mask; + cp.p = p; + handle_async_reply( xcb_get_geometry( c, icon ).sequence, + handle_get_geometry, cp ); + } else + replace_icons( window, 0, NULL, NULL, NULL, &icon ); + } break; diff --git a/managed.h b/managed.h index e20633c..f222290 100644 --- a/managed.h +++ b/managed.h @@ -12,6 +12,8 @@ extern void match_managed_shape( struct gwm_window *window ); extern void managed_property_change( struct gwm_window *window, int prop, xcb_get_property_reply_t *p ); +extern void async_get_property( struct gwm_window *window, + enum gwm_property_type prop ); extern const event_handler managed_handlers[]; diff --git a/menu.c b/menu.c index 36d0613..9843c32 100644 --- a/menu.c +++ b/menu.c @@ -185,12 +185,12 @@ extern void popup_menu( struct gwm_window *window, xcb_generic_event_t *ev, menu->u.menu.window_param = window->w; menu->u.menu.num_items = num_items; menu->u.menu.active_item = -1; + menu->u.menu.has_icons = FALSE; menu->u.menu.items = xmalloc( num_items * sizeof *menu->u.menu.items ); widths = alloca( num_items * sizeof *widths ); heights = alloca( num_items * sizeof *widths ); - menu->u.menu.width = height = 0; for( i = 0; i < num_items; i++ ) { menu->u.menu.items[ i ] = item = add_window( xcb_generate_id( c ) ); item->screen = screen; @@ -200,7 +200,13 @@ extern void popup_menu( struct gwm_window *window, xcb_generic_event_t *ev, NULL; item->u.menuitem.action = items[ i ].action; item->u.menuitem.cp = items[ i ].cp; - window_size( item, widths + i, heights + i ); + if( ( item->u.menuitem.icon = items[ i ].icon ) ) + menu->u.menu.has_icons = TRUE; + } + + menu->u.menu.width = height = 0; + for( i = 0; i < num_items; i++ ) { + window_size( menu->u.menu.items[ i ], widths + i, heights + i ); if( widths[ i ] > menu->u.menu.width ) menu->u.menu.width = widths[ i ]; @@ -208,6 +214,12 @@ extern void popup_menu( struct gwm_window *window, xcb_generic_event_t *ev, height += heights[ i ]; } + if( menu->u.menu.width > screens[ screen ]->width_in_pixels >> 1 ) + menu->u.menu.width = screens[ screen ]->width_in_pixels >> 1; + + if( height > screens[ screen ]->height_in_pixels ) + height = screens[ screen ]->height_in_pixels; + if( ev_x + menu->u.menu.width > screens[ screen ]->width_in_pixels ) ev_x = screens[ screen ]->width_in_pixels - menu->u.menu.width; if( ev_x < 0 ) diff --git a/menu.h b/menu.h index 7961e08..ef0e2e6 100644 --- a/menu.h +++ b/menu.h @@ -5,7 +5,8 @@ struct menuitem { const char *label; void ( *action )( struct gwm_window *window, xcb_generic_event_t *ev, union callback_param cp ); - union callback_param cp; + union callback_param cp; + struct gwm_window *icon; }; extern void popup_menu( struct gwm_window *window, xcb_generic_event_t *ev, int num_items, const struct menuitem *items ); -- 2.11.4.GIT