diff options
| author | aura <nw@moneybot.cc> | 2026-10-02 00:29:58 +0200 |
|---|---|---|
| committer | aura <nw@moneybot.cc> | 2026-10-02 00:29:58 +0200 |
| commit | fc25ef5568291528f6e1c3463b3b142139eb8e8d (patch) | |
| tree | f8df598fb171bf30c0939244372c487d0e017961 /openbox/tiling.c | |
| parent | 87e8271068ea3bccf1daf4e7e4b3667379c054bf (diff) | |
Diffstat (limited to 'openbox/tiling.c')
| -rw-r--r-- | openbox/tiling.c | 989 |
1 files changed, 989 insertions, 0 deletions
diff --git a/openbox/tiling.c b/openbox/tiling.c new file mode 100644 index 00000000..ec24b0df --- /dev/null +++ b/openbox/tiling.c @@ -0,0 +1,989 @@ +#include "tiling.h" +#include "client.h" +#include "config.h" +#include "event.h" +#include "focus.h" +#include "frame.h" +#include "moveresize.h" +#include "openbox.h" +#include "popup.h" +#include "screen.h" +#include "stacking.h" +#include "window.h" +#include "obt/display.h" +#include "obt/prop.h" +#include "obrender/render.h" + +#define MAX_TILES 32 +#define DROP_EDGE_PERCENT 10 +#define OUTLINE_WIDTH 3 +#define SHARE_SCALE 10000 + +typedef struct _TileLayout TileLayout; + +typedef struct { + ObClient *client; + TileLayout *layout; + guint stack; + guint row; + gdouble height_weight; + Rect saved; + Rect pre_max; + gboolean max_horz; + gboolean max_vert; + gboolean snapped_left; + gboolean snapped_right; +} Tile; + +struct _TileLayout { + guint monitor; + guint desktop; + gboolean enabled; + gboolean main_right; + guint main_share; + guint count[2]; + Tile *slots[2][MAX_TILES]; +}; + +static GList *layouts; +static GList *tiles; +static ObClient *keys[12]; +static ObInternalWindow outline[4]; +static guint pending; +static gboolean arranging; +static ObClient *preview_client; +static gint preview_x; +static gint preview_y; +static ObClient *resize_client; +static guint resize_share; +static gint resize_client_width; +static gint resize_column_width; +static gint resize_client_height; +static gint resize_client_y; +static guint resize_count; +static Tile *resize_members[MAX_TILES]; +static gdouble resize_weights[MAX_TILES]; +static gint resize_heights[MAX_TILES]; +static ObPopup *notification; +static guint notification_timer; +static gchar *notification_text; + +static gboolean hide_notification(gpointer data) +{ + popup_hide(notification); + notification_timer = 0; + return FALSE; +} + +static void show_notification(guint monitor, const gchar *text) +{ + const Rect *area; + + if (!config_desktop_popup_time || monitor >= screen_num_monitors) + return; + if (!notification) { + notification = popup_new(); + popup_height(notification, POPUP_HEIGHT); + popup_set_text_align(notification, RR_JUSTIFY_CENTER); + } + if (notification_timer) + g_source_remove(notification_timer); + g_free(notification_text); + notification_text = g_strdup(text); + area = screen_physical_area_monitor(monitor); + popup_position(notification, CenterGravity, + area->x + area->width / 2, area->y + area->height / 2); + popup_max_width(notification, MIN(area->width, + MAX(area->width / 3, POPUP_WIDTH))); + OBT_PROP_SETS(notification->bg, NET_WM_NAME, notification_text); + popup_show(notification, notification_text); + notification_timer = g_timeout_add(config_desktop_popup_time, + hide_notification, NULL); +} + +static Tile *find_tile(ObClient *client) +{ + GList *it; + + for (it = tiles; it; it = it->next) { + Tile *tile = it->data; + if (tile->client == client) + return tile; + } + return NULL; +} + +static TileLayout *find_layout(guint monitor, guint desktop, gboolean create) +{ + GList *it; + TileLayout *layout; + + if (monitor >= screen_num_monitors || desktop >= screen_num_desktops) + return NULL; + for (it = layouts; it; it = it->next) { + layout = it->data; + if (layout->monitor == monitor && layout->desktop == desktop) + return layout; + } + if (!create) + return NULL; + layout = g_new0(TileLayout, 1); + layout->monitor = monitor; + layout->desktop = desktop; + layout->main_share = SHARE_SCALE * 3 / 5; + layout->count[0] = layout->count[1] = 1; + layouts = g_list_append(layouts, layout); + return layout; +} + +static gboolean eligible(ObClient *client) +{ + return client && client->managed && client->frame && + client->type == OB_CLIENT_TYPE_NORMAL && !client->transient && + !client->above && !client->fullscreen && !client->iconic && + !client->shaded && client->desktop != DESKTOP_ALL && + (client->functions & OB_CLIENT_FUNC_MOVE) && + (client->functions & OB_CLIENT_FUNC_RESIZE); +} + +static Rect slot_area_for_count(TileLayout *layout, guint stack, guint row, + guint count) +{ + Rect *work = screen_area(layout->desktop, layout->monitor, NULL); + Rect result = *work; + gint split = ((gint64)work->width * layout->main_share + SHARE_SCALE / 2) / + SHARE_SCALE; + gdouble total = 0, before = 0, through = 0; + guint i; + gint top, bottom; + + for (i = 0; i < count; ++i) { + Tile *tile = layout->slots[stack][i]; + gdouble weight = tile ? tile->height_weight : 1.0; + + total += weight; + if (i < row) + before += weight; + if (i <= row) + through += weight; + } + top = work->height * before / total; + bottom = row + 1 == count ? work->height : work->height * through / total; + + if (layout->main_right) { + if (stack == 0) { + result.x += work->width - split; + result.width = split; + } else + result.width = work->width - split; + } else { + if (stack == 0) + result.width = split; + else { + result.x += split; + result.width -= split; + } + } + result.y += top; + result.height = bottom - top; + g_slice_free(Rect, work); + return result; +} + +static Rect slot_area(TileLayout *layout, guint stack, guint row) +{ + return slot_area_for_count(layout, stack, row, layout->count[stack]); +} + +static void hide_outline(void) +{ + guint i; + gulong serial; + + if (!outline[0].window) + return; + serial = event_start_ignore_all_enters(); + + for (i = 0; i < G_N_ELEMENTS(outline); ++i) + if (outline[i].window) + XUnmapWindow(obt_display, outline[i].window); + event_end_ignore_all_enters(serial); +} + +static void show_outline(const Rect *area) +{ + guint i; + Rect edges[4]; + XSetWindowAttributes attr; + gulong serial = event_start_ignore_all_enters(); + gint border = MIN(OUTLINE_WIDTH, MIN(area->width, area->height)); + + RECT_SET(edges[0], area->x, area->y, area->width, border); + RECT_SET(edges[1], area->x, area->y, border, area->height); + RECT_SET(edges[2], area->x + area->width - border, area->y, + border, area->height); + RECT_SET(edges[3], area->x, area->y + area->height - border, + area->width, border); + attr.override_redirect = True; + attr.background_pixel = WhitePixel(obt_display, ob_screen); + for (i = 0; i < G_N_ELEMENTS(outline); ++i) { + if (!outline[i].window) { + outline[i].type = OB_WINDOW_CLASS_INTERNAL; + outline[i].window = XCreateWindow( + obt_display, obt_root(ob_screen), 0, 0, 1, 1, 0, + CopyFromParent, InputOutput, CopyFromParent, + CWOverrideRedirect | CWBackPixel, &attr); + stacking_add(INTERNAL_AS_WINDOW(&outline[i])); + } + XMoveResizeWindow(obt_display, outline[i].window, + edges[i].x, edges[i].y, + edges[i].width, edges[i].height); + XMapWindow(obt_display, outline[i].window); + stacking_raise(INTERNAL_AS_WINDOW(&outline[i])); + } + event_end_ignore_all_enters(serial); +} + +static void remove_tile(Tile *tile, gboolean restore) +{ + ObClient *client = tile->client; + + tile->layout->slots[tile->stack][tile->row] = NULL; + tiles = g_list_remove(tiles, tile); + if (restore && client->managed) { + if (client->fullscreen) { + client->pre_fullscreen_area = tile->saved; + client->pre_fullscreen_max_horz = tile->max_horz; + client->pre_fullscreen_max_vert = tile->max_vert; + if (tile->max_horz) { + client->pre_fullscreen_area.x = tile->pre_max.x; + client->pre_fullscreen_area.width = tile->pre_max.width; + } + if (tile->max_vert) { + client->pre_fullscreen_area.y = tile->pre_max.y; + client->pre_fullscreen_area.height = tile->pre_max.height; + } + } else { + client_configure(client, tile->saved.x, tile->saved.y, + tile->saved.width, tile->saved.height, + TRUE, TRUE, FALSE); + if (tile->max_horz) + client_maximize(client, TRUE, 1); + if (tile->max_vert) + client_maximize(client, TRUE, 2); + } + client->pre_max_area = tile->pre_max; + client->snapped_left = tile->snapped_left; + client->snapped_right = tile->snapped_right; + } + g_free(tile); +} + +static gboolean place_tile(Tile *tile) +{ + ObClient *client = tile->client; + Rect area = slot_area(tile->layout, tile->stack, tile->row); + gint x = area.x; + gint y = area.y; + gint width = MAX(1, area.width - client->frame->size.left - + client->frame->size.right); + gint height = MAX(1, area.height - client->frame->size.top - + client->frame->size.bottom); + gint logical_width, logical_height; + + frame_frame_gravity(client->frame, &x, &y); + client_try_configure(client, &x, &y, &width, &height, + &logical_width, &logical_height, TRUE); + if (width + client->frame->size.left + client->frame->size.right > + area.width || + height + client->frame->size.top + client->frame->size.bottom > + area.height) + return FALSE; + client_configure(client, x, y, width, height, TRUE, TRUE, FALSE); + return TRUE; +} + +static void compact_stack(TileLayout *layout, guint stack) +{ + guint row, next = 0; + + for (row = 0; row < layout->count[stack]; ++row) { + Tile *tile = layout->slots[stack][row]; + + layout->slots[stack][row] = NULL; + if (tile) { + tile->row = next; + layout->slots[stack][next++] = tile; + } + } + layout->count[stack] = MAX(1, next); +} + +static void queue_arrange(void); + +static gboolean arrange(gpointer data) +{ + GList *it, *next; + gboolean retry = FALSE; + + pending = 0; + arranging = TRUE; + for (it = tiles; it; it = next) { + Tile *tile = it->data; + ObClient *client = tile->client; + TileLayout *layout = tile->layout; + next = it->next; + + if (moveresize_in_progress && moveresize_client == client) + continue; + if (!layout->enabled || layout->monitor >= screen_num_monitors || + layout->desktop >= screen_num_desktops) { + remove_tile(tile, TRUE); + continue; + } + if (client->above) { + remove_tile(tile, TRUE); + continue; + } + if (client->desktop != layout->desktop) { + remove_tile(tile, TRUE); + continue; + } + if (client->iconic || client->fullscreen || client->shaded) + continue; + if (!eligible(client) || client_monitor(client) != layout->monitor || + client->max_horz || client->max_vert) { + remove_tile(tile, FALSE); + continue; + } + } + for (it = layouts; it; it = it->next) { + TileLayout *layout = it->data; + + if (layout->enabled) { + compact_stack(layout, 0); + compact_stack(layout, 1); + } + } + for (it = tiles; it; it = next) { + Tile *tile = it->data; + next = it->next; + + if (!eligible(tile->client) || + (moveresize_in_progress && moveresize_client == tile->client)) + continue; + if (!place_tile(tile)) { + remove_tile(tile, TRUE); + retry = TRUE; + } + } + arranging = FALSE; + if (retry) + queue_arrange(); + return FALSE; +} + +static void queue_arrange(void) +{ + if (!arranging && !pending && layouts) + pending = g_idle_add(arrange, NULL); +} + +static Tile *add_tile(ObClient *client, TileLayout *layout, + guint stack, guint row) +{ + Tile *tile = g_new0(Tile, 1); + + tile->client = client; + tile->layout = layout; + tile->stack = stack; + tile->row = row; + tile->height_weight = 1.0; + tile->saved = client->area; + tile->pre_max = client->pre_max_area; + tile->max_horz = client->max_horz; + tile->max_vert = client->max_vert; + tile->snapped_left = client->snapped_left; + tile->snapped_right = client->snapped_right; + client->snapped_left = client->snapped_right = FALSE; + if (client->max_horz || client->max_vert) + client_maximize(client, FALSE, 0); + layout->slots[stack][row] = tile; + tiles = g_list_append(tiles, tile); + return tile; +} + +static void put_client(ObClient *client, TileLayout *layout, + guint stack, guint row) +{ + Tile *tile = find_tile(client); + Tile *displaced = layout->slots[stack][row]; + + if (tile == displaced && tile) { + stacking_raise(CLIENT_AS_WINDOW(client)); + return; + } + if (displaced && (displaced->client->fullscreen || + displaced->client->iconic || displaced->client->shaded)) { + remove_tile(displaced, TRUE); + displaced = NULL; + } + if (tile) { + TileLayout *old = tile->layout; + guint old_stack = tile->stack; + guint old_row = tile->row; + + old->slots[old_stack][old_row] = displaced; + if (displaced) { + displaced->layout = old; + displaced->stack = old_stack; + displaced->row = old_row; + if (place_tile(displaced)) + stacking_raise(CLIENT_AS_WINDOW(displaced->client)); + } + tile->layout = layout; + tile->stack = stack; + tile->row = row; + layout->slots[stack][row] = tile; + } else { + if (displaced) + remove_tile(displaced, TRUE); + tile = add_tile(client, layout, stack, row); + } + if (place_tile(tile)) + stacking_raise(CLIENT_AS_WINDOW(client)); + queue_arrange(); +} + +static guint stack_members(TileLayout *layout, guint stack, Tile *exclude) +{ + guint row, count = 0; + + for (row = 0; row < layout->count[stack]; ++row) + if (layout->slots[stack][row] && layout->slots[stack][row] != exclude) + ++count; + return count; +} + +static void append_client(ObClient *client, TileLayout *layout, guint stack) +{ + Tile *tile = find_tile(client); + guint row, next = 0; + guint count = stack_members(layout, stack, tile); + + if (count == MAX_TILES) + return; + if (tile) + tile->layout->slots[tile->stack][tile->row] = NULL; + for (row = 0; row < layout->count[stack]; ++row) { + Tile *member = layout->slots[stack][row]; + + layout->slots[stack][row] = NULL; + if (member) { + member->row = next; + layout->slots[stack][next++] = member; + } + } + layout->count[stack] = count + 1; + if (tile) { + tile->layout = layout; + tile->stack = stack; + tile->row = count; + layout->slots[stack][count] = tile; + } else + tile = add_tile(client, layout, stack, count); + if (place_tile(tile)) + stacking_raise(CLIENT_AS_WINDOW(client)); + queue_arrange(); +} + +static ObClient *next_floating(TileLayout *layout) +{ + GList *it; + + if (eligible(focus_client) && !find_tile(focus_client) && + focus_client->desktop == layout->desktop && + client_monitor(focus_client) == layout->monitor) + return focus_client; + for (it = client_list; it; it = it->next) { + ObClient *client = it->data; + if (eligible(client) && !find_tile(client) && + client->desktop == layout->desktop && + client_monitor(client) == layout->monitor) + return client; + } + return NULL; +} + +void tiling_toggle(guint monitor) +{ + TileLayout *layout = find_layout(monitor, screen_desktop, TRUE); + guint stack, row; + + if (!layout || moveresize_in_progress) + return; + layout->enabled = !layout->enabled; + for (stack = 0; stack < 2; ++stack) + for (row = 0; row < layout->count[stack]; ++row) { + if (!layout->enabled) { + if (layout->slots[stack][row]) + remove_tile(layout->slots[stack][row], TRUE); + } else { + ObClient *client = next_floating(layout); + if (client) + put_client(client, layout, stack, row); + } + } + queue_arrange(); + hide_outline(); + show_notification(monitor, layout->enabled ? "tiling: on" : "tiling: off"); +} + +void tiling_flip(guint monitor) +{ + TileLayout *layout = find_layout(monitor, screen_desktop, TRUE); + + if (layout && !moveresize_in_progress) { + layout->main_right = !layout->main_right; + queue_arrange(); + } +} + +void tiling_change_count(guint monitor, gboolean main_stack, gint delta) +{ + TileLayout *layout = find_layout(monitor, screen_desktop, TRUE); + guint stack = main_stack ? 0 : 1; + guint old, count, row; + gchar *message; + + if (!layout || moveresize_in_progress) + return; + old = layout->count[stack]; + count = CLAMP((gint)old + CLAMP(delta, -MAX_TILES, MAX_TILES), 1, MAX_TILES); + for (row = count; row < old; ++row) + if (layout->slots[stack][row]) + remove_tile(layout->slots[stack][row], TRUE); + layout->count[stack] = count; + if (layout->enabled) + for (row = old; row < count; ++row) { + ObClient *client = next_floating(layout); + if (client) + put_client(client, layout, stack, row); + } + queue_arrange(); + if (layout->enabled) { + compact_stack(layout, 0); + compact_stack(layout, 1); + } + message = g_strdup_printf("main: %u | side: %u", + layout->count[0], layout->count[1]); + show_notification(monitor, message); + g_free(message); +} + +void tiling_promote(ObClient *client) +{ + TileLayout *layout; + + if (!eligible(client) || moveresize_in_progress) + return; + layout = find_layout(client_monitor(client), client->desktop, FALSE); + if (layout && layout->enabled) + put_client(client, layout, 0, 0); +} + +guint tiling_key(ObClient *client) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS(keys); ++i) + if (keys[i] == client) + return i + 1; + return 0; +} + +void tiling_assign(ObClient *client, guint key) +{ + guint old; + ObClient *previous; + gchar *message; + + if (!client || !client->managed || key > G_N_ELEMENTS(keys)) + return; + old = tiling_key(client); + if (old) + keys[old - 1] = NULL; + previous = key ? keys[key - 1] : NULL; + if (key) + keys[key - 1] = client; + if (previous && previous != client) + client_update_title(previous); + client_update_title(client); + message = key ? g_strdup_printf("[F%u] -> %s", key, client->original_title) : + g_strdup("window key cleared"); + show_notification(client_monitor(client), message); + g_free(message); +} + +void tiling_recall(guint key) +{ + ObClient *client; + + if (!key || key > G_N_ELEMENTS(keys) || moveresize_in_progress) + return; + client = keys[key - 1]; + if (client) { + client_activate(client, TRUE, FALSE, TRUE, TRUE, TRUE); + tiling_promote(client); + } +} + +static TileLayout *drop_target(ObClient *client, gint x, gint y, + guint *stack, guint *row, Rect *area) +{ + TileLayout *layout; + guint monitor; + const Rect *physical; + gint edge; + gboolean right; + TileLayout preview; + Tile incoming; + Tile *moving_tile; + guint i, next = 0; + + if (!eligible(client)) + return NULL; + monitor = screen_find_monitor_point(x, y); + layout = find_layout(monitor, client->desktop, FALSE); + if (!layout || !layout->enabled) + return NULL; + physical = screen_physical_area_monitor(monitor); + if (!RECT_CONTAINS(*physical, x, y)) + return NULL; + edge = MAX(1, physical->width * DROP_EDGE_PERCENT / 100); + right = x >= physical->x + physical->width - edge; + if (!right && x >= physical->x + edge) + return NULL; + *stack = right == layout->main_right ? 0 : 1; + *row = stack_members(layout, *stack, find_tile(client)); + if (*row == MAX_TILES) + return NULL; + preview = *layout; + moving_tile = find_tile(client); + for (i = 0; i < MAX_TILES; ++i) + preview.slots[*stack][i] = NULL; + for (i = 0; i < layout->count[*stack]; ++i) { + Tile *member = layout->slots[*stack][i]; + + if (member && member != moving_tile) + preview.slots[*stack][next++] = member; + } + incoming.height_weight = moving_tile ? moving_tile->height_weight : 1.0; + preview.slots[*stack][*row] = &incoming; + preview.count[*stack] = *row + 1; + *area = slot_area(&preview, *stack, *row); + return layout; +} + +gboolean tiling_move_preview(ObClient *client, gint x, gint y) +{ + guint stack, row; + Rect area; + TileLayout *layout = find_layout(screen_find_monitor_point(x, y), + client->desktop, FALSE); + + preview_client = client; + preview_x = x; + preview_y = y; + if (drop_target(client, x, y, &stack, &row, &area)) + show_outline(&area); + else + hide_outline(); + return find_tile(client) || (layout && layout->enabled); +} + +void tiling_move_end(ObClient *client, gboolean cancel) +{ + guint stack, row; + Rect area; + TileLayout *layout; + Tile *tile; + + hide_outline(); + if (cancel || preview_client != client) { + preview_client = NULL; + tile = find_tile(client); + if (tile) + place_tile(tile); + return; + } + layout = drop_target(client, preview_x, preview_y, &stack, &row, &area); + preview_client = NULL; + if (layout) + append_client(client, layout, stack); + else { + tile = find_tile(client); + if (tile) + remove_tile(tile, FALSE); + } + queue_arrange(); +} + +void tiling_action_moved(ObClient *client) +{ + TileLayout *layout; + Tile *tile; + Rect *work; + gint left, right, edge; + guint stack; + + if (!eligible(client) || client->max_horz || client->max_vert || + moveresize_in_progress) + return; + tile = find_tile(client); + layout = find_layout(client_monitor(client), client->desktop, FALSE); + if (!layout || !layout->enabled) { + if (tile) + remove_tile(tile, FALSE); + queue_arrange(); + return; + } + work = screen_area(layout->desktop, layout->monitor, NULL); + left = ABS(client->frame->area.x - work->x); + right = ABS(RECT_RIGHT(client->frame->area) - RECT_RIGHT(*work)); + edge = MAX(1, work->width * DROP_EDGE_PERCENT / 100); + g_slice_free(Rect, work); + if (MIN(left, right) > edge) { + if (tile) + remove_tile(tile, FALSE); + queue_arrange(); + return; + } + stack = (right < left) == layout->main_right ? 0 : 1; + if (tile && tile->layout == layout && tile->stack == stack) { + stacking_raise(CLIENT_AS_WINDOW(client)); + queue_arrange(); + } else + append_client(client, layout, stack); +} + +void tiling_resize_begin(ObClient *client) +{ + Tile *tile = find_tile(client); + guint row; + + resize_client = NULL; + if (tile) { + Rect area = slot_area(tile->layout, tile->stack, tile->row); + + resize_client = client; + resize_share = tile->layout->main_share; + resize_client_width = client->area.width; + resize_column_width = area.width; + resize_client_height = client->area.height; + resize_client_y = client->area.y; + resize_count = tile->layout->count[tile->stack]; + for (row = 0; row < resize_count; ++row) { + Tile *member = tile->layout->slots[tile->stack][row]; + Rect slot = slot_area(tile->layout, tile->stack, row); + + resize_members[row] = member; + resize_weights[row] = member ? member->height_weight : 1.0; + resize_heights[row] = slot.height; + } + } +} + +static gint stack_min_width(TileLayout *layout, guint stack) +{ + guint row; + gint width = 1; + + for (row = 0; row < layout->count[stack]; ++row) { + Tile *tile = layout->slots[stack][row]; + + if (tile) { + ObClient *client = tile->client; + width = MAX(width, client->min_size.width + + client->frame->size.left + client->frame->size.right); + } + } + return width; +} + +static void restore_resize_weights(Tile *tile) +{ + guint row, old; + + for (row = 0; row < tile->layout->count[tile->stack]; ++row) { + Tile *member = tile->layout->slots[tile->stack][row]; + + if (member) + for (old = 0; old < resize_count; ++old) + if (member == resize_members[old]) { + member->height_weight = resize_weights[old]; + break; + } + } +} + +static gint tile_min_height(Tile *tile) +{ + ObClient *client = tile->client; + + return MAX(1, client->min_size.height) + client->frame->size.top + + client->frame->size.bottom; +} + +static void resize_vertical(Tile *tile, gint y, gint height) +{ + guint row, neighbor; + Tile *other; + gint pair_height, minimum, maximum, requested; + gdouble pair_weight; + + if (resize_count != tile->layout->count[tile->stack]) + return; + for (row = 0; row < resize_count; ++row) + if (tile->layout->slots[tile->stack][row] != resize_members[row]) + return; + restore_resize_weights(tile); + if (y != resize_client_y) { + if (!tile->row) + return; + neighbor = tile->row - 1; + } else { + if (tile->row + 1 == resize_count) + return; + neighbor = tile->row + 1; + } + other = tile->layout->slots[tile->stack][neighbor]; + if (!other) + return; + pair_height = resize_heights[tile->row] + resize_heights[neighbor]; + /* Keep rounding from violating either window's minimum height. */ + minimum = tile_min_height(tile) + 1; + maximum = pair_height - tile_min_height(other) - 1; + if (minimum > maximum || height == resize_client_height) + return; + requested = CLAMP(resize_heights[tile->row] + height - resize_client_height, + minimum, maximum); + pair_weight = resize_weights[tile->row] + resize_weights[neighbor]; + tile->height_weight = pair_weight * requested / pair_height; + other->height_weight = pair_weight - tile->height_weight; +} + +void tiling_resize_adjust(ObClient *client, gint *x, gint *y, + gint *width, gint *height) +{ + Tile *tile = find_tile(client); + TileLayout *layout; + Rect *work, area; + gint column_width, minimum, maximum; + + if (client != resize_client || !tile) + return; + layout = tile->layout; + work = screen_area(layout->desktop, layout->monitor, NULL); + minimum = stack_min_width(layout, 0); + maximum = work->width - stack_min_width(layout, 1); + if (tile->stack == 0 && minimum <= maximum && work->width > 0) { + column_width = CLAMP(resize_column_width + *width - resize_client_width, + minimum, maximum); + layout->main_share = CLAMP( + ((gint64)column_width * SHARE_SCALE + work->width / 2) / work->width, + 1, SHARE_SCALE - 1); + } + g_slice_free(Rect, work); + resize_vertical(tile, *y, *height); + area = slot_area(layout, tile->stack, tile->row); + *x = area.x; + *y = area.y; + *width = MAX(1, area.width - client->frame->size.left - client->frame->size.right); + *height = MAX(1, area.height - client->frame->size.top - client->frame->size.bottom); + frame_frame_gravity(client->frame, x, y); + queue_arrange(); +} + +void tiling_resize_end(ObClient *client, gboolean cancel) +{ + Tile *tile = find_tile(client); + + if (resize_client == client && tile && cancel) { + tile->layout->main_share = resize_share; + restore_resize_weights(tile); + } + resize_client = NULL; + queue_arrange(); +} + +void tiling_forget(ObClient *client) +{ + Tile *tile = find_tile(client); + guint key = tiling_key(client); + + if (key) + keys[key - 1] = NULL; + if (resize_client == client) + resize_client = NULL; + if (preview_client == client) { + preview_client = NULL; + hide_outline(); + } + if (tile) + remove_tile(tile, FALSE); + queue_arrange(); +} + +void tiling_client_changed(ObClient *client) +{ + if (find_tile(client)) + queue_arrange(); +} + +void tiling_screen_changed(void) +{ + hide_outline(); + queue_arrange(); +} + +void tiling_shutdown(gboolean reconfigure) +{ + guint i; + + if (notification_timer) { + g_source_remove(notification_timer); + notification_timer = 0; + } + popup_free(notification); + notification = NULL; + g_free(notification_text); + notification_text = NULL; + if (reconfigure) + return; + + if (pending) { + g_source_remove(pending); + pending = 0; + } + arranging = TRUE; + while (tiles) + remove_tile(tiles->data, TRUE); + arranging = FALSE; + while (layouts) { + g_free(layouts->data); + layouts = g_list_delete_link(layouts, layouts); + } + for (i = 0; i < G_N_ELEMENTS(keys); ++i) { + ObClient *client = keys[i]; + keys[i] = NULL; + if (client) + client_update_title(client); + } + for (i = 0; i < G_N_ELEMENTS(outline); ++i) + if (outline[i].window) { + stacking_remove(INTERNAL_AS_WINDOW(&outline[i])); + XDestroyWindow(obt_display, outline[i].window); + outline[i].window = None; + } +} |
