diff --git a/include/floating.h b/include/floating.h index d4942e56..d368048b 100644 --- a/include/floating.h +++ b/include/floating.h @@ -21,7 +21,10 @@ typedef void(*callback_t)(xcb_connection_t*, Client*, Rect*, uint32_t, uint32_t, void *extra) /** On which border was the dragging initiated? */ -typedef enum { BORDER_LEFT, BORDER_RIGHT, BORDER_TOP, BORDER_BOTTOM} border_t; +typedef enum { BORDER_LEFT = (1 << 0), + BORDER_RIGHT = (1 << 1), + BORDER_TOP = (1 << 2), + BORDER_BOTTOM = (1 << 3)} border_t; /** * Enters floating mode for the given client. Correctly takes care of the diff --git a/src/floating.c b/src/floating.c index 9a94e409..0d97c27a 100644 --- a/src/floating.c +++ b/src/floating.c @@ -169,13 +169,13 @@ void floating_assign_to_workspace(Client *client, Workspace *new_workspace) { * extension and only on Mac OS X systems at the moment). * */ -struct callback_params { +struct resize_callback_params { border_t border; xcb_button_press_event_t *event; }; DRAGGING_CB(resize_callback) { - struct callback_params *params = extra; + struct resize_callback_params *params = extra; xcb_button_press_event_t *event = params->event; switch (params->border) { case BORDER_RIGHT: { @@ -251,7 +251,7 @@ int floating_border_click(xcb_connection_t *conn, Client *client, xcb_button_pre DLOG("border = %d\n", border); - struct callback_params params = { border, event }; + struct resize_callback_params params = { border, event }; drag_pointer(conn, client, event, XCB_NONE, border, resize_callback, ¶ms); @@ -282,17 +282,49 @@ void floating_drag_window(xcb_connection_t *conn, Client *client, xcb_button_pre drag_pointer(conn, client, event, XCB_NONE, BORDER_TOP /* irrelevant */, drag_window_callback, event); } +/* + * This is an ugly data structure which we need because there is no standard + * way of having nested functions (only available as a gcc extension at the + * moment, clang doesn’t support it) or blocks (only available as a clang + * extension and only on Mac OS X systems at the moment). + * + */ +struct resize_window_callback_params { + border_t corner; + xcb_button_press_event_t *event; +}; + DRAGGING_CB(resize_window_callback) { - xcb_button_press_event_t *event = extra; - int32_t new_width = old_rect->width + (new_x - event->root_x); - int32_t new_height = old_rect->height + (new_y - event->root_y); + struct resize_window_callback_params *params = extra; + xcb_button_press_event_t *event = params->event; + border_t corner = params->corner; + + int32_t dest_x = client->rect.x; + int32_t dest_y = client->rect.y; + uint32_t dest_width; + uint32_t dest_height; + + if (corner & BORDER_LEFT) { + dest_x = old_rect->x + (new_x - event->root_x); + dest_width = old_rect->width - (new_x - event->root_x); + } else dest_width = old_rect->width + (new_x - event->root_x); + + if (corner & BORDER_TOP) { + dest_y = old_rect->y + (new_y - event->root_y); + dest_height = old_rect->height - (new_y - event->root_y); + } else dest_height = old_rect->height + (new_y - event->root_y); + /* Obey minimum window size and reposition the client */ - if (new_width > 0 && new_width >= client_min_width(client)) - client->rect.width = new_width; + if (dest_width > 0 && dest_width >= client_min_width(client)) { + client->rect.x = dest_x; + client->rect.width = dest_width; + } - if (new_height > 0 && new_height >= client_min_height(client)) - client->rect.height = new_height; + if (dest_height > 0 && dest_height >= client_min_height(client)) { + client->rect.y = dest_y; + client->rect.height = dest_height; + } /* resize_client flushes */ resize_client(conn, client); @@ -307,7 +339,21 @@ DRAGGING_CB(resize_window_callback) { void floating_resize_window(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event) { DLOG("floating_resize_window\n"); - drag_pointer(conn, client, event, XCB_NONE, BORDER_TOP /* irrelevant */, resize_window_callback, event); + /* corner saves the nearest corner to the original click. It contains + * a bitmask of the nearest borders (BORDER_LEFT, BORDER_RIGHT, …) */ + border_t corner = 0; + + if (event->event_x <= (client->rect.width / 2)) + corner |= BORDER_LEFT; + else corner |= BORDER_RIGHT; + + if (event->event_y <= (client->rect.height / 2)) + corner |= BORDER_TOP; + else corner |= BORDER_RIGHT; + + struct resize_window_callback_params params = { corner, event }; + + drag_pointer(conn, client, event, XCB_NONE, BORDER_TOP /* irrelevant */, resize_window_callback, ¶ms); }