Implement floating (please test and find bugs)

Details which are missing: A command to hide/show all floating clients,
moving/resizing clients with your mouse holding Mod1 (click anywhere
in the client, not just on its borders), resize/move by keyboard, select
next/previous client by keyboard
next
Michael Stapelberg 2009-05-23 16:34:03 +02:00
parent 6bb0c82588
commit 5b8e2ecb18
12 changed files with 525 additions and 60 deletions

View File

@ -15,6 +15,9 @@ bind Mod1+43 s
# 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

View File

@ -253,6 +253,8 @@ struct Client {
/* 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;
@ -282,6 +284,9 @@ struct Client {
/* 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;

37
include/floating.h Normal file
View File

@ -0,0 +1,37 @@
/*
* 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

View File

@ -36,6 +36,18 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
*/
void redecorate_window(xcb_connection_t *conn, Client *client);
/**
* Pushes the clients x and y coordinates to X11
*
*/
void reposition_client(xcb_connection_t *conn, Client *client);
/**
* Pushes the clients 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)

View File

@ -126,4 +126,10 @@ void fake_absolute_configure_notify(xcb_connection_t *conn, Client *client);
*/
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

View File

@ -24,6 +24,7 @@
#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, were done */
@ -418,6 +419,49 @@ static void snap_current_container(xcb_connection_t *conn, direction_t direction
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 were 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 were 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
*
@ -463,7 +507,9 @@ static void move_current_window_to_workspace(xcb_connection_t *conn, int workspa
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;
@ -564,6 +610,14 @@ void show_workspace(xcb_connection_t *conn, int workspace) {
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)
@ -683,6 +737,12 @@ static void travel_focus_stack(xcb_connection_t *conn, const char *arguments) {
*/
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;
@ -708,17 +768,17 @@ void parse_command(xcb_connection_t *conn, const char *command) {
}
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] == '"')
@ -736,19 +796,34 @@ void parse_command(xcb_connection_t *conn, const char *command) {
/* 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>? */
@ -764,7 +839,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
}
}
/* It's a normal <cmd> */
/* Its a normal <cmd> */
char *rest = NULL;
enum { ACTION_FOCUS, ACTION_MOVE, ACTION_SNAP } action = ACTION_FOCUS;
direction_t direction;
@ -793,7 +868,14 @@ void parse_command(xcb_connection_t *conn, const char *command) {
}
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;
}

267
src/floating.c Normal file
View File

@ -0,0 +1,267 @@
/*
* 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, /* dont display a special cursor */
XCB_CURRENT_TIME);
/* Go into our own event loop */
xcb_flush(conn);
xcb_generic_event_t *inside_event;
/* Ive 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);
}

View File

@ -34,6 +34,7 @@
#include "resize.h"
#include "client.h"
#include "manage.h"
#include "floating.h"
/* After mapping/unmapping windows, a notify event is generated. However, we dont want it,
since itd trigger an infinite loop of switching between the different windows when
@ -322,7 +323,7 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
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);
@ -334,6 +335,9 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
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;
}
@ -342,9 +346,22 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
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)
@ -508,6 +525,7 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
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;
@ -524,6 +542,8 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
/* 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) {
@ -543,13 +563,10 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
}
/* Lets see how many clients there are left on the workspace to delete it if its 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 dont delete it */
i3Screen *screen;
TAILQ_FOREACH(screen, virtual_screens, screens)
if (screen->current_workspace == client->workspace->num) {
@ -564,6 +581,10 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
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;
}
@ -755,7 +776,7 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *
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;

View File

@ -83,7 +83,7 @@ int get_unoccupied_y(Workspace *workspace, int col) {
*
*/
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 */
@ -105,12 +105,13 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
border_color;
/* Clients without a container (docks) wont 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");
@ -133,14 +134,14 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
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 containers 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 clients 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)
@ -179,7 +180,7 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
* Pushes the clients 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 */
@ -190,7 +191,7 @@ static void reposition_client(xcb_connection_t *conn, Client *client) {
* Pushes the clients 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);

View File

@ -27,6 +27,7 @@
#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
@ -333,6 +334,18 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
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 */

View File

@ -262,6 +262,15 @@ void unmap_workspace(xcb_connection_t *conn, Workspace *u_ws) {
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 */
@ -296,7 +305,7 @@ void set_focus(xcb_connection_t *conn, Client *client, bool set_anyways) {
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)) {
@ -307,47 +316,51 @@ void set_focus(xcb_connection_t *conn, Client *client, bool set_anyways) {
/* 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);
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);
/* 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 (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 were 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 dont need to */
redecorate_window(conn, client);
@ -521,18 +534,14 @@ Client *get_matching_client(xcb_connection_t *conn, const char *window_classtitl
if (workspaces[workspace].screen == NULL)
continue;
FOR_TABLE(&(workspaces[workspace])) {
Container *con = workspaces[workspace].table[cols][rows];
Client *client;
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;
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;
matching = client;
goto done;
}
matching = client;
goto done;
}
}

View File

@ -278,3 +278,12 @@ void xcb_get_numlock_mask(xcb_connection_t *conn) {
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);
}