diff options
Diffstat (limited to 'matchbox/mb-wm-layout.c')
-rw-r--r-- | matchbox/mb-wm-layout.c | 753 |
1 files changed, 753 insertions, 0 deletions
diff --git a/matchbox/mb-wm-layout.c b/matchbox/mb-wm-layout.c new file mode 100644 index 0000000..7e92998 --- /dev/null +++ b/matchbox/mb-wm-layout.c @@ -0,0 +1,753 @@ +#include "matchbox.h" +#include "mb-wm-client-input.h" + +static void +mb_wm_layout_real_update (MBWMLayout * layout); +static void +mb_wm_layout_real_layout_panels (MBWMLayout *layout, MBGeometry * avail_geom); +static void +mb_wm_layout_real_layout_input (MBWMLayout *layout, MBGeometry * avail_geom); +static void +mb_wm_layout_real_layout_free (MBWMLayout *layout, MBGeometry * avail_geom); +static void +mb_wm_layout_real_layout_fullscreen (MBWMLayout *layout, MBGeometry * avail_geom); + +static void +mb_wm_layout_class_init (MBWMObjectClass *klass) +{ + MBWMLayoutClass * layout_class = MB_WM_LAYOUT_CLASS (klass); + + layout_class->update = mb_wm_layout_real_update; + layout_class->layout_panels = mb_wm_layout_real_layout_panels; + layout_class->layout_input = mb_wm_layout_real_layout_input; + layout_class->layout_free = mb_wm_layout_real_layout_free; + layout_class->layout_fullscreen = mb_wm_layout_real_layout_fullscreen; + +#if MBWM_WANT_DEBUG + klass->klass_name = "MBWMLayout"; +#endif +} + +static void +mb_wm_layout_destroy (MBWMObject *this) +{ +} + +static int +mb_wm_layout_init (MBWMObject *this, va_list vap) +{ + MBWMLayout *layout = MB_WM_LAYOUT (this); + MBWMObjectProp prop; + MBWindowManager *wm = NULL; + + prop = va_arg(vap, MBWMObjectProp); + while (prop) + { + switch (prop) + { + case MBWMObjectPropWm: + wm = va_arg(vap, MBWindowManager *); + break; + default: + MBWMO_PROP_EAT (vap, prop); + } + + prop = va_arg(vap, MBWMObjectProp); + } + + MBWM_ASSERT (wm); + + layout->wm = wm; + + return 1; +} + +int +mb_wm_layout_class_type () +{ + static int type = 0; + + if (UNLIKELY(type == 0)) + { + static MBWMObjectClassInfo info = { + sizeof (MBWMLayoutClass), + sizeof (MBWMLayout), + mb_wm_layout_init, + mb_wm_layout_destroy, + mb_wm_layout_class_init + }; + + type = mb_wm_object_register_class (&info, MB_WM_TYPE_OBJECT, 0); + } + + return type; +} + +MBWMLayout* +mb_wm_layout_new (MBWindowManager *wm) +{ + MBWMLayout *layout; + + layout = MB_WM_LAYOUT (mb_wm_object_new (MB_WM_TYPE_LAYOUT, + MBWMObjectPropWm, wm, + NULL)); + return layout; +} + +Bool +mb_wm_layout_clip_geometry (MBGeometry *geom, + MBGeometry *min, + int flags) +{ + Bool changed = False; + + if (flags & SET_X) + if (geom->x < min->x || geom->x > min->x + min->width) + { + geom->x = min->x; + changed = True; + } + + if (flags & SET_Y) + if (geom->y < min->y || geom->y > min->y + min->height) + { + geom->y = min->y; + changed = True; + } + + if (flags & SET_WIDTH) + if (geom->x + geom->width > min->x + min->width) + { + int old_width = geom->width; + + geom->width = min->x + min->width - geom->x; + + /* + * if we are about to reduce the width, and are asked also to set the x + * coord, see if we can move the window left so it can be bigger. + */ + if ((flags & SET_X) && old_width > geom->width && geom->x > min->x) + { + int w_diff = old_width - geom->width; + int x = geom->x - w_diff; + int x_diff; + + if (x < min->x) + x = min->x; + + x_diff = geom->x - x; + + geom->x = x; + geom->width += x_diff; + } + + changed = True; + } + + if (flags & SET_HEIGHT) + if (geom->y + geom->height > min->y + min->height) + { + int old_height = geom->height; + + geom->height = min->y + min->height - geom->y; + + /* + * if we are about to reduce the height, and are asked also to set the + * y coord, see if we can move the window up so it can be bigger. + */ + if ((flags & SET_Y) && old_height > geom->height && geom->y > min->y) + { + int h_diff = old_height - geom->height; + int y = geom->y - h_diff; + int y_diff; + + if (y < min->y) + y = min->y; + + y_diff = geom->y - y; + + geom->y = y; + geom->height += y_diff; + } + changed = True; + } + + return changed; +} + +Bool +mb_wm_layout_maximise_geometry (MBGeometry *geom, + MBGeometry *max, + int flags) +{ + Bool changed = False; + + if (flags & SET_X && geom->x != max->x) + { + geom->x = max->x; + changed = True; + } + + if (flags & SET_Y && geom->y != max->y) + { + geom->y = max->y; + changed = True; + } + + if (flags & SET_WIDTH && geom->width != max->width) + { + geom->width = max->width; + changed = True; + } + + if (flags & SET_HEIGHT && geom->height != max->height) + { + geom->height = max->height; + changed = True; + } + + return changed; +} + +static void +mb_wm_layout_real_layout_panels (MBWMLayout *layout, MBGeometry * avail_geom) +{ + MBWindowManager *wm = layout->wm; + MBWindowManagerClient *client; + MBGeometry coverage; + Bool need_change; + + /* FIXME: need to enumerate by *age* in case multiple panels ? */ + mb_wm_stack_enumerate(wm, client) + if ((mb_wm_client_get_layout_hints(client) & LayoutPrefReserveEdgeNorth) && + (mb_wm_client_get_layout_hints(client) & LayoutPrefVisible)) + { + int flags = SET_Y; + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefFixedX)) + flags |= SET_X | SET_WIDTH; + + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, flags); + /* Too high */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_HEIGHT); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + /* FIXME: what if this returns False ? */ + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefOverlaps)) + { + avail_geom->y = coverage.y + coverage.height; + avail_geom->height = avail_geom->height - coverage.height; + } + } + + mb_wm_stack_enumerate(wm, client) + if ((mb_wm_client_get_layout_hints(client) & LayoutPrefReserveEdgeSouth) && + (mb_wm_client_get_layout_hints(client) & LayoutPrefVisible)) + { + int y; + + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefFixedX)) + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_X | SET_WIDTH); + else + need_change = False; + + y = avail_geom->y + avail_geom->height - coverage.height; + + if (y != coverage.y) + { + coverage.y = y; + need_change = True; + } + + /* Too high */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_HEIGHT); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefOverlaps)) + avail_geom->height = avail_geom->height - coverage.height; + } + + mb_wm_stack_enumerate(wm, client) + if ((mb_wm_client_get_layout_hints(client) & LayoutPrefReserveEdgeWest) && + (mb_wm_client_get_layout_hints(client) & LayoutPrefVisible)) + { + int flags = SET_X; + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefFixedY)) + flags |= SET_Y | SET_HEIGHT; + + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + flags); + /* Too wide */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefOverlaps)) + { + avail_geom->x = coverage.x + coverage.width; + avail_geom->width = avail_geom->width - coverage.width; + } + } + + + mb_wm_stack_enumerate(wm, client) + if ((mb_wm_client_get_layout_hints(client) & LayoutPrefReserveEdgeEast) && + (mb_wm_client_get_layout_hints(client) & LayoutPrefVisible)) + { + int x; + + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefFixedY)) + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_Y|SET_HEIGHT); + else + need_change = False; + + x = avail_geom->x + avail_geom->width - coverage.width; + + if (x != coverage.x) + { + coverage.x = x; + need_change = True; + } + + /* Too wide */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + if (!(mb_wm_client_get_layout_hints (client) & LayoutPrefOverlaps)) + avail_geom->width = avail_geom->width - coverage.width; + } +} + +static void +mb_wm_layout_real_layout_input (MBWMLayout *layout, MBGeometry * avail_geom) +{ + MBWindowManager *wm = layout->wm; + MBWindowManagerClient *client; + MBGeometry coverage; + Bool need_change; + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefReserveNorth|LayoutPrefVisible)) + { + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_X|SET_Y|SET_WIDTH); + + if (client->transient_for && + (client->transient_for->window->ewmh_state & + MBWMClientWindowEWMHStateFullscreen) && + coverage.width != wm->xdpy_width) + { + coverage.width = wm->xdpy_width; + coverage.x = 0; + need_change = True; + } + + /* Too high */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_HEIGHT); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + /* FIXME: what if this returns False ? */ + + avail_geom->y = coverage.y + coverage.height; + avail_geom->height = avail_geom->height - coverage.height; + } + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefReserveSouth|LayoutPrefVisible)) + { + int y; + + mb_wm_client_get_coverage (client, &coverage); + + /* First of all, tweak the y value so that we start with position + * that as much as possible respects the request for south edge + * for the current geometry. For example, the hildon input method + * initially maps with height 1 and y 399; it then requests resize + * to some sensible height, but does not adjust the y value. + */ + y = avail_geom->y + avail_geom->height - coverage.height; + + if (y < 0) + y = 0; + + if (y != coverage.y) + need_change = True; + + coverage.y = y; + + /* set x and width */ + need_change |= mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_X|SET_WIDTH); + + if (client->transient_for && + (client->transient_for->window->ewmh_state & + MBWMClientWindowEWMHStateFullscreen) && + coverage.width != wm->xdpy_width) + { + coverage.width = wm->xdpy_width; + coverage.x = 0; + need_change = True; + } + + /* Too high */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_HEIGHT); + + if (coverage.y != avail_geom->y + avail_geom->height - coverage.height) + { + coverage.y = avail_geom->y + avail_geom->height - coverage.height; + need_change = True; + } + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + avail_geom->height = avail_geom->height - coverage.height; + } + + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefReserveWest|LayoutPrefVisible)) + { + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_X|SET_Y|SET_HEIGHT); + + if (client->transient_for && + (client->transient_for->window->ewmh_state & + MBWMClientWindowEWMHStateFullscreen) && + coverage.height != wm->xdpy_height) + { + coverage.height = wm->xdpy_height; + coverage.y = 0; + need_change = True; + } + + /* Too wide */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + avail_geom->x = coverage.x + coverage.width; + avail_geom->width = avail_geom->width - coverage.width; + } + + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefReserveEast|LayoutPrefVisible)) + { + mb_wm_client_get_coverage (client, &coverage); + + /* set x,y to avail and max width */ + need_change = mb_wm_layout_maximise_geometry (&coverage, + avail_geom, + SET_Y|SET_HEIGHT); + + if (client->transient_for && + (client->transient_for->window->ewmh_state & + MBWMClientWindowEWMHStateFullscreen) && + coverage.height != wm->xdpy_height) + { + coverage.height = wm->xdpy_height; + coverage.y = 0; + need_change = True; + } + + /* Too wide */ + need_change |= mb_wm_layout_clip_geometry (&coverage, + avail_geom, SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + + if (coverage.x != avail_geom->x + avail_geom->width - coverage.width) + { + coverage.x = avail_geom->x + avail_geom->width - coverage.width; + need_change = True; + } + + avail_geom->width = avail_geom->width - coverage.width; + } +} + +static void +mb_wm_layout_real_layout_free (MBWMLayout *layout, MBGeometry * avail_geom) +{ + MBWindowManager *wm = layout->wm; + MBWindowManagerClient *client; + MBGeometry coverage; + Bool need_change; + int min_x, max_x, min_y, max_y; + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefGrowToFreeSpace|LayoutPrefVisible)) + { + mb_wm_client_get_coverage (client, &coverage); + + if (coverage.x != avail_geom->x + || coverage.width != avail_geom->width + || coverage.y != avail_geom->y + || coverage.height != avail_geom->height) + { + MBWM_DBG("available geom for free space: %i+%i %ix%i", + min_x, min_y, max_x - min_x, max_y - min_y); + + coverage.width = avail_geom->width; + coverage.height = avail_geom->height; + coverage.x = avail_geom->x; + coverage.y = avail_geom->y; + + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + } + } + + mb_wm_stack_enumerate(wm, client) + { + MBWMClientLayoutHints hints = mb_wm_client_get_layout_hints (client); + + if ((hints & LayoutPrefPositionFree) && (hints & LayoutPrefVisible) && + !(hints & (LayoutPrefFixedX|LayoutPrefFixedY))) + { + /* Clip if needed */ + mb_wm_client_get_coverage (client, &coverage); + + need_change = mb_wm_layout_clip_geometry (&coverage, + avail_geom, + SET_X | SET_Y | + SET_HEIGHT | SET_WIDTH); + + if (need_change) + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + } + } + +} + +static void +mb_wm_layout_real_layout_fullscreen (MBWMLayout *layout, MBGeometry * avail_geom) +{ + MBWindowManager *wm = layout->wm; + MBWindowManagerClient *client; + MBGeometry coverage; + + mb_wm_stack_enumerate(wm, client) + if (mb_wm_client_get_layout_hints (client) == + (LayoutPrefFullscreen|LayoutPrefVisible)) + { + MBWMList *l = mb_wm_client_get_transients (client); + + mb_wm_client_get_coverage (client, &coverage); + + /* See if this client comes with an input method and if so, + * adjust the available geometry accordingly + */ + while (l) + { + MBWindowManagerClient * c = l->data; + + if (MB_WM_CLIENT_CLIENT_TYPE (c) == MBWMClientTypeInput) + { + MBGeometry geom; + mb_wm_client_get_coverage (c, &geom); + + if (mb_wm_client_get_layout_hints (c) == + (LayoutPrefReserveSouth|LayoutPrefVisible)) + { + if (geom.y < avail_geom->y + avail_geom->height) + { + avail_geom->height = geom.y - avail_geom->y; + } + } + else if (mb_wm_client_get_layout_hints (c) == + (LayoutPrefReserveNorth|LayoutPrefVisible)) + { + if (geom.height && geom.height + geom.y > avail_geom->y) + { + int y = avail_geom->y; + + avail_geom->y = geom.y + geom.height; + avail_geom->height -= y - avail_geom->y; + } + } + else if (mb_wm_client_get_layout_hints (c) == + (LayoutPrefReserveWest|LayoutPrefVisible)) + { + if (geom.x < avail_geom->x + avail_geom->width) + { + avail_geom->width = geom.x - avail_geom->x; + } + } + else if (mb_wm_client_get_layout_hints (c) == + (LayoutPrefReserveEast|LayoutPrefVisible)) + { + if (geom.width && geom.width + geom.x > avail_geom->x) + { + int x = avail_geom->x; + + avail_geom->x = geom.x + geom.width; + avail_geom->width -= x - avail_geom->x; + } + } + + break; + } + + l = l->next; + } + + if (coverage.x != avail_geom->x + || coverage.width != avail_geom->width + || coverage.y != avail_geom->y + || coverage.height != avail_geom->height) + { + coverage.width = avail_geom->width; + coverage.height = avail_geom->height; + coverage.x = avail_geom->x; + coverage.y = avail_geom->y; + + mb_wm_client_request_geometry (client, + &coverage, + MBWMClientReqGeomIsViaLayoutManager); + } + + mb_wm_util_list_free (l); + } +} + +static void +mb_wm_layout_real_update (MBWMLayout * layout) +{ + MBWMLayoutClass *klass; + MBWindowManager *wm = layout->wm; + MBGeometry avail_geom; + + klass = MB_WM_LAYOUT_CLASS (MB_WM_OBJECT_GET_CLASS (layout)); + + MBWM_ASSERT (klass->layout_panels); + MBWM_ASSERT (klass->layout_input); + MBWM_ASSERT (klass->layout_free); + MBWM_ASSERT (klass->layout_fullscreen); + + mb_wm_get_display_geometry (wm, &avail_geom); + + /* + cycle through clients, laying out each in below order. + Note they must have LayoutPrefVisible set. + + LayoutPrefReserveEdgeNorth + LayoutPrefReserveEdgeSouth + + LayoutPrefReserveEdgeEast + LayoutPrefReserveEdgeWest + + LayoutPrefReserveNorth + LayoutPrefReserveSouth + + LayoutPrefReserveEast + LayoutPrefReserveWest + + LayoutPrefGrowToFreeSpace + + LayoutPrefFullscreen + + XXX need to check they are mapped too + + foreach client with LayoutPrefReserveEdgeNorth & LayoutPrefVisible + grab there current geometry + does it fit well into current restraints ( min_, max_ ) + yes leave + no resize so it does, mark dirty + set min_x, max_y, min_y, max_y to current size + repeat for next condition + + mb_wm_client_get_coverage (MBWindowManagerClient *client, + MBGeometry *coverage) + + */ + + klass->layout_panels (layout, &avail_geom); + klass->layout_input (layout, &avail_geom); + klass->layout_free (layout, &avail_geom); + + mb_wm_get_display_geometry (wm, &avail_geom); + klass->layout_fullscreen (layout, &avail_geom); +} + +void +mb_wm_layout_update (MBWMLayout * layout) +{ + MBWMLayoutClass *klass; + + klass = MB_WM_LAYOUT_CLASS (MB_WM_OBJECT_GET_CLASS (layout)); + + MBWM_ASSERT (klass->update); + + klass->update (layout); +} |