summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Wong <gtw@gnu.org>2009-09-06 12:29:38 -0600
committerGary Wong <gtw@gnu.org>2009-09-06 12:29:38 -0600
commit273f014b05bdb206718bf5488bde7971cdbd1ce7 (patch)
tree69c6ef3ac0f74cea932fd0e0f0fad0be0c5e0c06
parent34f65098ed115aaed51299fbb3cec631587743d0 (diff)
Added basic icon support.
-rw-r--r--ChangeLog19
-rw-r--r--Makefile.am1
-rw-r--r--actions.c1
-rw-r--r--decorate-core.c7
-rw-r--r--decorate-core.h4
-rw-r--r--decorate-render.c666
-rw-r--r--decorate-render.h6
-rw-r--r--gwm.c121
-rw-r--r--gwm.h25
-rw-r--r--image.c64
-rw-r--r--image.h12
-rw-r--r--managed.c151
-rw-r--r--managed.h2
-rw-r--r--menu.c16
-rw-r--r--menu.h3
15 files changed, 935 insertions, 163 deletions
diff --git a/ChangeLog b/ChangeLog
index 93cb66c..e183958 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2009-09-06 Gary Wong <gtw@gnu.org>
+
+ * 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 <gtw@gnu.org>
* 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, <gtw@gnu.org>.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * $Id$
+ */
+
+#include <config.h>
+
+#include <xcb/xcb.h>
+
+#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 );