# Default (Mod1+e)
bind Mod1+26 d
+# Toggle tiling/floating of the current window
+bind Mod1+Shift+65 t
+
# Focus (Mod1+j/k/l/;)
bind Mod1+44 h
bind Mod1+45 j
/* x, y, width, height of the frame */
Rect rect;
+ /* Position in floating mode and in tiling mode are saved separately */
+ Rect floating_rect;
/* x, y, width, height of the child (relative to its frame) */
Rect child_rect;
/* fullscreen is pretty obvious */
bool fullscreen;
+ /* floating? (= not in tiling layout) */
+ bool floating;
+
/* Ensure TITLEBAR_TOP maps to 0 because we use calloc for initialization later */
enum { TITLEBAR_TOP = 0, TITLEBAR_LEFT, TITLEBAR_RIGHT, TITLEBAR_BOTTOM, TITLEBAR_OFF } titlebar_position;
--- /dev/null
+/*
+ * vim:ts=8:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ *
+ * (c) 2009 Michael Stapelberg and contributors
+ *
+ * See file LICENSE for license information.
+ *
+ */
+#ifndef _FLOATING_H
+#define _FLOATING_H
+
+/**
+ * Enters floating mode for the given client.
+ * Correctly takes care of the position/size (separately stored for tiling/floating mode)
+ * and repositions/resizes/redecorates the client.
+ *
+ */
+void toggle_floating_mode(xcb_connection_t *conn, Client *client);
+
+/**
+ * Called whenever the user clicks on a border (not the titlebar!) of a floating window.
+ * Determines on which border the user clicked and launches the drag_pointer function
+ * with the resize_callback.
+ *
+ */
+int floating_border_click(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event);
+
+/**
+ * Called when the user clicked on the titlebar of a floating window.
+ * Calls the drag_pointer function with the drag_window callback
+ *
+ */
+void floating_drag_window(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event);
+
+#endif
*/
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
+ *
+ */
+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 xcb_get_numlock_mask(xcb_connection_t *conn);
+/**
+ * Raises the given window (typically client->frame) above all other windows
+ *
+ */
+void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window);
+
#endif
#include "i3.h"
#include "xinerama.h"
#include "client.h"
+#include "floating.h"
bool focus_window_in_container(xcb_connection_t *conn, Container *container, direction_t direction) {
/* If this container is empty, we’re done */
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 = &(workspaces[workspace-1]);
+
+ LOG("moving floating\n");
+
+ if (t_ws->screen == NULL) {
+ LOG("initializing new workspace, setting num to %d\n", workspace-1);
+ t_ws->screen = c_ws->screen;
+ /* Copy the dimensions from the virtual screen */
+ memcpy(&(t_ws->rect), &(t_ws->screen->rect), sizeof(Rect));
+ } else {
+ /* 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)) {
+ LOG("Not moving: Fullscreen client already existing on destination workspace.\n");
+ return;
+ }
+ }
+
+ /* Remove from focus stack */
+ SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients);
+
+ if (client->workspace->fullscreen_client == client)
+ client->workspace->fullscreen_client = NULL;
+
+ /* Insert into destination focus stack */
+ client->workspace = t_ws;
+ SLIST_INSERT_HEAD(&(t_ws->focus_stack), client, focus_clients);
+ if (client->fullscreen)
+ t_ws->fullscreen_client = client;
+
+ /* If we’re moving it to an invisible screen, we need to unmap it */
+ if (t_ws->screen->current_workspace != t_ws->num) {
+ LOG("This workspace is not visible, unmapping\n");
+ xcb_unmap_window(conn, client->frame);
+ }
+
+ LOG("done\n");
+
+ render_layout(conn);
+}
+
/*
* Moves the currently selected window to the given workspace
*
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);
if (current_client->fullscreen)
t_ws->fullscreen_client = current_client;
CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients)
xcb_map_window(conn, client->frame);
+ /* Map all floating clients */
+ SLIST_FOREACH(client, &(c_ws->focus_stack), focus_clients) {
+ if (!client->floating)
+ continue;
+
+ xcb_map_window(conn, client->frame);
+ }
+
/* Map all stack windows, if any */
struct Stack_Window *stack_win;
SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
*/
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;
}
if (STARTS_WITH(command, "kill")) {
- if (CUR_CELL->currently_focused == NULL) {
+ if (last_focused == NULL) {
LOG("There is no window to kill\n");
return;
}
LOG("Killing current window\n");
- client_kill(conn, CUR_CELL->currently_focused);
+ client_kill(conn, last_focused);
return;
}
- /* Is it a jump to a specified workspae, row, col? */
+ /* Is it a jump to a specified workspace, row, col? */
if (STARTS_WITH(command, "jump ")) {
const char *arguments = command + strlen("jump ");
if (arguments[0] == '"')
/* Is it 'f' for fullscreen? */
if (command[0] == 'f') {
- if (CUR_CELL->currently_focused == NULL)
+ if (last_focused == NULL)
return;
- toggle_fullscreen(conn, CUR_CELL->currently_focused);
+ toggle_fullscreen(conn, last_focused);
return;
}
/* Is it just 's' for stacking or 'd' for default? */
if ((command[0] == 's' || command[0] == 'd') && (command[1] == '\0')) {
+ if (last_focused->floating) {
+ LOG("not switching, this is a floating client\n");
+ return;
+ }
LOG("Switching mode for current container\n");
switch_layout_mode(conn, CUR_CELL, (command[0] == 's' ? MODE_STACK : MODE_DEFAULT));
return;
}
+ /* Is it 't' for toggle tiling/floating? */
+ if (command[0] == 't') {
+ if (last_focused == NULL) {
+ LOG("Cannot toggle tiling/floating: workspace empty\n");
+ return;
+ }
+
+ toggle_floating_mode(conn, last_focused);
+ return;
+ }
+
enum { WITH_WINDOW, WITH_CONTAINER } with = WITH_WINDOW;
/* Is it a <with>? */
}
}
- /* It's a normal <cmd> */
+ /* It’s a normal <cmd> */
char *rest = NULL;
enum { ACTION_FOCUS, ACTION_MOVE, ACTION_SNAP } action = ACTION_FOCUS;
direction_t direction;
}
if (*rest == '\0') {
- move_current_window_to_workspace(conn, workspace);
+ if (last_focused->floating)
+ move_floating_window_to_workspace(conn, last_focused, workspace);
+ else move_current_window_to_workspace(conn, workspace);
+ return;
+ }
+
+ if (last_focused->floating) {
+ LOG("Not performing because this is a floating window\n");
return;
}
--- /dev/null
+/*
+ * vim:ts=8:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ *
+ * © 2009 Michael Stapelberg and contributors
+ *
+ * See file LICENSE for license information.
+ *
+ * src/floating.c: contains all functions for handling floating clients
+ *
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_event.h>
+
+#include "i3.h"
+#include "data.h"
+#include "util.h"
+#include "xcb.h"
+#include "debug.h"
+#include "layout.h"
+
+/* On which border was the dragging initiated? */
+typedef enum { BORDER_LEFT, BORDER_RIGHT, BORDER_TOP, BORDER_BOTTOM} border_t;
+/* Callback for dragging */
+typedef void(*callback_t)(xcb_connection_t*, Client*, border_t, Rect*, xcb_button_press_event_t*, uint32_t, uint32_t);
+
+/* Forward definitions */
+static void drag_pointer(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event,
+ border_t border, callback_t callback);
+
+/*
+ * Toggles floating mode for the given client.
+ * Correctly takes care of the position/size (separately stored for tiling/floating mode)
+ * and repositions/resizes/redecorates the client.
+ *
+ */
+void toggle_floating_mode(xcb_connection_t *conn, Client *client) {
+ Container *con = client->container;
+
+ if (con == NULL) {
+ LOG("This client is already in floating (container == NULL), re-inserting\n");
+ Client *next_tiling;
+ SLIST_FOREACH(next_tiling, &(client->workspace->focus_stack), focus_clients)
+ if (!next_tiling->floating)
+ break;
+ /* If there are no tiling clients on this workspace, there can only be one
+ * container: the first one */
+ if (next_tiling == SLIST_END(&(client->workspace->focus_stack)))
+ con = client->workspace->table[0][0];
+ else con = next_tiling->container;
+
+ LOG("destination container = %p\n", con);
+ Client *old_focused = con->currently_focused;
+ /* Preserve position/size */
+ memcpy(&(client->floating_rect), &(client->rect), sizeof(Rect));
+
+ client->floating = false;
+ client->container = con;
+
+ if (old_focused != NULL && !old_focused->dock)
+ CIRCLEQ_INSERT_AFTER(&(con->clients), old_focused, client, clients);
+ else CIRCLEQ_INSERT_TAIL(&(con->clients), client, clients);
+
+ LOG("Re-inserted the client into the matrix.\n");
+ con->currently_focused = client;
+
+ render_container(conn, con);
+ xcb_flush(conn);
+
+ return;
+ }
+
+ LOG("Entering floating for client %08x\n", client->child);
+
+ /* Remove the client of its container */
+ CIRCLEQ_REMOVE(&(con->clients), client, clients);
+ client->container = NULL;
+
+ if (con->currently_focused == client) {
+ LOG("Need to re-adjust currently_focused\n");
+ /* Get the next client in the focus stack for this particular container */
+ con->currently_focused = get_last_focused_client(conn, con, NULL);
+ }
+
+ client->floating = true;
+
+ /* Initialize the floating position from the position in tiling mode, if this
+ * client never was floating (width == 0) */
+ if (client->floating_rect.width == 0) {
+ memcpy(&(client->floating_rect), &(client->rect), sizeof(Rect));
+ LOG("(%d, %d) size (%d, %d)\n", client->floating_rect.x, client->floating_rect.y,
+ client->floating_rect.width, client->floating_rect.height);
+ } else {
+ /* If the client was already in floating before we restore the old position / size */
+ memcpy(&(client->rect), &(client->floating_rect), sizeof(Rect));
+ }
+
+ /* Raise the client */
+ xcb_raise_window(conn, client->frame);
+ reposition_client(conn, client);
+ resize_client(conn, client);
+ /* redecorate_window flushes */
+ redecorate_window(conn, client);
+
+ /* Re-render the tiling layout of this container */
+ render_container(conn, con);
+ xcb_flush(conn);
+}
+
+/*
+ * Callback for resizing windows
+ *
+ */
+static void resize_callback(xcb_connection_t *conn, Client *client, border_t border, Rect *old_rect,
+ xcb_button_press_event_t *event, uint32_t new_x, uint32_t new_y) {
+ switch (border) {
+ case BORDER_RIGHT:
+ client->rect.width = old_rect->width + (new_x - event->root_x);
+ break;
+
+ case BORDER_BOTTOM:
+ client->rect.height = old_rect->height + (new_y - event->root_y);
+ break;
+
+ case BORDER_TOP:
+ client->rect.y = old_rect->y + (new_y - event->root_y);
+ client->rect.height = old_rect->height + (event->root_y - new_y);
+ break;
+
+ case BORDER_LEFT:
+ client->rect.x = old_rect->x + (new_x - event->root_x);
+ client->rect.width = old_rect->width + (event->root_x - new_x);
+ break;
+ }
+
+ /* Push the new position/size to X11 */
+ reposition_client(conn, client);
+ resize_client(conn, client);
+ xcb_flush(conn);
+}
+
+/*
+ * Called whenever the user clicks on a border (not the titlebar!) of a floating window.
+ * Determines on which border the user clicked and launches the drag_pointer function
+ * with the resize_callback.
+ *
+ */
+int floating_border_click(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event) {
+
+ LOG("floating border click\n");
+
+ border_t border;
+ if (event->event_y < 2)
+ border = BORDER_TOP;
+ else if (event->event_y >= (client->rect.height - 2))
+ border = BORDER_BOTTOM;
+ else if (event->event_x <= 2)
+ border = BORDER_LEFT;
+ else if (event->event_x > 2)
+ border = BORDER_RIGHT;
+ else {
+ LOG("Not on any border, not doing anything.\n");
+ return 1;
+ }
+
+ LOG("border = %d\n", border);
+
+ drag_pointer(conn, client, event, border, resize_callback);
+
+ return 1;
+}
+
+static void drag_window_callback(xcb_connection_t *conn, Client *client, border_t border, Rect *old_rect,
+ xcb_button_press_event_t *event, uint32_t new_x, uint32_t new_y) {
+ /* Reposition the client correctly while moving */
+ client->rect.x = old_rect->x + (new_x - event->root_x);
+ client->rect.y = old_rect->y + (new_y - event->root_y);
+ reposition_client(conn, client);
+ xcb_flush(conn);
+}
+
+/*
+ * Called when the user clicked on the titlebar of a floating window.
+ * Calls the drag_pointer function with the drag_window callback
+ *
+ */
+void floating_drag_window(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event) {
+ LOG("floating_drag_window\n");
+
+ drag_pointer(conn, client, event, BORDER_TOP /* irrelevant */, drag_window_callback);
+}
+
+/*
+ * This function grabs your pointer and lets you drag stuff around (borders).
+ * Every time you move your mouse, an XCB_MOTION_NOTIFY event will be received
+ * and the given callback will be called with the parameters specified (client,
+ * border on which the click originally was), the original rect of the client,
+ * the event and the new coordinates (x, y).
+ *
+ */
+static void drag_pointer(xcb_connection_t *conn, Client *client, xcb_button_press_event_t *event,
+ border_t border, callback_t callback) {
+ xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
+ uint32_t new_x, new_y;
+ Rect old_rect;
+ memcpy(&old_rect, &(client->rect), sizeof(Rect));
+
+ /* Grab the pointer */
+ /* TODO: returncode */
+ xcb_grab_pointer(conn,
+ false, /* get all pointer events specified by the following mask */
+ root, /* grab the root window */
+ XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, /* which events to let through */
+ XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */
+ XCB_GRAB_MODE_ASYNC, /* keyboard mode */
+ XCB_NONE, /* confine_to = in which window should the cursor stay */
+ XCB_NONE, /* don’t display a special cursor */
+ XCB_CURRENT_TIME);
+
+ /* Go into our own event loop */
+ xcb_flush(conn);
+
+ xcb_generic_event_t *inside_event;
+ /* I’ve always wanted to have my own eventhandler… */
+ while ((inside_event = xcb_wait_for_event(conn))) {
+ /* Same as get_event_handler in xcb */
+ int nr = inside_event->response_type;
+ if (nr == 0) {
+ /* An error occured */
+ handle_event(NULL, conn, inside_event);
+ free(inside_event);
+ continue;
+ }
+ assert(nr < 256);
+ nr &= XCB_EVENT_RESPONSE_TYPE_MASK;
+ assert(nr >= 2);
+
+ /* Check if we need to escape this loop */
+ if (nr == XCB_BUTTON_RELEASE)
+ break;
+
+ switch (nr) {
+ case XCB_MOTION_NOTIFY:
+ new_x = ((xcb_motion_notify_event_t*)inside_event)->root_x;
+ new_y = ((xcb_motion_notify_event_t*)inside_event)->root_y;
+
+ callback(conn, client, border, &old_rect, event, new_x, new_y);
+
+ break;
+ default:
+ LOG("Passing to original handler\n");
+ /* Use original handler */
+ xcb_event_handle(&evenths, inside_event);
+ break;
+ }
+ free(inside_event);
+ }
+
+ xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
+ xcb_flush(conn);
+}
+
#include "resize.h"
#include "client.h"
#include "manage.h"
+#include "floating.h"
/* After mapping/unmapping windows, a notify event is generated. However, we don’t want it,
since it’d trigger an infinite loop of switching between the different windows when
Container *con = client->container;
int first, second;
- if (con == NULL) {
+ if (client->dock) {
LOG("dock. done.\n");
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
xcb_flush(conn);
if (!border_click) {
LOG("client. done.\n");
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
+ /* Floating clients should be raised on click */
+ if (client->floating)
+ xcb_raise_window(conn, client->frame);
xcb_flush(conn);
return 1;
}
i3Font *font = load_font(conn, config.font);
if (event->event_y >= 2 && event->event_y <= (font->height + 2 + 2)) {
LOG("click on titlebar\n");
+
+ /* Floating clients can be dragged by grabbing their titlebar */
+ if (client->floating) {
+ /* Firstly, we raise it. Maybe the user just wanted to raise it without grabbing */
+ uint32_t values[] = { XCB_STACK_MODE_ABOVE };
+ xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_STACK_MODE, values);
+ xcb_flush(conn);
+
+ floating_drag_window(conn, client, event);
+ }
return 1;
}
+ if (client->floating)
+ return floating_border_click(conn, client, event);
+
if (event->event_y < 2) {
/* This was a press on the top border */
if (con->row == 0)
if (client->name != NULL)
free(client->name);
+ /* Clients without a container are either floating or dock windows */
if (client->container != NULL) {
Container *con = client->container;
/* Only if this is the active container, we need to really change focus */
if ((con->currently_focused != NULL) && ((con == CUR_CELL) || client->fullscreen))
set_focus(conn, con->currently_focused, true);
+ } else if (client->floating) {
+ SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients);
}
if (client->dock) {
}
/* Let’s see how many clients there are left on the workspace to delete it if it’s empty */
- bool workspace_empty = true;
- FOR_TABLE(client->workspace)
- if (!CIRCLEQ_EMPTY(&(client->workspace->table[cols][rows]->clients))) {
- workspace_empty = false;
- break;
- }
+ bool workspace_empty = SLIST_EMPTY(&(client->workspace->focus_stack));
+ Client *to_focus = (!workspace_empty ? SLIST_FIRST(&(client->workspace->focus_stack)) : NULL);
+ /* If this workspace is currently active, we don’t delete it */
i3Screen *screen;
TAILQ_FOREACH(screen, virtual_screens, screens)
if (screen->current_workspace == client->workspace->num) {
render_layout(conn);
+ /* Ensure the focus is set to the next client in the focus stack */
+ if (to_focus != NULL)
+ set_focus(conn, to_focus, true);
+
return 1;
}
return 1;
}
- if (client->container->mode != MODE_STACK)
+ if (client->container == NULL || client->container->mode != MODE_STACK)
decorate_window(conn, client, client->frame, client->titlegc, 0);
else {
uint32_t background_color;
*
*/
void redecorate_window(xcb_connection_t *conn, Client *client) {
- if (client->container->mode == MODE_STACK) {
+ if (client->container != NULL && client->container->mode == MODE_STACK) {
render_container(conn, client->container);
/* We clear the frame to generate exposure events, because the color used
in drawing may be different */
border_color;
/* Clients without a container (docks) won’t get decorated */
- if (client->container == NULL)
+ if (client->dock)
return;
- if (client->container->currently_focused == client) {
+ LOG("redecorating child %08x\n", client->child);
+ if (client->floating || client->container->currently_focused == client) {
/* Distinguish if the window is currently focused… */
- if (CUR_CELL->currently_focused == client)
+ if (client->floating || CUR_CELL->currently_focused == client)
background_color = get_colorpixel(conn, "#285577");
/* …or if it is the focused window in a not focused container */
else background_color = get_colorpixel(conn, "#555555");
xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, background_color);
/* In stacking mode, we only render the rect for this specific decoration */
- if (client->container->mode == MODE_STACK) {
- xcb_rectangle_t rect = {0, offset, client->container->width, offset + decoration_height };
- xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect);
- } else {
+ if (client->container != NULL && client->container->mode == MODE_STACK) {
/* 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 = {0, 0, client->container->width, client->rect.height};
+ xcb_rectangle_t rect = {0, offset, client->container->width, offset + 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)
* Pushes the client’s x and y coordinates to X11
*
*/
-static void reposition_client(xcb_connection_t *conn, Client *client) {
+void reposition_client(xcb_connection_t *conn, Client *client) {
LOG("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 */
* Pushes the client’s width/height to X11 and resizes the child window
*
*/
-static void resize_client(xcb_connection_t *conn, Client *client) {
+void resize_client(xcb_connection_t *conn, Client *client) {
i3Font *font = load_font(conn, config.font);
LOG("resizing client 0x%08x to %d x %d\n", client->frame, client->rect.width, client->rect.height);
#include "handlers.h"
#include "layout.h"
#include "manage.h"
+#include "floating.h"
/*
* Go through all existing windows (if the window manager is restarted) and manage them
else CIRCLEQ_INSERT_TAIL(&(new->container->clients), new, clients);
SLIST_INSERT_HEAD(&(new->container->workspace->focus_stack), new, focus_clients);
+
+ /* Ensure that it is below all floating clients */
+ Client *first_floating;
+ SLIST_FOREACH(first_floating, &(new->container->workspace->focus_stack), focus_clients)
+ if (first_floating->floating)
+ break;
+
+ if (first_floating != SLIST_END(&(new->container->workspace->focus_stack))) {
+ LOG("Setting below floating\n");
+ uint32_t values[] = { first_floating->frame, XCB_STACK_MODE_BELOW };
+ xcb_configure_window(conn, new->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
+ }
}
/* Check if the window already got the fullscreen hint set */
unmapped_clients++;
}
+ /* To find floating clients, we traverse the focus stack */
+ SLIST_FOREACH(client, &(u_ws->focus_stack), focus_clients) {
+ if (!client->floating)
+ continue;
+
+ xcb_unmap_window(conn, client->frame);
+ unmapped_clients++;
+ }
+
/* If we did not unmap any clients, the workspace is empty and we can destroy it */
if (unmapped_clients == 0) {
/* Re-assign the workspace of all dock clients which use this workspace */
return;
/* Store the old client */
- Client *old_client = CUR_CELL->currently_focused;
+ 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)) {
/* Store current_row/current_col */
c_ws->current_row = current_row;
c_ws->current_col = current_col;
- c_ws = client->container->workspace;
+ c_ws = client->workspace;
/* Update container */
- client->container->currently_focused = client;
+ if (client->container != NULL) {
+ client->container->currently_focused = client;
- current_col = client->container->col;
- current_row = client->container->row;
+ current_col = client->container->col;
+ current_row = client->container->row;
+ }
LOG("set_focus(frame %08x, child %08x, name %s)\n", client->frame, client->child, client->name);
/* 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);
- /* Get the client which was last focused in this particular container, it may be a different
- one than old_client */
- Client *last_focused = get_last_focused_client(conn, client->container, NULL);
-
- /* In stacking containers, raise the client in respect to the one which was focused before */
- if (client->container->mode == MODE_STACK && client->container->workspace->fullscreen_client == NULL) {
- /* We need to get the client again, this time excluding the current client, because
- * we might have just gone into stacking mode and need to raise */
- Client *last_focused = get_last_focused_client(conn, client->container, client);
-
- if (last_focused != NULL) {
- LOG("raising above frame %p / child %p\n", last_focused->frame, last_focused->child);
- uint32_t values[] = { last_focused->frame, XCB_STACK_MODE_ABOVE };
- xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
+ if (client->container != NULL) {
+ /* Get the client which was last focused in this particular container, it may be a different
+ one than old_client */
+ Client *last_focused = get_last_focused_client(conn, client->container, NULL);
+
+ /* In stacking containers, raise the client in respect to the one which was focused before */
+ if (client->container->mode == MODE_STACK && client->container->workspace->fullscreen_client == NULL) {
+ /* We need to get the client again, this time excluding the current client, because
+ * we might have just gone into stacking mode and need to raise */
+ Client *last_focused = get_last_focused_client(conn, client->container, client);
+
+ if (last_focused != NULL) {
+ LOG("raising above frame %p / child %p\n", last_focused->frame, last_focused->child);
+ uint32_t values[] = { last_focused->frame, XCB_STACK_MODE_ABOVE };
+ xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
+ }
}
- }
- /* If it is the same one as old_client, we save us the unnecessary redecorate */
- if ((last_focused != NULL) && (last_focused != old_client))
- redecorate_window(conn, last_focused);
+ /* If it is the same one as old_client, we save us the unnecessary redecorate */
+ if ((last_focused != NULL) && (last_focused != old_client))
+ redecorate_window(conn, last_focused);
+ }
/* If we’re in stacking mode, this renders the container to update changes in the title
bars and to raise the focused client */
if ((old_client != NULL) && (old_client != client) && !old_client->dock)
redecorate_window(conn, old_client);
- SLIST_REMOVE(&(client->container->workspace->focus_stack), client, Client, focus_clients);
- SLIST_INSERT_HEAD(&(client->container->workspace->focus_stack), client, focus_clients);
+ SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients);
+ SLIST_INSERT_HEAD(&(client->workspace->focus_stack), client, focus_clients);
/* redecorate_window flushes, so we don’t need to */
redecorate_window(conn, client);
if (workspaces[workspace].screen == NULL)
continue;
- FOR_TABLE(&(workspaces[workspace])) {
- Container *con = workspaces[workspace].table[cols][rows];
- Client *client;
-
- CIRCLEQ_FOREACH(client, &(con->clients), clients) {
- LOG("Checking client with class=%s, name=%s\n", client->window_class, client->name);
- if (!client_matches_class_name(client, to_class, to_title, to_title_ucs, to_title_ucs_len))
- continue;
+ Client *client;
+ SLIST_FOREACH(client, &(workspaces[workspace].focus_stack), focus_clients) {
+ LOG("Checking client with class=%s, name=%s\n", client->window_class, client->name);
+ if (!client_matches_class_name(client, to_class, to_title, to_title_ucs, to_title_ucs_len))
+ continue;
- matching = client;
- goto done;
- }
+ matching = client;
+ goto done;
}
}
xcb_key_symbols_free(keysyms);
free(reply);
}
+
+/*
+ * Raises the given window (typically client->frame) above all other windows
+ *
+ */
+void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window) {
+ uint32_t values[] = { XCB_STACK_MODE_ABOVE };
+ xcb_configure_window(conn, window, XCB_CONFIG_WINDOW_STACK_MODE, values);
+}