From: Michael Stapelberg Date: Sun, 11 Jul 2010 20:12:25 +0000 (+0200) Subject: Remove some dead code (to be re-implemented), rename nc.c to main.c X-Git-Tag: tree-pr1~158 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=a79d33fc7fd41ab6e9b853f5356eeec64aa66ef5;p=i3%2Fi3 Remove some dead code (to be re-implemented), rename nc.c to main.c --- diff --git a/Makefile b/Makefile index 0b56d69b..8aaf20cf 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/common.mk # Depend on the object files of all source-files in src/*.c and on all header files AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c src/cmdparse.tab.c src/cmdparse.yy.c -FILES:=src/ipc.c src/nc.c src/log.c src/util.c src/tree.c src/xcb.c src/manage.c src/workspace.c src/x.c src/floating.c src/click.c src/config.c src/handlers.c src/randr.c src/xinerama.c src/con.c src/load_layout.c src/render.c src/window.c src/match.c +FILES:=src/ipc.c src/main.c src/log.c src/util.c src/tree.c src/xcb.c src/manage.c src/workspace.c src/x.c src/floating.c src/click.c src/config.c src/handlers.c src/randr.c src/xinerama.c src/con.c src/load_layout.c src/render.c src/window.c src/match.c FILES:=$(FILES:.c=.o) HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h)) diff --git a/include/all.h b/include/all.h index acf5e337..2890b9c6 100644 --- a/include/all.h +++ b/include/all.h @@ -30,7 +30,6 @@ #include #include "util.h" -#include "commands.h" #include "ipc.h" #include "tree.h" #include "log.h" diff --git a/include/client.h b/include/client.h deleted file mode 100644 index 627cab84..00000000 --- a/include/client.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * © 2009 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ -#include - -#include "data.h" - -#ifndef _CLIENT_H -#define _CLIENT_H - -/** - * Removes the given client from the container, either because it will be - * inserted into another one or because it was unmapped - * - */ -void client_remove_from_container(xcb_connection_t *conn, Client *client, - Container *container, - bool remove_from_focusstack); - -/** - * Warps the pointer into the given client (in the middle of it, to be - * specific), therefore selecting it - * - */ -void client_warp_pointer_into(xcb_connection_t *conn, Client *client); - -/** - * Kills the given window using WM_DELETE_WINDOW or xcb_kill_window - * - */ -void client_kill(xcb_connection_t *conn, Client *window); - -/** - * Checks if the given window class and title match the given client Window - * title is passed as "normal" string and as UCS-2 converted string for - * matching _NET_WM_NAME capable clients as well as those using legacy hints. - * - */ -bool client_matches_class_name(Client *client, char *to_class, char *to_title, - char *to_title_ucs, int to_title_ucs_len); - -/** - * Sets the position of the given client in the X stack to the highest (tiling - * layer is always on the same position, so this doesn’t matter) below the - * first floating client, so that floating windows are always on top. - * - */ -void client_set_below_floating(xcb_connection_t *conn, Client *client); - -/** - * Returns true if the client is floating. Makes the code more beatiful, as - * floating is not simply a boolean, but also saves whether the user selected - * the current state or whether it was automatically set. - * - */ -bool client_is_floating(Client *client); - -/** - * Change the border type for the given client to normal (n), 1px border (p) or - * completely borderless (b). - * - */ -void client_change_border(xcb_connection_t *conn, Client *client, char border_type); - -/** - * Change the border type for the given client to normal (n), 1px border (p) or - * completely borderless (b) without actually re-rendering the layout. Useful - * for calling it when initializing a new client. - * - */ -bool client_init_border(xcb_connection_t *conn, Client *client, char border_type); - -/** - * Unmap the client, correctly setting any state which is needed. - * - */ -void client_unmap(xcb_connection_t *conn, Client *client); - -/** - * Map the client, correctly restoring any state needed. - * - */ -void client_map(xcb_connection_t *conn, Client *client); - -/** - * Set the given mark for this client. Used for jumping to the client - * afterwards (like m and ' in vim). - * - */ -void client_mark(xcb_connection_t *conn, Client *client, const char *mark); - -/** - * Returns the minimum height of a specific window. The height is calculated - * by using 2 pixels (for the client window itself), possibly padding this to - * comply with the client’s base_height and then adding the decoration height. - * - */ -uint32_t client_min_height(Client *client); - -/** - * See client_min_height. - * - */ -uint32_t client_min_width(Client *client); - -/** - * Pretty-prints the client’s information into the logfile. - * - */ -#define CLIENT_LOG(client) do { \ - DLOG("Window: frame 0x%08x, child 0x%08x\n", client->frame, client->child); \ - } while (0) - -#endif diff --git a/include/commands.h b/include/commands.h deleted file mode 100644 index c6b45d1f..00000000 --- a/include/commands.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * (c) 2009 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ -#ifndef _COMMANDS_H -#define _COMMANDS_H - -#include - -#if 0 -bool focus_window_in_container(xcb_connection_t *conn, Container *container, - direction_t direction); -#endif - -/** Parses a command, see file CMDMODE for more information */ -void parse_command(const char *command); - -#endif diff --git a/include/container.h b/include/container.h deleted file mode 100644 index 78938508..00000000 --- a/include/container.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * © 2009 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ -#include "data.h" - -#ifndef _CONTAINER_H -#define _CONTAINER_H - -/** - * Returns the mode of the given container (or MODE_DEFAULT if a NULL pointer - * was passed in order to save a few explicit checks in other places). If - * for_frame was set to true, the special case of having exactly one client - * in a container is handled so that MODE_DEFAULT is returned. For some parts - * of the rendering, this is interesting, other parts need the real mode. - * - */ -int container_mode(Container *con, bool for_frame); - -#endif diff --git a/include/layout.h b/include/layout.h deleted file mode 100644 index 1cbb7837..00000000 --- a/include/layout.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * (c) 2009 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ -#include - -#ifndef _LAYOUT_H -#define _LAYOUT_H - -/** - * Gets the unoccupied space (= space which is available for windows which - * were resized by the user) This is necessary to render both, customly - * resized windows and never touched windows correctly, meaning that the - * aspect ratio will be maintained when opening new windows. - * - */ -int get_unoccupied_x(Workspace *workspace); - -/** See get_unoccupied_x */ -int get_unoccupied_y(Workspace *workspace); - -/** - * (Re-)draws window decorations for a given Client onto the given - * drawable/graphic context. When in stacking mode, the window decorations - * are drawn onto an own window. - * - */ -void decorate_window(xcb_connection_t *conn, Client *client, - xcb_drawable_t drawable, xcb_gcontext_t gc, - int offset_x, int offset_y); - -/** - * Redecorates the given client correctly by checking if it’s in a stacking - * container and re-rendering the stack window or just calling decorate_window - * if it’s not in a stacking container. - * - */ -void redecorate_window(xcb_connection_t *conn, Client *client); - -/** - * Pushes the client’s x and y coordinates to X11 - * - */ -void reposition_client(xcb_connection_t *conn, Client *client); - -/** - * Pushes the client’s width/height to X11 and resizes the child window. This - * function also updates the client’s position, so if you work on tiling clients - * only, you can use this function instead of separate calls to reposition_client - * and resize_client to reduce flickering. - * - */ -void resize_client(xcb_connection_t *conn, Client *client); - -/** - * Renders the given container. Is called by render_layout() or individually - * (for example when focus changes in a stacking container) - * - */ -void render_container(xcb_connection_t *conn, Container *container); - -/** - * Modifies the event mask of all clients on the given workspace to either - * ignore or to handle enter notifies. It is handy to ignore notifies because - * they will be sent when a window is mapped under the cursor, thus when the - * user didn’t enter the window actively at all. - * - */ -void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, - bool ignore_enter_notify); - -/** - * Renders the given workspace on the given screen - * - */ -void render_workspace(xcb_connection_t *conn, Output *output, Workspace *r_ws); - -/** - * Renders the whole layout, that is: Go through each screen, each workspace, - * each container and render each client. This also renders the bars. - * - * If you don’t need to render *everything*, you should call render_container - * on the container you want to refresh. - * - */ -void render_layout(xcb_connection_t *conn); - -#endif diff --git a/include/resize.h b/include/resize.h deleted file mode 100644 index c187e837..00000000 --- a/include/resize.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * (c) 2009 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ - -#ifndef _RESIZE_H -#define _RESIZE_H - -#include - -typedef enum { O_HORIZONTAL, O_VERTICAL } resize_orientation_t; - -/** - * Renders the resize window between the first/second container and resizes - * the table column/row. - * - */ -int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, - int second, resize_orientation_t orientation, - xcb_button_press_event_t *event); -/** - * Resizes a column/row by the given amount of pixels. Called by - * resize_graphical_handler (the user clicked) or parse_resize_command (the - * user issued the command) - * - */ -void resize_container(xcb_connection_t *conn, Workspace *ws, int first, int second, - resize_orientation_t orientation, int pixels); - -#endif diff --git a/src/client.c b/src/client.c deleted file mode 100644 index 43fcd7b4..00000000 --- a/src/client.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * © 2009-2010 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - * client.c: holds all client-specific functions - * - */ -#include -#include -#include -#include - -#include -#include - -#include "data.h" -#include "i3.h" -#include "xcb.h" -#include "util.h" -#include "queue.h" -#include "layout.h" -#include "client.h" -#include "table.h" -#include "workspace.h" -#include "config.h" -#include "log.h" - -/* - * Removes the given client from the container, either because it will be inserted into another - * one or because it was unmapped - * - */ -void client_remove_from_container(xcb_connection_t *conn, Client *client, Container *container, bool remove_from_focusstack) { - CIRCLEQ_REMOVE(&(container->clients), client, clients); - - if (remove_from_focusstack) - SLIST_REMOVE(&(container->workspace->focus_stack), client, Client, focus_clients); - - /* If the container will be empty now and is in stacking mode, we need to - unmap the stack_win */ - if (CIRCLEQ_EMPTY(&(container->clients)) && - (container->mode == MODE_STACK || - container->mode == MODE_TABBED)) { - DLOG("Unmapping stack window\n"); - struct Stack_Window *stack_win = &(container->stack_win); - stack_win->rect.height = 0; - xcb_unmap_window(conn, stack_win->window); - xcb_flush(conn); - } -} - -/* - * Warps the pointer into the given client (in the middle of it, to be specific), therefore - * selecting it - * - */ -void client_warp_pointer_into(xcb_connection_t *conn, Client *client) { - int mid_x = client->rect.width / 2, - mid_y = client->rect.height / 2; - xcb_warp_pointer(conn, XCB_NONE, client->child, 0, 0, 0, 0, mid_x, mid_y); -} - -/* - * Returns true if the client supports the given protocol atom (like WM_DELETE_WINDOW) - * - */ -static bool client_supports_protocol(xcb_connection_t *conn, Client *client, xcb_atom_t atom) { - xcb_get_property_cookie_t cookie; - xcb_get_wm_protocols_reply_t protocols; - bool result = false; - - cookie = xcb_get_wm_protocols_unchecked(conn, client->child, atoms[WM_PROTOCOLS]); - if (xcb_get_wm_protocols_reply(conn, cookie, &protocols, NULL) != 1) - return false; - - /* Check if the client’s protocols have the requested atom set */ - for (uint32_t i = 0; i < protocols.atoms_len; i++) - if (protocols.atoms[i] == atom) - result = true; - - xcb_get_wm_protocols_reply_wipe(&protocols); - - return result; -} - -/* - * Kills the given window using WM_DELETE_WINDOW or xcb_kill_window - * - */ -void client_kill(xcb_connection_t *conn, Client *window) { - /* If the client does not support WM_DELETE_WINDOW, we kill it the hard way */ - if (!client_supports_protocol(conn, window, atoms[WM_DELETE_WINDOW])) { - LOG("Killing window the hard way\n"); - xcb_kill_client(conn, window->child); - return; - } - - xcb_client_message_event_t ev; - - memset(&ev, 0, sizeof(xcb_client_message_event_t)); - - ev.response_type = XCB_CLIENT_MESSAGE; - ev.window = window->child; - ev.type = atoms[WM_PROTOCOLS]; - ev.format = 32; - ev.data.data32[0] = atoms[WM_DELETE_WINDOW]; - ev.data.data32[1] = XCB_CURRENT_TIME; - - LOG("Sending WM_DELETE to the client\n"); - xcb_send_event(conn, false, window->child, XCB_EVENT_MASK_NO_EVENT, (char*)&ev); - xcb_flush(conn); -} - -/* - * Checks if the given window class and title match the given client - * Window title is passed as "normal" string and as UCS-2 converted string for - * matching _NET_WM_NAME capable clients as well as those using legacy hints. - * - */ -bool client_matches_class_name(Client *client, char *to_class, char *to_title, - char *to_title_ucs, int to_title_ucs_len) { - /* Check if the given class is part of the window class */ - if ((client->window_class_instance == NULL || - strcasestr(client->window_class_instance, to_class) == NULL) && - (client->window_class_class == NULL || - strcasestr(client->window_class_class, to_class) == NULL)) - return false; - - /* If no title was given, we’re done */ - if (to_title == NULL) - return true; - - if (client->name_len > -1) { - /* UCS-2 converted window titles */ - if (client->name == NULL || memmem(client->name, (client->name_len * 2), to_title_ucs, (to_title_ucs_len * 2)) == NULL) - return false; - } else { - /* Legacy hints */ - if (client->name == NULL || strcasestr(client->name, to_title) == NULL) - return false; - } - - return true; -} - -/* - * Sets the position of the given client in the X stack to the highest (tiling layer is always - * on the same position, so this doesn’t matter) below the first floating client, so that - * floating windows are always on top. - * - */ -void client_set_below_floating(xcb_connection_t *conn, Client *client) { - /* Ensure that it is below all floating clients */ - Workspace *ws = client->workspace; - Client *first_floating = TAILQ_FIRST(&(ws->floating_clients)); - if (first_floating == TAILQ_END(&(ws->floating_clients))) - return; - - DLOG("Setting below floating\n"); - uint32_t values[] = { first_floating->frame, XCB_STACK_MODE_BELOW }; - xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values); - - if (client->workspace->fullscreen_client == NULL) - return; - - DLOG("(and below fullscreen)\n"); - /* Ensure that the window is still below the fullscreen window */ - values[0] = client->workspace->fullscreen_client->frame; - xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values); -} - -/* - * Returns true if the client is floating. Makes the code more beatiful, as floating - * is not simply a boolean, but also saves whether the user selected the current state - * or whether it was automatically set. - * - */ -bool client_is_floating(Client *client) { - return (client->floating >= FLOATING_AUTO_ON); -} - -/* - * Change the border type for the given client to normal (n), 1px border (p) or - * completely borderless (b) without actually re-rendering the layout. Useful - * for calling it when initializing a new client. - * - */ -bool client_init_border(xcb_connection_t *conn, Client *client, char border_type) { - switch (border_type) { - case 'n': - LOG("Changing to normal border\n"); - client->titlebar_position = TITLEBAR_TOP; - client->borderless = false; - return true; - case 'p': - LOG("Changing to 1px border\n"); - client->titlebar_position = TITLEBAR_OFF; - client->borderless = false; - return true; - case 'b': - LOG("Changing to borderless\n"); - client->titlebar_position = TITLEBAR_OFF; - client->borderless = true; - return true; - default: - LOG("Unknown border mode\n"); - return false; - } -} - -/* - * Change the border type for the given client to normal (n), 1px border (p) or - * completely borderless (b). - * - */ -void client_change_border(xcb_connection_t *conn, Client *client, char border_type) { - if (!client_init_border(conn, client, border_type)) - return; - - /* Ensure that the child’s position inside our window gets updated */ - client->force_reconfigure = true; - - /* For clients inside a container, we can simply render the container */ - if (client->container != NULL) - render_container(conn, client->container); - else { - /* If the client is floating, directly push its size */ - if (client_is_floating(client)) - resize_client(conn, client); - /* Otherwise, it may be a dock client, thus render the whole layout */ - else render_layout(conn); - } - - redecorate_window(conn, client); -} - -/* - * Unmap the client, correctly setting any state which is needed. - * - */ -void client_unmap(xcb_connection_t *conn, Client *client) { - /* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */ - long data[] = { XCB_WM_STATE_WITHDRAWN, XCB_NONE }; - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data); - - xcb_unmap_window(conn, client->frame); -} - -/* - * Map the client, correctly restoring any state needed. - * - */ -void client_map(xcb_connection_t *conn, Client *client) { - /* Set WM_STATE_NORMAL because GTK applications don’t want to drag & drop if we don’t. - * Also, xprop(1) needs that to work. */ - long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE }; - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data); - - xcb_map_window(conn, client->frame); -} - -/* - * Set the given mark for this client. Used for jumping to the client - * afterwards (like m and ' in vim). - * - */ -void client_mark(xcb_connection_t *conn, Client *client, const char *mark) { - if (client->mark != NULL) - free(client->mark); - client->mark = sstrdup(mark); - - /* Make sure no other client has this mark set */ - Client *current; - Workspace *ws; - TAILQ_FOREACH(ws, workspaces, workspaces) - SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) { - if (current == client || - current->mark == NULL || - strcmp(current->mark, mark) != 0) - continue; - - free(current->mark); - current->mark = NULL; - /* We can break here since there can only be one other - * client with this mark. */ - break; - } -} - -/* - * Returns the minimum height of a specific window. The height is calculated - * by using 2 pixels (for the client window itself), possibly padding this to - * comply with the client’s base_height and then adding the decoration height. - * - */ -uint32_t client_min_height(Client *client) { - uint32_t height = max(2, client->base_height); - i3Font *font = load_font(global_conn, config.font); - - if (client->titlebar_position == TITLEBAR_OFF && client->borderless) - return height; - - if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) - return height + 2; - - return height + font->height + 2 + 2; -} - -/* - * See client_min_height. - * - */ -uint32_t client_min_width(Client *client) { - uint32_t width = max(2, client->base_width); - - if (client->titlebar_position == TITLEBAR_OFF && client->borderless) - return width; - - if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) - return width + 2; - - return width + 2 + 2; -} diff --git a/src/commands.c b/src/commands.c deleted file mode 100644 index 2d8155f2..00000000 --- a/src/commands.c +++ /dev/null @@ -1,1301 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * © 2009-2010 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ -#include -#include -#include -#include -#include -#include - -#include - -#include "util.h" -#include "data.h" -#include "table.h" -#include "layout.h" -#include "i3.h" -#include "randr.h" -#include "client.h" -#include "floating.h" -#include "xcb.h" -#include "config.h" -#include "workspace.h" -#include "commands.h" -#include "resize.h" -#include "log.h" -#include "sighandler.h" -#include "manage.h" -#include "ipc.h" - -bool focus_window_in_container(xcb_connection_t *conn, Container *container, direction_t direction) { - /* If this container is empty, we’re done */ - if (container->currently_focused == NULL) - return false; - - /* Get the previous/next client or wrap around */ - Client *candidate = NULL; - if (direction == D_UP) { - if ((candidate = CIRCLEQ_PREV_OR_NULL(&(container->clients), container->currently_focused, clients)) == NULL) - candidate = CIRCLEQ_LAST(&(container->clients)); - } - else if (direction == D_DOWN) { - if ((candidate = CIRCLEQ_NEXT_OR_NULL(&(container->clients), container->currently_focused, clients)) == NULL) - candidate = CIRCLEQ_FIRST(&(container->clients)); - } else ELOG("Direction not implemented!\n"); - - /* If we could not switch, the container contains exactly one client. We return false */ - if (candidate == container->currently_focused) - return false; - - /* Set focus */ - set_focus(conn, candidate, true); - - return true; -} - -typedef enum { THING_WINDOW, THING_CONTAINER, THING_SCREEN } thing_t; - -static void jump_to_mark(xcb_connection_t *conn, const char *mark) { - Client *current; - LOG("Jumping to \"%s\"\n", mark); - - Workspace *ws; - TAILQ_FOREACH(ws, workspaces, workspaces) - SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) { - if (current->mark == NULL || strcmp(current->mark, mark) != 0) - continue; - - workspace_show(conn, current->workspace->num + 1); - set_focus(conn, current, true); - return; - } - - ELOG("No window with this mark found\n"); -} - -static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t thing) { - DLOG("focusing direction %d\n", direction); - - int new_row = current_row, - new_col = current_col; - Container *container = CUR_CELL; - Workspace *t_ws = c_ws; - - /* Makes sure new_col and new_row are within bounds of the new workspace */ -#define CHECK_COLROW_BOUNDARIES \ - do { \ - if (new_col >= t_ws->cols) \ - new_col = (t_ws->cols - 1); \ - if (new_row >= t_ws->rows) \ - new_row = (t_ws->rows - 1); \ - } while (0) - - /* There always is a container. If not, current_col or current_row is wrong */ - assert(container != NULL); - - if (container->workspace->fullscreen_client != NULL) { - LOG("You're in fullscreen mode. Forcing focus to operate on whole screens\n"); - thing = THING_SCREEN; - } - - /* For focusing screens, situation is different: we get the rect - * of the current screen, then get the screen which is on its - * right/left/bottom/top and just switch to the workspace on - * the target screen. */ - if (thing == THING_SCREEN) { - Output *cs = c_ws->output; - assert(cs != NULL); - Rect bounds = cs->rect; - - if (direction == D_RIGHT) - bounds.x += bounds.width; - else if (direction == D_LEFT) - bounds.x -= bounds.width; - else if (direction == D_UP) - bounds.y -= bounds.height; - else bounds.y += bounds.height; - - Output *target = get_output_containing(bounds.x, bounds.y); - if (target == NULL) { - DLOG("Target output NULL\n"); - /* Wrap around if the target screen is out of bounds */ - if (direction == D_RIGHT) - target = get_output_most(D_LEFT, cs); - else if (direction == D_LEFT) - target = get_output_most(D_RIGHT, cs); - else if (direction == D_UP) - target = get_output_most(D_DOWN, cs); - else target = get_output_most(D_UP, cs); - } - - DLOG("Switching to ws %d\n", target->current_workspace + 1); - workspace_show(conn, target->current_workspace->num + 1); - return; - } - - /* TODO: for horizontal default layout, this has to be expanded to LEFT/RIGHT */ - if (direction == D_UP || direction == D_DOWN) { - if (thing == THING_WINDOW) - /* Let’s see if we can perform up/down focus in the current container */ - if (focus_window_in_container(conn, container, direction)) - return; - - if (direction == D_DOWN && cell_exists(t_ws, current_col, current_row+1)) - new_row = current_row + t_ws->table[current_col][current_row]->rowspan; - else if (direction == D_UP && cell_exists(c_ws, current_col, current_row-1)) { - /* Set new_row as a sane default, but it may get overwritten in a second */ - new_row--; - - /* Search from the top to correctly handle rowspanned containers */ - for (int rows = 0; rows < current_row; rows += t_ws->table[current_col][rows]->rowspan) { - if (new_row > (rows + (t_ws->table[current_col][rows]->rowspan - 1))) - continue; - - new_row = rows; - break; - } - } else { - /* Let’s see if there is a screen down/up there to which we can switch */ - DLOG("container is at %d with height %d\n", container->y, container->height); - Output *output; - int destination_y = (direction == D_UP ? (container->y - 1) : (container->y + container->height + 1)); - if ((output = get_output_containing(container->x, destination_y)) == NULL) { - DLOG("Wrapping screen around vertically\n"); - /* No screen found? Then wrap */ - output = get_output_most((direction == D_UP ? D_DOWN : D_UP), container->workspace->output); - } - t_ws = output->current_workspace; - new_row = (direction == D_UP ? (t_ws->rows - 1) : 0); - } - - CHECK_COLROW_BOUNDARIES; - - DLOG("new_col = %d, new_row = %d\n", new_col, new_row); - if (t_ws->table[new_col][new_row]->currently_focused == NULL) { - DLOG("Cell empty, checking for colspanned client above...\n"); - for (int cols = 0; cols < new_col; cols += t_ws->table[cols][new_row]->colspan) { - if (new_col > (cols + (t_ws->table[cols][new_row]->colspan - 1))) - continue; - - new_col = cols; - DLOG("Fixed it to new col %d\n", new_col); - break; - } - } - - if (t_ws->table[new_col][new_row]->currently_focused == NULL) { - DLOG("Cell still empty, checking for full cols above spanned width...\n"); - DLOG("new_col = %d\n", new_col); - DLOG("colspan = %d\n", container->colspan); - for (int cols = new_col; - cols < container->col + container->colspan; - cols += t_ws->table[cols][new_row]->colspan) { - DLOG("candidate: new_row = %d, cols = %d\n", new_row, cols); - if (t_ws->table[cols][new_row]->currently_focused == NULL) - continue; - - new_col = cols; - DLOG("Fixed it to new col %d\n", new_col); - break; - } - } - } else if (direction == D_LEFT || direction == D_RIGHT) { - if (direction == D_RIGHT && cell_exists(t_ws, current_col+1, current_row)) - new_col = current_col + t_ws->table[current_col][current_row]->colspan; - else if (direction == D_LEFT && cell_exists(t_ws, current_col-1, current_row)) { - /* Set new_col as a sane default, but it may get overwritten in a second */ - new_col--; - - /* Search from the left to correctly handle colspanned containers */ - for (int cols = 0; cols < current_col; cols += t_ws->table[cols][current_row]->colspan) { - if (new_col > (cols + (t_ws->table[cols][current_row]->colspan - 1))) - continue; - - new_col = cols; - break; - } - } else { - /* Let’s see if there is a screen left/right here to which we can switch */ - DLOG("container is at %d with width %d\n", container->x, container->width); - Output *output; - int destination_x = (direction == D_LEFT ? (container->x - 1) : (container->x + container->width + 1)); - if ((output = get_output_containing(destination_x, container->y)) == NULL) { - DLOG("Wrapping screen around horizontally\n"); - output = get_output_most((direction == D_LEFT ? D_RIGHT : D_LEFT), container->workspace->output); - } - t_ws = output->current_workspace; - new_col = (direction == D_LEFT ? (t_ws->cols - 1) : 0); - } - - CHECK_COLROW_BOUNDARIES; - - DLOG("new_col = %d, new_row = %d\n", new_col, new_row); - if (t_ws->table[new_col][new_row]->currently_focused == NULL) { - DLOG("Cell empty, checking for rowspanned client above...\n"); - for (int rows = 0; rows < new_row; rows += t_ws->table[new_col][rows]->rowspan) { - if (new_row > (rows + (t_ws->table[new_col][rows]->rowspan - 1))) - continue; - - new_row = rows; - DLOG("Fixed it to new row %d\n", new_row); - break; - } - } - - if (t_ws->table[new_col][new_row]->currently_focused == NULL) { - DLOG("Cell still empty, checking for full cols near full spanned height...\n"); - DLOG("new_row = %d\n", new_row); - DLOG("rowspan = %d\n", container->rowspan); - for (int rows = new_row; - rows < container->row + container->rowspan; - rows += t_ws->table[new_col][rows]->rowspan) { - DLOG("candidate: new_col = %d, rows = %d\n", new_col, rows); - if (t_ws->table[new_col][rows]->currently_focused == NULL) - continue; - - new_row = rows; - DLOG("Fixed it to new col %d\n", new_row); - break; - } - } - - } else { - ELOG("direction unhandled\n"); - return; - } - - CHECK_COLROW_BOUNDARIES; - - if (t_ws->table[new_col][new_row]->currently_focused != NULL) - set_focus(conn, t_ws->table[new_col][new_row]->currently_focused, true); -} - -/* - * Tries to move the window inside its current container. - * - * Returns true if the window could be moved, false otherwise. - * - */ -static bool move_current_window_in_container(xcb_connection_t *conn, Client *client, - direction_t direction) { - assert(client->container != NULL); - - Client *other = (direction == D_UP ? CIRCLEQ_PREV(client, clients) : - CIRCLEQ_NEXT(client, clients)); - - if (other == CIRCLEQ_END(&(client->container->clients))) - return false; - - DLOG("i can do that\n"); - /* We can move the client inside its current container */ - CIRCLEQ_REMOVE(&(client->container->clients), client, clients); - if (direction == D_UP) - CIRCLEQ_INSERT_BEFORE(&(client->container->clients), other, client, clients); - else CIRCLEQ_INSERT_AFTER(&(client->container->clients), other, client, clients); - render_layout(conn); - return true; -} - -/* - * Moves the current window or whole container to the given direction, creating a column/row if - * necessary. - * - */ -static void move_current_window(xcb_connection_t *conn, direction_t direction) { - LOG("moving window to direction %s\n", (direction == D_UP ? "up" : (direction == D_DOWN ? "down" : - (direction == D_LEFT ? "left" : "right")))); - /* Get current window */ - Container *container = CUR_CELL, - *new = NULL; - - /* There has to be a container, see focus_window() */ - assert(container != NULL); - - /* If there is no window or the dock window is focused, we’re done */ - if (container->currently_focused == NULL || - container->currently_focused->dock) - return; - - /* As soon as the client is moved away, the last focused client in the old - * container needs to get focus, if any. Therefore, we save it here. */ - Client *current_client = container->currently_focused; - Client *to_focus = get_last_focused_client(conn, container, current_client); - - if (to_focus == NULL) { - to_focus = CIRCLEQ_NEXT_OR_NULL(&(container->clients), current_client, clients); - if (to_focus == NULL) - to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients); - } - - switch (direction) { - case D_LEFT: - /* If we’re at the left-most position, move the rest of the table right */ - if (current_col == 0) { - expand_table_cols_at_head(c_ws); - new = CUR_CELL; - } else - new = CUR_TABLE[--current_col][current_row]; - break; - case D_RIGHT: - if (current_col == (c_ws->cols-1)) - expand_table_cols(c_ws); - - new = CUR_TABLE[++current_col][current_row]; - break; - case D_UP: - if (move_current_window_in_container(conn, current_client, D_UP)) - return; - - /* if we’re at the up-most position, move the rest of the table down */ - if (current_row == 0) { - expand_table_rows_at_head(c_ws); - new = CUR_CELL; - } else - new = CUR_TABLE[current_col][--current_row]; - break; - case D_DOWN: - if (move_current_window_in_container(conn, current_client, D_DOWN)) - return; - - if (current_row == (c_ws->rows-1)) - expand_table_rows(c_ws); - - new = CUR_TABLE[current_col][++current_row]; - break; - /* To make static analyzers happy: */ - default: - return; - } - - /* Remove it from the old container and put it into the new one */ - client_remove_from_container(conn, current_client, container, true); - - if (new->currently_focused != NULL) - CIRCLEQ_INSERT_AFTER(&(new->clients), new->currently_focused, current_client, clients); - else CIRCLEQ_INSERT_TAIL(&(new->clients), current_client, clients); - SLIST_INSERT_HEAD(&(new->workspace->focus_stack), current_client, focus_clients); - - /* Update data structures */ - current_client->container = new; - current_client->workspace = new->workspace; - container->currently_focused = to_focus; - new->currently_focused = current_client; - - Workspace *workspace = container->workspace; - - /* delete all empty columns/rows */ - cleanup_table(conn, workspace); - - /* Fix colspan/rowspan if it’d overlap */ - fix_colrowspan(conn, workspace); - - render_workspace(conn, workspace->output, workspace); - xcb_flush(conn); - - set_focus(conn, current_client, true); -} - -static void move_current_container(xcb_connection_t *conn, direction_t direction) { - LOG("moving container to direction %s\n", (direction == D_UP ? "up" : (direction == D_DOWN ? "down" : - (direction == D_LEFT ? "left" : "right")))); - /* Get current window */ - Container *container = CUR_CELL, - *new = NULL; - - Container **old = &CUR_CELL; - - /* There has to be a container, see focus_window() */ - assert(container != NULL); - - switch (direction) { - case D_LEFT: - /* If we’re at the left-most position, move the rest of the table right */ - if (current_col == 0) { - expand_table_cols_at_head(c_ws); - new = CUR_CELL; - old = &CUR_TABLE[current_col+1][current_row]; - } else - new = CUR_TABLE[--current_col][current_row]; - break; - case D_RIGHT: - if (current_col == (c_ws->cols-1)) - expand_table_cols(c_ws); - - new = CUR_TABLE[++current_col][current_row]; - break; - case D_UP: - /* if we’re at the up-most position, move the rest of the table down */ - if (current_row == 0) { - expand_table_rows_at_head(c_ws); - new = CUR_CELL; - old = &CUR_TABLE[current_col][current_row+1]; - } else - new = CUR_TABLE[current_col][--current_row]; - break; - case D_DOWN: - if (current_row == (c_ws->rows-1)) - expand_table_rows(c_ws); - - new = CUR_TABLE[current_col][++current_row]; - break; - /* To make static analyzers happy: */ - default: - return; - } - - DLOG("old = %d,%d and new = %d,%d\n", container->col, container->row, new->col, new->row); - - /* Swap the containers */ - int col = new->col; - int row = new->row; - - *old = new; - new->col = container->col; - new->row = container->row; - - CUR_CELL = container; - container->col = col; - container->row = row; - - Workspace *workspace = container->workspace; - - /* delete all empty columns/rows */ - cleanup_table(conn, workspace); - - /* Fix colspan/rowspan if it’d overlap */ - fix_colrowspan(conn, workspace); - - render_layout(conn); -} - -/* - * "Snaps" the current container (not possible for windows, because it works at table base) - * to the given direction, that is, adjusts cellspan/rowspan - * - */ -static void snap_current_container(xcb_connection_t *conn, direction_t direction) { - LOG("snapping container to direction %d\n", direction); - - Container *container = CUR_CELL; - - assert(container != NULL); - - switch (direction) { - case D_LEFT: - /* Snap to the left is actually a move to the left and then a snap right */ - if (!cell_exists(container->workspace, container->col - 1, container->row) || - CUR_TABLE[container->col-1][container->row]->currently_focused != NULL) { - ELOG("cannot snap to left - the cell is already used\n"); - return; - } - - move_current_window(conn, D_LEFT); - snap_current_container(conn, D_RIGHT); - return; - case D_RIGHT: { - /* Check if the cell is used */ - int new_col = container->col + container->colspan; - for (int i = 0; i < container->rowspan; i++) - if (!cell_exists(container->workspace, new_col, container->row + i) || - CUR_TABLE[new_col][container->row + i]->currently_focused != NULL) { - ELOG("cannot snap to right - the cell is already used\n"); - return; - } - - /* Check if there are other cells with rowspan, which are in our way. - * If so, reduce their rowspan. */ - for (int i = container->row-1; i >= 0; i--) { - DLOG("we got cell %d, %d with rowspan %d\n", - new_col, i, CUR_TABLE[new_col][i]->rowspan); - while ((CUR_TABLE[new_col][i]->rowspan-1) >= (container->row - i)) - CUR_TABLE[new_col][i]->rowspan--; - DLOG("new rowspan = %d\n", CUR_TABLE[new_col][i]->rowspan); - } - - container->colspan++; - break; - } - case D_UP: - if (!cell_exists(container->workspace, container->col, container->row - 1) || - CUR_TABLE[container->col][container->row-1]->currently_focused != NULL) { - ELOG("cannot snap to top - the cell is already used\n"); - return; - } - - move_current_window(conn, D_UP); - snap_current_container(conn, D_DOWN); - return; - case D_DOWN: { - DLOG("snapping down\n"); - int new_row = container->row + container->rowspan; - for (int i = 0; i < container->colspan; i++) - if (!cell_exists(container->workspace, container->col + i, new_row) || - CUR_TABLE[container->col + i][new_row]->currently_focused != NULL) { - ELOG("cannot snap down - the cell is already used\n"); - return; - } - - for (int i = container->col-1; i >= 0; i--) { - DLOG("we got cell %d, %d with colspan %d\n", - i, new_row, CUR_TABLE[i][new_row]->colspan); - while ((CUR_TABLE[i][new_row]->colspan-1) >= (container->col - i)) - CUR_TABLE[i][new_row]->colspan--; - DLOG("new colspan = %d\n", CUR_TABLE[i][new_row]->colspan); - - } - - container->rowspan++; - break; - } - /* To make static analyzers happy: */ - default: - return; - } - - render_layout(conn); -} - -static void move_floating_window_to_workspace(xcb_connection_t *conn, Client *client, int workspace) { - /* t_ws (to workspace) is just a container pointer to the workspace we’re switching to */ - Workspace *t_ws = workspace_get(workspace-1), - *old_ws = client->workspace; - - LOG("moving floating\n"); - - workspace_initialize(t_ws, c_ws->output, false); - - /* Check if there is already a fullscreen client on the destination workspace and - * stop moving if so. */ - if (client->fullscreen && (t_ws->fullscreen_client != NULL)) { - ELOG("Not moving: Fullscreen client already existing on destination workspace.\n"); - return; - } - - floating_assign_to_workspace(client, t_ws); - - /* If we’re moving it to an invisible screen, we need to unmap it */ - if (!workspace_is_visible(t_ws)) { - DLOG("This workspace is not visible, unmapping\n"); - client_unmap(conn, client); - } else { - /* If this is not the case, we move the window to a workspace - * which is on another screen, so we also need to adjust its - * coordinates. */ - DLOG("before x = %d, y = %d\n", client->rect.x, client->rect.y); - uint32_t relative_x = client->rect.x - old_ws->rect.x, - relative_y = client->rect.y - old_ws->rect.y; - DLOG("rel_x = %d, rel_y = %d\n", relative_x, relative_y); - client->rect.x = t_ws->rect.x + relative_x; - client->rect.y = t_ws->rect.y + relative_y; - DLOG("after x = %d, y = %d\n", client->rect.x, client->rect.y); - reposition_client(conn, client); - xcb_flush(conn); - } - - /* Configure the window above all tiling windows (or below a fullscreen - * window, if any) */ - if (t_ws->fullscreen_client != NULL) { - uint32_t values[] = { t_ws->fullscreen_client->frame, XCB_STACK_MODE_BELOW }; - xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values); - } else { - Client *last_tiling; - SLIST_FOREACH(last_tiling, &(t_ws->focus_stack), focus_clients) - if (!client_is_floating(last_tiling)) - break; - if (last_tiling != SLIST_END(&(t_ws->focus_stack))) { - uint32_t values[] = { last_tiling->frame, XCB_STACK_MODE_ABOVE }; - xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values); - } - } - - DLOG("done\n"); - - render_layout(conn); - - if (workspace_is_visible(t_ws)) { - client_warp_pointer_into(conn, client); - set_focus(conn, client, true); - } -} - -/* - * Moves the currently selected window to the given workspace - * - */ -static void move_current_window_to_workspace(xcb_connection_t *conn, int workspace) { - LOG("Moving current window to workspace %d\n", workspace); - - Container *container = CUR_CELL; - - assert(container != NULL); - - /* t_ws (to workspace) is just a container pointer to the workspace we’re switching to */ - Workspace *t_ws = workspace_get(workspace-1); - - Client *current_client = container->currently_focused; - if (current_client == NULL) { - ELOG("No currently focused client in current container.\n"); - return; - } - Client *to_focus = CIRCLEQ_NEXT_OR_NULL(&(container->clients), current_client, clients); - if (to_focus == NULL) - to_focus = CIRCLEQ_PREV_OR_NULL(&(container->clients), current_client, clients); - - workspace_initialize(t_ws, container->workspace->output, false); - /* Check if there is already a fullscreen client on the destination workspace and - * stop moving if so. */ - if (current_client->fullscreen && (t_ws->fullscreen_client != NULL)) { - ELOG("Not moving: Fullscreen client already existing on destination workspace.\n"); - return; - } - - Container *to_container = t_ws->table[t_ws->current_col][t_ws->current_row]; - - assert(to_container != NULL); - - client_remove_from_container(conn, current_client, container, true); - if (container->workspace->fullscreen_client == current_client) - container->workspace->fullscreen_client = NULL; - - /* TODO: insert it to the correct position */ - CIRCLEQ_INSERT_TAIL(&(to_container->clients), current_client, clients); - - SLIST_INSERT_HEAD(&(to_container->workspace->focus_stack), current_client, focus_clients); - DLOG("Moved.\n"); - - current_client->container = to_container; - current_client->workspace = to_container->workspace; - container->currently_focused = to_focus; - to_container->currently_focused = current_client; - - /* If we’re moving it to an invisible screen, we need to unmap it */ - if (!workspace_is_visible(to_container->workspace)) { - DLOG("This workspace is not visible, unmapping\n"); - client_unmap(conn, current_client); - } else { - if (current_client->fullscreen) { - DLOG("Calling client_enter_fullscreen again\n"); - client_enter_fullscreen(conn, current_client, false); - } - } - - /* delete all empty columns/rows */ - cleanup_table(conn, container->workspace); - - render_layout(conn); - - if (workspace_is_visible(to_container->workspace)) { - client_warp_pointer_into(conn, current_client); - set_focus(conn, current_client, true); - } -} - -/* - * Jumps to the given window class / title. - * Title is matched using strstr, that is, matches if it appears anywhere - * in the string. Regular expressions seem to be a bit overkill here. However, - * if we need them for something else somewhen, we may introduce them here, too. - * - */ -static void jump_to_window(xcb_connection_t *conn, const char *arguments) { - char *classtitle; - Client *client; - - /* The first character is a quote, this was checked before */ - classtitle = sstrdup(arguments+1); - /* The last character is a quote, we just set it to NULL */ - classtitle[strlen(classtitle)-1] = '\0'; - - if ((client = get_matching_client(conn, classtitle, NULL)) == NULL) { - free(classtitle); - ELOG("No matching client found.\n"); - return; - } - - free(classtitle); - workspace_show(conn, client->workspace->num + 1); - set_focus(conn, client, true); -} - -/* - * Jump directly to the specified workspace, row and col. - * Great for reaching windows that you always keep in the same spot (hello irssi, I'm looking at you) - * - */ -static void jump_to_container(xcb_connection_t *conn, const char *arguments) { - int ws, row, col; - int result; - - result = sscanf(arguments, "%d %d %d", &ws, &col, &row); - LOG("Jump called with %d parameters (\"%s\")\n", result, arguments); - - /* No match? Either no arguments were specified, or no numbers */ - if (result < 1) { - ELOG("At least one valid argument required\n"); - return; - } - - /* Move to the target workspace */ - workspace_show(conn, ws); - - if (result < 3) - return; - - DLOG("Boundary-checking col %d, row %d... (max cols %d, max rows %d)\n", col, row, c_ws->cols, c_ws->rows); - - /* Move to row/col */ - if (row >= c_ws->rows) - row = c_ws->rows - 1; - if (col >= c_ws->cols) - col = c_ws->cols - 1; - - DLOG("Jumping to col %d, row %d\n", col, row); - if (c_ws->table[col][row]->currently_focused != NULL) - set_focus(conn, c_ws->table[col][row]->currently_focused, true); -} - -/* - * Travels the focus stack by the given number of times (or once, if no argument - * was specified). That is, selects the window you were in before you focused - * the current window. - * - * The special values 'floating' (select the next floating window), 'tiling' - * (select the next tiling window), 'ft' (if the current window is floating, - * select the next tiling window and vice-versa) are also valid - * - */ -static void travel_focus_stack(xcb_connection_t *conn, const char *arguments) { - /* Start count at -1 to always skip the first element */ - int times, count = -1; - Client *current; - bool floating_criteria; - - /* Either it’s one of the special values… */ - if (strcasecmp(arguments, "floating") == 0) { - floating_criteria = true; - } else if (strcasecmp(arguments, "tiling") == 0) { - floating_criteria = false; - } else if (strcasecmp(arguments, "ft") == 0) { - Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack)); - if (last_focused == SLIST_END(&(c_ws->focus_stack))) { - ELOG("Cannot select the next floating/tiling client because there is no client at all\n"); - return; - } - - floating_criteria = !client_is_floating(last_focused); - } else { - /* …or a number was specified */ - if (sscanf(arguments, "%u", ×) != 1) { - ELOG("No or invalid argument given (\"%s\"), using default of 1 times\n", arguments); - times = 1; - } - - SLIST_FOREACH(current, &(CUR_CELL->workspace->focus_stack), focus_clients) { - if (++count < times) { - DLOG("Skipping\n"); - continue; - } - - DLOG("Focussing\n"); - set_focus(conn, current, true); - break; - } - return; - } - - /* Select the next client matching the criteria parsed above */ - SLIST_FOREACH(current, &(CUR_CELL->workspace->focus_stack), focus_clients) - if (client_is_floating(current) == floating_criteria) { - set_focus(conn, current, true); - break; - } -} - -/* - * Switch to next or previous existing workspace - * - */ -static void next_previous_workspace(xcb_connection_t *conn, int direction) { - Workspace *ws = c_ws; - - if (direction == 'n') { - while (1) { - ws = TAILQ_NEXT(ws, workspaces); - - if (ws == TAILQ_END(workspaces)) - ws = TAILQ_FIRST(workspaces); - - if (ws == c_ws) - return; - - if (ws->output == NULL) - continue; - - workspace_show(conn, ws->num + 1); - return; - } - } else if (direction == 'p') { - while (1) { - ws = TAILQ_PREV(ws, workspaces_head, workspaces); - - if (ws == TAILQ_END(workspaces)) - ws = TAILQ_LAST(workspaces, workspaces_head); - - if (ws == c_ws) - return; - - if (ws->output == NULL) - continue; - - workspace_show(conn, ws->num + 1); - return; - } - } -} - -static void parse_resize_command(xcb_connection_t *conn, Client *last_focused, const char *command) { - int first, second; - resize_orientation_t orientation = O_VERTICAL; - Container *con = last_focused->container; - Workspace *ws = last_focused->workspace; - - if (client_is_floating(last_focused)) { - DLOG("Resizing a floating client\n"); - if (STARTS_WITH(command, "left")) { - command += strlen("left"); - last_focused->rect.width += atoi(command); - last_focused->rect.x -= atoi(command); - } else if (STARTS_WITH(command, "right")) { - command += strlen("right"); - last_focused->rect.width += atoi(command); - } else if (STARTS_WITH(command, "top")) { - command += strlen("top"); - last_focused->rect.height += atoi(command); - last_focused->rect.y -= atoi(command); - } else if (STARTS_WITH(command, "bottom")) { - command += strlen("bottom"); - last_focused->rect.height += atoi(command); - } else { - ELOG("Syntax: resize [+|-]\n"); - return; - } - - /* resize_client flushes */ - resize_client(conn, last_focused); - - return; - } - - if (STARTS_WITH(command, "left")) { - if (con->col == 0) - return; - first = con->col - 1; - second = con->col; - command += strlen("left"); - } else if (STARTS_WITH(command, "right")) { - first = con->col + (con->colspan - 1); - DLOG("column %d\n", first); - - if (!cell_exists(ws, first, con->row) || - (first == (ws->cols-1))) - return; - - second = first + 1; - command += strlen("right"); - } else if (STARTS_WITH(command, "top")) { - if (con->row == 0) - return; - first = con->row - 1; - second = con->row; - orientation = O_HORIZONTAL; - command += strlen("top"); - } else if (STARTS_WITH(command, "bottom")) { - first = con->row + (con->rowspan - 1); - if (!cell_exists(ws, con->col, first) || - (first == (ws->rows-1))) - return; - - second = first + 1; - orientation = O_HORIZONTAL; - command += strlen("bottom"); - } else { - ELOG("Syntax: resize [+|-]\n"); - return; - } - - int pixels = atoi(command); - if (pixels == 0) - return; - - resize_container(conn, ws, first, second, orientation, pixels); -} - -/* - * Parses a command, see file CMDMODE for more information - * - */ -void parse_command(xcb_connection_t *conn, const char *command) { - LOG("--- parsing command \"%s\" ---\n", command); - /* Get the first client from focus stack because floating clients are not - * in any container, therefore CUR_CELL is not appropriate. */ - Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack)); - if (last_focused == SLIST_END(&(c_ws->focus_stack))) - last_focused = NULL; - - /* Hmm, just to be sure */ - if (command[0] == '\0') - return; - - /* Is it an ? Then execute the given command. */ - if (STARTS_WITH(command, "exec ")) { - LOG("starting \"%s\"\n", command + strlen("exec ")); - start_application(command+strlen("exec ")); - return; - } - - if (STARTS_WITH(command, "mark")) { - if (last_focused == NULL) { - ELOG("There is no window to mark\n"); - return; - } - const char *rest = command + strlen("mark"); - while (*rest == ' ') - rest++; - if (*rest == '\0') { - DLOG("interactive mark starting\n"); - start_application("i3-input -p 'mark ' -l 1 -P 'Mark: '"); - } else { - LOG("mark with \"%s\"\n", rest); - client_mark(conn, last_focused, rest); - } - return; - } - - if (STARTS_WITH(command, "goto")) { - const char *rest = command + strlen("goto"); - while (*rest == ' ') - rest++; - if (*rest == '\0') { - DLOG("interactive go to mark starting\n"); - start_application("i3-input -p 'goto ' -l 1 -P 'Goto: '"); - } else { - LOG("go to \"%s\"\n", rest); - jump_to_mark(conn, rest); - } - return; - } - - if (STARTS_WITH(command, "stack-limit ")) { - if (last_focused == NULL || client_is_floating(last_focused)) { - ELOG("No container focused\n"); - return; - } - const char *rest = command + strlen("stack-limit "); - if (strncmp(rest, "rows ", strlen("rows ")) == 0) { - last_focused->container->stack_limit = STACK_LIMIT_ROWS; - rest += strlen("rows "); - } else if (strncmp(rest, "cols ", strlen("cols ")) == 0) { - last_focused->container->stack_limit = STACK_LIMIT_COLS; - rest += strlen("cols "); - } else { - ELOG("Syntax: stack-limit \n"); - return; - } - - last_focused->container->stack_limit_value = atoi(rest); - if (last_focused->container->stack_limit_value == 0) - last_focused->container->stack_limit = STACK_LIMIT_NONE; - - return; - } - - if (STARTS_WITH(command, "resize ")) { - if (last_focused == NULL) - return; - const char *rest = command + strlen("resize "); - parse_resize_command(conn, last_focused, rest); - return; - } - - if (STARTS_WITH(command, "mode ")) { - const char *rest = command + strlen("mode "); - switch_mode(conn, rest); - return; - } - - /* Is it an ? */ - if (STARTS_WITH(command, "exit")) { - LOG("User issued exit-command, exiting without error.\n"); - restore_geometry(global_conn); - ipc_shutdown(); - exit(EXIT_SUCCESS); - } - - /* Is it a ? */ - if (STARTS_WITH(command, "reload")) { - load_configuration(conn, NULL, true); - render_layout(conn); - /* Send an IPC event just in case the ws names have changed */ - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}"); - return; - } - - /* Is it ? Then restart in place. */ - if (STARTS_WITH(command, "restart")) { - i3_restart(); - } - - if (STARTS_WITH(command, "kill")) { - if (last_focused == NULL) { - ELOG("There is no window to kill\n"); - return; - } - - LOG("Killing current window\n"); - client_kill(conn, last_focused); - return; - } - - /* Is it a jump to a specified workspace, row, col? */ - if (STARTS_WITH(command, "jump ")) { - const char *arguments = command + strlen("jump "); - if (arguments[0] == '"') - jump_to_window(conn, arguments); - else jump_to_container(conn, arguments); - return; - } - - /* Should we travel the focus stack? */ - if (STARTS_WITH(command, "focus")) { - const char *arguments = command + strlen("focus "); - travel_focus_stack(conn, arguments); - return; - } - - /* Is it 'f' for fullscreen, or 'fg' for fullscreen_global? */ - if (command[0] == 'f') { - if (last_focused == NULL) - return; - if (command[1] == 'g') - client_toggle_fullscreen_global(conn, last_focused); - else - client_toggle_fullscreen(conn, last_focused); - return; - } - - /* Is it just 's' for stacking or 'd' for default? */ - if ((command[0] == 's' || command[0] == 'd' || command[0] == 'T') && (command[1] == '\0')) { - if (last_focused != NULL && client_is_floating(last_focused)) { - ELOG("not switching, this is a floating client\n"); - return; - } - LOG("Switching mode for current container\n"); - int new_mode = MODE_DEFAULT; - if (command[0] == 's' && CUR_CELL->mode != MODE_STACK) - new_mode = MODE_STACK; - if (command[0] == 'T' && CUR_CELL->mode != MODE_TABBED) - new_mode = MODE_TABBED; - switch_layout_mode(conn, CUR_CELL, new_mode); - return; - } - - /* Is it 'bn' (border normal), 'bp' (border 1pixel) or 'bb' (border borderless)? */ - /* or even 'bt' (toggle border: 'bp' -> 'bb' -> 'bn' ) */ - if (command[0] == 'b') { - if (last_focused == NULL) { - ELOG("No window focused, cannot change border type\n"); - return; - } - - char com = command[1]; - if (command[1] == 't') { - if (last_focused->titlebar_position == TITLEBAR_TOP && - !last_focused->borderless) - com = 'p'; - else if (last_focused->titlebar_position == TITLEBAR_OFF && - !last_focused->borderless) - com = 'b'; - else com = 'n'; - } - - client_change_border(conn, last_focused, com); - return; - } - - if (command[0] == 'H') { - LOG("Hiding all floating windows\n"); - floating_toggle_hide(conn, c_ws); - return; - } - - enum { WITH_WINDOW, WITH_CONTAINER, WITH_WORKSPACE, WITH_SCREEN } with = WITH_WINDOW; - - /* Is it a ? */ - if (command[0] == 'w') { - command++; - /* TODO: implement */ - if (command[0] == 'c') { - with = WITH_CONTAINER; - command++; - } else if (command[0] == 'w') { - with = WITH_WORKSPACE; - command++; - } else if (command[0] == 's') { - with = WITH_SCREEN; - command++; - } else { - ELOG("not yet implemented.\n"); - return; - } - } - - /* Is it 't' for toggle tiling/floating? */ - if (command[0] == 't') { - if (with == WITH_WORKSPACE) { - c_ws->auto_float = !c_ws->auto_float; - LOG("autofloat is now %d\n", c_ws->auto_float); - return; - } - if (last_focused == NULL) { - ELOG("Cannot toggle tiling/floating: workspace empty\n"); - return; - } - - Workspace *ws = last_focused->workspace; - - if (last_focused->fullscreen) - client_leave_fullscreen(conn, last_focused); - - toggle_floating_mode(conn, last_focused, false); - /* delete all empty columns/rows */ - cleanup_table(conn, ws); - - /* Fix colspan/rowspan if it’d overlap */ - fix_colrowspan(conn, ws); - - render_workspace(conn, ws->output, ws); - - /* Re-focus the client because cleanup_table sets the focus to the last - * focused client inside a container only. */ - set_focus(conn, last_focused, true); - - return; - } - - /* Is it 'n' or 'p' for next/previous workspace? (nw) */ - if ((command[0] == 'n' || command[0] == 'p') && command[1] == 'w') { - next_previous_workspace(conn, command[0]); - return; - } - - /* It’s a normal */ - char *rest = NULL; - enum { ACTION_FOCUS, ACTION_MOVE, ACTION_SNAP } action = ACTION_FOCUS; - direction_t direction; - int times = strtol(command, &rest, 10); - if (rest == NULL) { - ELOG("Invalid command (\"%s\")\n", command); - return; - } - - if (*rest == '\0') { - /* No rest? This was a workspace number, not a times specification */ - workspace_show(conn, times); - return; - } - - if (*rest == 'm' || *rest == 's') { - action = (*rest == 'm' ? ACTION_MOVE : ACTION_SNAP); - rest++; - } - - int workspace = strtol(rest, &rest, 10); - - if (rest == NULL) { - ELOG("Invalid command (\"%s\")\n", command); - return; - } - - if (*rest == '\0') { - if (last_focused != NULL && client_is_floating(last_focused)) - move_floating_window_to_workspace(conn, last_focused, workspace); - else move_current_window_to_workspace(conn, workspace); - return; - } - - if (last_focused == NULL) { - ELOG("Not performing (no window found)\n"); - return; - } - - if (client_is_floating(last_focused) && - (action != ACTION_FOCUS && action != ACTION_MOVE)) { - ELOG("Not performing (floating)\n"); - return; - } - - /* Now perform action to */ - while (*rest != '\0') { - if (*rest == 'h') - direction = D_LEFT; - else if (*rest == 'j') - direction = D_DOWN; - else if (*rest == 'k') - direction = D_UP; - else if (*rest == 'l') - direction = D_RIGHT; - else { - ELOG("unknown direction: %c\n", *rest); - return; - } - rest++; - - if (action == ACTION_FOCUS) { - if (with == WITH_SCREEN) { - focus_thing(conn, direction, THING_SCREEN); - continue; - } - if (client_is_floating(last_focused)) { - floating_focus_direction(conn, last_focused, direction); - continue; - } - focus_thing(conn, direction, (with == WITH_WINDOW ? THING_WINDOW : THING_CONTAINER)); - continue; - } - - if (action == ACTION_MOVE) { - if (with == WITH_SCREEN) { - /* TODO: this should swap the screen’s contents - * (e.g. all workspaces) with the next/previous/… - * screen */ - ELOG("Not yet implemented\n"); - continue; - } - if (client_is_floating(last_focused)) { - floating_move(conn, last_focused, direction); - continue; - } - if (with == WITH_WINDOW) - move_current_window(conn, direction); - else move_current_container(conn, direction); - continue; - } - - if (action == ACTION_SNAP) { - if (with == WITH_SCREEN) { - ELOG("You cannot snap a screen (it makes no sense).\n"); - continue; - } - snap_current_container(conn, direction); - continue; - } - } -} diff --git a/src/container.c b/src/container.c deleted file mode 100644 index 8533fd49..00000000 --- a/src/container.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * © 2009 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ - -#include "data.h" -#include "log.h" - -/* - * Returns the mode of the given container (or MODE_DEFAULT if a NULL pointer - * was passed in order to save a few explicit checks in other places). If - * for_frame was set to true, the special case of having exactly one client - * in a container is handled so that MODE_DEFAULT is returned. For some parts - * of the rendering, this is interesting, other parts need the real mode. - * - */ -int container_mode(Container *con, bool for_frame) { - int num_clients = 0; - Client *client; - - if (con == NULL || con->mode == MODE_DEFAULT) - return MODE_DEFAULT; - - if (!for_frame) - return con->mode; - - CIRCLEQ_FOREACH(client, &(con->clients), clients) - num_clients++; - - /* If the container contains only one client, mode is irrelevant */ - if (num_clients == 1) { - DLOG("mode to default\n"); - return MODE_DEFAULT; - } - - return con->mode; -} diff --git a/src/layout.c b/src/layout.c deleted file mode 100644 index b1338040..00000000 --- a/src/layout.c +++ /dev/null @@ -1,779 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * © 2009-2010 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - * layout.c: Functions handling layout/drawing of window decorations - * - */ -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "i3.h" -#include "xcb.h" -#include "table.h" -#include "util.h" -#include "randr.h" -#include "layout.h" -#include "client.h" -#include "floating.h" -#include "handlers.h" -#include "workspace.h" -#include "log.h" -#include "container.h" - -/* - * Gets the unoccupied space (= space which is available for windows which were resized by the user) - * for the given row. This is necessary to render both, customly resized windows and never touched - * windows correctly, meaning that the aspect ratio will be maintained when opening new windows. - * - */ -int get_unoccupied_x(Workspace *workspace) { - double unoccupied = workspace->rect.width; - double default_factor = ((float)workspace->rect.width / workspace->cols) / workspace->rect.width; - - DLOG("get_unoccupied_x(), starting with %f, default_factor = %f\n", unoccupied, default_factor); - - for (int cols = 0; cols < workspace->cols; cols++) { - DLOG("width_factor[%d] = %f, unoccupied = %f\n", cols, workspace->width_factor[cols], unoccupied); - - if (workspace->width_factor[cols] == 0) - unoccupied -= workspace->rect.width * default_factor; - } - - DLOG("unoccupied space: %f\n", unoccupied); - return unoccupied; -} - -/* See get_unoccupied_x() */ -int get_unoccupied_y(Workspace *workspace) { - int height = workspace_height(workspace); - double unoccupied = height; - double default_factor = ((float)height / workspace->rows) / height; - - DLOG("get_unoccupied_y(), starting with %f, default_factor = %f\n", unoccupied, default_factor); - - for (int rows = 0; rows < workspace->rows; rows++) { - DLOG("height_factor[%d] = %f, unoccupied = %f\n", rows, workspace->height_factor[rows], unoccupied); - if (workspace->height_factor[rows] == 0) - unoccupied -= height * default_factor; - } - - DLOG("unoccupied space: %f\n", unoccupied); - return unoccupied; -} - -/* - * Redecorates the given client correctly by checking if it’s in a stacking container and - * re-rendering the stack window or just calling decorate_window if it’s not in a stacking - * container. - * - */ -void redecorate_window(xcb_connection_t *conn, Client *client) { - if (client->container != NULL && - (client->container->mode == MODE_STACK || - client->container->mode == MODE_TABBED)) { - render_container(conn, client->container); - /* We clear the frame to generate exposure events, because the color used - in drawing may be different */ - xcb_clear_area(conn, true, client->frame, 0, 0, client->rect.width, client->rect.height); - } else decorate_window(conn, client, client->frame, client->titlegc, 0, 0); - xcb_flush(conn); -} - -/* - * (Re-)draws window decorations for a given Client onto the given drawable/graphic context. - * When in stacking mode, the window decorations are drawn onto an own window. - * - */ -void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t drawable, - xcb_gcontext_t gc, int offset_x, int offset_y) { - i3Font *font = load_font(conn, config.font); - int decoration_height = font->height + 2 + 2; - struct Colortriple *color; - Client *last_focused; - - /* Clients without a container (docks) won’t get decorated */ - if (client->dock) - return; - - last_focused = SLIST_FIRST(&(client->workspace->focus_stack)); - /* Is the window urgent? */ - if (client->urgent) - color = &(config.client.urgent); - else { - if (client_is_floating(client)) { - if (last_focused == client) - color = &(config.client.focused); - else color = &(config.client.unfocused); - } else { - if (client->container->currently_focused == client) { - /* Distinguish if the window is currently focused… */ - if (last_focused == client && c_ws == client->workspace) - color = &(config.client.focused); - /* …or if it is the focused window in a not focused container */ - else color = &(config.client.focused_inactive); - } else color = &(config.client.unfocused); - } - } - - /* Our plan is the following: - - Draw a rect around the whole client in color->background - - Draw two lines in a lighter color - - Draw the window’s title - */ - int mode = container_mode(client->container, true); - - /* Draw a rectangle in background color around the window */ - if (client->borderless && mode == MODE_DEFAULT) - xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); - else xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, color->background); - - /* In stacking mode, we only render the rect for this specific decoration */ - if (mode == MODE_STACK || mode == MODE_TABBED) { - /* We need to use the container’s width because it is the more recent value - when - in stacking mode, clients get reconfigured only on demand (the not active client - is not reconfigured), so the client’s rect.width would be wrong */ - xcb_rectangle_t rect = {offset_x, offset_y, - offset_x + client->container->width, - offset_y + decoration_height }; - xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect); - } else { - xcb_rectangle_t rect = {0, 0, client->rect.width, client->rect.height}; - xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect); - - /* Draw the inner background to have a black frame around clients (such as mplayer) - which cannot be resized exactly in our frames and therefore are centered */ - xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); - if (client->titlebar_position == TITLEBAR_OFF && client->borderless) { - xcb_rectangle_t crect = {0, 0, client->rect.width, client->rect.height}; - xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect); - } else if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) { - xcb_rectangle_t crect = {1, 1, client->rect.width - (1 + 1), client->rect.height - (1 + 1)}; - xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect); - } else { - xcb_rectangle_t crect = {2, decoration_height, - client->rect.width - (2 + 2), client->rect.height - 2 - decoration_height}; - xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect); - } - } - - mode = container_mode(client->container, false); - - if (client->titlebar_position != TITLEBAR_OFF) { - /* Draw the lines */ - xcb_draw_line(conn, drawable, gc, color->border, offset_x, offset_y, offset_x + client->rect.width, offset_y); - xcb_draw_line(conn, drawable, gc, color->border, - offset_x + 2, /* x */ - offset_y + font->height + 3, /* y */ - offset_x + client->rect.width - 3, /* to_x */ - offset_y + font->height + 3 /* to_y */); - } - - /* If the client has a title, we draw it */ - if (client->name != NULL && - (mode != MODE_DEFAULT || client->titlebar_position != TITLEBAR_OFF)) { - /* Draw the font */ - uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT; - uint32_t values[] = { color->text, color->background, font->id }; - xcb_change_gc(conn, gc, mask, values); - - /* name_len == -1 means this is a legacy application which does not specify _NET_WM_NAME, - and we don’t handle the old window name (COMPOUND_TEXT) but only _NET_WM_NAME, which - is UTF-8 */ - if (client->name_len == -1) - xcb_image_text_8(conn, strlen(client->name), drawable, gc, offset_x + 3 /* X */, - offset_y + font->height /* Y = baseline of font */, client->name); - else - xcb_image_text_16(conn, client->name_len, drawable, gc, offset_x + 3 /* X */, - offset_y + font->height /* Y = baseline of font */, (xcb_char2b_t*)client->name); - } -} - -/* - * Pushes the client’s x and y coordinates to X11 - * - */ -void reposition_client(xcb_connection_t *conn, Client *client) { - Output *output; - - DLOG("frame 0x%08x needs to be pushed to %dx%d\n", client->frame, 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 */ - xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->rect.x)); - - if (!client_is_floating(client)) - return; - - /* If the client is floating, we need to check if we moved it to a different workspace */ - output = get_output_containing(client->rect.x + (client->rect.width / 2), - client->rect.y + (client->rect.height / 2)); - if (client->workspace->output == output) - return; - - if (output == NULL) { - DLOG("Boundary checking disabled, no output found for (%d, %d)\n", client->rect.x, client->rect.y); - return; - } - - if (output->current_workspace == NULL) { - DLOG("Boundary checking deferred, no current workspace on output\n"); - client->force_reconfigure = true; - return; - } - - DLOG("Client is on workspace %p with output %p\n", client->workspace, client->workspace->output); - DLOG("but output at %d, %d is %p\n", client->rect.x, client->rect.y, output); - floating_assign_to_workspace(client, output->current_workspace); - - set_focus(conn, client, true); -} - -/* - * Pushes the client’s width/height to X11 and resizes the child window. This - * function also updates the client’s position, so if you work on tiling clients - * only, you can use this function instead of separate calls to reposition_client - * and resize_client to reduce flickering. - * - */ -void resize_client(xcb_connection_t *conn, Client *client) { - i3Font *font = load_font(conn, config.font); - - DLOG("frame 0x%08x needs to be pushed to %dx%d\n", client->frame, client->rect.x, client->rect.y); - DLOG("resizing client 0x%08x to %d x %d\n", client->frame, client->rect.width, client->rect.height); - xcb_set_window_rect(conn, client->frame, client->rect); - - /* Adjust the position of the child inside its frame. - * The coordinates of the child are relative to its frame, we - * add a border of 2 pixel to each value */ - Rect *rect = &(client->child_rect); - switch (container_mode(client->container, true)) { - case MODE_STACK: - case MODE_TABBED: - rect->x = 2; - rect->y = 0; - rect->width = client->rect.width - (2 + 2); - rect->height = client->rect.height - 2; - break; - default: - if (client->titlebar_position == TITLEBAR_OFF && client->borderless) { - rect->x = 0; - rect->y = 0; - rect->width = client->rect.width; - rect->height = client->rect.height; - } else if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) { - rect->x = 1; - rect->y = 1; - rect->width = client->rect.width - 1 - 1; - rect->height = client->rect.height - 1 - 1; - } else { - rect->x = 2; - rect->y = font->height + 2 + 2; - rect->width = client->rect.width - (2 + 2); - rect->height = client->rect.height - ((font->height + 2 + 2) + 2); - } - break; - } - - rect->width -= (2 * client->border_width); - rect->height -= (2 * client->border_width); - - /* Obey the ratio, if any */ - if (client->proportional_height != 0 && - client->proportional_width != 0) { - DLOG("proportional height = %d, width = %d\n", client->proportional_height, client->proportional_width); - double new_height = rect->height + 1; - int new_width = rect->width; - - while (new_height > rect->height) { - new_height = ((double)client->proportional_height / client->proportional_width) * new_width; - - if (new_height > rect->height) - new_width--; - } - /* Center the window */ - rect->y += ceil(rect->height / 2) - floor(new_height / 2); - rect->x += ceil(rect->width / 2) - floor(new_width / 2); - - rect->height = new_height; - rect->width = new_width; - DLOG("new_height = %f, new_width = %d\n", new_height, new_width); - } - - if (client->height_increment > 1) { - int old_height = rect->height; - rect->height -= (rect->height - client->base_height) % client->height_increment; - DLOG("Lost %d pixel due to client's height_increment (%d px, base_height = %d)\n", - old_height - rect->height, client->height_increment, client->base_height); - } - - if (client->width_increment > 1) { - int old_width = rect->width; - rect->width -= (rect->width - client->base_width) % client->width_increment; - DLOG("Lost %d pixel due to client's width_increment (%d px, base_width = %d)\n", - old_width - rect->width, client->width_increment, client->base_width); - } - - DLOG("child will be at %dx%d with size %dx%d\n", rect->x, rect->y, rect->width, rect->height); - - xcb_set_window_rect(conn, client->child, *rect); - - /* After configuring a child window we need to fake a configure_notify_event (see ICCCM 4.2.3). - * This is necessary to inform the client of its position relative to the root window, - * not relative to its frame (as done in the configure_notify_event by the x server). */ - fake_absolute_configure_notify(conn, client); - - /* Force redrawing after resizing the window because any now lost - * pixels could contain old garbage. */ - xcb_expose_event_t generated; - generated.window = client->frame; - generated.count = 0; - handle_expose_event(NULL, conn, &generated); -} - -/* - * Renders the given container. Is called by render_layout() or individually (for example - * when focus changes in a stacking container) - * - */ -void render_container(xcb_connection_t *conn, Container *container) { - Client *client; - int num_clients = 0, current_client = 0; - - CIRCLEQ_FOREACH(client, &(container->clients), clients) - num_clients++; - - if (container->mode == MODE_DEFAULT) { - int height = (container->height / max(1, num_clients)); - int rest_pixels = (container->height % max(1, num_clients)); - DLOG("height per client = %d, rest = %d\n", height, rest_pixels); - - CIRCLEQ_FOREACH(client, &(container->clients), clients) { - /* If the client is in fullscreen mode, it does not get reconfigured */ - if (container->workspace->fullscreen_client == client) { - current_client++; - continue; - } - - /* If we have some pixels left to distribute, add one - * pixel to each client as long as possible. */ - int this_height = height; - if (rest_pixels > 0) { - height++; - rest_pixels--; - } - /* 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 | - update_if_necessary(&(client->rect.x), container->x) | - update_if_necessary(&(client->rect.y), container->y + - (container->height / num_clients) * current_client) | - update_if_necessary(&(client->rect.width), container->width) | - update_if_necessary(&(client->rect.height), this_height)) - resize_client(conn, client); - - /* TODO: vertical default layout */ - - client->force_reconfigure = false; - - current_client++; - } - } else { - i3Font *font = load_font(conn, config.font); - int decoration_height = (font->height + 2 + 2); - struct Stack_Window *stack_win = &(container->stack_win); - /* The size for each tab (width), necessary as a separate variable - * because num_clients gets fixed to 1 in tabbed mode. */ - int size_each = (num_clients == 0 ? container->width : container->width / num_clients); - int stack_lines = num_clients; - - /* Check if we need to remap our stack title window, it gets unmapped when the container - is empty in src/handlers.c:unmap_notify() */ - if (stack_win->rect.height == 0 && num_clients > 1) { - DLOG("remapping stack win\n"); - xcb_map_window(conn, stack_win->window); - } else DLOG("not remapping stackwin, height = %d, num_clients = %d\n", - stack_win->rect.height, num_clients); - - if (container->mode == MODE_TABBED) { - /* By setting num_clients to 1 we force that the stack window will be only one line - * high. The rest of the code is useful in both cases. */ - DLOG("tabbed mode, setting num_clients = 1\n"); - if (stack_lines > 1) - stack_lines = 1; - } - - if (container->stack_limit == STACK_LIMIT_COLS) { - stack_lines = ceil((float)num_clients / container->stack_limit_value); - } else if (container->stack_limit == STACK_LIMIT_ROWS) { - stack_lines = min(num_clients, container->stack_limit_value); - } - - int height = decoration_height * stack_lines; - if (num_clients == 1) { - height = 0; - stack_win->rect.height = 0; - xcb_unmap_window(conn, stack_win->window); - - DLOG("Just one client, setting height to %d\n", height); - } - - /* Check if we need to reconfigure our stack title window */ - if (height > 0 && ( - update_if_necessary(&(stack_win->rect.x), container->x) | - update_if_necessary(&(stack_win->rect.y), container->y) | - update_if_necessary(&(stack_win->rect.width), container->width) | - update_if_necessary(&(stack_win->rect.height), height))) { - - /* Configuration can happen in two slightly different ways: - - If there is no client in fullscreen mode, 5 parameters are passed - (x, y, width, height, stack mode is set to above which means top-most position). - - If there is a fullscreen client, the fourth parameter is set to to the - fullscreen window as sibling and the stack mode is set to below, which means - that the stack_window will be placed just below the sibling, that is, under - the fullscreen window. - */ - uint32_t values[] = { stack_win->rect.x, stack_win->rect.y, - stack_win->rect.width, stack_win->rect.height, - XCB_STACK_MODE_ABOVE, XCB_STACK_MODE_BELOW }; - uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | - XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | - XCB_CONFIG_WINDOW_STACK_MODE; - - /* Raise the stack window, but keep it below the first floating client - * and below the fullscreen client (if any) */ - Client *first_floating = TAILQ_FIRST(&(container->workspace->floating_clients)); - if (first_floating != TAILQ_END(&(container->workspace->floating_clients))) { - mask |= XCB_CONFIG_WINDOW_SIBLING; - values[4] = first_floating->frame; - } else if (container->workspace->fullscreen_client != NULL) { - mask |= XCB_CONFIG_WINDOW_SIBLING; - values[4] = container->workspace->fullscreen_client->frame; - } - - xcb_configure_window(conn, stack_win->window, mask, values); - } - - /* Prepare the pixmap for usage */ - if (num_clients > 1) - cached_pixmap_prepare(conn, &(stack_win->pixmap)); - - int current_row = 0, current_col = 0; - int wrap = 0; - - if (container->stack_limit == STACK_LIMIT_COLS) { - /* wrap stores the number of rows after which we will - * wrap to a new column. */ - wrap = ceil((float)num_clients / container->stack_limit_value); - } else if (container->stack_limit == STACK_LIMIT_ROWS) { - /* When limiting rows, the wrap variable serves a - * slightly different purpose: it holds the number of - * pixels which each client will get. This is constant - * during the following loop, so it saves us some - * divisions and ceil()ing. */ - wrap = (stack_win->rect.width / ceil((float)num_clients / container->stack_limit_value)); - } - - /* Render the decorations of all clients */ - CIRCLEQ_FOREACH(client, &(container->clients), clients) { - /* If the client is in fullscreen mode, it does not get reconfigured */ - if (container->workspace->fullscreen_client == client) { - current_client++; - continue; - } - - /* Check if we changed client->x or client->y by updating it. - * Note the bitwise OR instead of logical OR to force evaluation of all statements */ - if (client->force_reconfigure | - update_if_necessary(&(client->rect.x), container->x) | - update_if_necessary(&(client->rect.y), container->y + height) | - update_if_necessary(&(client->rect.width), container->width) | - update_if_necessary(&(client->rect.height), container->height - height)) - resize_client(conn, client); - - client->force_reconfigure = false; - - int offset_x = 0; - int offset_y = 0; - if (container->mode == MODE_STACK || - (container->mode == MODE_TABBED && - container->stack_limit == STACK_LIMIT_COLS)) { - if (container->stack_limit == STACK_LIMIT_COLS) { - offset_x = current_col * (stack_win->rect.width / container->stack_limit_value); - offset_y = current_row * decoration_height; - current_row++; - if ((current_row % wrap) == 0) { - current_col++; - current_row = 0; - } - } else if (container->stack_limit == STACK_LIMIT_ROWS) { - offset_x = current_col * wrap; - offset_y = current_row * decoration_height; - current_row++; - if ((current_row % container->stack_limit_value) == 0) { - current_col++; - current_row = 0; - } - } else { - offset_y = current_client * decoration_height; - } - current_client++; - } else if (container->mode == MODE_TABBED) { - if (container->stack_limit == STACK_LIMIT_ROWS) { - LOG("You limited a tabbed container in its rows. " - "This makes no sense in tabbing mode.\n"); - } - offset_x = current_client++ * size_each; - } - if (stack_win->pixmap.id == XCB_NONE) - continue; - decorate_window(conn, client, stack_win->pixmap.id, - stack_win->pixmap.gc, offset_x, offset_y); - } - - /* Check if we need to fill one column because of an uneven - * amount of windows */ - if (container->mode == MODE_STACK) { - if (container->stack_limit == STACK_LIMIT_COLS && (current_col % 2) != 0) { - xcb_change_gc_single(conn, stack_win->pixmap.gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); - - int offset_x = current_col * (stack_win->rect.width / container->stack_limit_value); - int offset_y = current_row * decoration_height; - xcb_rectangle_t rect = {offset_x, offset_y, - offset_x + container->width, - offset_y + decoration_height }; - xcb_poly_fill_rectangle(conn, stack_win->pixmap.id, stack_win->pixmap.gc, 1, &rect); - } else if (container->stack_limit == STACK_LIMIT_ROWS && (current_row % 2) != 0) { - xcb_change_gc_single(conn, stack_win->pixmap.gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); - - int offset_x = current_col * wrap; - int offset_y = current_row * decoration_height; - xcb_rectangle_t rect = {offset_x, offset_y, - offset_x + container->width, - offset_y + decoration_height }; - xcb_poly_fill_rectangle(conn, stack_win->pixmap.id, stack_win->pixmap.gc, 1, &rect); - } - } - - if (stack_win->pixmap.id == XCB_NONE) - return; - xcb_copy_area(conn, stack_win->pixmap.id, stack_win->window, stack_win->pixmap.gc, - 0, 0, 0, 0, stack_win->rect.width, stack_win->rect.height); - } -} - -static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int *height) { - Client *client; - SLIST_FOREACH(client, &(r_ws->output->dock_clients), dock_clients) { - DLOG("client is at %d, should be at %d\n", client->rect.y, *height); - if (client->force_reconfigure | - update_if_necessary(&(client->rect.x), r_ws->rect.x) | - update_if_necessary(&(client->rect.y), *height)) - reposition_client(conn, client); - - if (client->force_reconfigure | - update_if_necessary(&(client->rect.width), width) | - update_if_necessary(&(client->rect.height), client->desired_height)) - resize_client(conn, client); - - client->force_reconfigure = false; - DLOG("desired_height = %d\n", client->desired_height); - *height += client->desired_height; - } -} - -static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int width, int height) { - i3Font *font = load_font(conn, config.font); - Output *output = r_ws->output; - enum { SET_NORMAL = 0, SET_FOCUSED = 1 }; - - /* Fill the whole bar in black */ - xcb_change_gc_single(conn, output->bargc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); - xcb_rectangle_t rect = {0, 0, width, height}; - xcb_poly_fill_rectangle(conn, output->bar, output->bargc, 1, &rect); - - /* Set font */ - xcb_change_gc_single(conn, output->bargc, XCB_GC_FONT, font->id); - - int drawn = 0; - Workspace *ws; - TAILQ_FOREACH(ws, workspaces, workspaces) { - if (ws->output != output) - continue; - - struct Colortriple *color; - - if (output->current_workspace == ws) - color = &(config.bar.focused); - else if (ws->urgent) - color = &(config.bar.urgent); - else color = &(config.bar.unfocused); - - /* Draw the outer rect */ - xcb_draw_rect(conn, output->bar, output->bargc, color->border, - drawn, /* x */ - 1, /* y */ - ws->text_width + 5 + 5, /* width = text width + 5 px left + 5px right */ - height - 2 /* height = max. height - 1 px upper and 1 px bottom border */); - - /* Draw the background of this rect */ - xcb_draw_rect(conn, output->bar, output->bargc, color->background, - drawn + 1, - 2, - ws->text_width + 4 + 4, - height - 4); - - xcb_change_gc_single(conn, output->bargc, XCB_GC_FOREGROUND, color->text); - xcb_change_gc_single(conn, output->bargc, XCB_GC_BACKGROUND, color->background); - xcb_image_text_16(conn, ws->name_len, output->bar, output->bargc, drawn + 5 /* X */, - font->height + 1 /* Y = baseline of font */, - (xcb_char2b_t*)ws->name); - drawn += ws->text_width + 12; - } -} - -/* - * Modifies the event mask of all clients on the given workspace to either ignore or to handle - * enter notifies. It is handy to ignore notifies because they will be sent when a window is mapped - * under the cursor, thus when the user didn’t enter the window actively at all. - * - */ -void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bool ignore_enter_notify) { - Client *client; - uint32_t values[1]; - - FOR_TABLE(workspace) { - if (workspace->table[cols][rows] == NULL) - continue; - - CIRCLEQ_FOREACH(client, &(workspace->table[cols][rows]->clients), clients) { - /* Change event mask for the decorations */ - values[0] = FRAME_EVENT_MASK; - if (ignore_enter_notify) - values[0] &= ~(XCB_EVENT_MASK_ENTER_WINDOW); - xcb_change_window_attributes(conn, client->frame, XCB_CW_EVENT_MASK, values); - - /* Change event mask for the child itself */ - values[0] = CHILD_EVENT_MASK; - if (ignore_enter_notify) - values[0] &= ~(XCB_EVENT_MASK_ENTER_WINDOW); - xcb_change_window_attributes(conn, client->child, XCB_CW_EVENT_MASK, values); - } - } -} - -/* - * Renders the given workspace on the given screen - * - */ -void render_workspace(xcb_connection_t *conn, Output *output, Workspace *r_ws) { - i3Font *font = load_font(conn, config.font); - int width = r_ws->rect.width; - int height = r_ws->rect.height; - - /* Reserve space for dock clients */ - Client *client; - SLIST_FOREACH(client, &(output->dock_clients), dock_clients) - height -= client->desired_height; - - /* Space for the internal bar */ - if (!config.disable_workspace_bar) - height -= (font->height + 6); - - int xoffset[r_ws->rows]; - int yoffset[r_ws->cols]; - /* Initialize offsets */ - for (int cols = 0; cols < r_ws->cols; cols++) - yoffset[cols] = r_ws->rect.y; - for (int rows = 0; rows < r_ws->rows; rows++) - xoffset[rows] = r_ws->rect.x; - - ignore_enter_notify_forall(conn, r_ws, true); - - /* Go through the whole table and render what’s necessary */ - FOR_TABLE(r_ws) { - Container *container = r_ws->table[cols][rows]; - if (container == NULL) - continue; - int single_width = -1, single_height = -1; - /* Update position of the container */ - container->row = rows; - container->col = cols; - container->x = xoffset[rows]; - container->y = yoffset[cols]; - container->width = 0; - - for (int c = 0; c < container->colspan; c++) { - if (r_ws->width_factor[cols+c] == 0) - container->width += (width / r_ws->cols); - else container->width += get_unoccupied_x(r_ws) * r_ws->width_factor[cols+c]; - - if (single_width == -1) - single_width = container->width; - } - - DLOG("height is %d\n", height); - - container->height = 0; - - for (int c = 0; c < container->rowspan; c++) { - if (r_ws->height_factor[rows+c] == 0) - container->height += (height / r_ws->rows); - else container->height += get_unoccupied_y(r_ws) * r_ws->height_factor[rows+c]; - - if (single_height == -1) - single_height = container->height; - } - - /* Render the container if it is not empty */ - render_container(conn, container); - - xoffset[rows] += single_width; - yoffset[cols] += single_height; - } - - /* Reposition all floating clients with force_reconfigure == true */ - TAILQ_FOREACH(client, &(r_ws->floating_clients), floating_clients) { - if (!client->force_reconfigure) - continue; - - client->force_reconfigure = false; - reposition_client(conn, client); - resize_client(conn, client); - } - - ignore_enter_notify_forall(conn, r_ws, false); - - render_bars(conn, r_ws, width, &height); - if (!config.disable_workspace_bar) - render_internal_bar(conn, r_ws, width, font->height + 6); -} - -/* - * Renders the whole layout, that is: Go through each screen, each workspace, each container - * and render each client. This also renders the bars. - * - * If you don’t need to render *everything*, you should call render_container on the container - * you want to refresh. - * - */ -void render_layout(xcb_connection_t *conn) { - Output *output; - - TAILQ_FOREACH(output, &outputs, outputs) - if (output->current_workspace != NULL) - render_workspace(conn, output, output->current_workspace); - - xcb_flush(conn); -} diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..a5769d17 --- /dev/null +++ b/src/main.c @@ -0,0 +1,339 @@ +/* + * vim:ts=4:sw=4:expandtab + */ +#include +#include +#include "all.h" + +static int xkb_event_base; + +int xkb_current_group; + +extern Con *focused; + +char **start_argv; + +xcb_connection_t *conn; +xcb_event_handlers_t evenths; +xcb_property_handlers_t prophs; +xcb_atom_t atoms[NUM_ATOMS]; + +xcb_window_t root; +uint8_t root_depth; + +xcb_key_symbols_t *keysyms; + +/* The list of key bindings */ +struct bindings_head *bindings; + +/* The list of exec-lines */ +struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts); + +/* The list of assignments */ +struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments); + +/* + * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb. + * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop + * + */ +static void xcb_got_event(EV_P_ struct ev_io *w, int revents) { + /* empty, because xcb_prepare_cb and xcb_check_cb are used */ +} + +/* + * Flush before blocking (and waiting for new events) + * + */ +static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) { + xcb_flush(conn); +} + +/* + * Instead of polling the X connection socket we leave this to + * xcb_poll_for_event() which knows better than we can ever know. + * + */ +static void xcb_check_cb(EV_P_ ev_check *w, int revents) { + xcb_generic_event_t *event; + + while ((event = xcb_poll_for_event(conn)) != NULL) { + xcb_event_handle(&evenths, event); + free(event); + } +} + +int main(int argc, char *argv[]) { + //parse_cmd("[ foo ] attach, attach ; focus"); + int screens; + char *override_configpath = NULL; + bool autostart = true; + bool only_check_config = false; + bool force_xinerama = false; + xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS]; + static struct option long_options[] = { + {"no-autostart", no_argument, 0, 'a'}, + {"config", required_argument, 0, 'c'}, + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"force-xinerama", no_argument, 0, 0}, + {0, 0, 0, 0} + }; + int option_index = 0, opt; + + setlocale(LC_ALL, ""); + + /* Disable output buffering to make redirects in .xsession actually useful for debugging */ + if (!isatty(fileno(stdout))) + setbuf(stdout, NULL); + + start_argv = argv; + + while ((opt = getopt_long(argc, argv, "c:Cvahld:V", long_options, &option_index)) != -1) { + switch (opt) { + case 'a': + LOG("Autostart disabled using -a\n"); + autostart = false; + break; + case 'c': + override_configpath = sstrdup(optarg); + break; + case 'C': + LOG("Checking configuration file only (-C)\n"); + only_check_config = true; + break; + case 'v': + printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n"); + exit(EXIT_SUCCESS); + case 'V': + set_verbosity(true); + break; + case 'd': + LOG("Enabling debug loglevel %s\n", optarg); + add_loglevel(optarg); + break; + case 'l': + /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */ + break; + case 0: + if (strcmp(long_options[option_index].name, "force-xinerama") == 0) { + force_xinerama = true; + ELOG("Using Xinerama instead of RandR. This option should be " + "avoided at all cost because it does not refresh the list " + "of screens, so you cannot configure displays at runtime. " + "Please check if your driver really does not support RandR " + "and disable this option as soon as you can.\n"); + break; + } + /* fall-through */ + default: + fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "-a: disable autostart\n"); + fprintf(stderr, "-v: display version and exit\n"); + fprintf(stderr, "-V: enable verbose mode\n"); + fprintf(stderr, "-d : enable debug loglevel \n"); + fprintf(stderr, "-c : use the provided configfile instead\n"); + fprintf(stderr, "-C: check configuration file and exit\n"); + fprintf(stderr, "--force-xinerama: Use Xinerama instead of RandR. This " + "option should only be used if you are stuck with the " + "nvidia closed source driver which does not support RandR.\n"); + exit(EXIT_FAILURE); + } + } + + LOG("i3 (tree) version " I3_VERSION " starting\n"); + + conn = xcb_connect(NULL, &screens); + if (xcb_connection_has_error(conn)) + errx(EXIT_FAILURE, "Cannot open display\n"); + + load_configuration(conn, override_configpath, false); + if (only_check_config) { + LOG("Done checking configuration file. Exiting.\n"); + exit(0); + } + + xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); + root = root_screen->root; + root_depth = root_screen->root_depth; + + uint32_t mask = XCB_CW_EVENT_MASK; + uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | + XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video + projector), the root window gets a + ConfigureNotify */ + XCB_EVENT_MASK_POINTER_MOTION | + XCB_EVENT_MASK_PROPERTY_CHANGE | + XCB_EVENT_MASK_ENTER_WINDOW }; + xcb_void_cookie_t cookie; + cookie = xcb_change_window_attributes_checked(conn, root, mask, values); + check_error(conn, cookie, "Another window manager seems to be running"); + + /* Place requests for the atoms we need as soon as possible */ + #define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(conn, 0, strlen(#name), #name); + + REQUEST_ATOM(_NET_SUPPORTED); + REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN); + REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK); + REQUEST_ATOM(_NET_WM_NAME); + REQUEST_ATOM(_NET_WM_STATE); + REQUEST_ATOM(_NET_WM_WINDOW_TYPE); + REQUEST_ATOM(_NET_WM_DESKTOP); + REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK); + REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DIALOG); + REQUEST_ATOM(_NET_WM_WINDOW_TYPE_UTILITY); + REQUEST_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); + REQUEST_ATOM(_NET_WM_WINDOW_TYPE_SPLASH); + REQUEST_ATOM(_NET_WM_STRUT_PARTIAL); + REQUEST_ATOM(WM_PROTOCOLS); + REQUEST_ATOM(WM_DELETE_WINDOW); + REQUEST_ATOM(UTF8_STRING); + REQUEST_ATOM(WM_STATE); + REQUEST_ATOM(WM_CLIENT_LEADER); + REQUEST_ATOM(_NET_CURRENT_DESKTOP); + REQUEST_ATOM(_NET_ACTIVE_WINDOW); + REQUEST_ATOM(_NET_WORKAREA); + + memset(&evenths, 0, sizeof(xcb_event_handlers_t)); + memset(&prophs, 0, sizeof(xcb_property_handlers_t)); + + xcb_event_handlers_init(conn, &evenths); + xcb_property_handlers_init(&prophs, &evenths); + xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL); + + xcb_event_set_button_press_handler(&evenths, handle_button_press, NULL); + + xcb_event_set_map_request_handler(&evenths, handle_map_request, NULL); + + xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL); + xcb_event_set_destroy_notify_handler(&evenths, handle_destroy_notify_event, NULL); + + xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL); + + /* Enter window = user moved his mouse over the window */ + xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, NULL); + + /* Client message are sent to the root window. The only interesting client message + for us is _NET_WM_STATE, we honour _NET_WM_STATE_FULLSCREEN */ + xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL); + + /* Setup NetWM atoms */ + #define GET_ATOM(name) \ + do { \ + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \ + if (!reply) { \ + ELOG("Could not get atom " #name "\n"); \ + exit(-1); \ + } \ + atoms[name] = reply->atom; \ + free(reply); \ + } while (0) + + GET_ATOM(_NET_SUPPORTED); + GET_ATOM(_NET_WM_STATE_FULLSCREEN); + GET_ATOM(_NET_SUPPORTING_WM_CHECK); + GET_ATOM(_NET_WM_NAME); + GET_ATOM(_NET_WM_STATE); + GET_ATOM(_NET_WM_WINDOW_TYPE); + GET_ATOM(_NET_WM_DESKTOP); + GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK); + GET_ATOM(_NET_WM_WINDOW_TYPE_DIALOG); + GET_ATOM(_NET_WM_WINDOW_TYPE_UTILITY); + GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); + GET_ATOM(_NET_WM_WINDOW_TYPE_SPLASH); + GET_ATOM(_NET_WM_STRUT_PARTIAL); + GET_ATOM(WM_PROTOCOLS); + GET_ATOM(WM_DELETE_WINDOW); + GET_ATOM(UTF8_STRING); + GET_ATOM(WM_STATE); + GET_ATOM(WM_CLIENT_LEADER); + GET_ATOM(_NET_CURRENT_DESKTOP); + GET_ATOM(_NET_ACTIVE_WINDOW); + GET_ATOM(_NET_WORKAREA); + + /* Watch _NET_WM_NAME (title of the window encoded in UTF-8) */ + xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL); + + /* Watch WM_HINTS (contains the urgent property) */ + xcb_property_set_handler(&prophs, WM_HINTS, UINT_MAX, handle_hints, NULL); + + /* Watch WM_NAME (title of the window encoded in COMPOUND_TEXT) */ + xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL); + + /* Set up the atoms we support */ + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 7, atoms); + /* Set up the window manager’s name */ + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME], atoms[UTF8_STRING], 8, strlen("i3"), "i3"); + + keysyms = xcb_key_symbols_alloc(conn); + + xcb_get_numlock_mask(conn); + + translate_keysyms(); + grab_all_keys(conn, false); + + int randr_base; + if (force_xinerama) { + xinerama_init(); + } else { + DLOG("Checking for XRandR...\n"); + randr_init(&randr_base); + +#if 0 + xcb_event_set_handler(&evenths, + randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY, + handle_screen_change, + NULL); +#endif + } + + if (!tree_restore()) + tree_init(); + tree_render(); + + /* proof-of-concept for assignments */ + Con *ws = workspace_get("3"); + + Match *current_swallow = scalloc(sizeof(Match)); + TAILQ_INSERT_TAIL(&(ws->swallow_head), current_swallow, matches); + + current_swallow->insert_where = M_ACTIVE; + current_swallow->class = strdup("xterm"); + + struct ev_loop *loop = ev_loop_new(0); + if (loop == NULL) + die("Could not initialize libev. Bad LIBEV_FLAGS?\n"); + + /* Create the UNIX domain socket for IPC */ + if (config.ipc_socket_path != NULL) { + int ipc_socket = ipc_create_socket(config.ipc_socket_path); + if (ipc_socket == -1) { + ELOG("Could not create the IPC socket, IPC disabled\n"); + } else { + struct ev_io *ipc_io = scalloc(sizeof(struct ev_io)); + ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ); + ev_io_start(loop, ipc_io); + } + } + + struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); + struct ev_check *xcb_check = scalloc(sizeof(struct ev_check)); + struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare)); + + ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ); + ev_io_start(loop, xcb_watcher); + + ev_check_init(xcb_check, xcb_check_cb); + ev_check_start(loop, xcb_check); + + ev_prepare_init(xcb_prepare, xcb_prepare_cb); + ev_prepare_start(loop, xcb_prepare); + + xcb_flush(conn); + + manage_existing_windows(root); + + ev_loop(loop, 0); +} diff --git a/src/mainx.c b/src/mainx.c deleted file mode 100644 index e779361b..00000000 --- a/src/mainx.c +++ /dev/null @@ -1,600 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * © 2009-2010 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "config.h" -#include "data.h" -#include "debug.h" -#include "handlers.h" -#include "click.h" -#include "i3.h" -#include "layout.h" -#include "queue.h" -#include "table.h" -#include "util.h" -#include "xcb.h" -#include "randr.h" -#include "xinerama.h" -#include "manage.h" -#include "ipc.h" -#include "log.h" -#include "sighandler.h" - -static int xkb_event_base; - -int xkb_current_group; - -xcb_connection_t *global_conn; - -/* This is the path to i3, copied from argv[0] when starting up */ -char **start_argv; - -/* This is our connection to X11 for use with XKB */ -Display *xkbdpy; - -xcb_key_symbols_t *keysyms; - -/* The list of key bindings */ -struct bindings_head *bindings; - -/* The list of exec-lines */ -struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts); - -/* The list of assignments */ -struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments); - -/* This is a list of Stack_Windows, global, for easier/faster access on expose events */ -struct stack_wins_head stack_wins = SLIST_HEAD_INITIALIZER(stack_wins); - -/* The event handlers need to be global because they are accessed by our custom event handler - in handle_button_press(), needed for graphical resizing */ -xcb_event_handlers_t evenths; -xcb_atom_t atoms[NUM_ATOMS]; - -xcb_window_t root; -int num_screens = 0; - -/* The depth of the root screen (used e.g. for creating new pixmaps later) */ -uint8_t root_depth; - -/* We hope that XKB is supported and set this to false */ -bool xkb_supported = true; - -/* - * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb. - * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop - * - */ -static void xcb_got_event(EV_P_ struct ev_io *w, int revents) { - /* empty, because xcb_prepare_cb and xcb_check_cb are used */ -} - -/* - * Flush before blocking (and waiting for new events) - * - */ -static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) { - xcb_flush(evenths.c); -} - -/* - * Instead of polling the X connection socket we leave this to - * xcb_poll_for_event() which knows better than we can ever know. - * - */ -static void xcb_check_cb(EV_P_ ev_check *w, int revents) { - xcb_generic_event_t *event; - - while ((event = xcb_poll_for_event(evenths.c)) != NULL) { - xcb_event_handle(&evenths, event); - free(event); - } -} - -/* - * When using xmodmap to change the keyboard mapping, this event - * is only sent via XKB. Therefore, we need this special handler. - * - */ -static void xkb_got_event(EV_P_ struct ev_io *w, int revents) { - DLOG("Handling XKB event\n"); - XkbEvent ev; - - /* When using xmodmap, every change (!) gets an own event. - * Therefore, we just read all events and only handle the - * mapping_notify once. */ - bool mapping_changed = false; - while (XPending(xkbdpy)) { - XNextEvent(xkbdpy, (XEvent*)&ev); - /* While we should never receive a non-XKB event, - * better do sanity checking */ - if (ev.type != xkb_event_base) - continue; - - if (ev.any.xkb_type == XkbMapNotify) { - mapping_changed = true; - continue; - } - - if (ev.any.xkb_type != XkbStateNotify) { - ELOG("Unknown XKB event received (type %d)\n", ev.any.xkb_type); - continue; - } - - /* See The XKB Extension: Library Specification, section 14.1 */ - /* We check if the current group (each group contains - * two levels) has been changed. Mode_switch activates - * group XkbGroup2Index */ - if (xkb_current_group == ev.state.group) - continue; - - xkb_current_group = ev.state.group; - - if (ev.state.group == XkbGroup2Index) { - DLOG("Mode_switch enabled\n"); - grab_all_keys(global_conn, true); - } - - if (ev.state.group == XkbGroup1Index) { - DLOG("Mode_switch disabled\n"); - ungrab_all_keys(global_conn); - grab_all_keys(global_conn, false); - } - } - - if (!mapping_changed) - return; - - DLOG("Keyboard mapping changed, updating keybindings\n"); - xcb_key_symbols_free(keysyms); - keysyms = xcb_key_symbols_alloc(global_conn); - - xcb_get_numlock_mask(global_conn); - - ungrab_all_keys(global_conn); - DLOG("Re-grabbing...\n"); - translate_keysyms(); - grab_all_keys(global_conn, (xkb_current_group == XkbGroup2Index)); - DLOG("Done\n"); -} - - -int main(int argc, char *argv[], char *env[]) { - int i, screens, opt; - char *override_configpath = NULL; - bool autostart = true; - bool only_check_config = false; - bool force_xinerama = false; - xcb_connection_t *conn; - xcb_property_handlers_t prophs; - xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS]; - static struct option long_options[] = { - {"no-autostart", no_argument, 0, 'a'}, - {"config", required_argument, 0, 'c'}, - {"version", no_argument, 0, 'v'}, - {"help", no_argument, 0, 'h'}, - {"force-xinerama", no_argument, 0, 0}, - {0, 0, 0, 0} - }; - int option_index = 0; - - setlocale(LC_ALL, ""); - - /* Disable output buffering to make redirects in .xsession actually useful for debugging */ - if (!isatty(fileno(stdout))) - setbuf(stdout, NULL); - - start_argv = argv; - - while ((opt = getopt_long(argc, argv, "c:Cvahld:V", long_options, &option_index)) != -1) { - switch (opt) { - case 'a': - LOG("Autostart disabled using -a\n"); - autostart = false; - break; - case 'c': - override_configpath = sstrdup(optarg); - break; - case 'C': - LOG("Checking configuration file only (-C)\n"); - only_check_config = true; - break; - case 'v': - printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n"); - exit(EXIT_SUCCESS); - case 'V': - set_verbosity(true); - break; - case 'd': - LOG("Enabling debug loglevel %s\n", optarg); - add_loglevel(optarg); - break; - case 'l': - /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */ - break; - case 0: - if (strcmp(long_options[option_index].name, "force-xinerama") == 0) { - force_xinerama = true; - ELOG("Using Xinerama instead of RandR. This option should be " - "avoided at all cost because it does not refresh the list " - "of screens, so you cannot configure displays at runtime. " - "Please check if your driver really does not support RandR " - "and disable this option as soon as you can.\n"); - break; - } - /* fall-through */ - default: - fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]); - fprintf(stderr, "\n"); - fprintf(stderr, "-a: disable autostart\n"); - fprintf(stderr, "-v: display version and exit\n"); - fprintf(stderr, "-V: enable verbose mode\n"); - fprintf(stderr, "-d : enable debug loglevel \n"); - fprintf(stderr, "-c : use the provided configfile instead\n"); - fprintf(stderr, "-C: check configuration file and exit\n"); - fprintf(stderr, "--force-xinerama: Use Xinerama instead of RandR. This " - "option should only be used if you are stuck with the " - "nvidia closed source driver which does not support RandR.\n"); - exit(EXIT_FAILURE); - } - } - - LOG("i3 version " I3_VERSION " starting\n"); - - /* Initialize the table data structures for each workspace */ - init_table(); - - memset(&evenths, 0, sizeof(xcb_event_handlers_t)); - memset(&prophs, 0, sizeof(xcb_property_handlers_t)); - - conn = global_conn = xcb_connect(NULL, &screens); - - if (xcb_connection_has_error(conn)) - die("Cannot open display\n"); - - load_configuration(conn, override_configpath, false); - if (only_check_config) { - LOG("Done checking configuration file. Exiting.\n"); - exit(0); - } - - /* Create the initial container on the first workspace. This used to - * be part of init_table, but since it possibly requires an X - * connection and a loaded configuration (default mode for new - * containers may be stacking, which requires a new window to be - * created), it had to be delayed. */ - expand_table_cols(TAILQ_FIRST(workspaces)); - expand_table_rows(TAILQ_FIRST(workspaces)); - - /* Place requests for the atoms we need as soon as possible */ - #define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(conn, 0, strlen(#name), #name); - - REQUEST_ATOM(_NET_SUPPORTED); - REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN); - REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK); - REQUEST_ATOM(_NET_WM_NAME); - REQUEST_ATOM(_NET_WM_STATE); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE); - REQUEST_ATOM(_NET_WM_DESKTOP); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DIALOG); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_UTILITY); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_SPLASH); - REQUEST_ATOM(_NET_WM_STRUT_PARTIAL); - REQUEST_ATOM(WM_PROTOCOLS); - REQUEST_ATOM(WM_DELETE_WINDOW); - REQUEST_ATOM(UTF8_STRING); - REQUEST_ATOM(WM_STATE); - REQUEST_ATOM(WM_CLIENT_LEADER); - REQUEST_ATOM(_NET_CURRENT_DESKTOP); - REQUEST_ATOM(_NET_ACTIVE_WINDOW); - REQUEST_ATOM(_NET_WORKAREA); - - /* TODO: this has to be more beautiful somewhen */ - int major, minor, error; - - major = XkbMajorVersion; - minor = XkbMinorVersion; - - int errBase; - - if ((xkbdpy = XkbOpenDisplay(getenv("DISPLAY"), &xkb_event_base, &errBase, &major, &minor, &error)) == NULL) { - ELOG("ERROR: XkbOpenDisplay() failed, disabling XKB support\n"); - xkb_supported = false; - } - - if (xkb_supported) { - if (fcntl(ConnectionNumber(xkbdpy), F_SETFD, FD_CLOEXEC) == -1) { - fprintf(stderr, "Could not set FD_CLOEXEC on xkbdpy\n"); - return 1; - } - - int i1; - if (!XkbQueryExtension(xkbdpy,&i1,&xkb_event_base,&errBase,&major,&minor)) { - fprintf(stderr, "XKB not supported by X-server\n"); - return 1; - } - /* end of ugliness */ - - if (!XkbSelectEvents(xkbdpy, XkbUseCoreKbd, - XkbMapNotifyMask | XkbStateNotifyMask, - XkbMapNotifyMask | XkbStateNotifyMask)) { - fprintf(stderr, "Could not set XKB event mask\n"); - return 1; - } - } - - /* Initialize event loop using libev */ - struct ev_loop *loop = ev_loop_new(0); - if (loop == NULL) - die("Could not initialize libev. Bad LIBEV_FLAGS?\n"); - - struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); - struct ev_io *xkb = scalloc(sizeof(struct ev_io)); - struct ev_check *xcb_check = scalloc(sizeof(struct ev_check)); - struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare)); - - ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ); - ev_io_start(loop, xcb_watcher); - - if (xkb_supported) { - ev_io_init(xkb, xkb_got_event, ConnectionNumber(xkbdpy), EV_READ); - ev_io_start(loop, xkb); - - /* Flush the buffer so that libev can properly get new events */ - XFlush(xkbdpy); - } - - ev_check_init(xcb_check, xcb_check_cb); - ev_check_start(loop, xcb_check); - - ev_prepare_init(xcb_prepare, xcb_prepare_cb); - ev_prepare_start(loop, xcb_prepare); - - /* Grab the server to delay any events until we enter the eventloop */ - xcb_grab_server(conn); - - xcb_event_handlers_init(conn, &evenths); - - /* DEBUG: Trap all events and print them */ - for (i = 2; i < 128; ++i) - xcb_event_set_handler(&evenths, i, handle_event, 0); - - for (i = 0; i < 256; ++i) - xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t)handle_event, 0); - - /* Expose = an Application should redraw itself, in this case it’s our titlebars. */ - xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL); - - /* Key presses are pretty obvious, I think */ - xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL); - - /* Enter window = user moved his mouse over the window */ - xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, NULL); - - /* Button press = user pushed a mouse button over one of our windows */ - xcb_event_set_button_press_handler(&evenths, handle_button_press, NULL); - - /* Map notify = there is a new window */ - xcb_event_set_map_request_handler(&evenths, handle_map_request, &prophs); - - /* Unmap notify = window disappeared. When sent from a client, we don’t manage - it any longer. Usually, the client destroys the window shortly afterwards. */ - xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL); - - /* Destroy notify is handled the same as unmap notify */ - xcb_event_set_destroy_notify_handler(&evenths, handle_destroy_notify_event, NULL); - - /* Configure notify = window’s configuration (geometry, stacking, …). We only need - it to set up ignore the following enter_notify events */ - xcb_event_set_configure_notify_handler(&evenths, handle_configure_event, NULL); - - /* Configure request = window tried to change size on its own */ - xcb_event_set_configure_request_handler(&evenths, handle_configure_request, NULL); - - /* Motion notify = user moved his cursor (over the root window and may - * cross virtual screen boundaries doing that) */ - xcb_event_set_motion_notify_handler(&evenths, handle_motion_notify, NULL); - - /* Mapping notify = keyboard mapping changed (Xmodmap), re-grab bindings */ - xcb_event_set_mapping_notify_handler(&evenths, handle_mapping_notify, NULL); - - /* Client message are sent to the root window. The only interesting client message - for us is _NET_WM_STATE, we honour _NET_WM_STATE_FULLSCREEN */ - xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL); - - /* Initialize the property handlers */ - xcb_property_handlers_init(&prophs, &evenths); - - /* Watch size hints (to obey correct aspect ratio) */ - xcb_property_set_handler(&prophs, WM_NORMAL_HINTS, UINT_MAX, handle_normal_hints, NULL); - - /* Get the root window and set the event mask */ - xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); - root = root_screen->root; - root_depth = root_screen->root_depth; - - uint32_t mask = XCB_CW_EVENT_MASK; - uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video - projector), the root window gets a - ConfigureNotify */ - XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_PROPERTY_CHANGE | - XCB_EVENT_MASK_ENTER_WINDOW }; - xcb_void_cookie_t cookie; - cookie = xcb_change_window_attributes_checked(conn, root, mask, values); - check_error(conn, cookie, "Another window manager seems to be running"); - - /* Setup NetWM atoms */ - #define GET_ATOM(name) { \ - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \ - if (!reply) { \ - ELOG("Could not get atom " #name "\n"); \ - exit(-1); \ - } \ - atoms[name] = reply->atom; \ - free(reply); \ - } - - GET_ATOM(_NET_SUPPORTED); - GET_ATOM(_NET_WM_STATE_FULLSCREEN); - GET_ATOM(_NET_SUPPORTING_WM_CHECK); - GET_ATOM(_NET_WM_NAME); - GET_ATOM(_NET_WM_STATE); - GET_ATOM(_NET_WM_WINDOW_TYPE); - GET_ATOM(_NET_WM_DESKTOP); - GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK); - GET_ATOM(_NET_WM_WINDOW_TYPE_DIALOG); - GET_ATOM(_NET_WM_WINDOW_TYPE_UTILITY); - GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); - GET_ATOM(_NET_WM_WINDOW_TYPE_SPLASH); - GET_ATOM(_NET_WM_STRUT_PARTIAL); - GET_ATOM(WM_PROTOCOLS); - GET_ATOM(WM_DELETE_WINDOW); - GET_ATOM(UTF8_STRING); - GET_ATOM(WM_STATE); - GET_ATOM(WM_CLIENT_LEADER); - GET_ATOM(_NET_CURRENT_DESKTOP); - GET_ATOM(_NET_ACTIVE_WINDOW); - GET_ATOM(_NET_WORKAREA); - - xcb_property_set_handler(&prophs, atoms[_NET_WM_WINDOW_TYPE], UINT_MAX, handle_window_type, NULL); - /* TODO: In order to comply with EWMH, we have to watch _NET_WM_STRUT_PARTIAL */ - - /* Watch _NET_WM_NAME (= title of the window in UTF-8) property */ - xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL); - - /* Watch WM_TRANSIENT_FOR property (to which client this popup window belongs) */ - xcb_property_set_handler(&prophs, WM_TRANSIENT_FOR, UINT_MAX, handle_transient_for, NULL); - - /* Watch WM_NAME (= title of the window in compound text) property for legacy applications */ - xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL); - - /* Watch WM_CLASS (= class of the window) */ - xcb_property_set_handler(&prophs, WM_CLASS, 128, handle_windowclass_change, NULL); - - /* Watch WM_CLIENT_LEADER (= logical parent window for toolbars etc.) */ - xcb_property_set_handler(&prophs, atoms[WM_CLIENT_LEADER], UINT_MAX, handle_clientleader_change, NULL); - - /* Watch WM_HINTS (contains the urgent property) */ - xcb_property_set_handler(&prophs, WM_HINTS, UINT_MAX, handle_hints, NULL); - - /* Set up the atoms we support */ - check_error(conn, xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], - ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED"); - /* Set up the window manager’s name */ - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME], atoms[UTF8_STRING], 8, strlen("i3"), "i3"); - - keysyms = xcb_key_symbols_alloc(conn); - - xcb_get_numlock_mask(conn); - - translate_keysyms(); - grab_all_keys(conn, false); - - int randr_base; - if (force_xinerama) { - initialize_xinerama(conn); - } else { - DLOG("Checking for XRandR...\n"); - initialize_randr(conn, &randr_base); - - xcb_event_set_handler(&evenths, - randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY, - handle_screen_change, - NULL); - } - - xcb_flush(conn); - - /* Get pointer position to see on which screen we’re starting */ - xcb_query_pointer_reply_t *reply; - if ((reply = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL)) == NULL) { - ELOG("Could not get pointer position\n"); - return 1; - } - - Output *screen = get_output_containing(reply->root_x, reply->root_y); - if (screen == NULL) { - ELOG("ERROR: No screen at %d x %d, starting on the first screen\n", - reply->root_x, reply->root_y); - screen = get_first_output(); - } - - DLOG("Starting on %p\n", screen->current_workspace); - c_ws = screen->current_workspace; - - manage_existing_windows(conn, &prophs, root); - - /* Create the UNIX domain socket for IPC */ - if (config.ipc_socket_path != NULL) { - int ipc_socket = ipc_create_socket(config.ipc_socket_path); - if (ipc_socket == -1) { - ELOG("Could not create the IPC socket, IPC disabled\n"); - } else { - struct ev_io *ipc_io = scalloc(sizeof(struct ev_io)); - ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ); - ev_io_start(loop, ipc_io); - } - } - - /* Handle the events which arrived until now */ - xcb_check_cb(NULL, NULL, 0); - - setup_signal_handler(); - - /* Ignore SIGPIPE to survive errors when an IPC client disconnects - * while we are sending him a message */ - signal(SIGPIPE, SIG_IGN); - - /* Ungrab the server to receive events and enter libev’s eventloop */ - xcb_ungrab_server(conn); - - /* Autostarting exec-lines */ - struct Autostart *exec; - if (autostart) { - TAILQ_FOREACH(exec, &autostarts, autostarts) { - LOG("auto-starting %s\n", exec->command); - start_application(exec->command); - } - } - - ev_loop(loop, 0); - - /* not reached */ - return 0; -} diff --git a/src/nc.c b/src/nc.c deleted file mode 100644 index a5769d17..00000000 --- a/src/nc.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - */ -#include -#include -#include "all.h" - -static int xkb_event_base; - -int xkb_current_group; - -extern Con *focused; - -char **start_argv; - -xcb_connection_t *conn; -xcb_event_handlers_t evenths; -xcb_property_handlers_t prophs; -xcb_atom_t atoms[NUM_ATOMS]; - -xcb_window_t root; -uint8_t root_depth; - -xcb_key_symbols_t *keysyms; - -/* The list of key bindings */ -struct bindings_head *bindings; - -/* The list of exec-lines */ -struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts); - -/* The list of assignments */ -struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments); - -/* - * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb. - * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop - * - */ -static void xcb_got_event(EV_P_ struct ev_io *w, int revents) { - /* empty, because xcb_prepare_cb and xcb_check_cb are used */ -} - -/* - * Flush before blocking (and waiting for new events) - * - */ -static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) { - xcb_flush(conn); -} - -/* - * Instead of polling the X connection socket we leave this to - * xcb_poll_for_event() which knows better than we can ever know. - * - */ -static void xcb_check_cb(EV_P_ ev_check *w, int revents) { - xcb_generic_event_t *event; - - while ((event = xcb_poll_for_event(conn)) != NULL) { - xcb_event_handle(&evenths, event); - free(event); - } -} - -int main(int argc, char *argv[]) { - //parse_cmd("[ foo ] attach, attach ; focus"); - int screens; - char *override_configpath = NULL; - bool autostart = true; - bool only_check_config = false; - bool force_xinerama = false; - xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS]; - static struct option long_options[] = { - {"no-autostart", no_argument, 0, 'a'}, - {"config", required_argument, 0, 'c'}, - {"version", no_argument, 0, 'v'}, - {"help", no_argument, 0, 'h'}, - {"force-xinerama", no_argument, 0, 0}, - {0, 0, 0, 0} - }; - int option_index = 0, opt; - - setlocale(LC_ALL, ""); - - /* Disable output buffering to make redirects in .xsession actually useful for debugging */ - if (!isatty(fileno(stdout))) - setbuf(stdout, NULL); - - start_argv = argv; - - while ((opt = getopt_long(argc, argv, "c:Cvahld:V", long_options, &option_index)) != -1) { - switch (opt) { - case 'a': - LOG("Autostart disabled using -a\n"); - autostart = false; - break; - case 'c': - override_configpath = sstrdup(optarg); - break; - case 'C': - LOG("Checking configuration file only (-C)\n"); - only_check_config = true; - break; - case 'v': - printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n"); - exit(EXIT_SUCCESS); - case 'V': - set_verbosity(true); - break; - case 'd': - LOG("Enabling debug loglevel %s\n", optarg); - add_loglevel(optarg); - break; - case 'l': - /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */ - break; - case 0: - if (strcmp(long_options[option_index].name, "force-xinerama") == 0) { - force_xinerama = true; - ELOG("Using Xinerama instead of RandR. This option should be " - "avoided at all cost because it does not refresh the list " - "of screens, so you cannot configure displays at runtime. " - "Please check if your driver really does not support RandR " - "and disable this option as soon as you can.\n"); - break; - } - /* fall-through */ - default: - fprintf(stderr, "Usage: %s [-c configfile] [-d loglevel] [-a] [-v] [-V] [-C]\n", argv[0]); - fprintf(stderr, "\n"); - fprintf(stderr, "-a: disable autostart\n"); - fprintf(stderr, "-v: display version and exit\n"); - fprintf(stderr, "-V: enable verbose mode\n"); - fprintf(stderr, "-d : enable debug loglevel \n"); - fprintf(stderr, "-c : use the provided configfile instead\n"); - fprintf(stderr, "-C: check configuration file and exit\n"); - fprintf(stderr, "--force-xinerama: Use Xinerama instead of RandR. This " - "option should only be used if you are stuck with the " - "nvidia closed source driver which does not support RandR.\n"); - exit(EXIT_FAILURE); - } - } - - LOG("i3 (tree) version " I3_VERSION " starting\n"); - - conn = xcb_connect(NULL, &screens); - if (xcb_connection_has_error(conn)) - errx(EXIT_FAILURE, "Cannot open display\n"); - - load_configuration(conn, override_configpath, false); - if (only_check_config) { - LOG("Done checking configuration file. Exiting.\n"); - exit(0); - } - - xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); - root = root_screen->root; - root_depth = root_screen->root_depth; - - uint32_t mask = XCB_CW_EVENT_MASK; - uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video - projector), the root window gets a - ConfigureNotify */ - XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_PROPERTY_CHANGE | - XCB_EVENT_MASK_ENTER_WINDOW }; - xcb_void_cookie_t cookie; - cookie = xcb_change_window_attributes_checked(conn, root, mask, values); - check_error(conn, cookie, "Another window manager seems to be running"); - - /* Place requests for the atoms we need as soon as possible */ - #define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(conn, 0, strlen(#name), #name); - - REQUEST_ATOM(_NET_SUPPORTED); - REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN); - REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK); - REQUEST_ATOM(_NET_WM_NAME); - REQUEST_ATOM(_NET_WM_STATE); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE); - REQUEST_ATOM(_NET_WM_DESKTOP); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DIALOG); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_UTILITY); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_SPLASH); - REQUEST_ATOM(_NET_WM_STRUT_PARTIAL); - REQUEST_ATOM(WM_PROTOCOLS); - REQUEST_ATOM(WM_DELETE_WINDOW); - REQUEST_ATOM(UTF8_STRING); - REQUEST_ATOM(WM_STATE); - REQUEST_ATOM(WM_CLIENT_LEADER); - REQUEST_ATOM(_NET_CURRENT_DESKTOP); - REQUEST_ATOM(_NET_ACTIVE_WINDOW); - REQUEST_ATOM(_NET_WORKAREA); - - memset(&evenths, 0, sizeof(xcb_event_handlers_t)); - memset(&prophs, 0, sizeof(xcb_property_handlers_t)); - - xcb_event_handlers_init(conn, &evenths); - xcb_property_handlers_init(&prophs, &evenths); - xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL); - - xcb_event_set_button_press_handler(&evenths, handle_button_press, NULL); - - xcb_event_set_map_request_handler(&evenths, handle_map_request, NULL); - - xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL); - xcb_event_set_destroy_notify_handler(&evenths, handle_destroy_notify_event, NULL); - - xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL); - - /* Enter window = user moved his mouse over the window */ - xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, NULL); - - /* Client message are sent to the root window. The only interesting client message - for us is _NET_WM_STATE, we honour _NET_WM_STATE_FULLSCREEN */ - xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL); - - /* Setup NetWM atoms */ - #define GET_ATOM(name) \ - do { \ - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \ - if (!reply) { \ - ELOG("Could not get atom " #name "\n"); \ - exit(-1); \ - } \ - atoms[name] = reply->atom; \ - free(reply); \ - } while (0) - - GET_ATOM(_NET_SUPPORTED); - GET_ATOM(_NET_WM_STATE_FULLSCREEN); - GET_ATOM(_NET_SUPPORTING_WM_CHECK); - GET_ATOM(_NET_WM_NAME); - GET_ATOM(_NET_WM_STATE); - GET_ATOM(_NET_WM_WINDOW_TYPE); - GET_ATOM(_NET_WM_DESKTOP); - GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK); - GET_ATOM(_NET_WM_WINDOW_TYPE_DIALOG); - GET_ATOM(_NET_WM_WINDOW_TYPE_UTILITY); - GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); - GET_ATOM(_NET_WM_WINDOW_TYPE_SPLASH); - GET_ATOM(_NET_WM_STRUT_PARTIAL); - GET_ATOM(WM_PROTOCOLS); - GET_ATOM(WM_DELETE_WINDOW); - GET_ATOM(UTF8_STRING); - GET_ATOM(WM_STATE); - GET_ATOM(WM_CLIENT_LEADER); - GET_ATOM(_NET_CURRENT_DESKTOP); - GET_ATOM(_NET_ACTIVE_WINDOW); - GET_ATOM(_NET_WORKAREA); - - /* Watch _NET_WM_NAME (title of the window encoded in UTF-8) */ - xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL); - - /* Watch WM_HINTS (contains the urgent property) */ - xcb_property_set_handler(&prophs, WM_HINTS, UINT_MAX, handle_hints, NULL); - - /* Watch WM_NAME (title of the window encoded in COMPOUND_TEXT) */ - xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL); - - /* Set up the atoms we support */ - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 7, atoms); - /* Set up the window manager’s name */ - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME], atoms[UTF8_STRING], 8, strlen("i3"), "i3"); - - keysyms = xcb_key_symbols_alloc(conn); - - xcb_get_numlock_mask(conn); - - translate_keysyms(); - grab_all_keys(conn, false); - - int randr_base; - if (force_xinerama) { - xinerama_init(); - } else { - DLOG("Checking for XRandR...\n"); - randr_init(&randr_base); - -#if 0 - xcb_event_set_handler(&evenths, - randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY, - handle_screen_change, - NULL); -#endif - } - - if (!tree_restore()) - tree_init(); - tree_render(); - - /* proof-of-concept for assignments */ - Con *ws = workspace_get("3"); - - Match *current_swallow = scalloc(sizeof(Match)); - TAILQ_INSERT_TAIL(&(ws->swallow_head), current_swallow, matches); - - current_swallow->insert_where = M_ACTIVE; - current_swallow->class = strdup("xterm"); - - struct ev_loop *loop = ev_loop_new(0); - if (loop == NULL) - die("Could not initialize libev. Bad LIBEV_FLAGS?\n"); - - /* Create the UNIX domain socket for IPC */ - if (config.ipc_socket_path != NULL) { - int ipc_socket = ipc_create_socket(config.ipc_socket_path); - if (ipc_socket == -1) { - ELOG("Could not create the IPC socket, IPC disabled\n"); - } else { - struct ev_io *ipc_io = scalloc(sizeof(struct ev_io)); - ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ); - ev_io_start(loop, ipc_io); - } - } - - struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); - struct ev_check *xcb_check = scalloc(sizeof(struct ev_check)); - struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare)); - - ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ); - ev_io_start(loop, xcb_watcher); - - ev_check_init(xcb_check, xcb_check_cb); - ev_check_start(loop, xcb_check); - - ev_prepare_init(xcb_prepare, xcb_prepare_cb); - ev_prepare_start(loop, xcb_prepare); - - xcb_flush(conn); - - manage_existing_windows(root); - - ev_loop(loop, 0); -} diff --git a/src/resize.c b/src/resize.c deleted file mode 100644 index a32e5755..00000000 --- a/src/resize.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * vim:ts=8:expandtab - * - * i3 - an improved dynamic tiling window manager - * - * © 2009-2010 Michael Stapelberg and contributors - * - * See file LICENSE for license information. - * - * This file contains the functions for resizing table columns/rows because - * it’s actually lots of work, compared to the other handlers. - * - */ -#include -#include - -#include -#include - -#include "i3.h" -#include "data.h" -#include "resize.h" -#include "util.h" -#include "xcb.h" -#include "debug.h" -#include "layout.h" -#include "randr.h" -#include "config.h" -#include "floating.h" -#include "workspace.h" -#include "log.h" - -/* - * 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 callback_params { - resize_orientation_t orientation; - Output *screen; - xcb_window_t helpwin; - uint32_t *new_position; -}; - -DRAGGING_CB(resize_callback) { - struct callback_params *params = extra; - Output *screen = params->screen; - DLOG("new x = %d, y = %d\n", new_x, new_y); - if (params->orientation == O_VERTICAL) { - /* Check if the new coordinates are within screen boundaries */ - if (new_x > (screen->rect.x + screen->rect.width - 25) || - new_x < (screen->rect.x + 25)) - return; - - *(params->new_position) = new_x; - xcb_configure_window(conn, params->helpwin, XCB_CONFIG_WINDOW_X, params->new_position); - } else { - if (new_y > (screen->rect.y + screen->rect.height - 25) || - new_y < (screen->rect.y + 25)) - return; - - *(params->new_position) = new_y; - xcb_configure_window(conn, params->helpwin, XCB_CONFIG_WINDOW_Y, params->new_position); - } - - xcb_flush(conn); -} - -/* - * Renders the resize window between the first/second container and resizes - * the table column/row. - * - */ -int resize_graphical_handler(xcb_connection_t *conn, Workspace *ws, int first, int second, - resize_orientation_t orientation, xcb_button_press_event_t *event) { - uint32_t new_position; - Output *screen = get_output_containing(event->root_x, event->root_y); - if (screen == NULL) { - ELOG("BUG: No screen found at this position (%d, %d)\n", event->root_x, event->root_y); - return 1; - } - - /* We cannot use the X root window's width_in_pixels or height_in_pixels - * attributes here since they are not updated when you configure new - * screens during runtime. Instead, we just use the most right and most - * bottom Xinerama screen and use their position + width/height to get - * the area of pixels currently in use */ - Output *most_right = get_output_most(D_RIGHT, screen), - *most_bottom = get_output_most(D_DOWN, screen); - - DLOG("event->event_x = %d, event->root_x = %d\n", event->event_x, event->root_x); - - DLOG("Screen dimensions: (%d, %d) %d x %d\n", screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height); - - uint32_t mask = 0; - uint32_t values[2]; - - mask = XCB_CW_OVERRIDE_REDIRECT; - values[0] = 1; - - /* Open a new window, the resizebar. Grab the pointer and move the window around - as the user moves the pointer. */ - Rect grabrect = {0, - 0, - most_right->rect.x + most_right->rect.width, - most_bottom->rect.x + most_bottom->rect.height}; - xcb_window_t grabwin = create_window(conn, grabrect, XCB_WINDOW_CLASS_INPUT_ONLY, -1, true, mask, values); - - Rect helprect; - if (orientation == O_VERTICAL) { - helprect.x = event->root_x; - helprect.y = screen->rect.y; - helprect.width = 2; - helprect.height = screen->rect.height; - new_position = event->root_x; - } else { - helprect.x = screen->rect.x; - helprect.y = event->root_y; - helprect.width = screen->rect.width; - helprect.height = 2; - new_position = event->root_y; - } - - mask = XCB_CW_BACK_PIXEL; - values[0] = config.client.focused.border; - - mask |= XCB_CW_OVERRIDE_REDIRECT; - values[1] = 1; - - xcb_window_t helpwin = create_window(conn, helprect, XCB_WINDOW_CLASS_INPUT_OUTPUT, - (orientation == O_VERTICAL ? - XCB_CURSOR_SB_H_DOUBLE_ARROW : - XCB_CURSOR_SB_V_DOUBLE_ARROW), true, mask, values); - - xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, helpwin); - - xcb_flush(conn); - - struct callback_params params = { orientation, screen, helpwin, &new_position }; - - drag_pointer(conn, NULL, event, grabwin, BORDER_TOP, resize_callback, ¶ms); - - xcb_destroy_window(conn, helpwin); - xcb_destroy_window(conn, grabwin); - xcb_flush(conn); - - int pixels; - if (orientation == O_VERTICAL) - pixels = (new_position - event->root_x); - else pixels = (new_position - event->root_y); - resize_container(conn, ws, first, second, orientation, pixels); - - return 1; -} - -/* - * Resizes a column/row by the given amount of pixels. Called by - * resize_graphical_handler (the user clicked) or parse_resize_command (the - * user issued the command) - * - */ -void resize_container(xcb_connection_t *conn, Workspace *ws, int first, int second, - resize_orientation_t orientation, int pixels) { - - /* TODO: refactor this, both blocks are very identical */ - if (orientation == O_VERTICAL) { - int default_width = ws->rect.width / ws->cols; - int old_unoccupied_x = get_unoccupied_x(ws); - - /* We pre-calculate the unoccupied space to see if we need to adapt sizes before - * doing the resize */ - int new_unoccupied_x = old_unoccupied_x; - - if (old_unoccupied_x == 0) - old_unoccupied_x = ws->rect.width; - - if (ws->width_factor[first] == 0) - new_unoccupied_x += default_width; - - if (ws->width_factor[second] == 0) - new_unoccupied_x += default_width; - - DLOG("\n\n\n"); - DLOG("old = %d, new = %d\n", old_unoccupied_x, new_unoccupied_x); - - int cols_without_wf = 0; - int old_width, old_second_width; - for (int col = 0; col < ws->cols; col++) - if (ws->width_factor[col] == 0) - cols_without_wf++; - - DLOG("old_unoccupied_x = %d\n", old_unoccupied_x); - - DLOG("Updating first (before = %f)\n", ws->width_factor[first]); - /* Convert 0 (for default width_factor) to actual numbers */ - if (ws->width_factor[first] == 0) - old_width = (old_unoccupied_x / max(cols_without_wf, 1)); - else old_width = ws->width_factor[first] * old_unoccupied_x; - - DLOG("second (before = %f)\n", ws->width_factor[second]); - if (ws->width_factor[second] == 0) - old_second_width = (old_unoccupied_x / max(cols_without_wf, 1)); - else old_second_width = ws->width_factor[second] * old_unoccupied_x; - - DLOG("middle = %f\n", ws->width_factor[first]); - - /* If the space used for customly resized columns has changed we need to adapt the - * other customly resized columns, if any */ - if (new_unoccupied_x != old_unoccupied_x) - for (int col = 0; col < ws->cols; col++) { - if (ws->width_factor[col] == 0) - continue; - - DLOG("Updating other column (%d) (current width_factor = %f)\n", col, ws->width_factor[col]); - ws->width_factor[col] = (ws->width_factor[col] * old_unoccupied_x) / new_unoccupied_x; - DLOG("to %f\n", ws->width_factor[col]); - } - - DLOG("Updating first (before = %f)\n", ws->width_factor[first]); - /* Convert 0 (for default width_factor) to actual numbers */ - if (ws->width_factor[first] == 0) - ws->width_factor[first] = ((float)ws->rect.width / ws->cols) / new_unoccupied_x; - - DLOG("first->width = %d, pixels = %d\n", old_width, pixels); - ws->width_factor[first] *= (float)(old_width + pixels) / old_width; - DLOG("-> %f\n", ws->width_factor[first]); - - - DLOG("Updating second (before = %f)\n", ws->width_factor[second]); - if (ws->width_factor[second] == 0) - ws->width_factor[second] = ((float)ws->rect.width / ws->cols) / new_unoccupied_x; - - DLOG("middle = %f\n", ws->width_factor[second]); - DLOG("second->width = %d, pixels = %d\n", old_second_width, pixels); - ws->width_factor[second] *= (float)(old_second_width - pixels) / old_second_width; - DLOG("-> %f\n", ws->width_factor[second]); - - DLOG("new unoccupied_x = %d\n", get_unoccupied_x(ws)); - - DLOG("\n\n\n"); - } else { - int ws_height = workspace_height(ws); - int default_height = ws_height / ws->rows; - int old_unoccupied_y = get_unoccupied_y(ws); - - /* We pre-calculate the unoccupied space to see if we need to adapt sizes before - * doing the resize */ - int new_unoccupied_y = old_unoccupied_y; - - if (old_unoccupied_y == 0) - old_unoccupied_y = ws_height; - - if (ws->height_factor[first] == 0) - new_unoccupied_y += default_height; - - if (ws->height_factor[second] == 0) - new_unoccupied_y += default_height; - - int cols_without_hf = 0; - int old_height, old_second_height; - for (int row = 0; row < ws->rows; row++) - if (ws->height_factor[row] == 0) - cols_without_hf++; - - DLOG("old_unoccupied_y = %d\n", old_unoccupied_y); - - DLOG("Updating first (before = %f)\n", ws->height_factor[first]); - /* Convert 0 (for default width_factor) to actual numbers */ - if (ws->height_factor[first] == 0) - old_height = (old_unoccupied_y / max(cols_without_hf, 1)); - else old_height = ws->height_factor[first] * old_unoccupied_y; - - DLOG("second (before = %f)\n", ws->height_factor[second]); - if (ws->height_factor[second] == 0) - old_second_height = (old_unoccupied_y / max(cols_without_hf, 1)); - else old_second_height = ws->height_factor[second] * old_unoccupied_y; - - DLOG("middle = %f\n", ws->height_factor[first]); - - - DLOG("\n\n\n"); - DLOG("old = %d, new = %d\n", old_unoccupied_y, new_unoccupied_y); - - /* If the space used for customly resized columns has changed we need to adapt the - * other customly resized columns, if any */ - if (new_unoccupied_y != old_unoccupied_y) - for (int row = 0; row < ws->rows; row++) { - if (ws->height_factor[row] == 0) - continue; - - DLOG("Updating other column (%d) (current width_factor = %f)\n", row, ws->height_factor[row]); - ws->height_factor[row] = (ws->height_factor[row] * old_unoccupied_y) / new_unoccupied_y; - DLOG("to %f\n", ws->height_factor[row]); - } - - - DLOG("Updating first (before = %f)\n", ws->height_factor[first]); - /* Convert 0 (for default width_factor) to actual numbers */ - if (ws->height_factor[first] == 0) - ws->height_factor[first] = ((float)ws_height / ws->rows) / new_unoccupied_y; - - DLOG("first->width = %d, pixels = %d\n", old_height, pixels); - ws->height_factor[first] *= (float)(old_height + pixels) / old_height; - DLOG("-> %f\n", ws->height_factor[first]); - - - DLOG("Updating second (before = %f)\n", ws->height_factor[second]); - if (ws->height_factor[second] == 0) - ws->height_factor[second] = ((float)ws_height / ws->rows) / new_unoccupied_y; - DLOG("middle = %f\n", ws->height_factor[second]); - DLOG("second->width = %d, pixels = %d\n", old_second_height, pixels); - ws->height_factor[second] *= (float)(old_second_height - pixels) / old_second_height; - DLOG("-> %f\n", ws->height_factor[second]); - - DLOG("new unoccupied_y = %d\n", get_unoccupied_y(ws)); - - DLOG("\n\n\n"); - } - - render_layout(conn); -}