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,