diff options
Diffstat (limited to 'gwm.c')
-rw-r--r-- | gwm.c | 395 |
1 files changed, 365 insertions, 30 deletions
@@ -53,6 +53,9 @@ #if USE_DAMAGE #include <xcb/damage.h> #endif +#if USE_RANDR +#include <xcb/randr.h> +#endif #if USE_RENDER #include <xcb/render.h> #endif @@ -431,6 +434,20 @@ extern void show_error( xcb_generic_error_t *error ) { break; } +#if USE_RANDR + if( have_extension[ EXT_RANDR ] && + error->error_code >= extension_error[ EXT_RANDR ] && + error->error_code <= extension_error[ EXT_RANDR ] + + XCB_RANDR_BAD_MODE ) { + static const char *const randr_strings[ XCB_RANDR_BAD_MODE + 1 ] = { + "Output", "Crtc", "Mode" + }; + + type = randr_strings[ error->error_code - + extension_error[ EXT_RANDR ] ]; + } +#endif + #if USE_RENDER if( have_extension[ EXT_RENDER ] && error->error_code >= extension_error[ EXT_RENDER ] && @@ -1013,6 +1030,88 @@ extern CONST xcb_timestamp_t event_time( xcb_generic_event_t *ev ) { } } +extern int point_live( int screen, int x, int y ) { + +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) { + int i; + + for( i = 0; i < gwm_screens[ screen ].num_crtcs; i++ ) { + struct gwm_crtc *c = gwm_screens[ screen ].crtcs[ i ]; + + if( x >= c->x && x < c->x + c->width && + y >= c->y && y < c->y + c->height ) + return TRUE; + } + + return FALSE; + } +#endif + + return x >= 0 && x < screens[ screen ]->width_in_pixels && + y >= 0 && y < screens[ screen ]->height_in_pixels; +} + +extern void make_area_live( int screen, int *x, int *y, int width, int height, + int min_x, int min_y ) { + + if( min_x < 0 || min_x > width ) + min_x = width; + + if( min_y < 0 || min_y > height ) + min_y = height; + +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) { + int i, best_x, best_y, best = INT_MAX; + + for( i = 0; i < gwm_screens[ screen ].num_crtcs; i++ ) { + struct gwm_crtc *c = gwm_screens[ screen ].crtcs[ i ]; + int xc = *x, yc = *y, diff; + + if( min_x ) { + if( *x >= c->x + c->width - min_x ) + xc = c->x + c->width - min_x - 1; + else if( *x < c->x && *x + width < c->x + min_x ) + xc = c->x + min_x - width; + } + + if( min_y ) { + if( *y >= c->y + c->height - min_y ) + yc = c->y + c->height - min_y - 1; + else if( *y < c->y && *y + height < c->y + min_y ) + yc = c->y + min_y - height; + } + + if( ( diff = abs( xc - *x ) + abs( yc - *y ) ) < best ) { + best_x = xc; + best_y = yc; + best = diff; + } + } + + *x = best_x; + *y = best_y; + + return; + } +#endif + + if( min_x ) { + if( *x >= screens[ screen ]->width_in_pixels - min_x ) + *x = screens[ screen ]->width_in_pixels - min_x - 1; + else if( *x < 0 && *x + width < min_x ) + *x = min_x - width; + } + + if( min_y ) { + if( *y >= screens[ screen ]->height_in_pixels - min_y ) + *y = screens[ screen ]->height_in_pixels - min_y - 1; + else if( *y < 0 && *y + height < min_y ) + *y = min_y - height; + } +} + static void stop_listening( struct gwm_window *window ) { uint32_t value; @@ -1036,26 +1135,77 @@ static void stop_listening( struct gwm_window *window ) { static void place_window( struct gwm_window *window, int *x, int *y, int width, int height ) { - int sx = screens[ window->screen ]->width_in_pixels - width; - int sy = screens[ window->screen ]->height_in_pixels - height; + int sx, sy, xoff, yoff; #if defined( UINT64_MAX ) || defined( uint64_t ) uint64_t hash; #else unsigned long hash; #endif - + +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) { + int i; + unsigned long *scores, chosen, total = 0; + + scores = alloca( gwm_screens[ window->screen ].num_crtcs * + sizeof *scores ); + + /* Search for CRTCs big enough to hold the window. */ + for( i = 0; i < gwm_screens[ window->screen ].num_crtcs; i++ ) { + struct gwm_crtc *c = gwm_screens[ window->screen ].crtcs[ i ]; + + if( width <= c->width && height <= c->height ) + total += c->width + c->height - width - height; + + scores[ i ] = total; + } + + if( total ) { + struct gwm_crtc *c; + + /* Choose from among the candidates. */ + hash = window->w * 0xC27E3B59; +#if defined( UINT64_MAX ) || defined( uint64_t ) || ULONG_MAX > 0xFFFFFFFFUL + hash ^= hash >> 32; +#endif + + chosen = hash % total; + + for( i = 0; scores[ i ] < chosen; i++ ) + ; + + c = gwm_screens[ window->screen ].crtcs[ i ]; + + xoff = c->x; + yoff = c->y; + sx = c->width - width; + sy = c->height - height; + } else { + /* No CRTC is big enough to hold the window. */ + xoff = yoff = 0; + sx = screens[ window->screen ]->width_in_pixels - width; + sy = screens[ window->screen ]->height_in_pixels - height; + } + } else +#endif + { + xoff = yoff = 0; + sx = screens[ window->screen ]->width_in_pixels - width; + sy = screens[ window->screen ]->height_in_pixels - height; + } + if( sx <= 0 ) - *x = 0; + *x = xoff; else { hash = window->w * 0x9B4A36D1; #if defined( UINT64_MAX ) || defined( uint64_t ) || ULONG_MAX > 0xFFFFFFFFUL hash ^= hash >> 32; #endif - *x = hash % sx; + *x = xoff + hash % sx; } if( sy <= 0 ) - *y = 0; + *y = yoff; else { hash = window->w * 0xA6E34925; #if defined( UINT64_MAX ) || defined( uint64_t ) || ULONG_MAX > 0xFFFFFFFFUL @@ -1130,23 +1280,8 @@ static void start_managing_window( struct gwm_window *window, frame->u.frame.y = y; } - /* Don't create frames entirely off-screen; ensure that at least 8 pixels - are within the root. */ - if( frame->u.frame.x > screens[ frame->screen ]->width_in_pixels - 8 && - frame->u.frame.x + frame->u.frame.width > - screens[ frame->screen ]->width_in_pixels ) - frame->u.frame.x = screens[ frame->screen ]->width_in_pixels - 8; - else if( frame->u.frame.x < 0 && - frame->u.frame.x + frame->u.frame.width < 8 ) - frame->u.frame.x = 8 - frame->u.frame.width; - - if( frame->u.frame.y > screens[ frame->screen ]->height_in_pixels - 8 && - frame->u.frame.y + frame->u.frame.height > - screens[ frame->screen ]->height_in_pixels ) - frame->u.frame.y = screens[ frame->screen ]->height_in_pixels - 8; - else if( frame->u.frame.y < 0 && - frame->u.frame.y + frame->u.frame.height < 8 ) - frame->u.frame.y = 8 - frame->u.frame.height; + make_area_live( frame->screen, &frame->u.frame.x, &frame->u.frame.y, + frame->u.frame.width, frame->u.frame.height, 8, 8 ); values[ 0 ] = gwm_screens[ frame->screen ].pixels[ COL_FRAME_INACTIVE ]; /* background pixel */ @@ -1522,7 +1657,7 @@ static void fake_selection_request( struct gwm_window *window, } } -static const event_handler fake_handlers[] = { +static const event_handler fake_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ NULL, /* KeyPress */ @@ -1559,8 +1694,9 @@ static const event_handler fake_handlers[] = { NULL, /* ClientMessage */ NULL, /* MappingNotify */ NULL, /* (synthetic) */ - NULL /* ShapeNotify */ -}, feedback_handlers[] = { + NULL, /* RRNotify */ + NULL /* ShapeNotify */ +}, feedback_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ NULL, /* KeyPress */ @@ -1596,7 +1732,9 @@ static const event_handler fake_handlers[] = { NULL, /* ColormapNotify */ NULL, /* ClientMessage */ NULL, /* MappingNotify */ - NULL /* (synthetic) */ + NULL, /* (synthetic) */ + NULL, /* RRNotify */ + NULL /* ShapeNotify */ }; static const event_handler *const handlers[] = { @@ -1605,6 +1743,93 @@ static const event_handler *const handlers[] = { childless_handlers }; +#if USE_RANDR +struct crtc_callback { + int screen; + int crtc; + int *ok; +}; + +extern void update_crtc( int screen, int crtc, int width, int height, + int x, int y ) { + + struct gwm_screen *s = gwm_screens + screen; + int i; + + if( width && height ) { + for( i = 0; i < s->num_crtcs; i++ ) + if( s->crtcs[ i ]->crtc == crtc ) + goto found; + + s->crtcs = xrealloc( s->crtcs, ++s->num_crtcs * sizeof s->crtcs ); + s->crtcs[ i ] = xmalloc( sizeof *s->crtcs[ i ] ); + + found: + s->crtcs[ i ]->crtc = crtc; + s->crtcs[ i ]->x = x; + s->crtcs[ i ]->y = y; + s->crtcs[ i ]->width = width; + s->crtcs[ i ]->height = height; + } else + for( i = 0; i < s->num_crtcs; i++ ) + if( s->crtcs[ i ]->crtc == crtc ) { + free( s->crtcs[ i ] ); + s->crtcs[ i ] = s->crtcs[ --s->num_crtcs ]; + s->crtcs = xrealloc( s->crtcs, s->num_crtcs * + sizeof s->crtcs ); + + return; + } +} + +static INIT void handle_crtc_info( unsigned int sequence, void *reply, + xcb_generic_error_t *error, + union callback_param cp ) { + + struct crtc_callback *cc = cp.p; + + if( error ) { + cc->ok = FALSE; + + free( error ); + } + + if( reply ) { + /* A fixed version of the xcb_randr_get_crtc_info_reply_t + definition. Unfortunately XCB's randr protocol was incorrect + until commit c8129e1f19ceb2002c9764b94f03564600f13a8f. Rather + than checking for a defective randr.h, we simply define the + correct structure here. */ + struct fixed_xcb_randr_get_crtc_info_reply_t { + uint8_t response_type; + uint8_t status; + uint16_t sequence; + uint32_t length; + xcb_timestamp_t timestamp; + int16_t x; + int16_t y; + uint16_t width; + uint16_t height; + uint32_t mode; + uint16_t rotation; + uint16_t rotations; + uint16_t num_outputs; + uint16_t num_possible_outputs; + } *r = reply; + + if( r->status == XCB_RANDR_SET_CONFIG_SUCCESS ) + update_crtc( cc->screen, cc->crtc, r->width, r->height, + r->x, r->y ); + else + *cc->ok = FALSE; + + free( reply ); + } + + free( cc ); +} +#endif + static INIT void setup_display( void ) { static INITD const xcb_extension_t *const extensions[ EXTENSIONS_SIZE ] = { @@ -1614,6 +1839,9 @@ static INIT void setup_display( void ) { #if USE_DAMAGE &xcb_damage_id, #endif +#if USE_RANDR + &xcb_randr_id, +#endif #if USE_RENDER &xcb_render_id, #endif @@ -1635,6 +1863,10 @@ static INIT void setup_display( void ) { xcb_client_message_event_t msg; xcb_void_cookie_t *cookies; xcb_query_tree_cookie_t *tree_cookies; +#if USE_RANDR + xcb_randr_query_version_cookie_t randr_cookie; + xcb_randr_get_screen_resources_cookie_t *resources_cookies; +#endif table_init( &windows ); table_init( &update_windows ); @@ -1663,6 +1895,9 @@ static INIT void setup_display( void ) { screen_owner_cookies = alloca( num_screens * sizeof *screen_owner_cookies ); cookies = alloca( num_screens * sizeof *cookies ); tree_cookies = alloca( num_screens * sizeof *tree_cookies ); +#if USE_RANDR + resources_cookies = alloca( num_screens * sizeof *resources_cookies ); +#endif for( i = 0; iter.rem; i++, xcb_screen_next( &iter ) ) { char wm_atom_str[ 16 ]; @@ -1706,6 +1941,12 @@ static INIT void setup_display( void ) { } } +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) + randr_cookie = xcb_randr_query_version( c, XCB_RANDR_MAJOR_VERSION, + XCB_RANDR_MINOR_VERSION ); +#endif + for( i = 0; i < NUM_ATOMS; i++ ) { xcb_intern_atom_reply_t *r = xcb_intern_atom_reply( c, atom_cookies[ i ], NULL ); @@ -1999,6 +2240,23 @@ static INIT void setup_display( void ) { sigprocmask( SIG_SETMASK, &oldsigs, NULL ); } while( 0 ); +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) { + xcb_randr_query_version_reply_t *randr_reply; + + if( ( randr_reply = xcb_randr_query_version_reply( c, randr_cookie, + NULL ) ) ) { + /* We need at least version 1.2 to obtain CRTC information. */ + have_extension[ EXT_RANDR ] = randr_reply->major_version > 1 || + ( randr_reply->major_version == 1 && + randr_reply->minor_version >= 2 ); + + free( randr_reply ); + } else + have_extension[ EXT_RANDR ] = 0; + } +#endif + msg.response_type = XCB_CLIENT_MESSAGE; msg.format = 32; msg.sequence = 0; @@ -2010,11 +2268,22 @@ static INIT void setup_display( void ) { msg.data.data32[ 4 ] = 0; n = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | + XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_OWNER_GRAB_BUTTON; for( i = 0; i < num_screens; i++ ) { +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) { + xcb_randr_select_input( c, screens[ i ]->root, + XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE ); + + resources_cookies[ i ] = + xcb_randr_get_screen_resources( c, screens[ i ]->root ); + } +#endif + /* Send StructureNotify ClientMessage (ICCCM 2.0, section 2.8). */ msg.window = screens[ i ]->root; msg.data.data32[ 1 ] = gwm_screens[ i ].wm_atom; @@ -2084,8 +2353,62 @@ static INIT void setup_display( void ) { get_modifier_mapping(); xcb_flush( c ); /* BLOCK */ - sync_with_callback( cookies[ num_screens - 1 ].sequence ); + sync_with_callback( tree_cookies[ num_screens - 1 ].sequence ); + +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) { + int i, ok; + unsigned int sequence; + for( i = 0; i < num_screens; i++ ) { + gwm_screens[ i ].num_crtcs = 0; + gwm_screens[ i ].crtcs = NULL; + } + + /* It's essential to have up to date CRTC information before + proceeding, because the placement policy when we adopt existing + windows depends on it. */ + for( i = 0; i < num_screens; i++ ) { + xcb_randr_get_screen_resources_reply_t *r; + retry_race: + r = xcb_randr_get_screen_resources_reply( c, resources_cookies[ i ], + NULL ); + + if( r ) { + int crtc; + union callback_param cp; + uint32_t *crtcs = xcb_randr_get_screen_resources_crtcs( r ); + + for( crtc = 0; crtc < r->num_crtcs; crtc++ ) { + struct crtc_callback *cc = xmalloc( sizeof *cc ); + + cc->screen = i; + cc->crtc = crtcs[ crtc ]; + cc->ok = &ok; + cp.p = cc; + handle_async_reply( sequence = xcb_randr_get_crtc_info( + c, crtcs[ crtc ], + r->config_timestamp ).sequence, + handle_crtc_info, cp ); + } + + free( r ); + } + + ok = TRUE; + sync_with_callback( sequence ); + if( !ok ) { + resources_cookies[ i ] = + xcb_randr_get_screen_resources( c, screens[ i ]->root ); + + xcb_flush( c ); /* BLOCK */ + + goto retry_race; + } + } + } +#endif + for( i = 0; i < num_screens; i++ ) { xcb_generic_error_t *error = xcb_request_check( c, cookies[ i ] ); xcb_query_tree_reply_t *r; @@ -2325,6 +2648,18 @@ static void handle_events( void ) { break; default: +#if USE_RANDR + if( event_base_type == extension_event[ EXT_RANDR ] + + XCB_RANDR_NOTIFY && + ( (xcb_randr_notify_event_t *) ev )->subCode == + XCB_RANDR_NOTIFY_CRTC_CHANGE ) { + w = ( (xcb_randr_notify_event_t *) ev )->u.cc.window; + latest_timestamp = + ( (xcb_randr_notify_event_t *) ev )->u.cc.timestamp; + event_type = RANDR_CRTC_CHANGE_NOTIFY; + break; + } +#endif #if USE_SHAPE if( event_base_type == extension_event[ EXT_SHAPE ] ) { w = ( (xcb_shape_notify_event_t *) ev )->affected_window; @@ -2333,7 +2668,7 @@ static void handle_events( void ) { } #endif warning( "Unknown event" ); - break; + continue; } if( ev->response_type & 0x80 ) |