summaryrefslogtreecommitdiff
path: root/gwm.c
diff options
context:
space:
mode:
Diffstat (limited to 'gwm.c')
-rw-r--r--gwm.c395
1 files changed, 365 insertions, 30 deletions
diff --git a/gwm.c b/gwm.c
index 2f58a17..95cea02 100644
--- a/gwm.c
+++ b/gwm.c
@@ -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 )