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
This commit is contained in:
parent
6bb0c82588
commit
5b8e2ecb18
|
@ -15,6 +15,9 @@ bind Mod1+43 s
|
||||||
# Default (Mod1+e)
|
# Default (Mod1+e)
|
||||||
bind Mod1+26 d
|
bind Mod1+26 d
|
||||||
|
|
||||||
|
# Toggle tiling/floating of the current window
|
||||||
|
bind Mod1+Shift+65 t
|
||||||
|
|
||||||
# Focus (Mod1+j/k/l/;)
|
# Focus (Mod1+j/k/l/;)
|
||||||
bind Mod1+44 h
|
bind Mod1+44 h
|
||||||
bind Mod1+45 j
|
bind Mod1+45 j
|
||||||
|
|
|
@ -253,6 +253,8 @@ struct Client {
|
||||||
|
|
||||||
/* x, y, width, height of the frame */
|
/* x, y, width, height of the frame */
|
||||||
Rect rect;
|
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) */
|
/* x, y, width, height of the child (relative to its frame) */
|
||||||
Rect child_rect;
|
Rect child_rect;
|
||||||
|
|
||||||
|
@ -282,6 +284,9 @@ struct Client {
|
||||||
/* fullscreen is pretty obvious */
|
/* fullscreen is pretty obvious */
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
|
|
||||||
|
/* floating? (= not in tiling layout) */
|
||||||
|
bool floating;
|
||||||
|
|
||||||
/* Ensure TITLEBAR_TOP maps to 0 because we use calloc for initialization later */
|
/* 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;
|
enum { TITLEBAR_TOP = 0, TITLEBAR_LEFT, TITLEBAR_RIGHT, TITLEBAR_BOTTOM, TITLEBAR_OFF } titlebar_position;
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
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
|
* Renders the given container. Is called by render_layout() or individually (for example
|
||||||
* when focus changes in a stacking container)
|
* when focus changes in a stacking container)
|
||||||
|
|
|
@ -126,4 +126,10 @@ void fake_absolute_configure_notify(xcb_connection_t *conn, Client *client);
|
||||||
*/
|
*/
|
||||||
void xcb_get_numlock_mask(xcb_connection_t *conn);
|
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
|
#endif
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "i3.h"
|
#include "i3.h"
|
||||||
#include "xinerama.h"
|
#include "xinerama.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
#include "floating.h"
|
||||||
|
|
||||||
bool focus_window_in_container(xcb_connection_t *conn, Container *container, direction_t direction) {
|
bool focus_window_in_container(xcb_connection_t *conn, Container *container, direction_t direction) {
|
||||||
/* If this container is empty, we’re done */
|
/* If this container is empty, we’re done */
|
||||||
|
@ -418,6 +419,49 @@ static void snap_current_container(xcb_connection_t *conn, direction_t direction
|
||||||
render_layout(conn);
|
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
|
* 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)
|
if (container->workspace->fullscreen_client == current_client)
|
||||||
container->workspace->fullscreen_client = NULL;
|
container->workspace->fullscreen_client = NULL;
|
||||||
|
|
||||||
|
/* TODO: insert it to the correct position */
|
||||||
CIRCLEQ_INSERT_TAIL(&(to_container->clients), current_client, clients);
|
CIRCLEQ_INSERT_TAIL(&(to_container->clients), current_client, clients);
|
||||||
|
|
||||||
SLIST_INSERT_HEAD(&(to_container->workspace->focus_stack), current_client, focus_clients);
|
SLIST_INSERT_HEAD(&(to_container->workspace->focus_stack), current_client, focus_clients);
|
||||||
if (current_client->fullscreen)
|
if (current_client->fullscreen)
|
||||||
t_ws->fullscreen_client = current_client;
|
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)
|
CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients)
|
||||||
xcb_map_window(conn, client->frame);
|
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 */
|
/* Map all stack windows, if any */
|
||||||
struct Stack_Window *stack_win;
|
struct Stack_Window *stack_win;
|
||||||
SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
|
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) {
|
void parse_command(xcb_connection_t *conn, const char *command) {
|
||||||
LOG("--- parsing command \"%s\" ---\n", 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 */
|
/* Hmm, just to be sure */
|
||||||
if (command[0] == '\0')
|
if (command[0] == '\0')
|
||||||
return;
|
return;
|
||||||
|
@ -708,17 +768,17 @@ void parse_command(xcb_connection_t *conn, const char *command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (STARTS_WITH(command, "kill")) {
|
if (STARTS_WITH(command, "kill")) {
|
||||||
if (CUR_CELL->currently_focused == NULL) {
|
if (last_focused == NULL) {
|
||||||
LOG("There is no window to kill\n");
|
LOG("There is no window to kill\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("Killing current window\n");
|
LOG("Killing current window\n");
|
||||||
client_kill(conn, CUR_CELL->currently_focused);
|
client_kill(conn, last_focused);
|
||||||
return;
|
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 ")) {
|
if (STARTS_WITH(command, "jump ")) {
|
||||||
const char *arguments = command + strlen("jump ");
|
const char *arguments = command + strlen("jump ");
|
||||||
if (arguments[0] == '"')
|
if (arguments[0] == '"')
|
||||||
|
@ -736,19 +796,34 @@ void parse_command(xcb_connection_t *conn, const char *command) {
|
||||||
|
|
||||||
/* Is it 'f' for fullscreen? */
|
/* Is it 'f' for fullscreen? */
|
||||||
if (command[0] == 'f') {
|
if (command[0] == 'f') {
|
||||||
if (CUR_CELL->currently_focused == NULL)
|
if (last_focused == NULL)
|
||||||
return;
|
return;
|
||||||
toggle_fullscreen(conn, CUR_CELL->currently_focused);
|
toggle_fullscreen(conn, last_focused);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is it just 's' for stacking or 'd' for default? */
|
/* Is it just 's' for stacking or 'd' for default? */
|
||||||
if ((command[0] == 's' || command[0] == 'd') && (command[1] == '\0')) {
|
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");
|
LOG("Switching mode for current container\n");
|
||||||
switch_layout_mode(conn, CUR_CELL, (command[0] == 's' ? MODE_STACK : MODE_DEFAULT));
|
switch_layout_mode(conn, CUR_CELL, (command[0] == 's' ? MODE_STACK : MODE_DEFAULT));
|
||||||
return;
|
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;
|
enum { WITH_WINDOW, WITH_CONTAINER } with = WITH_WINDOW;
|
||||||
|
|
||||||
/* Is it a <with>? */
|
/* Is it a <with>? */
|
||||||
|
@ -764,7 +839,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* It's a normal <cmd> */
|
/* It’s a normal <cmd> */
|
||||||
char *rest = NULL;
|
char *rest = NULL;
|
||||||
enum { ACTION_FOCUS, ACTION_MOVE, ACTION_SNAP } action = ACTION_FOCUS;
|
enum { ACTION_FOCUS, ACTION_MOVE, ACTION_SNAP } action = ACTION_FOCUS;
|
||||||
direction_t direction;
|
direction_t direction;
|
||||||
|
@ -793,7 +868,14 @@ void parse_command(xcb_connection_t *conn, const char *command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*rest == '\0') {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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, /* 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);
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "resize.h"
|
#include "resize.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "manage.h"
|
#include "manage.h"
|
||||||
|
#include "floating.h"
|
||||||
|
|
||||||
/* After mapping/unmapping windows, a notify event is generated. However, we don’t want it,
|
/* 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
|
since it’d 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;
|
Container *con = client->container;
|
||||||
int first, second;
|
int first, second;
|
||||||
|
|
||||||
if (con == NULL) {
|
if (client->dock) {
|
||||||
LOG("dock. done.\n");
|
LOG("dock. done.\n");
|
||||||
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
|
@ -334,6 +335,9 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
|
||||||
if (!border_click) {
|
if (!border_click) {
|
||||||
LOG("client. done.\n");
|
LOG("client. done.\n");
|
||||||
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
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);
|
xcb_flush(conn);
|
||||||
return 1;
|
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);
|
i3Font *font = load_font(conn, config.font);
|
||||||
if (event->event_y >= 2 && event->event_y <= (font->height + 2 + 2)) {
|
if (event->event_y >= 2 && event->event_y <= (font->height + 2 + 2)) {
|
||||||
LOG("click on titlebar\n");
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (client->floating)
|
||||||
|
return floating_border_click(conn, client, event);
|
||||||
|
|
||||||
if (event->event_y < 2) {
|
if (event->event_y < 2) {
|
||||||
/* This was a press on the top border */
|
/* This was a press on the top border */
|
||||||
if (con->row == 0)
|
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)
|
if (client->name != NULL)
|
||||||
free(client->name);
|
free(client->name);
|
||||||
|
|
||||||
|
/* Clients without a container are either floating or dock windows */
|
||||||
if (client->container != NULL) {
|
if (client->container != NULL) {
|
||||||
Container *con = client->container;
|
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 */
|
/* Only if this is the active container, we need to really change focus */
|
||||||
if ((con->currently_focused != NULL) && ((con == CUR_CELL) || client->fullscreen))
|
if ((con->currently_focused != NULL) && ((con == CUR_CELL) || client->fullscreen))
|
||||||
set_focus(conn, con->currently_focused, true);
|
set_focus(conn, con->currently_focused, true);
|
||||||
|
} else if (client->floating) {
|
||||||
|
SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->dock) {
|
if (client->dock) {
|
||||||
|
@ -543,13 +563,10 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Let’s see how many clients there are left on the workspace to delete it if it’s empty */
|
/* Let’s see how many clients there are left on the workspace to delete it if it’s empty */
|
||||||
bool workspace_empty = true;
|
bool workspace_empty = SLIST_EMPTY(&(client->workspace->focus_stack));
|
||||||
FOR_TABLE(client->workspace)
|
Client *to_focus = (!workspace_empty ? SLIST_FIRST(&(client->workspace->focus_stack)) : NULL);
|
||||||
if (!CIRCLEQ_EMPTY(&(client->workspace->table[cols][rows]->clients))) {
|
|
||||||
workspace_empty = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* If this workspace is currently active, we don’t delete it */
|
||||||
i3Screen *screen;
|
i3Screen *screen;
|
||||||
TAILQ_FOREACH(screen, virtual_screens, screens)
|
TAILQ_FOREACH(screen, virtual_screens, screens)
|
||||||
if (screen->current_workspace == client->workspace->num) {
|
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);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,7 +776,7 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *
|
||||||
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);
|
decorate_window(conn, client, client->frame, client->titlegc, 0);
|
||||||
else {
|
else {
|
||||||
uint32_t background_color;
|
uint32_t background_color;
|
||||||
|
|
23
src/layout.c
23
src/layout.c
|
@ -83,7 +83,7 @@ int get_unoccupied_y(Workspace *workspace, int col) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void redecorate_window(xcb_connection_t *conn, Client *client) {
|
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);
|
render_container(conn, client->container);
|
||||||
/* We clear the frame to generate exposure events, because the color used
|
/* We clear the frame to generate exposure events, because the color used
|
||||||
in drawing may be different */
|
in drawing may be different */
|
||||||
|
@ -105,12 +105,13 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
|
||||||
border_color;
|
border_color;
|
||||||
|
|
||||||
/* Clients without a container (docks) won’t get decorated */
|
/* Clients without a container (docks) won’t get decorated */
|
||||||
if (client->container == NULL)
|
if (client->dock)
|
||||||
return;
|
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… */
|
/* 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");
|
background_color = get_colorpixel(conn, "#285577");
|
||||||
/* …or if it is the focused window in a not focused container */
|
/* …or if it is the focused window in a not focused container */
|
||||||
else background_color = get_colorpixel(conn, "#555555");
|
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);
|
xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, background_color);
|
||||||
|
|
||||||
/* In stacking mode, we only render the rect for this specific decoration */
|
/* In stacking mode, we only render the rect for this specific decoration */
|
||||||
if (client->container->mode == MODE_STACK) {
|
if (client->container != NULL && 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 {
|
|
||||||
/* We need to use the container’s width because it is the more recent value - when
|
/* 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
|
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 */
|
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);
|
xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect);
|
||||||
|
|
||||||
/* Draw the inner background to have a black frame around clients (such as mplayer)
|
/* 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 client’s x and y coordinates to X11
|
* 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);
|
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
|
/* Note: We can use a pointer to client->x like an array of uint32_ts
|
||||||
because it is followed by client->y by definition */
|
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 client’s width/height to X11 and resizes the child window
|
* 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);
|
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);
|
LOG("resizing client 0x%08x to %d x %d\n", client->frame, client->rect.width, client->rect.height);
|
||||||
|
|
13
src/manage.c
13
src/manage.c
|
@ -27,6 +27,7 @@
|
||||||
#include "handlers.h"
|
#include "handlers.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "manage.h"
|
#include "manage.h"
|
||||||
|
#include "floating.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Go through all existing windows (if the window manager is restarted) and manage them
|
* 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);
|
else CIRCLEQ_INSERT_TAIL(&(new->container->clients), new, clients);
|
||||||
|
|
||||||
SLIST_INSERT_HEAD(&(new->container->workspace->focus_stack), new, focus_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 */
|
/* Check if the window already got the fullscreen hint set */
|
||||||
|
|
77
src/util.c
77
src/util.c
|
@ -262,6 +262,15 @@ void unmap_workspace(xcb_connection_t *conn, Workspace *u_ws) {
|
||||||
unmapped_clients++;
|
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 we did not unmap any clients, the workspace is empty and we can destroy it */
|
||||||
if (unmapped_clients == 0) {
|
if (unmapped_clients == 0) {
|
||||||
/* Re-assign the workspace of all dock clients which use this workspace */
|
/* 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;
|
return;
|
||||||
|
|
||||||
/* Store the old client */
|
/* 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 */
|
/* Check if the focus needs to be changed at all */
|
||||||
if (!set_anyways && (old_client == client)) {
|
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 */
|
/* Store current_row/current_col */
|
||||||
c_ws->current_row = current_row;
|
c_ws->current_row = current_row;
|
||||||
c_ws->current_col = current_col;
|
c_ws->current_col = current_col;
|
||||||
c_ws = client->container->workspace;
|
c_ws = client->workspace;
|
||||||
|
|
||||||
/* Update container */
|
/* Update container */
|
||||||
client->container->currently_focused = client;
|
if (client->container != NULL) {
|
||||||
|
client->container->currently_focused = client;
|
||||||
|
|
||||||
current_col = client->container->col;
|
current_col = client->container->col;
|
||||||
current_row = client->container->row;
|
current_row = client->container->row;
|
||||||
|
}
|
||||||
|
|
||||||
LOG("set_focus(frame %08x, child %08x, name %s)\n", client->frame, client->child, client->name);
|
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 */
|
/* 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_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);
|
//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
|
if (client->container != NULL) {
|
||||||
one than old_client */
|
/* Get the client which was last focused in this particular container, it may be a different
|
||||||
Client *last_focused = get_last_focused_client(conn, client->container, NULL);
|
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 */
|
/* 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) {
|
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 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 */
|
* we might have just gone into stacking mode and need to raise */
|
||||||
Client *last_focused = get_last_focused_client(conn, client->container, client);
|
Client *last_focused = get_last_focused_client(conn, client->container, client);
|
||||||
|
|
||||||
if (last_focused != NULL) {
|
if (last_focused != NULL) {
|
||||||
LOG("raising above frame %p / child %p\n", last_focused->frame, last_focused->child);
|
LOG("raising above frame %p / child %p\n", last_focused->frame, last_focused->child);
|
||||||
uint32_t values[] = { last_focused->frame, XCB_STACK_MODE_ABOVE };
|
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);
|
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 it is the same one as old_client, we save us the unnecessary redecorate */
|
||||||
if ((last_focused != NULL) && (last_focused != old_client))
|
if ((last_focused != NULL) && (last_focused != old_client))
|
||||||
redecorate_window(conn, last_focused);
|
redecorate_window(conn, last_focused);
|
||||||
|
}
|
||||||
|
|
||||||
/* If we’re in stacking mode, this renders the container to update changes in the title
|
/* If we’re in stacking mode, this renders the container to update changes in the title
|
||||||
bars and to raise the focused client */
|
bars and to raise the focused client */
|
||||||
if ((old_client != NULL) && (old_client != client) && !old_client->dock)
|
if ((old_client != NULL) && (old_client != client) && !old_client->dock)
|
||||||
redecorate_window(conn, old_client);
|
redecorate_window(conn, old_client);
|
||||||
|
|
||||||
SLIST_REMOVE(&(client->container->workspace->focus_stack), client, Client, focus_clients);
|
SLIST_REMOVE(&(client->workspace->focus_stack), client, Client, focus_clients);
|
||||||
SLIST_INSERT_HEAD(&(client->container->workspace->focus_stack), client, focus_clients);
|
SLIST_INSERT_HEAD(&(client->workspace->focus_stack), client, focus_clients);
|
||||||
|
|
||||||
/* redecorate_window flushes, so we don’t need to */
|
/* redecorate_window flushes, so we don’t need to */
|
||||||
redecorate_window(conn, client);
|
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)
|
if (workspaces[workspace].screen == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FOR_TABLE(&(workspaces[workspace])) {
|
Client *client;
|
||||||
Container *con = workspaces[workspace].table[cols][rows];
|
SLIST_FOREACH(client, &(workspaces[workspace].focus_stack), focus_clients) {
|
||||||
Client *client;
|
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) {
|
matching = client;
|
||||||
LOG("Checking client with class=%s, name=%s\n", client->window_class, client->name);
|
goto done;
|
||||||
if (!client_matches_class_name(client, to_class, to_title, to_title_ucs, to_title_ucs_len))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
matching = client;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -278,3 +278,12 @@ void xcb_get_numlock_mask(xcb_connection_t *conn) {
|
||||||
xcb_key_symbols_free(keysyms);
|
xcb_key_symbols_free(keysyms);
|
||||||
free(reply);
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue