diff --git a/include/data.h b/include/data.h index b4dcd178..c1e10613 100644 --- a/include/data.h +++ b/include/data.h @@ -169,6 +169,8 @@ struct Container { /* Width/Height of the container. Changeable by the user */ int width; int height; + float width_factor; + float height_factor; /* Backpointer to the workspace this container is in */ Workspace *workspace; diff --git a/include/layout.h b/include/layout.h index 41d52a3f..4d7f0d9c 100644 --- a/include/layout.h +++ b/include/layout.h @@ -13,6 +13,7 @@ #ifndef _LAYOUT_H #define _LAYOUT_H +Rect get_unoccupied_space(Workspace *workspace); void decorate_window(xcb_connection_t *conn, Client *client); void render_layout(xcb_connection_t *conn); diff --git a/include/xcb.h b/include/xcb.h index 5a7a9a51..d234ac20 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -24,5 +24,6 @@ enum { _NET_SUPPORTED = 0, }; uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex); +xcb_window_t create_window(xcb_connection_t *conn, Rect r, uint16_t window_class, uint32_t mask, uint32_t *values); #endif diff --git a/src/handlers.c b/src/handlers.c index b8ae19d4..f1a247ee 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -128,8 +128,11 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ printf("button press!\n"); /* This was either a focus for a client’s parent (= titlebar)… */ Client *client = table_get(byChild, event->event); - if (client == NULL) + bool border_click = false; + if (client == NULL) { client = table_get(byParent, event->event); + border_click = true; + } if (client == NULL) return 1; @@ -140,56 +143,57 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ set_focus(conn, client); /* Let’s see if this was on the borders (= resize). If not, we’re done */ - i3Font *font = load_font(conn, pattern); printf("press button on x=%d, y=%d\n", event->event_x, event->event_y); - if (event->event_y <= (font->height + 2)) - return 1; - printf("that was resize\n"); + Container *con = client->container, + *first = NULL, + *second = NULL; + + printf("event->event_x = %d, client->rect.width = %d\n", event->event_x, client->rect.width); + + if (!border_click) { + printf("client. done.\n"); + return 1; + } + + if (event->event_y < 2) { + /* This was a press on the top border */ + if (con->row == 0) + return 1; + return 0; /* TODO: impl */ + //neighbor_con = this_con->workspace->table[this_con->col][this_con->row-1]; + } else if (event->event_y >= (client->rect.height - 2)) { + /* …bottom border */ + if (con->row == (con->workspace->rows-1)) + return 1; + return 0; /* TODO; impl */ + //neighbor_con = this_con->workspace->table[this_con->col][this_con->row+1]; + } else if (event->event_x < 2) { + /* …left border */ + if (con->col == 0) + return 1; + first = con->workspace->table[con->col-1][con->row]; + second = con; + } else if (event->event_x > 2) { + /* …right border */ + if (con->col == (con->workspace->cols-1)) + return 1; + first = con; + second = con->workspace->table[con->col+1][con->row]; + } /* Open a new window, the resizebar. Grab the pointer and move the window around as the user moves the pointer. */ + Rect grabrect = {0, 0, root_screen->width_in_pixels, root_screen->height_in_pixels}; + xcb_window_t grabwin = create_window(conn, grabrect, XCB_WINDOW_CLASS_INPUT_ONLY, 0, NULL); + Rect helprect = {event->root_x, 0, 2, root_screen->height_in_pixels /* this has to be the cell’s height */}; + xcb_window_t helpwin = create_window(conn, helprect, XCB_WINDOW_CLASS_INPUT_OUTPUT, 0, NULL); - /* TODO: the whole logic is missing. this is just a proof of concept */ - xcb_window_t grabwin = xcb_generate_id(conn); + uint32_t values[1] = {get_colorpixel(conn, helpwin, "#4c7899")}; + xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(conn, helpwin, XCB_CW_BACK_PIXEL, values); + check_error(conn, cookie, "Could not change window attributes (background color)"); - uint32_t mask = 0; - uint32_t values[3]; - - xcb_create_window(conn, - 0, - grabwin, - root, - 0, /* x */ - 0, /* y */ - root_screen->width_in_pixels, /* width */ - root_screen->height_in_pixels, /* height */ - /* border_width */ 0, - XCB_WINDOW_CLASS_INPUT_ONLY, - root_screen->root_visual, - 0, - values); - - /* Map the window on the screen (= make it visible) */ - xcb_map_window(conn, grabwin); - - xcb_window_t helpwin = xcb_generate_id(conn); - - mask = XCB_CW_BACK_PIXEL; - values[0] = root_screen->white_pixel; - xcb_create_window(conn, root_screen->root_depth, helpwin, root, - event->root_x, - 0, - 5, - root_screen->height_in_pixels, - /* bordor */ 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - root_screen->root_visual, - mask, - values); - - xcb_map_window(conn, helpwin); xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, helpwin); xcb_grab_pointer(conn, false, root, XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, @@ -203,7 +207,9 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ /* Same as get_event_handler in xcb */ int nr = inside_event->response_type; if (nr == 0) { + /* An error occured */ handle_event(NULL, conn, inside_event); + free(inside_event); continue; } assert(nr < 256); @@ -237,6 +243,21 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_ xcb_destroy_window(conn, grabwin); xcb_flush(conn); + Workspace *ws = con->workspace; + + printf("Resize was from X = %d to X = %d\n", event->root_x, values[0]); + + /* Convert 0 (for default width_factor) to actual numbers */ + if (first->width_factor == 0) + first->width_factor = ((float)ws->rect.width / ws->cols) / ws->rect.width; + if (second->width_factor == 0) + second->width_factor = ((float)ws->rect.width / ws->cols) / ws->rect.width; + + first->width_factor *= (float)(first->width + (values[0] - event->root_x)) / first->width; + second->width_factor *= (float)(second->width - (values[0] - event->root_x)) / second->width; + + render_layout(conn); + return 1; } diff --git a/src/layout.c b/src/layout.c index 7145aacf..a3b87f17 100644 --- a/src/layout.c +++ b/src/layout.c @@ -7,6 +7,8 @@ * * See file LICENSE for license information. * + * layout.c: Functions handling layout/drawing of window decorations + * */ #define _GNU_SOURCE #include @@ -21,7 +23,42 @@ #include "util.h" #include "xinerama.h" -/* All functions handling layout/drawing of window decorations */ +/* + * For resizing containers (= cells), we need to know the space which is unoccupied by "default" + * windows. The resized containers will be rendered relatively to this space, meaning that newly + * created columns/rows after a container was resized will start with their normal size. + * + */ +Rect get_unoccupied_space(Workspace *workspace) { + printf("getting unoccupied space\n"); + float default_factor_w = ((float)workspace->rect.width / (float)workspace->cols) / (float)workspace->rect.width; + float default_factor_h = (workspace->rect.height / workspace->rows) / workspace->rect.height; + Rect result = {0, 0, workspace->rect.width, workspace->rect.height}; + + printf("default factor is %f and %f\n", default_factor_w, default_factor_h); + printf("start w = %d, h = %d\n", result.width, result.height); + /* TODO: colspan/rowspan*/ + + for (int cols = 0; cols < workspace->cols; cols++) + for (int rows = 0; rows < workspace->rows; rows++) { + printf("oh hai. wf[%d][%d] = %f\n", cols, rows, workspace->table[cols][rows]->width_factor); + if (workspace->table[cols][rows]->width_factor == 0) + result.width -= workspace->rect.width * default_factor_w; + if (workspace->table[cols][rows]->height_factor == 0) + result.height -= workspace->rect.height * default_factor_h; + } + + /* If every container is using the default factor, we have the whole space available */ + if (result.width == 0) + result.width = workspace->rect.width; + + if (result.height == 0) + result.height = workspace->rect.height; + + printf("unoccupied x = %d, unoccupied y = %d\n", result.width, result.height); + + return result; +} /* * (Re-)draws window decorations for a given Client @@ -112,9 +149,9 @@ static void render_container(xcb_connection_t *connection, Container *container) /* Check if we changed client->x or client->y by updating it… * Note the bitwise OR instead of logical OR to force evaluation of both statements */ if (client->force_reconfigure | - (client->rect.x != (client->rect.x = container->x + (container->col * container->width))) | - (client->rect.y != (client->rect.y = container->y + (container->row * container->height + - (container->height / num_clients) * current_client)))) { + (client->rect.x != (client->rect.x = container->x)) | + (client->rect.y != (client->rect.y = container->y + + (container->height / num_clients) * current_client))) { printf("frame needs to be pushed to %dx%d\n", client->rect.x, client->rect.y); /* Note: We can use a pointer to client->x like an array of uint32_ts because it is followed by client->y by definition */ @@ -126,6 +163,7 @@ static void render_container(xcb_connection_t *connection, Container *container) if (client->force_reconfigure | (client->rect.width != (client->rect.width = container->width)) | (client->rect.height != (client->rect.height = container->height / num_clients))) { + printf("resizing client to %d x %d\n", client->rect.width, client->rect.height); xcb_configure_window(connection, client->frame, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, &(client->rect.width)); @@ -171,27 +209,37 @@ void render_layout(xcb_connection_t *connection) { continue; int width = r_ws->rect.width; int height = r_ws->rect.height; + int x = r_ws->rect.x; + int y = r_ws->rect.y; printf("got %d rows and %d cols\n", r_ws->rows, r_ws->cols); printf("each of them therefore is %d px width and %d px height\n", width / r_ws->cols, height / r_ws->rows); + Rect space = get_unoccupied_space(r_ws); + printf("got %d / %d unoc space\n", space.width, space.height); + /* Go through the whole table and render what’s necessary */ for (int cols = 0; cols < r_ws->cols; cols++) for (int rows = 0; rows < r_ws->rows; rows++) { Container *container = r_ws->table[cols][rows]; printf("container has %d colspan, %d rowspan\n", container->colspan, container->rowspan); + printf("container at %d, %d\n", x, y); /* Update position of the container */ container->row = rows; container->col = cols; - container->x = r_ws->rect.x; - container->y = r_ws->rect.y; - container->width = (width / r_ws->cols) * container->colspan; + container->x = x; + container->y = y; + if (container->width_factor == 0) + container->width = (width / r_ws->cols) * container->colspan; + else container->width = space.width * container->width_factor; container->height = (height / r_ws->rows) * container->rowspan; /* Render it */ render_container(connection, container); + + x += container->width; } } diff --git a/src/mainx.c b/src/mainx.c index 9bdd3ae3..70a0f819 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -162,30 +162,18 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, printf("Reparenting 0x%08x under 0x%08x.\n", child, new->frame); i3Font *font = load_font(conn, pattern); - - height = min(height, c_ws->rect.y + c_ws->rect.height); width = min(width, c_ws->rect.x + c_ws->rect.width); + height = min(height, c_ws->rect.y + c_ws->rect.height); + + Rect framerect = {x, y, + width + 2 + 2, /* 2 px border at each side */ + height + 2 + 2 + font->height}; /* 2 px border plus font’s height */ /* Yo dawg, I heard you like windows, so I create a window around your window… */ - xcb_void_cookie_t cookie = xcb_create_window_checked(conn, - depth, - new->frame, - root, - x, - y, - width + 2 + 2, /* 2 px border at each side */ - height + 2 + 2 + font->height, /* 2 px border plus font’s height */ - 0, /* border_width = 0, we draw our own borders */ - XCB_WINDOW_CLASS_INPUT_OUTPUT, - XCB_WINDOW_CLASS_COPY_FROM_PARENT, - mask, - values); - printf("x = %d, y = %d, width = %d, height = %d\n", x, y, width, height); - check_error(conn, cookie, "Could not create frame"); - xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child); + new->frame = create_window(conn, framerect, XCB_WINDOW_CLASS_INPUT_OUTPUT, mask, values); - /* Map the window on the screen (= make it visible) */ - xcb_map_window(conn, new->frame); + /* TODO: document */ + xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child); /* Generate a graphics context for the titlebar */ new->titlegc = xcb_generate_id(conn); @@ -197,7 +185,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, /* Moves the original window into the new frame we've created for it */ new->awaiting_useless_unmap = true; - cookie = xcb_reparent_window_checked(conn, child, new->frame, 0, font->height); + xcb_void_cookie_t cookie = xcb_reparent_window_checked(conn, child, new->frame, 0, font->height); check_error(conn, cookie, "Could not reparent window"); /* We are interested in property changes */ diff --git a/src/xcb.c b/src/xcb.c index f1563afb..176f2692 100644 --- a/src/xcb.c +++ b/src/xcb.c @@ -56,3 +56,29 @@ uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex) xcb_free_colormap(conn, colormap_id); return pixel; } + +xcb_window_t create_window(xcb_connection_t *conn, Rect dims, uint16_t window_class, uint32_t mask, uint32_t *values) { + xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root; + xcb_window_t result = xcb_generate_id(conn); + xcb_void_cookie_t cookie; + + /* If the window class is XCB_WINDOW_CLASS_INPUT_ONLY, depth has to be 0 */ + uint16_t depth = (window_class == XCB_WINDOW_CLASS_INPUT_ONLY ? 0 : XCB_COPY_FROM_PARENT); + + cookie = xcb_create_window_checked(conn, + depth, + result, /* the window id */ + root, /* parent == root */ + dims.x, dims.y, dims.width, dims.height, /* dimensions */ + 0, /* border = 0, we draw our own */ + window_class, + XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */ + mask, + values); + check_error(conn, cookie, "Could not create window"); + + /* Map the window (= make it visible) */ + xcb_map_window(conn, result); + + return result; +}