X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Ffloating.c;h=643f204b64110a12aede45c4bf4b6ee9e4851f86;hb=78e99440f6ff144bb6842f3fb33c1f65faadc4df;hp=c0154acac47cf673810a4765459a48f9b8a69a6c;hpb=f28afc8f6b43ad6ee9ff8cbd4ffaa70d5ae343ca;p=i3%2Fi3 diff --git a/src/floating.c b/src/floating.c index c0154aca..643f204b 100644 --- a/src/floating.c +++ b/src/floating.c @@ -1,3 +1,5 @@ +#undef I3__FILE__ +#define I3__FILE__ "floating.c" /* * vim:ts=4:sw=4:expandtab * @@ -26,6 +28,80 @@ static Rect total_outputs_dimensions(void) { return outputs_dimensions; } +/** + * Called when a floating window is created or resized. + * This function resizes the window if its size is higher or lower than the + * configured maximum/minimum size, respectively. + * + */ +void floating_check_size(Con *floating_con) { + /* Define reasonable minimal and maximal sizes for floating windows */ + const int floating_sane_min_height = 50; + const int floating_sane_min_width = 75; + Rect floating_sane_max_dimensions; + Con *focused_con = con_descend_focused(floating_con); + + /* obey size increments */ + if (focused_con->height_increment || focused_con->width_increment) { + Rect border_rect = con_border_style_rect(focused_con); + + /* We have to do the opposite calculations that render_con() do + * to get the exact size we want. */ + border_rect.width = -border_rect.width; + border_rect.width += 2 * focused_con->border_width; + border_rect.height = -border_rect.height; + border_rect.height += 2 * focused_con->border_width; + if (con_border_style(focused_con) == BS_NORMAL) + border_rect.height += render_deco_height(); + + if (focused_con->height_increment && + floating_con->rect.height >= focused_con->base_height + border_rect.height) { + floating_con->rect.height -= focused_con->base_height + border_rect.height; + floating_con->rect.height -= floating_con->rect.height % focused_con->height_increment; + floating_con->rect.height += focused_con->base_height + border_rect.height; + } + + if (focused_con->width_increment && + floating_con->rect.width >= focused_con->base_width + border_rect.width) { + floating_con->rect.width -= focused_con->base_width + border_rect.width; + floating_con->rect.width -= floating_con->rect.width % focused_con->width_increment; + floating_con->rect.width += focused_con->base_width + border_rect.width; + } + } + + /* Unless user requests otherwise (-1), ensure width/height do not exceed + * configured maxima or, if unconfigured, limit to combined width of all + * outputs */ + if (config.floating_minimum_height != -1) { + if (config.floating_minimum_height == 0) + floating_con->rect.height = max(floating_con->rect.height, floating_sane_min_height); + else + floating_con->rect.height = max(floating_con->rect.height, config.floating_minimum_height); + } + if (config.floating_minimum_width != -1) { + if (config.floating_minimum_width == 0) + floating_con->rect.width = max(floating_con->rect.width, floating_sane_min_width); + else + floating_con->rect.width = max(floating_con->rect.width, config.floating_minimum_width); + } + + /* Unless user requests otherwise (-1), raise the width/height to + * reasonable minimum dimensions */ + floating_sane_max_dimensions = total_outputs_dimensions(); + if (config.floating_maximum_height != -1) { + if (config.floating_maximum_height == 0) + floating_con->rect.height = min(floating_con->rect.height, floating_sane_max_dimensions.height); + else + floating_con->rect.height = min(floating_con->rect.height, config.floating_maximum_height); + } + if (config.floating_maximum_width != -1) { + if (config.floating_maximum_width == 0) + floating_con->rect.width = min(floating_con->rect.width, floating_sane_max_dimensions.width); + else + floating_con->rect.width = min(floating_con->rect.width, config.floating_maximum_width); + } +} + void floating_enable(Con *con, bool automatic) { bool set_focus = (con == focused); @@ -40,7 +116,7 @@ void floating_enable(Con *con, bool automatic) { } /* 1: If the container is a workspace container, we need to create a new - * split-container with the same orientation and make that one floating. We + * split-container with the same layout and make that one floating. We * cannot touch the workspace container itself because floating containers * are children of the workspace. */ if (con->type == CT_WORKSPACE) { @@ -52,7 +128,7 @@ void floating_enable(Con *con, bool automatic) { /* TODO: refactor this with src/con.c:con_set_layout */ Con *new = con_new(NULL, NULL); new->parent = con; - new->orientation = con->orientation; + new->layout = con->layout; /* since the new container will be set into floating mode directly * afterwards, we need to copy the workspace rect. */ @@ -97,8 +173,8 @@ void floating_enable(Con *con, bool automatic) { * otherwise. */ Con *ws = con_get_workspace(con); nc->parent = ws; - nc->orientation = NO_ORIENTATION; nc->type = CT_FLOATING_CON; + nc->layout = L_SPLITH; /* We insert nc already, even though its rect is not yet calculated. This * is necessary because otherwise the workspace might be empty (and get * closed in tree_close()) even though it’s not. */ @@ -118,7 +194,7 @@ void floating_enable(Con *con, bool automatic) { free(name); /* find the height for the decorations */ - int deco_height = config.font.height + 5; + int deco_height = render_deco_height(); DLOG("Original rect: (%d, %d) with %d x %d\n", con->rect.x, con->rect.y, con->rect.width, con->rect.height); DLOG("Geometry = (%d, %d) with %d x %d\n", con->geometry.x, con->geometry.y, con->geometry.width, con->geometry.height); @@ -136,49 +212,27 @@ void floating_enable(Con *con, bool automatic) { } } - /* Define reasonable minimal and maximal sizes for floating windows */ - const int floating_sane_min_height = 50; - const int floating_sane_min_width = 75; + floating_check_size(nc); - Rect floating_sane_max_dimensions; - floating_sane_max_dimensions = total_outputs_dimensions(); + /* 3: attach the child to the new parent container. We need to do this + * because con_border_style_rect() needs to access con->parent. */ + con->parent = nc; + con->percent = 1.0; + con->floating = FLOATING_USER_ON; - /* Unless user requests otherwise (-1), ensure width/height do not exceed - * configured maxima or, if unconfigured, limit to combined width of all - * outputs */ - if (config.floating_maximum_height != -1) { - if (config.floating_maximum_height == 0) - nc->rect.height = min(nc->rect.height, floating_sane_max_dimensions.height); - else - nc->rect.height = min(nc->rect.height, config.floating_maximum_height); - } - if (config.floating_maximum_width != -1) { - if (config.floating_maximum_width == 0) - nc->rect.width = min(nc->rect.width, floating_sane_max_dimensions.width); - else - nc->rect.width = min(nc->rect.width, config.floating_maximum_width); - } + /* 4: set the border style as specified with new_float */ + if (automatic) + con->border_style = config.default_floating_border; - /* Unless user requests otherwise (-1), raise the width/height to - * reasonable minimum dimensions */ - if (config.floating_minimum_height != -1) { - if (config.floating_minimum_height == 0) - nc->rect.height = max(nc->rect.height, floating_sane_min_height); - else - nc->rect.height = max(nc->rect.height, config.floating_minimum_height); - } - if (config.floating_minimum_width != -1) { - if (config.floating_minimum_width == 0) - nc->rect.width = max(nc->rect.width, floating_sane_min_width); - else - nc->rect.width = max(nc->rect.width, config.floating_minimum_width); - } + /* Add pixels for the decoration. */ + Rect border_style_rect = con_border_style_rect(con); + + nc->rect.height -= border_style_rect.height; + nc->rect.width -= border_style_rect.width; - /* add pixels for the decoration */ - /* TODO: don’t add them when the user automatically puts new windows into - * 1pixel/borderless mode */ - nc->rect.height += deco_height + 2; - nc->rect.width += 4; + /* Add some more pixels for the title bar */ + if(con_border_style(con) == BS_NORMAL) + nc->rect.height += deco_height; /* Honor the X11 border */ nc->rect.height += con->border_width * 2; @@ -203,34 +257,30 @@ void floating_enable(Con *con, bool automatic) { /* Sanity check: Are the coordinates on the appropriate output? If not, we * need to change them */ - Output *current_output = get_output_containing(nc->rect.x, nc->rect.y); + Output *current_output = get_output_containing(nc->rect.x + + (nc->rect.width / 2), nc->rect.y + (nc->rect.height / 2)); + Con *correct_output = con_get_output(ws); if (!current_output || current_output->con != correct_output) { DLOG("This floating window is on the wrong output, fixing coordinates (currently (%d, %d))\n", nc->rect.x, nc->rect.y); - /* Take the relative coordinates of the current output, then add them - * to the coordinate space of the correct output */ - uint32_t rel_x = (nc->rect.x - (current_output ? current_output->con->rect.x : 0)); - uint32_t rel_y = (nc->rect.y - (current_output ? current_output->con->rect.y : 0)); - nc->rect.x = correct_output->rect.x + rel_x; - nc->rect.y = correct_output->rect.y + rel_y; + + /* If moving from one output to another, keep the relative position + * consistent (e.g. a centered dialog will remain centered). */ + if (current_output) + floating_fix_coordinates(nc, ¤t_output->con->rect, &correct_output->rect); + else { + nc->rect.x = correct_output->rect.x; + nc->rect.y = correct_output->rect.y; + } } DLOG("Floating rect: (%d, %d) with %d x %d\n", nc->rect.x, nc->rect.y, nc->rect.width, nc->rect.height); - /* 3: attach the child to the new parent container */ - con->parent = nc; - con->percent = 1.0; - con->floating = FLOATING_USER_ON; - - /* 4: set the border style as specified with new_float */ - if (automatic) - con->border_style = config.default_floating_border; - /* 5: Subtract the deco_height in order to make the floating window appear * at precisely the position it specified in its original geometry (which * is what applications might remember). */ - deco_height = (con->border_style == BS_NORMAL ? config.font.height + 5 : 0); + deco_height = (con->border_style == BS_NORMAL ? render_deco_height() : 0); nc->rect.y -= deco_height; DLOG("Corrected y = %d (deco_height = %d)\n", nc->rect.y, deco_height); @@ -265,6 +315,8 @@ void floating_disable(Con *con, bool automatic) { return; } + const bool set_focus = (con == focused); + Con *ws = con_get_workspace(con); /* 1: detach from parent container */ @@ -294,8 +346,9 @@ void floating_disable(Con *con, bool automatic) { con_attach(con, con->parent, false); con_fix_percent(con->parent); - // TODO: don’t influence focus handling when Con was not focused before. - con_focus(con); + + if (set_focus) + con_focus(con); } /* @@ -371,6 +424,8 @@ DRAGGING_CB(drag_window_callback) { /* Check if we cross workspace boundaries while moving */ if (!floating_maybe_reassign_ws(con)) return; + /* Ensure not to warp the pointer while dragging */ + x_set_warp_to(NULL); tree_render(); } @@ -387,7 +442,12 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event) { tree_render(); /* Drag the window */ - drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, drag_window_callback, event); + drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, XCURSOR_CURSOR_MOVE, drag_window_callback, event); + + /* If this is a scratchpad window, don't auto center it from now on. */ + if (con->scratchpad_state == SCRATCHPAD_FRESH) + con->scratchpad_state = SCRATCHPAD_CHANGED; + tree_render(); } @@ -426,26 +486,27 @@ DRAGGING_CB(resize_window_callback) { 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 */ - Rect minimum = con_minimum_size(con); - dest_width = max(dest_width, minimum.width); - dest_height = max(dest_height, minimum.height); - /* User wants to keep proportions, so we may have to adjust our values */ if (params->proportional) { dest_width = max(dest_width, (int) (dest_height * ratio)); dest_height = max(dest_height, (int) (dest_width / ratio)); } + con->rect = (Rect) { dest_x, dest_y, dest_width, dest_height }; + + /* Obey window size */ + floating_check_size(con); + /* If not the lower right corner is grabbed, we must also reposition * the client by exactly the amount we resized it */ if (corner & BORDER_LEFT) - dest_x = old_rect->x + (old_rect->width - dest_width); + dest_x = old_rect->x + (old_rect->width - con->rect.width); if (corner & BORDER_TOP) - dest_y = old_rect->y + (old_rect->height - dest_height); + dest_y = old_rect->y + (old_rect->height - con->rect.height); - con->rect = (Rect) { dest_x, dest_y, dest_width, dest_height }; + con->rect.x = dest_x; + con->rect.y = dest_y; /* TODO: don’t re-render the whole tree just because we change * coordinates of a floating window */ @@ -471,13 +532,25 @@ void floating_resize_window(Con *con, const bool proportional, corner |= BORDER_LEFT; else corner |= BORDER_RIGHT; - if (event->event_y <= (con->rect.height / 2)) + int cursor = 0; + if (event->event_y <= (con->rect.height / 2)) { corner |= BORDER_TOP; - else corner |= BORDER_BOTTOM; + cursor = (corner & BORDER_LEFT) ? + XCURSOR_CURSOR_TOP_LEFT_CORNER : XCURSOR_CURSOR_TOP_RIGHT_CORNER; + } + else { + corner |= BORDER_BOTTOM; + cursor = (corner & BORDER_LEFT) ? + XCURSOR_CURSOR_BOTTOM_LEFT_CORNER : XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER; + } struct resize_window_callback_params params = { corner, proportional, event }; - drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, resize_window_callback, ¶ms); + drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, cursor, resize_window_callback, ¶ms); + + /* If this is a scratchpad window, don't auto center it from now on. */ + if (con->scratchpad_state == SCRATCHPAD_FRESH) + con->scratchpad_state = SCRATCHPAD_CHANGED; } /* @@ -489,13 +562,16 @@ void floating_resize_window(Con *con, const bool proportional, * */ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t - confine_to, border_t border, callback_t callback, const void *extra) + confine_to, border_t border, int cursor, callback_t callback, const void *extra) { uint32_t new_x, new_y; - Rect old_rect; + Rect old_rect = { 0, 0, 0, 0 }; if (con != NULL) memcpy(&old_rect, &(con->rect), sizeof(Rect)); + Cursor xcursor = (cursor && xcursor_supported) ? + xcursor_get_cursor(cursor) : XCB_NONE; + /* Grab the pointer */ xcb_grab_pointer_cookie_t cookie; xcb_grab_pointer_reply_t *reply; @@ -506,7 +582,7 @@ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */ XCB_GRAB_MODE_ASYNC, /* keyboard mode */ confine_to, /* confine_to = in which window should the cursor stay */ - XCB_NONE, /* don’t display a special cursor */ + xcursor, /* possibly display a special cursor */ XCB_CURRENT_TIME); if ((reply = xcb_grab_pointer_reply(conn, cookie, NULL)) == NULL) { @@ -562,7 +638,7 @@ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t free(inside_event); } while ((inside_event = xcb_poll_for_event(conn)) != NULL); - if (last_motion_notify == NULL) + if (last_motion_notify == NULL || loop_done) continue; new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x; @@ -586,11 +662,7 @@ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t void floating_reposition(Con *con, Rect newrect) { /* Sanity check: Are the new coordinates on any output? If not, we * ignore that request. */ - Output *output = get_output_containing( - newrect.x + (newrect.width / 2), - newrect.y + (newrect.height / 2)); - - if (!output) { + if (!contained_by_output(newrect)) { ELOG("No output found at destination coordinates. Not repositioning.\n"); return; } @@ -598,6 +670,11 @@ void floating_reposition(Con *con, Rect newrect) { con->rect = newrect; floating_maybe_reassign_ws(con); + + /* If this is a scratchpad window, don't auto center it from now on. */ + if (con->scratchpad_state == SCRATCHPAD_FRESH) + con->scratchpad_state = SCRATCHPAD_CHANGED; + tree_render(); } @@ -607,19 +684,26 @@ void floating_reposition(Con *con, Rect newrect) { * */ void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) { - DLOG("Fixing coordinates of floating window %p\n", con); + DLOG("Fixing coordinates of floating window %p (rect (%d, %d), %d x %d)\n", + con, con->rect.x, con->rect.y, con->rect.width, con->rect.height); + DLOG("old_rect = (%d, %d), %d x %d\n", + old_rect->x, old_rect->y, old_rect->width, old_rect->height); + DLOG("new_rect = (%d, %d), %d x %d\n", + new_rect->x, new_rect->y, new_rect->width, new_rect->height); /* First we get the x/y coordinates relative to the x/y coordinates * of the output on which the window is on */ - uint32_t rel_x = (con->rect.x - old_rect->x); - uint32_t rel_y = (con->rect.y - old_rect->y); + int32_t rel_x = con->rect.x - old_rect->x + (int32_t)(con->rect.width / 2); + int32_t rel_y = con->rect.y - old_rect->y + (int32_t)(con->rect.height / 2); /* Then we calculate a fraction, for example 0.63 for a window * which is at y = 1212 of a 1920 px high output */ DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n", rel_x, rel_y, (double)rel_x / old_rect->width, (double)rel_y / old_rect->height, old_rect->width, old_rect->height); /* Here we have to multiply at first. Or we will lose precision when not compiled with -msse2 */ - con->rect.x = new_rect->x + (double)(rel_x * new_rect->width) / old_rect->width; - con->rect.y = new_rect->y + (double)(rel_y * new_rect->height) / old_rect->height; + con->rect.x = (int32_t)new_rect->x + (double)(rel_x * (int32_t)new_rect->width) + / (int32_t)old_rect->width - (int32_t)(con->rect.width / 2); + con->rect.y = (int32_t)new_rect->y + (double)(rel_y * (int32_t)new_rect->height) + / (int32_t)old_rect->height - (int32_t)(con->rect.height / 2); DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y); }