/* * managed.c * * Part of gwm, the Gratuitous Window Manager, * by Gary Wong, . * * Copyright (C) 2009 Gary Wong * * This program is free software: you can redistribute it and/or modify * it under the terms of version 3 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * $Id$ */ #include #include #include #include #include #if USE_SHAPE #include #endif #include "gwm.h" #include "frame.h" #include "managed.h" #include "utf8.h" #include "window-table.h" extern void set_managed_state( struct gwm_window *window, int state ) { uint32_t values[ 2 ]; assert( window->type == WINDOW_MANAGED ); /* Place a WM_STATE property on the client window (ICCCM 2.0, section 4.1.3.1). */ values[ 0 ] = window->u.managed.state = state; values[ 1 ] = XCB_NONE; xcb_change_property( c, XCB_PROP_MODE_REPLACE, window->w, atoms[ ATOM_WM_STATE ], atoms[ ATOM_WM_STATE ], 32, 2, values ); } extern void iconic_to_normal( struct gwm_window *window ) { assert( window->type == WINDOW_MANAGED ); set_managed_state( window, STATE_NORMAL ); xcb_map_window( c, window->w ); xcb_map_window( c, window->u.managed.frame->w ); } extern void normal_to_iconic( struct gwm_window *window ) { uint32_t n; assert( window->type == WINDOW_MANAGED ); set_managed_state( window, STATE_ICONIC ); /* We need to unmap our child without invoking the normal UnmapNotify handler response. Unfortunately it's very difficult to communicate to the handler the distinction between the resultant UnmapNotify from an unmap here and an unrelated event. Checking the request sequence is not robust, because asynchronous events might not be assigned unique sequence numbers. Instead, we grab the server and temporarily change our event mask, which seems a heavyweight approach but does guarantee that only this event will be ignored. */ xcb_grab_server( c ); n = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_COLOR_MAP_CHANGE; xcb_change_window_attributes( c, window->w, XCB_CW_EVENT_MASK, &n ); xcb_unmap_window( c, window->w ); n = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_COLOR_MAP_CHANGE; xcb_change_window_attributes( c, window->w, XCB_CW_EVENT_MASK, &n ); xcb_ungrab_server( c ); xcb_unmap_window( c, window->u.managed.frame->w ); } static void managed_destroy_notify( struct gwm_window *window, xcb_destroy_notify_event_t *ev ) { assert( window->type == WINDOW_MANAGED ); /* This isn't the preferred notification mechanism, but it can happen: if a client has a window in the Iconic state and either terminates abnormally or doesn't send a synthetic UnmapNotify (as specified in ICCCM 2.0, section 4.1.4), then we might not find out about it until we receive a DestroyNotify. */ unmanage_window( window ); } static void managed_unmap_notify( struct gwm_window *window, xcb_unmap_notify_event_t *ev ) { assert( window->type == WINDOW_MANAGED ); /* A transition to the Withdrawn state (ICCCM 2.0, section 4.1.4). */ unmanage_window( window ); } static void managed_reparent_notify( struct gwm_window *window, xcb_reparent_notify_event_t *ev ) { assert( window->type == WINDOW_MANAGED ); if( ev->parent != window->u.managed.frame->w ) /* Handle an obscure case: a client window being reparented away from our frame while iconified. Much like managed_destroy_notify. */ unmanage_window( window->u.frame.child ); } struct managed_get_property { xcb_window_t w; enum gwm_property_type prop; }; static void handle_managed_get_property( unsigned int sequence, void *reply, xcb_generic_error_t *error, union callback_param cp ) { struct managed_get_property *p = cp.p; if( error ) { /* Ignore Window errors, since the window might have been destroyed in the meantime. */ if( error->error_code != XCB_WINDOW ) show_error( error ); free( error ); } if( 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 ); } free( p ); } 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->w = window->w; p->prop = prop; cp.p = p; handle_async_reply( xcb_get_property( c, FALSE, window->w, prop_atoms[ prop ], prop_types[ prop ], 0, PROP_SIZE ).sequence, 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 #define MOTIF_WM_HINTS_DECORATIONS_OFF 2 #define MOTIF_WM_HINTS_MIN_SIZE 3 /* ignore hint properties smaller than this */ #define MOTIF_WM_HINTS_DEC_ALL 0x1 #define MOTIF_WM_HINTS_DEC_BORDER 0x2 #define MOTIF_WM_HINTS_DEC_TITLE 0x8 #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_ICON_OFF 3 #define WM_HINTS_MASK_OFF 7 #define WM_HINTS_STATE_NORMAL 1 #define WM_HINTS_STATE_ICONIC 3 #define WM_NORMAL_HINTS_USER_POSITION 0x001 #define WM_NORMAL_HINTS_PROGRAM_POSITION 0x004 #define WM_NORMAL_HINTS_MIN_SIZE 0x010 #define WM_NORMAL_HINTS_MAX_SIZE 0x020 #define WM_NORMAL_HINTS_SIZE_INC 0x040 #define WM_NORMAL_HINTS_ASPECT 0x080 #define WM_NORMAL_HINTS_BASE_SIZE 0x100 #define WM_NORMAL_HINTS_WIN_GRAVITY 0x200 #define WM_NORMAL_HINTS_FLAGS_OFF 0 #define WM_NORMAL_HINTS_MIN_SIZE_OFF 5 #define WM_NORMAL_HINTS_MAX_SIZE_OFF 7 #define WM_NORMAL_HINTS_SIZE_INC_OFF 9 #define WM_NORMAL_HINTS_ASPECT_OFF 11 #define WM_NORMAL_HINTS_BASE_SIZE_OFF 15 #define WM_NORMAL_HINTS_WIN_GRAVITY_OFF 17 extern void managed_property_change( struct gwm_window *window, int prop, xcb_get_property_reply_t *p ) { uint32_t *p32, n32; int i; struct xcb_screen_t *screen; int value_len; int old_decoration; int num_icons; int len; assert( window->type == WINDOW_MANAGED ); switch( prop ) { case PROP__MOTIF_WM_HINTS: /* _MOTIF_WM_HINTS property. */ old_decoration = window->u.managed.frame->u.frame.decoration; window->u.managed.frame->u.frame.decoration = DEC_DEFAULT; if( p->format == 32 && p->value_len >= MOTIF_WM_HINTS_MIN_SIZE ) { p32 = xcb_get_property_value( p ); if( p32[ MOTIF_WM_HINTS_FLAGS_OFF ] & MOTIF_WM_HINTS_DECORATIONS ) { if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] & MOTIF_WM_HINTS_DEC_ALL ) { window->u.managed.frame->u.frame.decoration = DEC_BORDER | DEC_TITLE; if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] & MOTIF_WM_HINTS_DEC_BORDER ) window->u.managed.frame->u.frame.decoration &= ~DEC_BORDER; if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] & MOTIF_WM_HINTS_DEC_TITLE ) window->u.managed.frame->u.frame.decoration &= ~DEC_TITLE; } else { window->u.managed.frame->u.frame.decoration = 0; if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] & MOTIF_WM_HINTS_DEC_BORDER ) window->u.managed.frame->u.frame.decoration |= DEC_BORDER; if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] & MOTIF_WM_HINTS_DEC_TITLE ) window->u.managed.frame->u.frame.decoration |= DEC_TITLE; } } } #if USE_SHAPE if( window->u.managed.shaped ) /* Never apply borders to shaped windows. */ window->u.managed.frame->u.frame.decoration &= ~DEC_BORDER; #endif if( window->u.managed.state == STATE_NORMAL && window->u.managed.frame->u.frame.decoration != old_decoration ) { int new_decoration = window->u.managed.frame->u.frame.decoration; int x, y, width, height; uint32_t n[ 5 ]; window->u.managed.frame->u.frame.decoration = old_decoration; translate_frame_to_child( window->u.managed.frame, &x, &y, window->u.managed.frame->u.frame.x, window->u.managed.frame->u.frame.y, window->u.managed.border_width, window->u.managed.win_gravity ); window->u.managed.frame->u.frame.decoration = new_decoration; translate_child_to_frame( window->u.managed.frame, &x, &y, &width, &height, x, y, window->u.managed.frame->u.frame.width - frame_l( window->u.managed.frame, FALSE ) - frame_r( window->u.managed.frame, FALSE ), window->u.managed.frame->u.frame.height - frame_t( window->u.managed.frame, FALSE ) - frame_b( window->u.managed.frame, FALSE ), window->u.managed.border_width, window->u.managed.win_gravity ); if( new_decoration & DEC_TITLE ) xcb_map_window( c, window->u.managed.frame->u.frame.button->w ); else xcb_unmap_window( c, window->u.managed.frame->u.frame.button->w ); n[ 0 ] = frame_l( window->u.managed.frame, FALSE ); n[ 1 ] = frame_l( window->u.managed.frame, FALSE ); xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, n ); n[ 0 ] = x; n[ 1 ] = y; n[ 2 ] = width; n[ 3 ] = height; n[ 4 ] = frame_xb( window->u.managed.frame ); /* We'll also notify the client of any changes, in the ConfigureNotify handler for the event we expect to receive in response to this request. */ xcb_configure_window( c, window->u.managed.frame->w, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH, n ); update_frame_extents( window->u.managed.frame ); } 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 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 = TRUE; if( window->u.managed.state == STATE_NORMAL && ( window->u.managed.frame->u.frame.decoration & DEC_TITLE ) ) queue_window_update( window->u.managed.frame, 0, 0, window->u.managed.frame->u.frame.width, frame_t( window->u.managed.frame, FALSE ), FALSE ); } else { window->u.managed.name = NULL; 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 ); } break; case PROP_WM_COLORMAP_WINDOWS: /* WM_COLORMAP_WINDOWS property (see ICCCM 2.0, section 4.1.8). */ window->u.managed.cmap_window = XCB_NONE; if( p->format == 32 && p->value_len >= 2 ) { p32 = xcb_get_property_value( p ); if( p32[ 0 ] == window->w ) /* The window itself is given the highest priority, which is what we would do anyway -- ignore the list. */ break; for( i = 1; i < p->value_len; i++ ) if( p32[ i ] == window->w ) { /* The window is explicitly given a lower priority than some other one. Remember whichever is considered more important. */ window->u.managed.cmap_window = p32[ i ]; break; } } break; case PROP_WM_HINTS: /* 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->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; case PROP_WM_NAME: /* WM_NAME property (see ICCCM 2.0, section 4.1.2.1). */ if( window->u.managed.net_wm_name ) /* Ignore WM_NAME if _NET_WM_NAME is set. */ break; if( window->u.managed.name ) free( window->u.managed.name ); if( p->value_len && p->format == 8 ) window->u.managed.name = to_utf8( p->type == atoms[ ATOM_COMPOUND_TEXT ] ? ENCODING_COMPOUND : ENCODING_LATIN_1, xcb_get_property_value( p ), p->value_len ); else window->u.managed.name = NULL; if( window->u.managed.state == STATE_NORMAL && ( window->u.managed.frame->u.frame.decoration & DEC_TITLE ) ) queue_window_update( window->u.managed.frame, 0, 0, window->u.managed.frame->u.frame.width, frame_t( window->u.managed.frame, FALSE ), FALSE ); break; case PROP_WM_NORMAL_HINTS: /* WM_NORMAL_HINTS property (see ICCCM 2.0, section 4.1.2.3). */ window->u.managed.hints &= ~HINT_POSITION; window->u.managed.min_width = frame_t( window->u.managed.frame, FALSE ); window->u.managed.min_height = frame_t( window->u.managed.frame, FALSE ) >> 1; window->u.managed.max_width = 0x7FFF; window->u.managed.max_height = 0x7FFF; window->u.managed.width_inc = 1; window->u.managed.height_inc = 1; window->u.managed.min_aspect_x = 0; window->u.managed.min_aspect_y = 1; window->u.managed.max_aspect_x = 1; window->u.managed.max_aspect_y = 0; window->u.managed.base_width = 0; window->u.managed.base_height = 0; window->u.managed.win_gravity = XCB_GRAVITY_NORTH_WEST; if( p->value_len < 1 || p->format != 32 ) { n32 = 0; p32 = &n32; value_len = 1; } else { p32 = xcb_get_property_value( p ); value_len = p->value_len; } if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_USER_POSITION ) window->u.managed.hints |= HINT_USER_POSITION; else if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_PROGRAM_POSITION ) window->u.managed.hints |= HINT_PROGRAM_POSITION; if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_MIN_SIZE && value_len >= WM_NORMAL_HINTS_MIN_SIZE_OFF + 1 ) { window->u.managed.min_width = p32[ WM_NORMAL_HINTS_MIN_SIZE_OFF ]; window->u.managed.min_height = p32[ WM_NORMAL_HINTS_MIN_SIZE_OFF + 1 ]; } if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_MAX_SIZE && value_len >= WM_NORMAL_HINTS_MAX_SIZE_OFF + 1 ) { window->u.managed.max_width = p32[ WM_NORMAL_HINTS_MAX_SIZE_OFF ]; window->u.managed.max_height = p32[ WM_NORMAL_HINTS_MAX_SIZE_OFF + 1 ]; } if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_SIZE_INC && value_len >= WM_NORMAL_HINTS_SIZE_INC_OFF + 1 ) { window->u.managed.width_inc = p32[ WM_NORMAL_HINTS_SIZE_INC_OFF ]; window->u.managed.height_inc = p32[ WM_NORMAL_HINTS_SIZE_INC_OFF + 1 ]; if( window->u.managed.width_inc < 1 ) window->u.managed.width_inc = 1; if( window->u.managed.height_inc < 1 ) window->u.managed.height_inc = 1; } if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_ASPECT && value_len >= WM_NORMAL_HINTS_ASPECT_OFF + 3 ) { window->u.managed.min_aspect_x = p32[ WM_NORMAL_HINTS_ASPECT_OFF ]; window->u.managed.min_aspect_y = p32[ WM_NORMAL_HINTS_ASPECT_OFF + 1 ]; window->u.managed.max_aspect_x = p32[ WM_NORMAL_HINTS_ASPECT_OFF + 2 ]; window->u.managed.max_aspect_y = p32[ WM_NORMAL_HINTS_ASPECT_OFF + 3 ]; } if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_BASE_SIZE && value_len >= WM_NORMAL_HINTS_BASE_SIZE_OFF + 1 ) { window->u.managed.base_width = p32[ WM_NORMAL_HINTS_BASE_SIZE_OFF ]; window->u.managed.base_height = p32[ WM_NORMAL_HINTS_BASE_SIZE_OFF + 1 ]; if( !( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_MIN_SIZE ) ) { window->u.managed.min_width = window->u.managed.base_width; window->u.managed.min_width = window->u.managed.base_width; } } if( p32[ WM_NORMAL_HINTS_FLAGS_OFF ] & WM_NORMAL_HINTS_WIN_GRAVITY && value_len >= WM_NORMAL_HINTS_WIN_GRAVITY_OFF ) window->u.managed.win_gravity = p32[ WM_NORMAL_HINTS_WIN_GRAVITY_OFF ]; /* Sanity check. */ screen = screens[ window->screen ]; if( window->u.managed.min_width < frame_t( window->u.managed.frame, FALSE ) ) window->u.managed.min_width = frame_t( window->u.managed.frame, FALSE ); if( window->u.managed.min_width > screen->width_in_pixels ) window->u.managed.min_width = screen->width_in_pixels; if( window->u.managed.min_height < frame_t( window->u.managed.frame, FALSE ) >> 1 ) window->u.managed.min_height = frame_t( window->u.managed.frame, FALSE ) >> 1; if( window->u.managed.min_height > screen->height_in_pixels ) window->u.managed.min_height = screen->height_in_pixels; if( window->u.managed.max_width < window->u.managed.min_width ) window->u.managed.max_width = window->u.managed.min_width; if( window->u.managed.max_width > 0x7FFF ) window->u.managed.max_width = 0x7FFF; if( window->u.managed.max_height < window->u.managed.min_height ) window->u.managed.max_height = window->u.managed.min_height; if( window->u.managed.max_height > 0x7FFF ) window->u.managed.max_height = 0x7FFF; if( window->u.managed.min_aspect_x < 0 || window->u.managed.min_aspect_y < 1 || window->u.managed.max_aspect_x < 1 || window->u.managed.max_aspect_x < 0 || window->u.managed.min_aspect_x * window->u.managed.max_aspect_y > window->u.managed.min_aspect_y * window->u.managed.max_aspect_x ) { window->u.managed.min_aspect_x = 0; window->u.managed.min_aspect_y = 1; window->u.managed.max_aspect_x = 1; window->u.managed.max_aspect_y = 0; } if( window->u.managed.base_width < 0 || window->u.managed.base_width > window->u.managed.min_width || window->u.managed.base_height < 0 || window->u.managed.base_height > window->u.managed.min_height ) { window->u.managed.base_width = 0; window->u.managed.base_height = 0; } if( window->u.managed.win_gravity < XCB_GRAVITY_NORTH_WEST || window->u.managed.win_gravity > XCB_GRAVITY_STATIC ) window->u.managed.win_gravity = XCB_GRAVITY_NORTH_WEST; if( window->u.managed.state == STATE_NORMAL ) { /* Recalculate constraints. */ int width = window->u.managed.frame->u.frame.width - frame_l( window->u.managed.frame, FALSE ) - frame_r( window->u.managed.frame, FALSE ); int height = window->u.managed.frame->u.frame.height - frame_t( window->u.managed.frame, FALSE ) - frame_b( window->u.managed.frame, FALSE ); int old_width, old_height; old_width = width; old_height = height; apply_size_constraints( window, &width, &height ); if( width != old_width || height != old_height ) { xcb_configure_request_event_t ev; ev.response_type = XCB_CONFIGURE_REQUEST; ev.parent = window->u.managed.frame->w; ev.window = window->w; ev.width = width; ev.height = height; ev.value_mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; frame_handlers[ XCB_CONFIGURE_REQUEST ]( window->u.managed.frame, (xcb_generic_event_t *) &ev ); } } break; case PROP_WM_PROTOCOLS: /* WM_PROTOCOLS property (see ICCCM 2.0, section 4.1.2.7). */ /* FIXME Look for _NET_WM_SYNC_REQUEST. */ window->u.managed.protocols = 0; if( p->format == 32 ) { p32 = xcb_get_property_value( p ); for( i = 0; i < p->value_len; i++ ) if( p32[ i ] == atoms[ ATOM_WM_DELETE_WINDOW ] ) window->u.managed.protocols |= PROTOCOL_DELETE_WINDOW; else if( p32[ i ] == atoms[ ATOM_WM_TAKE_FOCUS ] ) window->u.managed.protocols |= PROTOCOL_TAKE_FOCUS; } if( window->u.managed.state == STATE_NORMAL ) { uint32_t n = window->u.managed.protocols & PROTOCOL_DELETE_WINDOW ? XCB_NONE : cursors[ CURSOR_DESTROY ]; xcb_change_window_attributes( c, window->u.managed.frame-> u.frame.button->w, XCB_CW_CURSOR, &n ); } break; } } static void managed_property_notify( struct gwm_window *window, xcb_property_notify_event_t *ev ) { enum gwm_property_type i; for( i = 0; i < NUM_PROPS; i++ ) if( ev->atom == prop_atoms[ i ] ) { async_get_property( window, i ); return; } } static void managed_colormap_notify( struct gwm_window *window, xcb_colormap_notify_event_t *ev ) { if( ev->_new ) { window->u.managed.cmap = ev->colormap; if( window->u.managed.frame == focus_frame ) /* Gah... the protocol doesn't specify a timestamp on ColormapNotify events. We'll have to make do with the most recent timestamp we've received instead. */ install_window_colormap( window->screen, window, latest_timestamp ); } } #if USE_SHAPE extern void match_managed_shape( struct gwm_window *window ) { if( window->u.managed.shaped ) { xcb_rectangle_t rect; rect.x = rect.y = 0; rect.width = window->u.managed.frame->u.frame.width; rect.height = frame_t( window->u.managed.frame, FALSE ); xcb_shape_rectangles( c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, 0, window->u.managed.frame->w, 0, 0, 1, &rect ); xcb_shape_combine( c, XCB_SHAPE_SO_UNION, XCB_SHAPE_SK_BOUNDING, XCB_SHAPE_SK_BOUNDING, window->u.managed.frame->w, frame_l( window->u.managed.frame, FALSE ), frame_t( window->u.managed.frame, FALSE ), window->w ); } else xcb_shape_mask( c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, window->u.managed.frame->w, 0, 0, XCB_NONE ); } static void managed_shape_notify( struct gwm_window *window, xcb_shape_notify_event_t *ev ) { if( ev->shape_kind == XCB_SHAPE_SK_BOUNDING ) { if( window->u.managed.shaped != ev->shaped && !( window->u.managed.shaped = ev->shaped ) ) /* The client is no longer shaped. Retrieve its original _MOTIF_WM_HINTS, since we might now want to apply a border. */ async_get_property( window, PROP__MOTIF_WM_HINTS ); match_managed_shape( window ); } } #endif const event_handler managed_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ NULL, /* KeyPress */ NULL, /* KeyRelease */ NULL, /* ButtonPress */ NULL, /* ButtonRelease */ NULL, /* MotionNotify */ NULL, /* EnterNotify */ NULL, /* LeaveNotify */ NULL, /* FocusIn */ NULL, /* FocusOut */ NULL, /* KeymapNotify */ NULL, /* Expose */ NULL, /* GraphicsExpose */ NULL, /* NoExposure */ NULL, /* VisibilityNotify */ NULL, /* CreateNotify */ (event_handler) managed_destroy_notify, (event_handler) managed_unmap_notify, NULL, /* MapNotify */ NULL, /* MapRequest */ (event_handler) managed_reparent_notify, NULL, /* ConfigureNotify */ NULL, /* ConfigureRequest */ NULL, /* GravityNotify */ NULL, /* ResizeRequest */ NULL, /* CirculateNotify */ NULL, /* CirculateRequest */ (event_handler) managed_property_notify, NULL, /* SelectionClear */ NULL, /* SelectionRequest */ NULL, /* SelectionNotify */ (event_handler) managed_colormap_notify, NULL, /* ClientMessage */ NULL, /* MappingNotify */ NULL, /* (synthetic) */ NULL, /* RRNotify */ #if USE_SHAPE (event_handler) managed_shape_notify #else NULL /* ShapeNotify */ #endif }; extern void withdrawn_map_request( struct gwm_window *window, xcb_map_request_event_t *ev ) { manage_window( ev->window, TRUE ); } extern void withdrawn_configure_request( struct gwm_window *window, xcb_configure_request_event_t *ev ) { int i = 0; uint32_t values[ 7 ]; /* Configuring an unmanaged window -- just honour the request as is. */ if( ev->value_mask & XCB_CONFIG_WINDOW_X ) values[ i++ ] = ev->x; if( ev->value_mask & XCB_CONFIG_WINDOW_Y ) values[ i++ ] = ev->y; if( ev->value_mask & XCB_CONFIG_WINDOW_WIDTH ) values[ i++ ] = ev->width; if( ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT ) values[ i++ ] = ev->height; if( ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH ) values[ i++ ] = ev->border_width; if( ev->value_mask & XCB_CONFIG_WINDOW_SIBLING ) values[ i++ ] = ev->sibling; if( ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE ) values[ i++ ] = ev->stack_mode; xcb_configure_window( c, ev->window, ev->value_mask, values ); }