diff --git a/data/rc.xsd b/data/rc.xsd index 75dd660e8..7a2318959 100644 --- a/data/rc.xsd +++ b/data/rc.xsd @@ -176,6 +176,7 @@ + diff --git a/openbox/actions/growtoedge.c b/openbox/actions/growtoedge.c index d5a7bfdd9..0fb376062 100644 --- a/openbox/actions/growtoedge.c +++ b/openbox/actions/growtoedge.c @@ -8,9 +8,12 @@ typedef struct { ObDirection dir; gboolean shrink; + gboolean fill; + gboolean once; } Options; -static gpointer setup_func(xmlNodePtr node); +static gpointer setup_grow_func(xmlNodePtr node); +static gpointer setup_fill_func(xmlNodePtr node); static gpointer setup_shrink_func(xmlNodePtr node); static void free_func(gpointer o); static gboolean run_func(ObActionsData *data, gpointer options); @@ -22,7 +25,9 @@ static gpointer setup_west_func(xmlNodePtr node); void action_growtoedge_startup(void) { - actions_register("GrowToEdge", setup_func, + actions_register("GrowToEdge", setup_grow_func, + free_func, run_func); + actions_register("GrowToFill", setup_fill_func, free_func, run_func); actions_register("ShrinkToEdge", setup_shrink_func, free_func, run_func); @@ -40,7 +45,6 @@ static gpointer setup_func(xmlNodePtr node) o = g_slice_new0(Options); o->dir = OB_DIRECTION_NORTH; - o->shrink = FALSE; if ((n = obt_xml_find_node(node, "direction"))) { gchar *s = obt_xml_node_string(n); @@ -59,6 +63,31 @@ static gpointer setup_func(xmlNodePtr node) g_free(s); } + if ((n = obt_xml_find_node(node, "once"))) + o->once = obt_xml_node_bool(n); + + return o; +} + +static gpointer setup_grow_func(xmlNodePtr node) +{ + Options *o; + + o = setup_func(node); + o->shrink = FALSE; + o->fill = FALSE; + + return o; +} + +static gpointer setup_fill_func(xmlNodePtr node) +{ + Options *o; + + o = setup_func(node); + o->shrink = FALSE; + o->fill = TRUE; + return o; } @@ -68,6 +97,7 @@ static gpointer setup_shrink_func(xmlNodePtr node) o = setup_func(node); o->shrink = TRUE; + o->fill = FALSE; return o; } @@ -97,6 +127,58 @@ static gboolean do_grow(ObActionsData *data, gint x, gint y, gint w, gint h) return FALSE; } +static gboolean do_grow_all_edges(ObActionsData* data, + ObClientDirectionalResizeType resize_type) +{ + gint x, y, w, h; + gint temp_x, temp_y, temp_w, temp_h; + + client_find_resize_directional(data->client, + OB_DIRECTION_NORTH, + resize_type, + &temp_x, &temp_y, &temp_w, &temp_h); + y = temp_y; + h = temp_h; + + client_find_resize_directional(data->client, + OB_DIRECTION_SOUTH, + resize_type, + &temp_x, &temp_y, &temp_w, &temp_h); + h += temp_h - data->client->area.height; + + + client_find_resize_directional(data->client, + OB_DIRECTION_WEST, + resize_type, + &temp_x, &temp_y, &temp_w, &temp_h); + x = temp_x; + w = temp_w; + + client_find_resize_directional(data->client, + OB_DIRECTION_EAST, + resize_type, + &temp_x, &temp_y, &temp_w, &temp_h); + w += temp_w - data->client->area.width; + + /* When filling, we allow the window to move to an arbitrary x/y + position, since we'll be growing the other edge as well. */ + int lw, lh; + client_try_configure(data->client, &x, &y, &w, &h, &lw, &lh, TRUE); + + if (x == data->client->area.x && + y == data->client->area.y && + w == data->client->area.width && + h == data->client->area.height) + { + return FALSE; + } + + actions_client_move(data, TRUE); + client_move_resize(data->client, x, y, w, h); + actions_client_move(data, FALSE); + return TRUE; +} + static void free_func(gpointer o) { g_slice_free(Options, o); @@ -106,34 +188,70 @@ static void free_func(gpointer o) static gboolean run_func(ObActionsData *data, gpointer options) { Options *o = options; - gint x, y, w, h; - ObDirection opp; - gint half; - if (!data->client || - /* don't allow vertical resize if shaded */ - ((o->dir == OB_DIRECTION_NORTH || o->dir == OB_DIRECTION_SOUTH) && - data->client->shaded)) - { + if (!data->client) + return FALSE; + + gboolean doing_verical_resize = + o->dir == OB_DIRECTION_NORTH || + o->dir == OB_DIRECTION_SOUTH || + o->fill; + if (data->client->shaded && doing_verical_resize) + return FALSE; + + if (o->fill) { + if (o->shrink) { + /* We don't have any implementation of shrinking for the FillToGrow + action. */ + return FALSE; + } + + if (do_grow_all_edges(data, CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE)) + return FALSE; + + /* If all the edges are blocked, then allow them to jump past their + current block points. */ + if (!o->once) + do_grow_all_edges(data, CLIENT_RESIZE_GROW); + return FALSE; } if (!o->shrink) { - /* try grow */ - client_find_resize_directional(data->client, o->dir, TRUE, + gint x, y, w, h; + + /* Try grow. */ + if (o->once) + client_find_resize_directional(data->client, + o->dir, + CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE, &x, &y, &w, &h); + else + client_find_resize_directional(data->client, + o->dir, + CLIENT_RESIZE_GROW, + &x, &y, &w, &h); + if (do_grow(data, x, y, w, h)) return FALSE; } - /* we couldn't grow, so try shrink! */ - opp = (o->dir == OB_DIRECTION_NORTH ? OB_DIRECTION_SOUTH : - (o->dir == OB_DIRECTION_SOUTH ? OB_DIRECTION_NORTH : - (o->dir == OB_DIRECTION_EAST ? OB_DIRECTION_WEST : - OB_DIRECTION_EAST))); - client_find_resize_directional(data->client, opp, FALSE, + /* We couldn't grow, so try shrink! */ + ObDirection opposite = + (o->dir == OB_DIRECTION_NORTH ? OB_DIRECTION_SOUTH : + (o->dir == OB_DIRECTION_SOUTH ? OB_DIRECTION_NORTH : + (o->dir == OB_DIRECTION_EAST ? OB_DIRECTION_WEST : + OB_DIRECTION_EAST))); + + gint x, y, w, h; + gint half; + + client_find_resize_directional(data->client, + opposite, + CLIENT_RESIZE_SHRINK, &x, &y, &w, &h); - switch (opp) { + + switch (opposite) { case OB_DIRECTION_NORTH: half = data->client->area.y + data->client->area.height / 2; if (y > half) { @@ -172,6 +290,8 @@ static gpointer setup_north_func(xmlNodePtr node) Options *o = g_slice_new0(Options); o->shrink = FALSE; o->dir = OB_DIRECTION_NORTH; + o->once = FALSE; + return o; } @@ -180,6 +300,8 @@ static gpointer setup_south_func(xmlNodePtr node) Options *o = g_slice_new0(Options); o->shrink = FALSE; o->dir = OB_DIRECTION_SOUTH; + o->once = FALSE; + return o; } @@ -188,6 +310,8 @@ static gpointer setup_east_func(xmlNodePtr node) Options *o = g_slice_new0(Options); o->shrink = FALSE; o->dir = OB_DIRECTION_EAST; + o->once = FALSE; + return o; } @@ -196,5 +320,7 @@ static gpointer setup_west_func(xmlNodePtr node) Options *o = g_slice_new0(Options); o->shrink = FALSE; o->dir = OB_DIRECTION_WEST; + o->once = FALSE; + return o; } diff --git a/openbox/client.c b/openbox/client.c index c97abd5ac..5e6569e0e 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -4535,8 +4535,9 @@ void client_find_move_directional(ObClient *self, ObDirection dir, frame_frame_gravity(self->frame, x, y); } -void client_find_resize_directional(ObClient *self, ObDirection side, - gboolean grow, +void client_find_resize_directional(ObClient *self, + ObDirection side, + ObClientDirectionalResizeType resize_type, gint *x, gint *y, gint *w, gint *h) { gint head; @@ -4544,31 +4545,84 @@ void client_find_resize_directional(ObClient *self, ObDirection side, gboolean near; ObDirection dir; + gboolean grow; + switch (resize_type) { + case CLIENT_RESIZE_GROW: + grow = TRUE; + break; + case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE: + grow = TRUE; + break; + case CLIENT_RESIZE_SHRINK: + grow = FALSE; + break; + } + switch (side) { case OB_DIRECTION_EAST: - head = RECT_RIGHT(self->frame->area) + - (self->size_inc.width - 1) * (grow ? 1 : 0); + head = RECT_RIGHT(self->frame->area); + switch (resize_type) { + case CLIENT_RESIZE_GROW: + head += self->size_inc.width - 1; + break; + case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE: + head -= 1; + break; + case CLIENT_RESIZE_SHRINK: + break; + } + e_start = RECT_TOP(self->frame->area); e_size = self->frame->area.height; dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST; break; case OB_DIRECTION_WEST: - head = RECT_LEFT(self->frame->area) - - (self->size_inc.width - 1) * (grow ? 1 : 0); + head = RECT_LEFT(self->frame->area); + switch (resize_type) { + case CLIENT_RESIZE_GROW: + head -= self->size_inc.width - 1; + break; + case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE: + head += 1; + break; + case CLIENT_RESIZE_SHRINK: + break; + } + e_start = RECT_TOP(self->frame->area); e_size = self->frame->area.height; dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST; break; case OB_DIRECTION_NORTH: - head = RECT_TOP(self->frame->area) - - (self->size_inc.height - 1) * (grow ? 1 : 0); + head = RECT_TOP(self->frame->area); + switch (resize_type) { + case CLIENT_RESIZE_GROW: + head -= self->size_inc.height - 1; + break; + case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE: + head += 1; + break; + case CLIENT_RESIZE_SHRINK: + break; + } + e_start = RECT_LEFT(self->frame->area); e_size = self->frame->area.width; dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH; break; case OB_DIRECTION_SOUTH: - head = RECT_BOTTOM(self->frame->area) + - (self->size_inc.height - 1) * (grow ? 1 : 0); + head = RECT_BOTTOM(self->frame->area); + switch (resize_type) { + case CLIENT_RESIZE_GROW: + head += self->size_inc.height - 1; + break; + case CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE: + head -= 1; + break; + case CLIENT_RESIZE_SHRINK: + break; + } + e_start = RECT_LEFT(self->frame->area); e_size = self->frame->area.width; dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH; @@ -4607,7 +4661,7 @@ void client_find_resize_directional(ObClient *self, ObDirection side, if (grow == near) --e; delta = e - RECT_BOTTOM(self->frame->area); *h += delta; - break; + break; default: g_assert_not_reached(); } diff --git a/openbox/client.h b/openbox/client.h index 5ae2d3d2f..11a014000 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -489,8 +489,17 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, gint *dest, gboolean *near_edge); void client_find_move_directional(ObClient *self, ObDirection dir, gint *x, gint *y); -void client_find_resize_directional(ObClient *self, ObDirection side, - gboolean grow, + +typedef enum { + CLIENT_RESIZE_GROW, + CLIENT_RESIZE_GROW_IF_NOT_ON_EDGE, + CLIENT_RESIZE_SHRINK, +} ObClientDirectionalResizeType; + +/*! Moves the client area passed in to grow/shrink the given edge. */ +void client_find_resize_directional(ObClient *self, + ObDirection side, + ObClientDirectionalResizeType resize_type, gint *x, gint *y, gint *w, gint *h); /*! Fullscreen's or unfullscreen's the client window diff --git a/openbox/moveresize.c b/openbox/moveresize.c index 333a1bea5..d12a64de5 100644 --- a/openbox/moveresize.c +++ b/openbox/moveresize.c @@ -795,8 +795,13 @@ static void resize_with_keys(KeySym sym, guint state) else /* if (sym == XK_Up)) */ dir = OB_DIRECTION_NORTH; - client_find_resize_directional(moveresize_client, key_resize_edge, - key_resize_edge == dir, + ObClientDirectionalResizeType resize_type = + key_resize_edge == dir ? CLIENT_RESIZE_GROW + : CLIENT_RESIZE_SHRINK; + + client_find_resize_directional(moveresize_client, + key_resize_edge, + resize_type, &x, &y, &w, &h); dw = w - moveresize_client->area.width; dh = h - moveresize_client->area.height; diff --git a/openbox/place_overlap.c b/openbox/place_overlap.c index ac7255bf0..3725b24f1 100644 --- a/openbox/place_overlap.c +++ b/openbox/place_overlap.c @@ -19,6 +19,7 @@ #include "config.h" #include "geom.h" #include "place_overlap.h" +#include "obt/bsearch.h" #include @@ -170,29 +171,23 @@ static int total_overlap(const Rect* client_rects, return overlap; } -/* Unfortunately, the libc bsearch() function cannot be used to find the - position of a value that is not in the array, and glib doesn't - provide a binary search function at all. So, tricky as it is, if we - want to avoid linear scan of the edge array, we have to roll our - own. */ -static int grid_position(int value, - const int* edges, - int max_edges) +static int find_first_grid_position_greater_or_equal(int search_value, + const int* edges, + int max_edges) { - int low = 0; - int high = max_edges - 1; - int mid = low + (high - low) / 2; - while (low != mid) { - if (value < edges[mid]) - high = mid; - else if (value > edges[mid]) - low = mid; - else /* value == edges[mid] */ - return mid; - mid = low + (high - low) / 2; - } - /* we get here when low == mid. can have low == high or low == high - 1 */ - return (value <= edges[low] ? low : high); + g_assert(max_edges >= 2); + g_assert(search_value >= edges[0]); + g_assert(search_value <= edges[max_edges - 1]); + + BSEARCH_SETUP(); + BSEARCH(int, edges, 0, max_edges, search_value); + + if (BSEARCH_FOUND()) + return BSEARCH_AT(); + + g_assert(BSEARCH_FOUND_NEAREST_SMALLER()); + /* Get the nearest larger instead. */ + return BSEARCH_AT() + 1; } static void expand_width(Rect* r, int by) @@ -263,9 +258,11 @@ static void center_in_field(Point* top_left, { /* Find minimal rectangle. */ int orig_right_edge_index = - grid_position(top_left->x + req_size->width, x_edges, max_edges); + find_first_grid_position_greater_or_equal( + top_left->x + req_size->width, x_edges, max_edges); int orig_bottom_edge_index = - grid_position(top_left->y + req_size->height, y_edges, max_edges); + find_first_grid_position_greater_or_equal( + top_left->y + req_size->height, y_edges, max_edges); ExpandInfo i = { .top_left = top_left, .orig_width = x_edges[orig_right_edge_index] - top_left->x,