diff options
author | Gary Wong <gtw@gnu.org> | 2010-11-09 12:12:44 -0700 |
---|---|---|
committer | Gary Wong <gtw@gnu.org> | 2012-01-30 13:20:12 -0700 |
commit | dfb575033ef2138bec13c0b08107e2ff7f30573f (patch) | |
tree | af67d38e2f394c0437dfdc20b69c9ec825babd9a | |
parent | 4c373f89d60630cc69e6ab4156734f1b3a431aec (diff) |
Add support for RANDR extension.
Track screen and CRTC geometry changes, and use sensible window and menu
placement policies when multiple CRTCs are present.
-rw-r--r-- | ChangeLog | 16 | ||||
-rw-r--r-- | actions.c | 59 | ||||
-rw-r--r-- | button.c | 6 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | frame.c | 78 | ||||
-rw-r--r-- | gwm.c | 395 | ||||
-rw-r--r-- | gwm.h | 32 | ||||
-rw-r--r-- | managed.c | 3 | ||||
-rw-r--r-- | menu.c | 209 | ||||
-rw-r--r-- | root.c | 57 |
10 files changed, 770 insertions, 88 deletions
@@ -1,3 +1,19 @@ +2010-11-09 Gary Wong <gtw@gnu.org> + + * gwm.c (show_error) [USE_RANDR]: Show RANDR errors. + (update_crtc, handle_crtc_info) [USE_RANDR]: New functions. + (setup_display) [USE_RANDR]: Query CRTC information, if possible. + * root.c (root_configure_notify): Handle screen size changes. + (root_rr_crtc_change_notify) [USE_RANDR]: New function. + + * frame.c (build_edges) [USE_RANDR]: Add CRTC edges to list. + * menu.c (fit_menu): New function. + * gwm.c (point_live, make_area_live): New functions. + (place_window) [USE_RANDR]: Keep windows within a CRTC, if possible. + + * actions.c (window_list_enter): Make windows live upon selection. + (window_list_leave): Restore window position if it had been made live. + 2010-11-05 Gary Wong <gtw@gnu.org> * decorate-core.c (core_update_window): Ignore childless frames. @@ -182,7 +182,7 @@ extern void action_window_menu( struct gwm_window *window, } static xcb_window_t place_holder; -static int window_was_iconic; +static int window_was_iconic, window_was_dead, dead_x, dead_y; static void window_list_activate( struct gwm_window *window, xcb_generic_event_t *ev, @@ -201,7 +201,8 @@ static void window_list_enter( struct gwm_window *window, struct gwm_window *client; if( ( client = lookup_window( cp.l ) ) ) { - uint32_t values[ 2 ]; + uint32_t values[ 4 ]; + int live_x, live_y; /* Introduce another (unmapped and otherwise unused) window into the stack to mark the original position of the client in the stacking @@ -221,10 +222,28 @@ static void window_list_enter( struct gwm_window *window, if( ( window_was_iconic = client->u.managed.state == STATE_ICONIC ) ) iconic_to_normal( client ); - /* Raise the client window to immediately below the menu. */ - values[ 0 ] = pointer_demux; /* the menu */ - values[ 1 ] = XCB_STACK_MODE_BELOW; + live_x = dead_x = client->u.managed.frame->u.frame.x; + live_y = dead_y = client->u.managed.frame->u.frame.y; + + make_area_live( client->screen, &live_x, &live_y, + client->u.managed.frame->u.frame.width, + client->u.managed.frame->u.frame.height, 8, 8 ); + + if( ( window_was_dead = live_x != dead_x || live_y != dead_y ) ) { + values[ 0 ] = live_x; + values[ 1 ] = live_y; + values[ 2 ] = pointer_demux; /* the menu */ + values[ 3 ] = XCB_STACK_MODE_BELOW; + } else { + values[ 0 ] = pointer_demux; /* the menu */ + values[ 1 ] = XCB_STACK_MODE_BELOW; + } + xcb_configure_window( c, client->u.managed.frame->w, + window_was_dead ? XCB_CONFIG_WINDOW_X | + XCB_CONFIG_WINDOW_Y | + XCB_CONFIG_WINDOW_SIBLING | + XCB_CONFIG_WINDOW_STACK_MODE : XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values ); @@ -245,16 +264,38 @@ static void window_list_leave( struct gwm_window *window, struct gwm_window *client; if( ( client = lookup_window( cp.l ) ) ) { - uint32_t values[ 2 ]; - - values[ 0 ] = place_holder; - values[ 1 ] = XCB_STACK_MODE_ABOVE; + uint32_t values[ 4 ]; + + if( window_was_dead ) { + values[ 0 ] = dead_x; + values[ 1 ] = dead_y; + values[ 2 ] = place_holder; + values[ 3 ] = XCB_STACK_MODE_ABOVE; + } else { + values[ 0 ] = place_holder; + values[ 1 ] = XCB_STACK_MODE_ABOVE; + } xcb_configure_window( c, client->u.managed.frame->w, + window_was_dead ? + XCB_CONFIG_WINDOW_X | + XCB_CONFIG_WINDOW_Y | + XCB_CONFIG_WINDOW_SIBLING | + XCB_CONFIG_WINDOW_STACK_MODE : XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values ); if( window_was_iconic ) normal_to_iconic( client ); + + if( focus_frame == client->u.managed.frame ) { + deactivate_focus_frame(); + + xcb_set_input_focus( c, XCB_INPUT_FOCUS_NONE, + XCB_INPUT_FOCUS_POINTER_ROOT, + event_time( ev ) ); + + focus_frame = NULL; + } } if( place_holder ) { @@ -156,7 +156,7 @@ static void button_leave_notify( struct gwm_window *window, set_button_active( window, FALSE ); } -const event_handler button_handlers[] = { +const event_handler button_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ NULL, /* KeyPress */ @@ -192,5 +192,7 @@ const event_handler button_handlers[] = { NULL, /* ColormapNotify */ NULL, /* ClientMessage */ NULL, /* MappingNotify */ - NULL /* (synthetic) */ + NULL, /* (synthetic) */ + NULL, /* RRScreenChangeNotify */ + NULL /* ShapeNotify */ }; diff --git a/configure.ac b/configure.ac index 7342276..e6903f2 100644 --- a/configure.ac +++ b/configure.ac @@ -56,12 +56,14 @@ PKG_PROG_PKG_CONFIG # Checks for libraries: AC_ARG_WITH(composite,[ --with-composite use the X Composite extension.]) AC_ARG_WITH(damage, [ --with-damage use the X DAMAGE extension.]) +AC_ARG_WITH(randr, [ --with-randr use the X RANDR extension.]) AC_ARG_WITH(render, [ --with-render use the X RENDER extension.]) AC_ARG_WITH(shape, [ --with-shape use the X SHAPE extension.]) AC_ARG_WITH(xfixes, [ --with-xfixes use the XFIXES extension.]) AH_TEMPLATE(USE_COMPOSITE,[Use the X Composite extension.]) AH_TEMPLATE(USE_DAMAGE,[Use the X DAMAGE extension.]) +AH_TEMPLATE(USE_RANDR,[Use the X RANDR extension.]) AH_TEMPLATE(USE_RENDER,[Use the X RENDER extension.]) AH_TEMPLATE(USE_SHAPE,[Use the X SHAPE extension.]) AH_TEMPLATE(USE_XFIXES,[Use the XFIXES extension.]) @@ -71,6 +73,7 @@ GWM_OPTIONAL_PACKAGE(fontconfig) GWM_OPTIONAL_PACKAGE(freetype2) GWM_OPTIONAL_PACKAGE(xcb-composite, composite) GWM_OPTIONAL_PACKAGE(xcb-damage, damage) +GWM_OPTIONAL_PACKAGE(xcb-randr, randr) GWM_OPTIONAL_PACKAGE(xcb-render, render, fontconfig freetype2) GWM_OPTIONAL_PACKAGE(xcb-shape, shape) GWM_OPTIONAL_PACKAGE(xcb-xfixes, xfixes) @@ -439,30 +439,60 @@ static int v_edge_compare( const void *v0, const void *v1 ) { static void build_edges( int screen ) { struct gwm_window *window, **windowp, **end; + int num_crtcs; assert( !t_edges ); assert( !b_edges ); assert( !l_edges ); assert( !r_edges ); - t_edges = xmalloc( ( windows.used + 1 ) * sizeof *t_edges ); - b_edges = xmalloc( ( windows.used + 1 ) * sizeof *b_edges ); - l_edges = xmalloc( ( windows.used + 1 ) * sizeof *l_edges ); - r_edges = xmalloc( ( windows.used + 1 ) * sizeof *r_edges ); - - t_edges[ 0 ].x_min = b_edges[ 0 ].x_min = 0; - t_edges[ 0 ].x_max = b_edges[ 0 ].x_max = - screens[ screen ]->width_in_pixels; - t_edges[ 0 ].y = screens[ screen ]->height_in_pixels; - b_edges[ 0 ].y = 0; +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) + num_crtcs = gwm_screens[ screen ].num_crtcs; + else +#endif + num_crtcs = 1; - l_edges[ 0 ].x = screens[ screen ]->width_in_pixels; - r_edges[ 0 ].x = 0; - l_edges[ 0 ].y_min = r_edges[ 0 ].y_min = 0; - l_edges[ 0 ].y_max = r_edges[ 0 ].y_max = - screens[ screen ]->height_in_pixels; + t_edges = xmalloc( ( windows.used + num_crtcs ) * sizeof *t_edges ); + b_edges = xmalloc( ( windows.used + num_crtcs ) * sizeof *b_edges ); + l_edges = xmalloc( ( windows.used + num_crtcs ) * sizeof *l_edges ); + r_edges = xmalloc( ( windows.used + num_crtcs ) * sizeof *r_edges ); + +#if USE_RANDR + if( have_extension[ EXT_RANDR ] ) { + int i; + struct gwm_crtc **crtcs = gwm_screens[ screen ].crtcs; + + for( i = 0; i < num_crtcs; i++ ) { + t_edges[ i ].x_min = b_edges[ i ].x_min = crtcs[ i ]->x; + t_edges[ i ].x_max = b_edges[ i ].x_max = crtcs[ i ]->x + + crtcs[ i ]->width; + t_edges[ i ].y = crtcs[ i ]->y + crtcs[ i ]->height; + b_edges[ i ].y = crtcs[ i ]->y; + + l_edges[ i ].x = crtcs[ i ]->x + crtcs[ i ]->width; + r_edges[ i ].x = crtcs[ i ]->x; + l_edges[ i ].y_min = r_edges[ i ].y_min = crtcs[ i ]->y; + l_edges[ i ].y_max = r_edges[ i ].y_max = crtcs[ i ]->y + + crtcs[ i ]->height; + } + } else +#endif + { + t_edges[ 0 ].x_min = b_edges[ 0 ].x_min = 0; + t_edges[ 0 ].x_max = b_edges[ 0 ].x_max = + screens[ screen ]->width_in_pixels; + t_edges[ 0 ].y = screens[ screen ]->height_in_pixels; + b_edges[ 0 ].y = 0; + + l_edges[ 0 ].x = screens[ screen ]->width_in_pixels; + r_edges[ 0 ].x = 0; + l_edges[ 0 ].y_min = r_edges[ 0 ].y_min = 0; + l_edges[ 0 ].y_max = r_edges[ 0 ].y_max = + screens[ screen ]->height_in_pixels; + } - num_t_edges = num_b_edges = num_l_edges = num_r_edges = 1; + num_t_edges = num_b_edges = num_l_edges = num_r_edges = num_crtcs; end = windows.values + windows.used; for( windowp = windows.values; windowp < end; windowp++ ) @@ -867,7 +897,11 @@ static void frame_motion_notify( struct gwm_window *window, } else return; } - + + if( !point_live( window->screen, ev->root_x, ev->root_y ) ) + /* Ignore motion events outside all CRTCs. */ + return; + switch( window_op ) { int old_t, old_b, old_l, old_r, new_t, new_b, new_l, new_r; @@ -1146,7 +1180,7 @@ static void frame_configure_request( struct gwm_window *window, synthetic_configure_notify( window ); } -const event_handler frame_handlers[] = { +const event_handler frame_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ NULL, /* KeyPress */ @@ -1183,10 +1217,11 @@ const event_handler frame_handlers[] = { NULL, /* ClientMessage */ NULL, /* MappingNotify */ NULL, /* (synthetic) */ - NULL /* ShapeNotify */ + NULL, /* RRNotify */ + NULL /* ShapeNotify */ }; -const event_handler childless_handlers[] = { +const event_handler childless_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ NULL, /* KeyPress */ @@ -1223,5 +1258,6 @@ const event_handler childless_handlers[] = { NULL, /* ClientMessage */ NULL, /* MappingNotify */ NULL, /* (synthetic) */ - NULL /* ShapeNotify */ + NULL, /* RRNotify */ + NULL /* ShapeNotify */ }; @@ -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 ) @@ -49,7 +49,9 @@ extern MALLOC void *xcalloc( size_t number, size_t size ); enum _extended_event_type { SYNTHETIC_EVENT = XCB_MAPPING_NOTIFY + 1, - SHAPE_NOTIFY + RANDR_CRTC_CHANGE_NOTIFY, + SHAPE_NOTIFY, + NUM_EXTENDED_EVENTS }; extern xcb_connection_t *c; @@ -124,6 +126,9 @@ enum gwm_extension { #if USE_DAMAGE EXT_DAMAGE, #endif +#if USE_RANDR + EXT_RANDR, +#endif #if USE_RENDER EXT_RENDER, #endif @@ -136,7 +141,8 @@ enum gwm_extension { NUM_EXTENSIONS }; -#if USE_COMPOSITE || USE_DAMAGE || USE_RENDER || USE_SHAPE || USE_FIXES +#if USE_COMPOSITE || USE_DAMAGE || USE_RANDR || USE_RENDER || USE_SHAPE || \ + USE_XFIXES #define EXTENSIONS_SIZE NUM_EXTENSIONS #else #define EXTENSIONS_SIZE 1 /* avoid zero length arrays */ @@ -166,12 +172,23 @@ enum decoration_col { extern int num_screens; extern xcb_screen_t **screens; +#if USE_RANDR +struct gwm_crtc { + uint32_t crtc; + int x, y, width, height; +}; +#endif + struct gwm_screen { xcb_visualtype_t *root_visual; xcb_atom_t wm_atom; /* the atom WM_Sn, where n is the screen number */ 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_RANDR + struct gwm_crtc **crtcs; + int num_crtcs; +#endif }; extern struct gwm_screen *gwm_screens; @@ -360,6 +377,14 @@ extern CONST int initial_press( xcb_button_press_event_t *ev ); terminate a passive grab). */ extern CONST int final_release( xcb_button_release_event_t *ev ); +/* Return TRUE iff a point is "live" (within at least one CRTC). */ +extern int point_live( int screen, int x, int y ); + +/* Move an area the shortest Manhattan distance so that at an area of at + least min_x x min_y pixels fall within at least one CRTC, if possible. */ +extern void make_area_live( int screen, int *x, int *y, int width, int height, + int min_x, int min_y ); + extern CONST xcb_timestamp_t event_time( xcb_generic_event_t *ev ); /* The window whose event handler(s) should be invoked for pointer events. @@ -372,6 +397,9 @@ extern xcb_window_t pointer_demux; extern void install_window_colormap( int screen, struct gwm_window *window, xcb_timestamp_t t ); +extern void update_crtc( int screen, int crtc, int width, int height, + int x, int y ); + extern void generic_expose( struct gwm_window *window, xcb_expose_event_t *ev ); @@ -795,7 +795,7 @@ static void managed_shape_notify( struct gwm_window *window, } #endif -const event_handler managed_handlers[] = { +const event_handler managed_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ NULL, /* KeyPress */ @@ -832,6 +832,7 @@ const event_handler managed_handlers[] = { NULL, /* ClientMessage */ NULL, /* MappingNotify */ NULL, /* (synthetic) */ + NULL, /* RRNotify */ #if USE_SHAPE (event_handler) managed_shape_notify #else @@ -162,6 +162,185 @@ static void menu_leave_notify( struct gwm_window *window, deactivate_menu_item( window, ev ); } +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; + + /* If the pointer falls outside all CRTCs, treat it as if it were + moved into the closest. */ + for( i = 0; i < gwm_screens[ screen ].num_crtcs; i++ ) { + struct gwm_crtc *c = gwm_screens[ screen ].crtcs[ i ]; + int cx, cy; + + if( *x < c->x ) + cx = c->x; + else if( *x >= c->x + c->width ) + cx = c->x + c->width - 1; + else + cx = *x; + + if( *y < c->y ) + cy = c->y; + else if( *y >= c->y + c->height ) + cy = c->y + c->height - 1; + else + cy = *y; + + if( abs( cx - *x ) + abs( cy - *y ) < + abs( best_x - *x ) + abs( best_y - *y ) ) { + best_x = cx; + best_y = cy; + + if( cx == *x && cy == *y ) + break; + } + } + + *x = best_x; + *y = best_y; + + /* Limit the width to half that of the widest CRTC containing the + pointer. */ + 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 && + c->width >> 1 > best_width ) + best_width = c->width >> 1; + } + + if( best_width < 8 ) + best_width = 8; + if( best_width < *width ) + *width = best_width; + + /* Limit the height to that of the tallest CRTC containing the + pointer. */ + 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 && c->height > best_height ) + best_height = c->height; + } + + if( best_height < 8 ) + best_height = 8; + if( best_height < *height ) + *height = best_height; + + /* If there exists some CRTC which contains the pointer and + the entire rectangle, we've finished. */ + for( i = 0; i < gwm_screens[ screen ].num_crtcs; i++ ) { + struct gwm_crtc *c = gwm_screens[ screen ].crtcs[ i ]; + + if( *x >= c->x && *x + *width <= c->x + c->width && + *y >= c->y && *y + *height <= c->y + c->height ) + return; + } + + /* 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_y = 0; + 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 && *width <= c->width && + *y >= c->y && *y < c->y + c->height && *height <= c->height ) { + int cx, cy; + + if( *x < c->x ) + cx = c->x; + else if( *x + *width > c->x + c->width ) + cx = c->x + c->width - *width; + else + cx = *x; + + if( *y < c->y ) + cy = c->y; + else if( *y + *height > c->y + c->height ) + cy = c->y + c->height - *height; + else + cy = *y; + + if( abs( cx - *x ) + abs( cy - *y ) < + abs( best_x - *x ) + abs( best_y - *y ) ) { + best_x = cx; + best_y = cy; + + if( cx == *x && cy == *y ) + break; + } + } + } + + if( best_x < INT_MAX >> 1 ) { + *x = best_x; + *y = best_y; + + return; + } + + /* We can't do a good job, no matter what we try. Find the + CRTC which will hold as much of the rectangle as possible, + and move it there. Prefer to show the top and the left. */ + best_width = 0; + for( i = 0; i < gwm_screens[ screen ].num_crtcs; i++ ) { + struct gwm_crtc *c = gwm_screens[ screen ].crtcs[ i ]; + int cw = ( c->width > *width ? *width : c->width ) + + ( c->height > *height ? *height : c->height ); + + if( cw > best_width ) { + best_width = cw; + + if( *x + *width > c->x + c->width ) + best_x = c->x + c->width - *width; + else + best_x = *x; + + if( best_x < c->x ) + best_x = c->x; + + if( *y + *height > c->y + c->height ) + best_y = c->y + c->height - *height; + else + best_y = *y; + + if( best_y < c->y ) + best_y = c->y; + } + } + + *x = best_x; + *y = best_y; + + return; + } +#endif + + if( *width > screens[ screen ]->width_in_pixels >> 1 ) + *width = screens[ screen ]->width_in_pixels >> 1; + + if( *height > screens[ screen ]->height_in_pixels ) + *height = screens[ screen ]->height_in_pixels; + + if( *x + *width > screens[ screen ]->width_in_pixels ) + *x = screens[ screen ]->width_in_pixels - *width; + if( *x < 0 ) + *x = 0; + + if( *y + *height > screens[ screen ]->height_in_pixels ) + *y = screens[ screen ]->height_in_pixels - *height; + if( *y < 0 ) + *y = 0; +} + extern void popup_menu( struct gwm_window *window, xcb_generic_event_t *ev, int num_items, const struct menuitem *items ) { @@ -228,21 +407,11 @@ 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 ) - ev_x = 0; - - if( ev_y + height > screens[ screen ]->height_in_pixels ) - ev_y = screens[ screen ]->height_in_pixels - height; - if( ev_y < 0 ) - ev_y = 0; + menu->u.menu.width += 2; /* account for border */ + height += 2; + fit_menu( screen, &ev_x, &ev_y, &menu->u.menu.width, &height ); + menu->u.menu.width -= 2; + height -= 2; values[ 0 ] = gwm_screens[ screen ].pixels[ COL_BORDER ]; /* border pixel */ values[ 1 ] = TRUE; /* override redirect */ @@ -275,7 +444,7 @@ extern void popup_menu( struct gwm_window *window, xcb_generic_event_t *ev, pointer_demux = menu->w; } -const event_handler menu_handlers[] = { +const event_handler menu_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ NULL, /* KeyPress */ @@ -312,8 +481,9 @@ const event_handler menu_handlers[] = { NULL, /* ClientMessage */ NULL, /* MappingNotify */ NULL, /* (synthetic) */ - NULL /* ShapeNotify */ -}, menuitem_handlers[] = { + NULL, /* RRNotify */ + NULL /* ShapeNotify */ +}, menuitem_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ NULL, /* KeyPress */ @@ -350,5 +520,6 @@ const event_handler menu_handlers[] = { NULL, /* ClientMessage */ NULL, /* MappingNotify */ NULL, /* (synthetic) */ - NULL /* ShapeNotify */ + NULL, /* RRNotify */ + NULL /* ShapeNotify */ }; @@ -23,6 +23,9 @@ #include <config.h> +#if USE_RANDR +#include <xcb/randr.h> +#endif #include <xcb/xcb.h> #include "gwm.h" @@ -147,8 +150,35 @@ static void root_reparent_notify( struct gwm_window *window, static void root_configure_notify( struct gwm_window *window, xcb_configure_notify_event_t *ev ) { - stack_move_above( &window_stack, ev->window, ev->above_sibling ? - ev->above_sibling : window->w | STACK_END ); + if( ev->event == ev->window ) { + /* The root window itself was configured. */ + if( screens[ window->screen ]->width_in_pixels != + ev->width || + screens[ window->screen ]->height_in_pixels != + ev->height ) { + uint32_t values[ 4 ]; + + screens[ window->screen ]->width_in_pixels = ev->width; + screens[ window->screen ]->height_in_pixels = ev->height; + + values[ 0 ] = ev->width; + values[ 1 ] = ev->height; + xcb_change_property( c, XCB_PROP_MODE_REPLACE, ev->window, + atoms[ ATOM__NET_DESKTOP_GEOMETRY ], CARDINAL, + 32, 2, values ); + + values[ 0 ] = 0; + values[ 1 ] = 0; + values[ 2 ] = ev->width; + values[ 3 ] = ev->height; + xcb_change_property( c, XCB_PROP_MODE_REPLACE, ev->window, + atoms[ ATOM__NET_WORKAREA ], CARDINAL, 32, 4, + values ); + } + } else + /* A child of the root was configured. */ + stack_move_above( &window_stack, ev->window, ev->above_sibling ? + ev->above_sibling : window->w | STACK_END ); } extern void root_circulate_request( struct gwm_window *window, @@ -210,7 +240,21 @@ static void root_synthetic( struct gwm_window *root, } } -const event_handler root_handlers[] = { +#if USE_RANDR +static void root_rr_crtc_change_notify( struct gwm_window *window, + xcb_randr_notify_event_t *ev ) { + + int rotated = ev->u.cc.rotation & ( XCB_RANDR_ROTATION_ROTATE_90 | + XCB_RANDR_ROTATION_ROTATE_270 ); + + update_crtc( window->screen, ev->u.cc.crtc, + rotated ? ev->u.cc.height : ev->u.cc.width, + rotated ? ev->u.cc.width : ev->u.cc.height, + ev->u.cc.x, ev->u.cc.y ); +} +#endif + +const event_handler root_handlers[ NUM_EXTENDED_EVENTS ] = { NULL, /* Error */ NULL, /* Reply */ (event_handler) root_key_press, @@ -247,5 +291,10 @@ const event_handler root_handlers[] = { NULL, /* ClientMessage */ NULL, /* MappingNotify */ (event_handler) root_synthetic, - NULL /* ShapeNotify */ +#if USE_RANDR + (event_handler) root_rr_crtc_change_notify, /* RRCrtcChangeNotify */ +#else + NULL, /* RRCrtcChangeNotify */ +#endif + NULL /* ShapeNotify */ }; |