From ea4ea8b24cd885d8a0217efbbf339546b69205cb Mon Sep 17 00:00:00 2001 From: Gary Wong Date: Thu, 1 Oct 2015 22:41:11 -0600 Subject: [PATCH] Add support for the zoomed state to managed windows. --- ChangeLog | 8 ++ actions.c | 16 +++- actions.h | 4 + frame.c | 40 ++++++++ gwm.c | 66 ++++++++++--- gwm.h | 12 +++ keyboard.c | 3 +- managed.c | 318 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- managed.h | 7 ++ menu.c | 6 +- 10 files changed, 436 insertions(+), 44 deletions(-) diff --git a/ChangeLog b/ChangeLog index f0a55df..f3a1453 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2015-10-01 Gary Wong + + * actions.c (action_max_window): New function. + * gwm.c (handle_client_message): Handle _NET_WM_STATE messages. + * managed.c (handle_set_managed_net_state, set_managed_net_state) + (enter_zoom, leave_zoom): New functions. + (managed_property_change): Handle _NET_WM_STATE property. + 2014-05-15 Gary Wong * gwm.c (handle_crtc_info): Add missing indirection in *cc->ok. diff --git a/actions.c b/actions.c index 2ea1dbe..bc7155d 100644 --- a/actions.c +++ b/actions.c @@ -129,6 +129,19 @@ extern void action_deiconify_window( struct gwm_window *window, iconic_to_normal( window ); } +extern void action_max_window( struct gwm_window *window, + xcb_generic_event_t *ev, + union callback_param cp ) { + + if( window->type == WINDOW_FRAME ) + window = window->u.frame.child; + + if( window->type == WINDOW_MANAGED ) + set_managed_net_state( window, + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ], + NET_STATE_TOGGLE ); +} + extern void action_map_raise( struct gwm_window *window, xcb_generic_event_t *ev, union callback_param cp ) { @@ -174,7 +187,8 @@ extern void action_window_menu( struct gwm_window *window, /* FIXME this should be configurable, of course */ const struct menuitem window_menu[] = { { "Iconify", action_iconify_window }, - { "Raise/lower", action_stack_opposite } + { "Raise/lower", action_stack_opposite }, + { "Zoom", action_max_window } }; popup_menu( window, ev, sizeof window_menu / sizeof *window_menu, diff --git a/actions.h b/actions.h index 7cf498e..a1c920d 100644 --- a/actions.h +++ b/actions.h @@ -21,6 +21,10 @@ extern void action_deiconify_window( struct gwm_window *window, xcb_generic_event_t *ev, union callback_param cp ); +extern void action_max_window( struct gwm_window *window, + xcb_generic_event_t *ev, + union callback_param cp ); + extern void action_map_raise( struct gwm_window *window, xcb_generic_event_t *ev, union callback_param cp ); diff --git a/frame.c b/frame.c index 9d5b4d7..f16ddde 100644 --- a/frame.c +++ b/frame.c @@ -849,6 +849,12 @@ static void frame_button_press( struct gwm_window *window, return; } + if( window->u.frame.zoom_state ) + /* It shouldn't be possible to generate button presses on + zoomed windows, since they are undecorated, but if it + happens somehow let's explicitly ignore it. */ + return; + pointer_demux = window->w; window_op = ( ev->detail > 1 ) == @@ -1143,9 +1149,43 @@ static void frame_configure_request( struct gwm_window *window, translate_child_to_frame( window, &frame_x, &frame_y, &frame_width, &frame_height, x, y, ev->width, ev->height, + /* FIXME is this really right? is there + a race condition here if we've previously + asked for a different size, the client didn't + set CONFIG_WINDOW_WIDTH or CONFIG_WINDOW, + but the server hasn't processed our most + recent request yet? */ window->u.frame.child->u.managed.border_width, window->u.frame.child->u.managed.win_gravity ); + if( window->u.frame.zoom_state ) { + /* Handle configure request for zoomed windows. Ignore (save) + changes to border width as normal, and honour stacking changes + unchanged. Update the saved state on geometry changes to + maximised windows, and leave zoom on geometry changes + to fullscreen. */ + window->u.frame.unzoomed_x = frame_x; + window->u.frame.unzoomed_y = frame_y; + window->u.frame.unzoomed_width = frame_width; + window->u.frame.unzoomed_height = frame_height; + + if( ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE ) { + values[ 0 ] = ev->stack_mode; + + xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_STACK_MODE, + values ); + } + + if( window->u.frame.zoom_state == ZOOM_MAXIMISE ) + synthetic_configure_notify( window ); + else + set_managed_net_state( window->u.frame.child, + atoms[ ATOM__NET_WM_STATE_FULLSCREEN ], + NET_STATE_REMOVE ); + + return; + } + values[ 0 ] = frame_x; values[ 1 ] = frame_y; values[ 2 ] = frame_width; diff --git a/gwm.c b/gwm.c index 548c9eb..59f0948 100644 --- a/gwm.c +++ b/gwm.c @@ -101,6 +101,10 @@ static const char *const atom_names[ NUM_ATOMS ] = { "_NET_SUPPORTING_WM_CHECK", "_NET_WM_ICON", "_NET_WM_NAME", + "_NET_WM_STATE", + "_NET_WM_STATE_FULLSCREEN", + "_NET_WM_STATE_MAXIMIZED_HORZ", + "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WORKAREA", "UTF8_STRING", "VERSION", @@ -293,11 +297,10 @@ static void show_window( char *label, xcb_window_t w ) { if( w == XCB_NONE ) fputs( "None", stdout ); else if( window ) { - struct gwm_window *client; + struct gwm_window *client = NULL; switch( window->type ) { case WINDOW_ROOT: printf( "root, screen %d", window->screen ); - client = NULL; break; case WINDOW_MANAGED: @@ -316,32 +319,26 @@ static void show_window( char *label, xcb_window_t w ) { case WINDOW_MENU: fputs( "menu", stdout ); - client = NULL; break; case WINDOW_MENUITEM: fputs( "menu item", stdout ); - client = NULL; break; case WINDOW_FAKE: fputs( "fake", stdout ); - client = NULL; break; case WINDOW_FEEDBACK: printf( "feedback, screen %d", window->screen ); - client = NULL; break; case WINDOW_INCOMPLETE: fputs( "incomplete", stdout ); - client = NULL; break; case WINDOW_CHILDLESS: fputs( "childless", stdout ); - client = NULL; break; } @@ -1247,12 +1244,13 @@ static void start_managing_window( struct gwm_window *window, #if USE_SHAPE window->u.managed.shaped = have_extension[ EXT_SHAPE ] && shape && shape->bounding_shaped; -#endif +#endif frame->screen = window->screen; frame->type = WINDOW_FRAME; frame->u.frame.child = window; frame->u.frame.button = button; frame->u.frame.decoration = DEC_DEFAULT; + frame->u.frame.zoom_state = ZOOM_NORMAL; for( i = 0; i < NUM_BORDER_REGIONS; i++ ) frame->u.frame.border_regions[ i ] = xcb_generate_id( c ); button->screen = window->screen; @@ -1260,7 +1258,8 @@ static void start_managing_window( struct gwm_window *window, button->u.button.frame = frame; for( i = 0; i < NUM_PROPS; i++ ) - managed_property_change( window, i, props[ i ] ); + if( i != PROP__NET_WM_STATE ) + managed_property_change( window, i, props[ i ] ); update_frame_extents( frame ); @@ -1282,6 +1281,11 @@ static void start_managing_window( struct gwm_window *window, make_area_live( frame->screen, &frame->u.frame.x, &frame->u.frame.y, frame->u.frame.width, frame->u.frame.height, 8, 8 ); + + /* Handle _NET_WM_STATE last, since it might zoom the window and we'd + like to have a useful saved state to restore to if we can. */ + managed_property_change( window, PROP__NET_WM_STATE, + props[ PROP__NET_WM_STATE ] ); values[ 0 ] = gwm_screens[ frame->screen ].pixels[ COL_FRAME_INACTIVE ]; /* background pixel */ @@ -1566,6 +1570,11 @@ extern void unmanage_window( struct gwm_window *window ) { handle_error_reply( xcb_delete_property_checked( c, w, atoms[ ATOM_WM_STATE ] ), ERR_MASK_WINDOW ); + /* We're also supposed to delete any _NET_WM_STATE on withdrawing + windows (see EWMH), but since this can only ever help clients + reusing windows and there's a horrible race condition there, we + won't even try. */ + handle_error_reply( xcb_configure_window_checked( c, w, XCB_CONFIG_WINDOW_BORDER_WIDTH, &border_width ), ERR_MASK_WINDOW ); @@ -1858,7 +1867,7 @@ static INIT void setup_display( void ) { const xcb_setup_t *setup; xcb_screen_iterator_t iter; xcb_intern_atom_cookie_t *screen_atom_cookies; - uint32_t n, values[ 4 ]; + uint32_t n, values[ 6 ]; xcb_get_selection_owner_cookie_t *screen_owner_cookies; xcb_window_t *screen_owners; xcb_client_message_event_t msg; @@ -1959,6 +1968,7 @@ static INIT void setup_display( void ) { 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__NET_WM_STATE ] = atoms[ ATOM__NET_WM_STATE ]; prop_atoms[ PROP_WM_COLORMAP_WINDOWS ] = atoms[ ATOM_WM_COLORMAP_WINDOWS ]; prop_atoms[ PROP_WM_HINTS ] = WM_HINTS; prop_atoms[ PROP_WM_NAME ] = WM_NAME; @@ -1968,6 +1978,7 @@ static INIT void setup_display( void ) { 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__NET_WM_STATE ] = ATOM; prop_types[ PROP_WM_COLORMAP_WINDOWS ] = WINDOW; prop_types[ PROP_WM_HINTS ] = WM_HINTS; prop_types[ PROP_WM_NAME ] = XCB_GET_PROPERTY_TYPE_ANY; @@ -2328,9 +2339,13 @@ static INIT void setup_display( void ) { values[ 0 ] = atoms[ ATOM__NET_WM_ICON ]; values[ 1 ] = atoms[ ATOM__NET_WM_NAME ]; + values[ 2 ] = atoms[ ATOM__NET_WM_STATE ]; + values[ 3 ] = atoms[ ATOM__NET_WM_STATE_FULLSCREEN ]; + values[ 4 ] = atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ]; + values[ 5 ] = atoms[ ATOM__NET_WM_STATE_MAXIMIZED_VERT ]; xcb_change_property( c, XCB_PROP_MODE_REPLACE, screens[ i ]->root, atoms[ ATOM__NET_SUPPORTED ], CARDINAL, 32, - 2, values ); + 6, values ); values[ 0 ] = fake_window->w; xcb_change_property( c, XCB_PROP_MODE_REPLACE, screens[ i ]->root, @@ -2464,7 +2479,29 @@ static void handle_mapping_notify( xcb_mapping_notify_event_t *ev ) { static void handle_client_message( xcb_client_message_event_t *ev ) { - if( ev->type == atoms[ ATOM_WM_CHANGE_STATE ] ) { + if( ev->type == atoms[ ATOM__NET_WM_STATE ] && ev->format == 32 ) { + struct gwm_window *window; + + if( ( window = lookup_window( ev->window ) ) && + window->type == WINDOW_MANAGED ) { + int i; + + for( i = 1; i < 3; i++ ) + if( ( ev->data.data32[ i ] == + atoms[ ATOM__NET_WM_STATE_FULLSCREEN ] ) || + ( ev->data.data32[ i ] == + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ] && + ( i == 1 || ev->data.data32[ 1 ] != + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_VERT ] ) ) || + ( ev->data.data32[ i ] == + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_VERT ] && + ( i == 1 || ev->data.data32[ 1 ] != + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ] ) ) ) + set_managed_net_state( window, ev->data.data32[ i ], + ev->data.data32[ 0 ] ); + } + } else if( ev->type == atoms[ ATOM_WM_CHANGE_STATE ] && + ev->format == 32 ) { struct gwm_window *window; if( ( window = lookup_window( ev->window ) ) && @@ -2472,7 +2509,8 @@ static void handle_client_message( xcb_client_message_event_t *ev ) { window->u.managed.state == STATE_NORMAL && ev->data.data32[ 0 ] == STATE_ICONIC ) normal_to_iconic( window ); - } else if( ev->type == atoms[ ATOM_WM_COLORMAP_NOTIFY ] ) { + } else if( ev->type == atoms[ ATOM_WM_COLORMAP_NOTIFY ] && + ev->format == 32 ) { struct gwm_window *window; if( ( window = lookup_window( ev->window ) ) && diff --git a/gwm.h b/gwm.h index ddcdb67..631b4cb 100644 --- a/gwm.h +++ b/gwm.h @@ -87,6 +87,10 @@ enum gwm_atom { ATOM__NET_SUPPORTING_WM_CHECK, ATOM__NET_WM_ICON, ATOM__NET_WM_NAME, + ATOM__NET_WM_STATE, + ATOM__NET_WM_STATE_FULLSCREEN, + ATOM__NET_WM_STATE_MAXIMIZED_HORZ, + ATOM__NET_WM_STATE_MAXIMIZED_VERT, ATOM__NET_WORKAREA, ATOM_UTF8_STRING, ATOM_VERSION, @@ -106,6 +110,7 @@ enum gwm_property_type { PROP__MOTIF_WM_HINTS, PROP__NET_WM_ICON, PROP__NET_WM_NAME, + PROP__NET_WM_STATE, PROP_WM_COLORMAP_WINDOWS, PROP_WM_HINTS, PROP_WM_NAME, @@ -246,6 +251,10 @@ struct gwm_window; #define DEC_TITLE 0x2 #define DEC_DEFAULT ( DEC_BORDER | DEC_TITLE ) +#define ZOOM_NORMAL 0x0 +#define ZOOM_FULLSCREEN 0x1 /* _NET_WM_STATE_FULLSCREEN */ +#define ZOOM_MAXIMISE 0x2 /* _NET_WM_STATE_MAXIMIZED_* */ + struct gwm_window { xcb_window_t w; int screen; @@ -289,6 +298,9 @@ struct gwm_window { struct gwm_window *child, *button; int x, y, width, height; int decoration; /* see DEC_* above */ + int unzoomed_x, unzoomed_y, unzoomed_width, unzoomed_height, + unzoomed_decoration; + int zoom_state; /* see ZOOM_* above */ xcb_window_t border_regions[ NUM_BORDER_REGIONS ]; } frame; struct _gwm_button { diff --git a/keyboard.c b/keyboard.c index 0532c6e..29a7983 100644 --- a/keyboard.c +++ b/keyboard.c @@ -70,7 +70,8 @@ const struct key_action key_actions[] = { /* FIXME This table should be configurable, of course. */ { KEYSYM_TAB, XCB_MOD_MASK_1, action_raise_lowest }, { KEYSYM_F1, XCB_MOD_MASK_1, action_iconify_window }, - { KEYSYM_F5, XCB_MOD_MASK_1, action_stack_opposite } + { KEYSYM_F5, XCB_MOD_MASK_1, action_stack_opposite }, + { KEYSYM_F10, XCB_MOD_MASK_1, action_max_window } }; const int num_key_actions = sizeof key_actions / sizeof *key_actions; diff --git a/managed.c b/managed.c index 1e73513..ce8be76 100644 --- a/managed.c +++ b/managed.c @@ -53,6 +53,98 @@ extern void set_managed_state( struct gwm_window *window, int state ) { 32, 2, values ); } +struct managed_net_state { + xcb_window_t w; + xcb_atom_t state; + int action; +}; + +static void handle_set_managed_net_state( unsigned int sequence, void *r, + xcb_generic_error_t *error, + union callback_param cp ) { + + struct managed_net_state *p = cp.p; + xcb_get_property_reply_t *reply = r; + + 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 ); + xcb_atom_t *state = xcb_get_property_value( reply ); + int state_len = reply->format == 32 ? reply->value_len : 0; + + /* Don't assume the window is valid: it might have been destroyed + or forgotten asynchronously. */ + if( window && window->type == WINDOW_MANAGED ) { + int len, i, found = FALSE; + + for( i = 0; i < state_len; i++ ) + if( state[ i ] == p->state ) { + found = TRUE; + break; + } + + if( found && p->action != NET_STATE_ADD ) { + xcb_atom_t *new_state = alloca( ( state_len - 1 ) * + sizeof (xcb_atom_t) ); + + for( i = 0, len = 0; i < state_len; i++ ) + if( state[ i ] != p->state && + ( p->state != + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ] || + state[ i ] != + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_VERT ] ) ) + new_state[ len++ ] = state[ i ]; + + xcb_change_property( c, XCB_PROP_MODE_REPLACE, window->w, + atoms[ ATOM__NET_WM_STATE ], ATOM, 32, + len, new_state ); + } else if( !found && p->action != NET_STATE_REMOVE ) { + xcb_atom_t new_state[ 2 ]; + + if( ( new_state[ 0 ] = p->state ) == + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ] ) { + new_state[ 1 ] = atoms[ ATOM__NET_WM_STATE_MAXIMIZED_VERT ]; + len = 2; + } else + len = 1; + + xcb_change_property( c, XCB_PROP_MODE_APPEND, window->w, + atoms[ ATOM__NET_WM_STATE ], ATOM, 32, + len, new_state ); + } + } + + free( reply ); + } + + free( p ); +} + +extern void set_managed_net_state( struct gwm_window *window, + xcb_atom_t state, int action ) { + + struct managed_net_state *p = xmalloc( sizeof *p ); + union callback_param cp; + + p->w = window->w; + p->state = state; + p->action = action; + + cp.p = p; + handle_async_reply( xcb_get_property( c, FALSE, window->w, + atoms[ ATOM__NET_WM_STATE ], + ATOM, 0, PROP_SIZE ).sequence, + handle_set_managed_net_state, cp ); +} + extern void iconic_to_normal( struct gwm_window *window ) { assert( window->type == WINDOW_MANAGED ); @@ -259,6 +351,160 @@ static void handle_get_geometry( unsigned int sequence, void *reply, #define WM_NORMAL_HINTS_BASE_SIZE_OFF 15 #define WM_NORMAL_HINTS_WIN_GRAVITY_OFF 17 +static void enter_zoom( struct gwm_window *window ) { + + int x, y, width, height; + uint32_t values[ 4 ]; + + assert( window->type == WINDOW_FRAME ); + + x = window->u.frame.unzoomed_x = window->u.frame.x; + y = window->u.frame.unzoomed_y = window->u.frame.y; + width = window->u.frame.unzoomed_width = window->u.frame.width; + height = window->u.frame.unzoomed_height = window->u.frame.height; + window->u.frame.unzoomed_decoration = window->u.frame.decoration; + +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) { + int crtc, best_crtc = 0; + long best_overlap = 0, best_size = 0; + + for( crtc = 0; crtc < gwm_screens[ window->screen ].num_crtcs; + crtc++ ) { + struct gwm_crtc *c = gwm_screens[ window->screen ].crtcs[ crtc ]; + int overlap; + + if( x + width <= c->x || x >= c->x + c->width || + y + height <= c->y || y >= c->y + c->height ) + overlap = 0; + else { + int l, r, t, b; + + l = x < c->x ? c->x : x; + r = x + width > c->x + c->width ? c->x + c->width : x + width; + t = y < c->y ? c->y : y; + b = y + height > c->y + c->height ? c->y + c->height : + y + height; + + overlap = ( r - l ) * ( b - t ); + } + + if( overlap > best_overlap || + ( overlap == best_overlap && + c->width * c->height > best_size ) ) { + best_crtc = crtc; + best_overlap = overlap; + best_size = c->width * c->height; + } + } + + x = gwm_screens[ window->screen ].crtcs[ best_crtc ]->x; + y = gwm_screens[ window->screen ].crtcs[ best_crtc ]->y; + width = gwm_screens[ window->screen ].crtcs[ best_crtc ]->width; + height = gwm_screens[ window->screen ].crtcs[ best_crtc ]->height; + } else +#endif + { + x = y = 0; + width = screens[ window->screen ]->width_in_pixels; + height = screens[ window->screen ]->height_in_pixels; + } + + /* If the size we came up with violates the client's min or max size, + give up and revert to unzoomed. We'll have to ignore increment and + aspect ratio constraints, since we really have no freedom to attempt + to satisfy those. */ + if( width < window->u.frame.child->u.managed.min_width || + width > window->u.frame.child->u.managed.max_width || + height < window->u.frame.child->u.managed.min_height || + height > window->u.frame.child->u.managed.max_height ) { + window->u.frame.zoom_state = 0; + set_managed_net_state( window->u.frame.child, + atoms[ ATOM__NET_WM_STATE_FULLSCREEN ], + NET_STATE_REMOVE ); + set_managed_net_state( window->u.frame.child, + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ], + NET_STATE_REMOVE ); + + return; + } + + if( window->u.frame.decoration & DEC_TITLE ) + xcb_unmap_window( c, window->u.frame.button->w ); + + window->u.frame.decoration = 0; + + values[ 0 ] = window->u.frame.x = x; + values[ 1 ] = window->u.frame.y = y; + values[ 2 ] = window->u.frame.width = width; + values[ 3 ] = window->u.frame.height = height; + xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X | + XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | + XCB_CONFIG_WINDOW_HEIGHT, values ); + + values[ 0 ] = values[ 1 ] = 0; + xcb_configure_window( c, window->u.frame.child->w, XCB_CONFIG_WINDOW_X | + XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | + XCB_CONFIG_WINDOW_HEIGHT, values ); +} + +static void leave_zoom( struct gwm_window *window ) { + + uint32_t values[ 4 ]; + int client_width, client_height; + + assert( window->type == WINDOW_FRAME ); + + if( window->u.frame.unzoomed_width > window->u.frame.width - 16 ) { + window->u.frame.x += 8; + window->u.frame.width -= 16; + } else { + window->u.frame.x = window->u.frame.unzoomed_x; + window->u.frame.width = window->u.frame.unzoomed_width; + } + + if( window->u.frame.unzoomed_height > window->u.frame.height - 16 ) { + window->u.frame.y += 8; + window->u.frame.height -= 16; + } else { + window->u.frame.y = window->u.frame.unzoomed_y; + window->u.frame.height = window->u.frame.unzoomed_height; + } + + window->u.frame.decoration = window->u.frame.unzoomed_decoration; + + client_width = window->u.frame.width - frame_l( window, FALSE ) - + frame_r( window, FALSE ); + client_height = window->u.frame.height - frame_t( window, FALSE ) - + frame_b( window, FALSE ); + apply_size_constraints( window->u.frame.child, &client_width, + &client_height ); + + window->u.frame.width = client_width + frame_l( window, FALSE ) + + frame_r( window, FALSE ); + window->u.frame.height = client_height + frame_t( window, FALSE ) + + frame_b( window, FALSE ); + + if( window->u.frame.decoration & DEC_TITLE ) + xcb_map_window( c, window->u.frame.button->w ); + + values[ 0 ] = window->u.frame.x; + values[ 1 ] = window->u.frame.y; + values[ 2 ] = window->u.frame.width; + values[ 3 ] = window->u.frame.height; + xcb_configure_window( c, window->w, XCB_CONFIG_WINDOW_X | + XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | + XCB_CONFIG_WINDOW_HEIGHT, values ); + + values[ 0 ] = frame_l( window, FALSE ); + values[ 1 ] = frame_t( window, FALSE ); + values[ 2 ] -= frame_l( window, FALSE ) + frame_r( window, FALSE ); + values[ 3 ] -= frame_t( window, FALSE ) + frame_b( window, FALSE ); + xcb_configure_window( c, window->u.frame.child->w, XCB_CONFIG_WINDOW_X | + XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | + XCB_CONFIG_WINDOW_HEIGHT, values ); +} + extern void managed_property_change( struct gwm_window *window, int prop, xcb_get_property_reply_t *p ) { @@ -266,7 +512,7 @@ extern void managed_property_change( struct gwm_window *window, int prop, int i; struct xcb_screen_t *screen; int value_len; - int old_decoration; + int dec, old_dec, old_zoom; int num_icons; int len; @@ -275,9 +521,8 @@ extern void managed_property_change( struct gwm_window *window, int prop, 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; + old_dec = window->u.managed.frame->u.frame.decoration; + dec = DEC_DEFAULT; if( p->format == 32 && p->value_len >= MOTIF_WM_HINTS_MIN_SIZE ) { p32 = xcb_get_property_value( p ); @@ -286,30 +531,25 @@ extern void managed_property_change( struct gwm_window *window, int prop, 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; + dec = 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; + dec &= ~DEC_BORDER; if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] & MOTIF_WM_HINTS_DEC_TITLE ) - window->u.managed.frame->u.frame.decoration &= - ~DEC_TITLE; + dec &= ~DEC_TITLE; } else { - window->u.managed.frame->u.frame.decoration = 0; + dec = 0; if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] & MOTIF_WM_HINTS_DEC_BORDER ) - window->u.managed.frame->u.frame.decoration |= - DEC_BORDER; + dec |= DEC_BORDER; if( p32[ MOTIF_WM_HINTS_DECORATIONS_OFF ] & MOTIF_WM_HINTS_DEC_TITLE ) - window->u.managed.frame->u.frame.decoration |= - DEC_TITLE; + dec |= DEC_TITLE; } } } @@ -317,24 +557,24 @@ extern void managed_property_change( struct gwm_window *window, int prop, #if USE_SHAPE if( window->u.managed.shaped ) /* Never apply borders to shaped windows. */ - window->u.managed.frame->u.frame.decoration &= ~DEC_BORDER; + dec &= ~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; + + if( window->u.managed.frame->u.frame.zoom_state ) + window->u.managed.frame->u.frame.unzoomed_decoration = dec; + else if( window->u.managed.state != STATE_NORMAL ) + window->u.managed.frame->u.frame.decoration = dec; + else if( dec != old_dec ) { 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; + window->u.managed.frame->u.frame.decoration = dec; translate_child_to_frame( window->u.managed.frame, &x, &y, &width, &height, x, y, @@ -351,7 +591,7 @@ extern void managed_property_change( struct gwm_window *window, int prop, window->u.managed.border_width, window->u.managed.win_gravity ); - if( new_decoration & DEC_TITLE ) + if( dec & DEC_TITLE ) xcb_map_window( c, window->u.managed.frame->u.frame.button->w ); else @@ -379,7 +619,7 @@ extern void managed_property_change( struct gwm_window *window, int prop, update_frame_extents( window->u.managed.frame ); } - + break; case PROP__NET_WM_ICON: @@ -441,6 +681,34 @@ extern void managed_property_change( struct gwm_window *window, int prop, } break; + + case PROP__NET_WM_STATE: + old_zoom = window->u.managed.frame->u.frame.zoom_state; + + window->u.managed.frame->u.frame.zoom_state = 0; + + if( p->format == 32 && p->value_len >= 1 ) { + p32 = xcb_get_property_value( p ); + + for( i = 0; i < p->value_len; i++ ) + if( p32[ i ] == atoms[ ATOM__NET_WM_STATE_FULLSCREEN ] ) + window->u.managed.frame->u.frame.zoom_state |= + ZOOM_FULLSCREEN; + else if( p32[ i ] == + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_HORZ ] || + p32[ i ] == + atoms[ ATOM__NET_WM_STATE_MAXIMIZED_VERT ] ) + window->u.managed.frame->u.frame.zoom_state |= + ZOOM_MAXIMISE; + } + + if( !old_zoom && window->u.managed.frame->u.frame.zoom_state ) + enter_zoom( window->u.managed.frame ); + + if( old_zoom && !window->u.managed.frame->u.frame.zoom_state ) + leave_zoom( window->u.managed.frame ); + + break; case PROP_WM_COLORMAP_WINDOWS: /* WM_COLORMAP_WINDOWS property (see ICCCM 2.0, section 4.1.8). */ diff --git a/managed.h b/managed.h index f222290..32a4303 100644 --- a/managed.h +++ b/managed.h @@ -6,6 +6,13 @@ extern void normal_to_iconic( struct gwm_window *window ); extern void set_managed_state( struct gwm_window *window, int state ); +#define NET_STATE_REMOVE 0 +#define NET_STATE_ADD 1 +#define NET_STATE_TOGGLE 2 + +extern void set_managed_net_state( struct gwm_window *window, + xcb_atom_t state, int action ); + #if USE_SHAPE extern void match_managed_shape( struct gwm_window *window ); #endif diff --git a/menu.c b/menu.c index 99d454f..b647445 100644 --- a/menu.c +++ b/menu.c @@ -168,7 +168,7 @@ static void fit_menu( int screen, int *x, int *y, int *width, int *height ) { #if USE_RANDR if( have_extension[ EXT_RANDR ] ) { int i; - int best_x = INT_MAX >> 1, best_y = 0, best_width = 0, best_height = 0; + int best_x = 0x4000000, best_y = 0, best_width = 0, best_height = 0; /* If the pointer falls outside all CRTCs, treat it as if it were moved into the closest. */ @@ -247,7 +247,7 @@ static void fit_menu( int screen, int *x, int *y, int *width, int *height ) { /* If there exists some CRTC which contains the pointer and could hold the entire rectangle if it were moved, move it into one of them. */ - best_x = INT_MAX >> 1; + best_x = 0x40000000; best_y = 0; for( i = 0; i < gwm_screens[ screen ].num_crtcs; i++ ) { struct gwm_crtc *c = gwm_screens[ screen ].crtcs[ i ]; @@ -281,7 +281,7 @@ static void fit_menu( int screen, int *x, int *y, int *width, int *height ) { } } - if( best_x < INT_MAX >> 1 ) { + if( best_x < 0x40000000 ) { *x = best_x; *y = best_y; -- 2.11.4.GIT