diff --git a/Makefile b/Makefile index 850693e3..419dc561 100644 --- a/Makefile +++ b/Makefile @@ -28,16 +28,19 @@ install: all $(INSTALL) -m 0644 i3.desktop $(DESTDIR)/usr/share/xsessions/ $(MAKE) TOPDIR=$(TOPDIR) -C i3-msg install -dist: clean +dist: distclean [ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION} [ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2 mkdir i3-${VERSION} - cp DEPENDS GOALS LICENSE PACKAGE-MAINTAINER TODO RELEASE-* i3.config i3.desktop pseudo-doc.doxygen i3-${VERSION} - cp -r src include man i3-${VERSION} + cp DEPENDS GOALS LICENSE PACKAGE-MAINTAINER TODO RELEASE-NOTES-${VERSION} i3.config i3.desktop pseudo-doc.doxygen Makefile i3-${VERSION} + cp -r src i3-msg include man i3-${VERSION} # Only copy toplevel documentation (important stuff) mkdir i3-${VERSION}/docs find docs -maxdepth 1 -type f ! -name "*.xcf" -exec cp '{}' i3-${VERSION}/docs \; - sed -e 's/^GIT_VERSION=\(.*\)/GIT_VERSION=${GIT_VERSION}/g;s/^VERSION=\(.*\)/VERSION=${VERSION}/g' Makefile > i3-${VERSION}/Makefile + sed -e 's/^GIT_VERSION=\(.*\)/GIT_VERSION=${GIT_VERSION}/g;s/^VERSION=\(.*\)/VERSION=${VERSION}/g' common.mk > i3-${VERSION}/common.mk + # Pre-generate a manpage to allow distributors to skip this step and save some dependencies + make -C man + cp man/i3.1 i3-${VERSION}/man/i3.1 tar cf i3-${VERSION}.tar i3-${VERSION} bzip2 -9 i3-${VERSION}.tar rm -rf i3-${VERSION} @@ -50,3 +53,4 @@ clean: distclean: clean rm -f i3 + $(MAKE) TOPDIR=$(TOPDIR) -C i3-msg distclean diff --git a/RELEASE-NOTES-3.c b/RELEASE-NOTES-3.c new file mode 100644 index 00000000..8005d366 --- /dev/null +++ b/RELEASE-NOTES-3.c @@ -0,0 +1,41 @@ +Release notes for i3 v3.γ +----------------------------- + +This is the third version (3.γ, transcribed 3.c) of i3. It is considered stable. + +This release contains many small improvements like using keysymbols in the +configuration file, named workspaces, borderless windows, an IPC interface +etc. (see below for a complete list of changes) + +Thanks for this release go out to bapt, badboy, Atsutane, tsdh, xeen, mxf, +and all other people who reported bugs/made suggestions. + +Special thanks go to steckdenis, yellowiscool and farvardin who designed a logo +for i3. + +A list of changes follows: + + * Implement a reload command + * Implement keysymbols in configuration file + * Implement assignments of workspaces to screens + * Implement named workspaces + * Implement borderless/1-px-border windows + * Implement command to focus screens + * Implement IPC via unix sockets + * Correctly render decoration of floating windows + * Map floating windows requesting (0x0) to center of their leader/workspace + * Optimization: Render stack windows on pixmaps to reduce flickering + * Optimization: Directly position new windows to their final position + * Bugfix: Repeatedly try to find screens if none are available + * Bugfix: Correctly redecorate clients when changing focus + * Bugfix: Don’t crash when clients reconfigure themselves + * Bugfix: Fix screen wrapping + * Bugfix: Fix selecting a different screen with your mouse when not having + any windows on the current workspace + * Bugfix: Correctly unmap stack windows and don’t re-map them too early + * Bugfix: Allow switching layout if there are no clients in the this container + * Bugfix: Set WM_STATE_WITHDRAWN when unmapping, unmap windows when + destroying + * Bugfix: Don’t hide assigned clients to inactive but visible workspaces + +-- Michael Stapelberg, 2009-08-19 diff --git a/debian/changelog b/debian/changelog index 8bb2c846..2610fc58 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,14 +1,29 @@ -i3-wm (3.c-0) unstable; urgency=low +i3-wm (3.c-1) unstable; urgency=low * Implement a reload command + * Implement keysymbols in configuration file + * Implement assignments of workspaces to screens + * Implement named workspaces + * Implement borderless/1-px-border windows + * Implement command to focus screens * Implement IPC via unix sockets + * Correctly render decoration of floating windows + * Map floating windows requesting (0x0) to center of their leader/workspace * Optimization: Render stack windows on pixmaps to reduce flickering * Optimization: Directly position new windows to their final position * Bugfix: Repeatedly try to find screens if none are available * Bugfix: Correctly redecorate clients when changing focus * Bugfix: Don’t crash when clients reconfigure themselves + * Bugfix: Fix screen wrapping + * Bugfix: Fix selecting a different screen with your mouse when not having + any windows on the current workspace + * Bugfix: Correctly unmap stack windows and don’t re-map them too early + * Bugfix: Allow switching layout if there are no clients in the this container + * Bugfix: Set WM_STATE_WITHDRAWN when unmapping, unmap windows when + destroying + * Bugfix: Don’t hide assigned clients to inactive but visible workspaces - -- Michael Stapelberg Sun, 02 Aug 2009 20:05:58 +0200 + -- Michael Stapelberg Wed, 19 Aug 2009 13:07:58 +0200 i3-wm (3.b-1) unstable; urgency=low diff --git a/docs/hacking-howto b/docs/hacking-howto index 22649c38..3a448537 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -127,6 +127,9 @@ src/handlers.c:: Contains all handlers for all kind of X events (new window title, new hints, unmapping, key presses, button presses, …). +src/ipc.c:: +Contains code for the IPC interface. + src/layout.c:: Renders your layout (screens, workspaces, containers). @@ -149,6 +152,9 @@ Manages the most important internal data structure, the design table. src/util.c:: Contains useful functions which are not really dependant on anything. +src/workspace.c:: +Contains all functions related to workspaces (displaying, hiding, renaming…) + src/xcb.c:: Contains wrappers to use xcb more easily. diff --git a/docs/userguide b/docs/userguide index ba8a752e..215e4aa0 100644 --- a/docs/userguide +++ b/docs/userguide @@ -505,7 +505,7 @@ or you can specify the position of the client if you always use the same layout. *Examples*: -------------------------------------- # Get me to the next open VIM instance -bind Mod1+38 jump "urxvt/VIM" +bindsym Mod1+a jump "urxvt/VIM" -------------------------------------- === Traveling the focus stack diff --git a/include/client.h b/include/client.h index 81cb5c6e..e43f81b9 100644 --- a/include/client.h +++ b/include/client.h @@ -97,4 +97,12 @@ void client_unmap(xcb_connection_t *conn, Client *client); */ void client_map(xcb_connection_t *conn, Client *client); +/** + * Pretty-prints the client’s information into the logfile. + * + */ +#define CLIENT_LOG(client) do { \ + LOG("Window: frame 0x%08x, child 0x%08x\n", client->frame, client->child); \ + } while (0) + #endif diff --git a/include/data.h b/include/data.h index f40de86e..440c9a8f 100644 --- a/include/data.h +++ b/include/data.h @@ -346,6 +346,11 @@ struct Client { int proportional_height; int proportional_width; + /** contains the minimum increment size as specified for the window + * (in pixels). */ + int width_increment; + int height_increment; + /** Height which was determined by reading the _NET_WM_STRUT_PARTIAL * top/bottom of the screen reservation */ int desired_height; diff --git a/include/xinerama.h b/include/xinerama.h index d4e7bb56..135ab1ab 100644 --- a/include/xinerama.h +++ b/include/xinerama.h @@ -57,6 +57,6 @@ i3Screen *get_screen_containing(int x, int y); * This function always returns a screen. * */ -i3Screen *get_screen_most(direction_t direction); +i3Screen *get_screen_most(direction_t direction, i3Screen *current); #endif diff --git a/man/i3.man b/man/i3.man index cf592c62..39260560 100644 --- a/man/i3.man +++ b/man/i3.man @@ -1,7 +1,7 @@ i3(1) ===== Michael Stapelberg -v3.beta, May 2009 +v3.gamma, August 2009 == NAME @@ -232,9 +232,13 @@ your login manager (xdm, slim, gdm, …) as soon as you login. # Disable DPMS turning off the screen xset dpms force on xset s off + # Disable bell xset -b +# Enable zapping (C-A- kills X) +setxkbmap -option terminate:ctrl_alt_bksp + # Enforce correct locales from the beginning unset LC_COLLATE export LC_CTYPE=de_DE.UTF-8 @@ -249,6 +253,9 @@ export LC_TELEPHONE=de_DE.UTF-8 export LC_MEASUREMENT=de_DE.UTF-8 export LC_IDENTIFICATION=de_DE.UTF-8 +# Use XToolkit in java applications +export AWT_TOOLKIT=XToolkit + # Set background color xsetroot -solid "#333333" diff --git a/src/commands.c b/src/commands.c index 6472d860..802f0fd4 100644 --- a/src/commands.c +++ b/src/commands.c @@ -104,12 +104,12 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t LOG("Target screen NULL\n"); /* Wrap around if the target screen is out of bounds */ if (direction == D_RIGHT) - target = get_screen_most(D_LEFT); + target = get_screen_most(D_LEFT, cs); else if (direction == D_LEFT) - target = get_screen_most(D_RIGHT); + target = get_screen_most(D_RIGHT, cs); else if (direction == D_UP) - target = get_screen_most(D_DOWN); - else target = get_screen_most(D_UP); + target = get_screen_most(D_DOWN, cs); + else target = get_screen_most(D_UP, cs); } LOG("Switching to ws %d\n", target->current_workspace + 1); @@ -146,7 +146,7 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t if ((screen = get_screen_containing(container->x, destination_y)) == NULL) { LOG("Wrapping screen around vertically\n"); /* No screen found? Then wrap */ - screen = get_screen_most((direction == D_UP ? D_DOWN : D_UP)); + screen = get_screen_most((direction == D_UP ? D_DOWN : D_UP), container->workspace->screen); } t_ws = &(workspaces[screen->current_workspace]); new_row = (direction == D_UP ? (t_ws->rows - 1) : 0); @@ -188,7 +188,7 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t int destination_x = (direction == D_LEFT ? (container->x - 1) : (container->x + container->width + 1)); if ((screen = get_screen_containing(destination_x, container->y)) == NULL) { LOG("Wrapping screen around horizontally\n"); - screen = get_screen_most((direction == D_LEFT ? D_RIGHT : D_LEFT)); + screen = get_screen_most((direction == D_LEFT ? D_RIGHT : D_LEFT), container->workspace->screen); } t_ws = &(workspaces[screen->current_workspace]); new_col = (direction == D_LEFT ? (t_ws->cols - 1) : 0); @@ -1069,6 +1069,4 @@ void parse_command(xcb_connection_t *conn, const char *command) { continue; } } - - LOG("--- done ---\n"); } diff --git a/src/config.c b/src/config.c index 65bfd67d..a5f635dc 100644 --- a/src/config.c +++ b/src/config.c @@ -50,9 +50,7 @@ static void replace_variable(char *buffer, const char *key, const char *value) { /* To prevent endless recursions when the user makes an error configuring, * we stop after 100 replacements. That should be vastly more than enough. */ int c = 0; - LOG("Replacing %s with %s\n", key, value); while ((pos = strcasestr(buffer, key)) != NULL && c++ < 100) { - LOG("replacing variable %s in \"%s\" with \"%s\"\n", key, buffer, value); char *rest = pos + strlen(key); *pos = '\0'; char *replaced; @@ -103,7 +101,6 @@ void grab_all_keys(xcb_connection_t *conn) { } /* We need to translate the symbol to a keycode */ - LOG("Translating symbol to keycode (\"%s\")\n", bind->symbol); xcb_keysym_t keysym = XStringToKeysym(bind->symbol); if (keysym == NoSymbol) { LOG("Could not translate string to key symbol: \"%s\"\n", bind->symbol); @@ -116,7 +113,7 @@ void grab_all_keys(xcb_connection_t *conn) { continue; } - uint32_t last_keycode; + uint32_t last_keycode = 0; bind->number_keycodes = 0; for (xcb_keycode_t *walk = keycodes; *walk != 0; walk++) { /* We hope duplicate keycodes will be returned in order @@ -127,7 +124,7 @@ void grab_all_keys(xcb_connection_t *conn) { last_keycode = *walk; bind->number_keycodes++; } - LOG("Got %d different keycodes\n", bind->number_keycodes); + LOG("Translated symbol \"%s\" to %d keycode\n", bind->symbol, bind->number_keycodes); bind->translated_to = smalloc(bind->number_keycodes * sizeof(xcb_keycode_t)); memcpy(bind->translated_to, keycodes, bind->number_keycodes * sizeof(xcb_keycode_t)); free(keycodes); diff --git a/src/handlers.c b/src/handlers.c index 3c13b8c4..2f98f019 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -48,8 +48,6 @@ static void add_ignore_event(const int sequence) { event->sequence = sequence; event->added = time(NULL); - LOG("Adding sequence %d to ignorelist\n", sequence); - SLIST_INSERT_HEAD(&ignore_events, event, ignore_events); } @@ -71,7 +69,6 @@ static bool event_is_ignored(const int sequence) { SLIST_FOREACH(event, &ignore_events, ignore_events) { if (event->sequence == sequence) { - LOG("Ignoring event (sequence %d)\n", sequence); SLIST_REMOVE(&ignore_events, event, Ignore_Event, ignore_events); free(event); return true; @@ -87,7 +84,6 @@ static bool event_is_ignored(const int sequence) { * */ int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_event_t *event) { - LOG("got key release, just passing\n"); xcb_allow_events(conn, XCB_ALLOW_REPLAY_KEYBOARD, event->time); xcb_flush(conn); return 1; @@ -248,7 +244,10 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_ * */ int handle_motion_notify(void *ignored, xcb_connection_t *conn, xcb_motion_notify_event_t *event) { - LOG("pointer motion notify, getting screen at %d x %d\n", event->root_x, event->root_y); + /* Skip events where the pointer was over a child window, we are only + * interested in events on the root window. */ + if (event->child != 0) + return 1; check_crossing_screen_boundary(event->root_x, event->root_y); @@ -261,20 +260,17 @@ int handle_motion_notify(void *ignored, xcb_connection_t *conn, xcb_motion_notif * */ int handle_mapping_notify(void *ignored, xcb_connection_t *conn, xcb_mapping_notify_event_t *event) { - LOG("\n\nmapping notify\n\n"); - if (event->request != XCB_MAPPING_KEYBOARD && event->request != XCB_MAPPING_MODIFIER) return 0; + LOG("Received mapping_notify for keyboard or modifier mapping, re-grabbing keys\n"); xcb_refresh_keyboard_mapping(keysyms, event); xcb_get_numlock_mask(conn); ungrab_all_keys(conn); - LOG("Re-grabbing...\n"); grab_all_keys(conn); - LOG("Done\n"); return 0; } @@ -366,8 +362,7 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e } int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_event_t *event) { - LOG("button press!\n"); - LOG("state = %d\n", event->state); + LOG("Button %d pressed\n", event->state); /* This was either a focus for a client’s parent (= titlebar)… */ Client *client = table_get(&by_child, event->event); bool border_click = false; @@ -523,13 +518,11 @@ int handle_map_request(void *prophs, xcb_connection_t *conn, xcb_map_request_eve * */ int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure_request_event_t *event) { - LOG("configure-request, serial %d\n", event->sequence); - LOG("event->window = %08x\n", event->window); - LOG("application wants to be at %dx%d with %dx%d\n", event->x, event->y, event->width, event->height); + LOG("window 0x%08x wants to be at %dx%d with %dx%d\n", + event->window, event->x, event->y, event->width, event->height); Client *client = table_get(&by_child, event->window); if (client == NULL) { - LOG("This client is not mapped, so we don't care and just tell the client that he will get its size\n"); uint32_t mask = 0; uint32_t values[7]; int c = 0; @@ -611,15 +604,12 @@ int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event) { xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root; - LOG("handle_configure_event for window %08x\n", event->window); - LOG("event->type = %d, \n", event->response_type); - LOG("event->x = %d, ->y = %d, ->width = %d, ->height = %d\n", event->x, event->y, event->width, event->height); - /* We ignore this sequence twice because events for child and frame should be ignored */ add_ignore_event(event->sequence); add_ignore_event(event->sequence); if (event->event == root) { + LOG("event->x = %d, ->y = %d, ->width = %d, ->height = %d\n", event->x, event->y, event->width, event->height); LOG("reconfigure of the root window, need to xinerama\n"); /* FIXME: Somehow, this is occuring too often. Therefore, we check for 0/0, but is there a better way? */ @@ -645,7 +635,6 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti /* First, we need to check if the client is awaiting an unmap-request which was generated by us reparenting the window. In that case, we just ignore it. */ if (client != NULL && client->awaiting_useless_unmap) { - LOG("Dropping this unmap request, it was generated by reparenting\n"); client->awaiting_useless_unmap = false; return 1; } @@ -723,11 +712,8 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti break; } - if (workspace_empty) { - LOG("setting ws to NULL for workspace %d (%p)\n", client->workspace->num, - client->workspace); + if (workspace_empty) client->workspace->screen = NULL; - } FREE(client->window_class); FREE(client->name); @@ -748,7 +734,6 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti */ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - LOG("window's name changed.\n"); if (prop == NULL || xcb_get_property_value_length(prop) == 0) { LOG("_NET_WM_NAME not specified, not changing\n"); return 1; @@ -763,7 +748,7 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), (char*)xcb_get_property_value(prop)); /* Convert it to UCS-2 here for not having to convert it later every time we want to pass it to X */ char *ucs2_name = convert_utf8_to_ucs2(new_name, &new_len); - LOG("Name should change to \"%s\"\n", new_name); + LOG("_NET_WM_NAME changed to \"%s\"\n", new_name); free(new_name); /* Check if they are the same and don’t update if so. @@ -773,7 +758,6 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, if ((new_len == client->name_len) && (client->name != NULL) && (memcmp(client->name, ucs2_name, new_len * 2) == 0)) { - LOG("Name did not change, not updating\n"); free(ucs2_name); return 1; } @@ -810,7 +794,6 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, */ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - LOG("window's name changed (legacy).\n"); if (prop == NULL || xcb_get_property_value_length(prop) == 0) { LOG("prop == NULL\n"); return 1; @@ -819,10 +802,9 @@ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t if (client == NULL) return 1; - if (client->uses_net_wm_name) { - LOG("This client is capable of _NET_WM_NAME, ignoring legacy name\n"); + /* Client capable of _NET_WM_NAME, ignore legacy name changes */ + if (client->uses_net_wm_name) return 1; - } /* Save the old pointer to make the update atomic */ char *new_name; @@ -832,18 +814,17 @@ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t return 1; } /* Convert it to UCS-2 here for not having to convert it later every time we want to pass it to X */ - LOG("Name should change to \"%s\"\n", new_name); + LOG("WM_NAME changed to \"%s\"\n", new_name); /* Check if they are the same and don’t update if so. */ if (client->name != NULL && strlen(new_name) == strlen(client->name) && strcmp(client->name, new_name) == 0) { - LOG("Name did not change, not updating\n"); free(new_name); return 1; } - LOG("Using legacy window title. Note that in order to get Unicode window titles in i3," + LOG("Using legacy window title. Note that in order to get Unicode window titles in i3, " "the application has to set _NET_WM_NAME which is in UTF-8 encoding.\n"); char *old_name = client->name; @@ -871,7 +852,6 @@ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t */ int handle_windowclass_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - LOG("window class changed\n"); if (prop == NULL || xcb_get_property_value_length(prop) == 0) { LOG("prop == NULL\n"); return 1; @@ -886,15 +866,13 @@ int handle_windowclass_change(void *data, xcb_connection_t *conn, uint8_t state, return 1; } - LOG("changed to %s\n", new_class); + LOG("WM_CLASS changed to %s\n", new_class); char *old_class = client->window_class; client->window_class = new_class; FREE(old_class); - if (!client->initialized) { - LOG("Client is not yet initialized, not putting it to floating\n"); + if (!client->initialized) return 1; - } if (strcmp(new_class, "tools") == 0 || strcmp(new_class, "Dialog") == 0) { LOG("tool/dialog window, should we put it floating?\n"); @@ -935,11 +913,8 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t * return 1; } - LOG("got client %s\n", client->name); - if (client->dock) { - LOG("this is a dock\n"); + if (client->dock) return 1; - } if (client->container == NULL || client->container->mode != MODE_STACK) decorate_window(conn, client, client->frame, client->titlegc, 0); @@ -976,14 +951,10 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t * * */ int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message_event_t *event) { - LOG("client_message\n"); - if (event->type == atoms[_NET_WM_STATE]) { if (event->format != 32 || event->data.data32[1] != atoms[_NET_WM_STATE_FULLSCREEN]) return 0; - LOG("fullscreen\n"); - Client *client = table_get(&by_child, event->window); if (client == NULL) return 0; @@ -1021,14 +992,14 @@ int handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_wi */ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t name, xcb_get_property_reply_t *reply) { - LOG("handle_normal_hints\n"); Client *client = table_get(&by_child, window); if (client == NULL) { - LOG("No such client\n"); + LOG("Received WM_SIZE_HINTS for unknown client\n"); return 1; } xcb_size_hints_t size_hints; - LOG("client is %08x / child %08x\n", client->frame, client->child); + + CLIENT_LOG(client); /* If the hints were already in this event, use them, if not, request them */ if (reply != NULL) @@ -1037,20 +1008,24 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w xcb_get_wm_normal_hints_reply(conn, xcb_get_wm_normal_hints_unchecked(conn, client->child), &size_hints, NULL); if ((size_hints.flags & XCB_SIZE_HINT_P_MIN_SIZE)) { - LOG("min size set\n"); - LOG("gots min_width = %d, min_height = %d\n", size_hints.min_width, size_hints.min_height); + // TODO: Minimum size is not yet implemented + //LOG("Minimum size: %d (width) x %d (height)\n", size_hints.min_width, size_hints.min_height); + } + + if ((size_hints.flags & XCB_SIZE_HINT_P_RESIZE_INC)) { + if (size_hints.width_inc > 0) + client->width_increment = size_hints.width_inc; + if (size_hints.height_inc > 0) + client->height_increment = size_hints.height_inc; } /* If no aspect ratio was set or if it was invalid, we ignore the hints */ if (!(size_hints.flags & XCB_SIZE_HINT_P_ASPECT) || (size_hints.min_aspect_num <= 0) || (size_hints.min_aspect_den <= 0)) { - LOG("No aspect ratio set, ignoring\n"); return 1; } - LOG("window is %08x / %s\n", client->child, client->name); - int base_width = 0, base_height = 0; /* base_width/height are the desired size of the window. @@ -1070,7 +1045,7 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w double min_aspect = (double)size_hints.min_aspect_num / size_hints.min_aspect_den; double max_aspect = (double)size_hints.max_aspect_num / size_hints.min_aspect_den; - LOG("min_aspect = %f, max_aspect = %f\n", min_aspect, max_aspect); + LOG("Aspect ratio set: minimum %f, maximum %f\n", min_aspect, max_aspect); LOG("width = %f, height = %f\n", width, height); /* Sanity checks, this is user-input, in a way */ @@ -1105,7 +1080,6 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w */ int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t name, xcb_get_property_reply_t *reply) { - LOG("Transient hint!\n"); Client *client = table_get(&by_child, window); if (client == NULL) { LOG("No such client\n"); @@ -1115,16 +1089,12 @@ int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_ xcb_window_t transient_for; if (reply != NULL) { - if (!xcb_get_wm_transient_for_from_reply(&transient_for, reply)) { - LOG("Not transient for any window\n"); + if (!xcb_get_wm_transient_for_from_reply(&transient_for, reply)) return 1; - } } else { if (!xcb_get_wm_transient_for_reply(conn, xcb_get_wm_transient_for_unchecked(conn, window), - &transient_for, NULL)) { - LOG("Not transient for any window\n"); + &transient_for, NULL)) return 1; - } } if (client->floating == FLOATING_AUTO_OFF) { @@ -1142,10 +1112,11 @@ int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_ */ int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t name, xcb_get_property_reply_t *prop) { - LOG("client leader changed\n"); if (prop == NULL) { prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, WM_CLIENT_LEADER, WINDOW, 0, 32), NULL); + if (prop == NULL) + return 1; } Client *client = table_get(&by_child, window); @@ -1153,10 +1124,10 @@ int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state return 1; xcb_window_t *leader = xcb_get_property_value(prop); - if (leader == NULL) + if (leader == NULL || *leader == 0) return 1; - LOG("changed to %08x\n", *leader); + LOG("Client leader changed to %08x\n", *leader); client->leader = *leader; diff --git a/src/layout.c b/src/layout.c index b1fee1b0..1d58097d 100644 --- a/src/layout.c +++ b/src/layout.c @@ -109,7 +109,6 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw if (client->dock) return; - LOG("redecorating child %08x\n", client->child); last_focused = SLIST_FIRST(&(client->workspace->focus_stack)); if (client_is_floating(client)) { if (last_focused == client) @@ -209,7 +208,6 @@ void reposition_client(xcb_connection_t *conn, Client *client) { LOG("Client is on workspace %p with screen %p\n", client->workspace, client->workspace->screen); LOG("but screen at %d, %d is %p\n", client->rect.x, client->rect.y, screen); floating_assign_to_workspace(client, &workspaces[screen->current_workspace]); - LOG("fixed that\n"); } /* @@ -288,6 +286,20 @@ void resize_client(xcb_connection_t *conn, Client *client) { LOG("new_height = %f, new_width = %d\n", new_height, new_width); } + if (client->height_increment > 1) { + int old_height = rect->height; + rect->height = ((int)(rect->height / client->height_increment) * client->height_increment) + 1; + LOG("Lost %d pixel due to client's height_increment (%d px)\n", + old_height - rect->height, client->height_increment); + } + + if (client->width_increment > 1) { + int old_width = rect->width; + rect->width = ((int)(rect->width / client->width_increment) * client->width_increment) + 1; + LOG("Lost %d pixel due to client's width_increment (%d px)\n", + old_width - rect->width, client->width_increment); + } + LOG("child will be at %dx%d with size %dx%d\n", rect->x, rect->y, rect->width, rect->height); xcb_configure_window(conn, client->child, mask, &(rect->x)); @@ -311,7 +323,6 @@ void render_container(xcb_connection_t *conn, Container *container) { num_clients++; if (container->mode == MODE_DEFAULT) { - LOG("got %d clients in this default container.\n", num_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) { @@ -434,7 +445,6 @@ static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int } static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int width, int height) { - LOG("Rendering internal bar\n"); i3Font *font = load_font(conn, config.font); i3Screen *screen = r_ws->screen; enum { SET_NORMAL = 0, SET_FOCUSED = 1 }; @@ -477,8 +487,6 @@ static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int wid (xcb_char2b_t*)ws->name); drawn += ws->text_width + 12; } - - LOG("done rendering internal\n"); } /* @@ -491,8 +499,6 @@ void ignore_enter_notify_forall(xcb_connection_t *conn, Workspace *workspace, bo Client *client; uint32_t values[1]; - LOG("Ignore enter_notify = %d\n", ignore_enter_notify); - FOR_TABLE(workspace) CIRCLEQ_FOREACH(client, &(workspace->table[cols][rows]->clients), clients) { /* Change event mask for the decorations */ @@ -526,8 +532,6 @@ void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws) /* Space for the internal bar */ height -= (font->height + 6); - LOG("got %d rows and %d cols\n", r_ws->rows, r_ws->cols); - int xoffset[r_ws->rows]; int yoffset[r_ws->cols]; /* Initialize offsets */ @@ -536,19 +540,12 @@ void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws) for (int rows = 0; rows < r_ws->rows; rows++) xoffset[rows] = r_ws->rect.x; - dump_table(conn, r_ws); - 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]; int single_width = -1, single_height; - LOG("\n"); - LOG("========\n"); - LOG("container has %d colspan, %d rowspan\n", - container->colspan, container->rowspan); - LOG("container at %d, %d\n", xoffset[rows], yoffset[cols]); /* Update position of the container */ container->row = rows; container->col = cols; @@ -576,7 +573,6 @@ void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws) xoffset[rows] += single_width; yoffset[cols] += single_height; - LOG("==========\n"); } ignore_enter_notify_forall(conn, r_ws, false); @@ -596,10 +592,8 @@ void render_workspace(xcb_connection_t *conn, i3Screen *screen, Workspace *r_ws) void render_layout(xcb_connection_t *conn) { i3Screen *screen; - TAILQ_FOREACH(screen, virtual_screens, screens) { - LOG("Rendering screen %d\n", screen->num); + TAILQ_FOREACH(screen, virtual_screens, screens) render_workspace(conn, screen, &(workspaces[screen->current_workspace])); - } xcb_flush(conn); } diff --git a/src/manage.c b/src/manage.c index a5a46237..dd726c6e 100644 --- a/src/manage.c +++ b/src/manage.c @@ -68,7 +68,6 @@ void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *pr void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn, xcb_window_t window, xcb_get_window_attributes_cookie_t cookie, bool needs_to_be_mapped) { - LOG("managing window.\n"); xcb_drawable_t d = { window }; xcb_get_geometry_cookie_t geomc; xcb_get_geometry_reply_t *geom; @@ -83,16 +82,12 @@ void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn, return; } - if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) { - LOG("Window not mapped, not managing\n"); + if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) goto out; - } /* Don’t manage clients with the override_redirect flag */ - if (attr->override_redirect) { - LOG("override_redirect set, not managing\n"); + if (attr->override_redirect) goto out; - } /* Check if the window is already managed */ if (table_get(&by_child, window)) @@ -158,7 +153,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, /* Events for already managed windows should already be filtered in manage_window() */ assert(new == NULL); - LOG("reparenting new client\n"); + LOG("Reparenting window 0x%08x\n", child); LOG("x = %d, y = %d, width = %d, height = %d\n", x, y, width, height); new = calloc(sizeof(Client), 1); new->force_reconfigure = true; @@ -177,6 +172,8 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, new->child = child; new->rect.width = width; new->rect.height = height; + new->width_increment = 1; + new->height_increment = 1; /* Pre-initialize the values for floating */ new->floating_rect.x = -1; new->floating_rect.width = width; @@ -192,8 +189,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, mask |= XCB_CW_EVENT_MASK; values[1] = FRAME_EVENT_MASK; - LOG("Reparenting 0x%08x under 0x%08x.\n", child, new->frame); - i3Font *font = load_font(conn, config.font); width = min(width, c_ws->rect.x + c_ws->rect.width); height = min(height, c_ws->rect.y + c_ws->rect.height); @@ -313,7 +308,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, preply = xcb_get_property_reply(conn, leader_cookie, NULL); handle_clientleader_change(NULL, conn, 0, new->child, atoms[WM_CLIENT_LEADER], preply); - LOG("DEBUG: should have all infos now\n"); struct Assignment *assign; TAILQ_FOREACH(assign, &assignments, assignments) { if (get_matching_client(conn, assign->windowclass_title, new) == NULL) @@ -435,10 +429,9 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, /* Map the window first to avoid flickering */ xcb_map_window(conn, child); - if (map_frame) { - LOG("Mapping client\n"); + if (map_frame) client_map(conn, new); - } + if (CUR_CELL->workspace->fullscreen_client == NULL && !new->dock) { /* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */ if (new->workspace->fullscreen_client == NULL) { diff --git a/src/util.c b/src/util.c index d1acc744..8cb5409c 100644 --- a/src/util.c +++ b/src/util.c @@ -240,10 +240,8 @@ void set_focus(xcb_connection_t *conn, Client *client, bool set_anyways) { Client *old_client = SLIST_FIRST(&(c_ws->focus_stack)); /* Check if the focus needs to be changed at all */ - if (!set_anyways && (old_client == client)) { - LOG("old_client == client, not changing focus\n"); + if (!set_anyways && (old_client == client)) return; - } /* Store current_row/current_col */ c_ws->current_row = current_row; @@ -261,7 +259,7 @@ void set_focus(xcb_connection_t *conn, Client *client, bool set_anyways) { current_row = client->container->row; } - LOG("set_focus(frame %08x, child %08x, name %s)\n", client->frame, client->child, client->name); + CLIENT_LOG(client); /* Set focus to the entered window, and flush xcb buffer immediately */ xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, client->child, XCB_CURRENT_TIME); //xcb_warp_pointer(conn, XCB_NONE, client->child, 0, 0, 0, 0, 10, 10); diff --git a/src/xcb.c b/src/xcb.c index 3f5d4280..4ed2f1aa 100644 --- a/src/xcb.c +++ b/src/xcb.c @@ -181,8 +181,6 @@ void fake_configure_notify(xcb_connection_t *conn, Rect r, xcb_window_t window) xcb_send_event(conn, false, window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char*)&generated_event); xcb_flush(conn); - - LOG("Told the client it is at %dx%d with %dx%d\n", r.x, r.y, r.width, r.height); } /* diff --git a/src/xinerama.c b/src/xinerama.c index 3bf988e7..59e7e225 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -92,7 +92,7 @@ i3Screen *get_screen_containing(int x, int y) { * This function always returns a screen. * */ -i3Screen *get_screen_most(direction_t direction) { +i3Screen *get_screen_most(direction_t direction, i3Screen *current) { i3Screen *screen, *candidate = NULL; int position = 0; TAILQ_FOREACH(screen, virtual_screens, screens) { @@ -104,6 +104,14 @@ i3Screen *get_screen_most(direction_t direction) { } \ break; + if (((direction == D_UP) || (direction == D_DOWN)) && + (current->rect.x != screen->rect.x)) + continue; + + if (((direction == D_LEFT) || (direction == D_RIGHT)) && + (current->rect.y != screen->rect.y)) + continue; + switch (direction) { case D_UP: WIN(screen->rect.y, <= position);