Merge branch 'next' into testcases
This commit is contained in:
commit
34c6748c5f
|
@ -85,4 +85,16 @@ bool client_is_floating(Client *client);
|
|||
*/
|
||||
void client_change_border(xcb_connection_t *conn, Client *client, char border_type);
|
||||
|
||||
/**
|
||||
* Unmap the client, correctly setting any state which is needed.
|
||||
*
|
||||
*/
|
||||
void client_unmap(xcb_connection_t *conn, Client *client);
|
||||
|
||||
/**
|
||||
* Map the client, correctly restoring any state needed.
|
||||
*
|
||||
*/
|
||||
void client_map(xcb_connection_t *conn, Client *client);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
bool focus_window_in_container(xcb_connection_t *conn, Container *container,
|
||||
direction_t direction);
|
||||
|
||||
/** Switches to the given workspace */
|
||||
void show_workspace(xcb_connection_t *conn, int workspace);
|
||||
|
||||
/** Parses a command, see file CMDMODE for more information */
|
||||
void parse_command(xcb_connection_t *conn, const char *command);
|
||||
|
||||
|
|
|
@ -79,6 +79,18 @@ struct Config {
|
|||
*
|
||||
*/
|
||||
void load_configuration(xcb_connection_t *conn, const char *override_configfile, bool reload);
|
||||
|
||||
/**
|
||||
* Ungrabs all keys, to be called before re-grabbing the keys because of a
|
||||
* mapping_notify event or a configuration file reload
|
||||
*
|
||||
*/
|
||||
void ungrab_all_keys(xcb_connection_t *conn);
|
||||
|
||||
/**
|
||||
* Grab the bound keys (tell X to send us keypress events for those keycodes)
|
||||
*
|
||||
*/
|
||||
void grab_all_keys(xcb_connection_t *conn);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -194,6 +194,13 @@ struct Workspace {
|
|||
/** Are the floating clients on this workspace currently hidden? */
|
||||
bool floating_hidden;
|
||||
|
||||
/** A <screen> specifier on which this workspace would like to be (if
|
||||
* the screen is available). screen := <number> | <position> */
|
||||
char *preferred_screen;
|
||||
|
||||
/** Temporary flag needed for re-querying xinerama screens */
|
||||
bool reassigned;
|
||||
|
||||
/** the client who is started in fullscreen mode on this workspace,
|
||||
* NULL if there is none */
|
||||
Client *fullscreen_client;
|
||||
|
@ -230,12 +237,29 @@ struct Workspace {
|
|||
*
|
||||
*/
|
||||
struct Binding {
|
||||
/** Symbol the user specified in configfile, if any. This needs to be
|
||||
* stored with the binding to be able to re-convert it into a keycode
|
||||
* if the keyboard mapping changes (using Xmodmap for example) */
|
||||
char *symbol;
|
||||
|
||||
/** Only in use if symbol != NULL. Gets set to the value to which the
|
||||
* symbol got translated when binding. Useful for unbinding and
|
||||
* checking which binding was used when a key press event comes in.
|
||||
*
|
||||
* This is an array of number_keycodes size. */
|
||||
xcb_keycode_t *translated_to;
|
||||
|
||||
uint32_t number_keycodes;
|
||||
|
||||
/** Keycode to bind */
|
||||
uint32_t keycode;
|
||||
|
||||
/** Bitmask consisting of BIND_MOD_1, BIND_MODE_SWITCH, … */
|
||||
uint32_t mods;
|
||||
|
||||
/** Command, like in command mode */
|
||||
char *command;
|
||||
|
||||
TAILQ_ENTRY(Binding) bindings;
|
||||
};
|
||||
|
||||
|
|
|
@ -35,6 +35,23 @@ int handle_key_press(void *ignored, xcb_connection_t *conn,
|
|||
int handle_enter_notify(void *ignored, xcb_connection_t *conn,
|
||||
xcb_enter_notify_event_t *event);
|
||||
|
||||
/**
|
||||
* When the user moves the mouse but does not change the active window
|
||||
* (e.g. when having no windows opened but moving mouse on the root screen
|
||||
* and crossing virtual screen boundaries), this callback gets called.
|
||||
*
|
||||
*/
|
||||
int handle_motion_notify(void *ignored, xcb_connection_t *conn,
|
||||
xcb_motion_notify_event_t *event);
|
||||
|
||||
/**
|
||||
* Called when the keyboard mapping changes (for example by using Xmodmap),
|
||||
* we need to update our key bindings then (re-translate symbols).
|
||||
*
|
||||
*/
|
||||
int handle_mapping_notify(void *ignored, xcb_connection_t *conn,
|
||||
xcb_mapping_notify_event_t *event);
|
||||
|
||||
/**
|
||||
* Checks if the button press was on a stack window, handles focus setting and
|
||||
* returns true if so, or false otherwise.
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_property.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
|
||||
#include <X11/XKBlib.h>
|
||||
|
||||
|
@ -23,6 +24,7 @@
|
|||
#define NUM_ATOMS 18
|
||||
|
||||
extern xcb_connection_t *global_conn;
|
||||
extern xcb_key_symbols_t *keysyms;
|
||||
extern char **start_argv;
|
||||
extern Display *xkbdpy;
|
||||
extern TAILQ_HEAD(bindings_head, Binding) bindings;
|
||||
|
|
|
@ -46,7 +46,10 @@ void redecorate_window(xcb_connection_t *conn, Client *client);
|
|||
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. This
|
||||
* function also updates the client’s position, so if you work on tiling clients
|
||||
* only, you can use this function instead of separate calls to reposition_client
|
||||
* and resize_client to reduce flickering.
|
||||
*
|
||||
*/
|
||||
void resize_client(xcb_connection_t *conn, Client *client);
|
||||
|
|
|
@ -130,27 +130,6 @@ char *convert_utf8_to_ucs2(char *input, int *real_strlen);
|
|||
Client *get_last_focused_client(xcb_connection_t *conn, Container *container,
|
||||
Client *exclude);
|
||||
|
||||
/**
|
||||
* Unmaps all clients (and stack windows) of the given workspace.
|
||||
*
|
||||
* This needs to be called separately when temporarily rendering a workspace
|
||||
* which is not the active workspace to force reconfiguration of all clients,
|
||||
* like in src/xinerama.c when re-assigning a workspace to another screen.
|
||||
*
|
||||
*/
|
||||
void unmap_workspace(xcb_connection_t *conn, Workspace *u_ws);
|
||||
|
||||
/**
|
||||
* Unmaps all clients (and stack windows) of the given workspace.
|
||||
*
|
||||
* This needs to be called separately when temporarily rendering
|
||||
* a workspace which is not the active workspace to force
|
||||
* reconfiguration of all clients, like in src/xinerama.c when
|
||||
* re-assigning a workspace to another screen.
|
||||
*
|
||||
*/
|
||||
void unmap_workspace(xcb_connection_t *conn, Workspace *u_ws);
|
||||
|
||||
/**
|
||||
* Sets the given client as focused by updating the data structures correctly,
|
||||
* updating the X input focus and finally re-decorating both windows (to
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <xcb/xcb.h>
|
||||
|
||||
#include "data.h"
|
||||
#include "xinerama.h"
|
||||
|
||||
#ifndef _WORKSPACE_H
|
||||
#define _WORKSPACE_H
|
||||
|
@ -32,4 +33,36 @@ void workspace_set_name(Workspace *ws, const char *name);
|
|||
*/
|
||||
bool workspace_is_visible(Workspace *ws);
|
||||
|
||||
/** Switches to the given workspace */
|
||||
void workspace_show(xcb_connection_t *conn, int workspace);
|
||||
|
||||
/**
|
||||
* Initializes the given workspace if it is not already initialized. The given
|
||||
* screen is to be understood as a fallback, if the workspace itself either
|
||||
* was not assigned to a particular screen or cannot be placed there because
|
||||
* the screen is not attached at the moment.
|
||||
*
|
||||
*/
|
||||
void workspace_initialize(Workspace *ws, i3Screen *screen);
|
||||
|
||||
/**
|
||||
* Gets the first unused workspace for the given screen, taking into account
|
||||
* the preferred_screen setting of every workspace (workspace assignments).
|
||||
*
|
||||
*/
|
||||
Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *screen);
|
||||
|
||||
/**
|
||||
* Unmaps all clients (and stack windows) of the given workspace.
|
||||
*
|
||||
* This needs to be called separately when temporarily rendering a workspace
|
||||
* which is not the active workspace to force reconfiguration of all clients,
|
||||
* like in src/xinerama.c when re-assigning a workspace to another screen.
|
||||
*
|
||||
*/
|
||||
void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws);
|
||||
|
||||
|
||||
void workspace_map_clients(xcb_connection_t *conn, Workspace *ws);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,6 +16,13 @@
|
|||
TAILQ_HEAD(screens_head, Screen);
|
||||
extern struct screens_head *virtual_screens;
|
||||
|
||||
/**
|
||||
* Returns true if both screen objects describe the same screen (checks their
|
||||
* size and position).
|
||||
*
|
||||
*/
|
||||
bool screens_are_equal(i3Screen *screen1, i3Screen *screen2);
|
||||
|
||||
/**
|
||||
* We have just established a connection to the X server and need the initial
|
||||
* Xinerama information to setup workspaces for each screen.
|
||||
|
|
25
src/client.c
25
src/client.c
|
@ -289,3 +289,28 @@ void client_change_border(xcb_connection_t *conn, Client *client, char border_ty
|
|||
|
||||
redecorate_window(conn, client);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmap the client, correctly setting any state which is needed.
|
||||
*
|
||||
*/
|
||||
void client_unmap(xcb_connection_t *conn, Client *client) {
|
||||
/* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */
|
||||
long data[] = { XCB_WM_STATE_WITHDRAWN, XCB_NONE };
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
|
||||
|
||||
xcb_unmap_window(conn, client->frame);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map the client, correctly restoring any state needed.
|
||||
*
|
||||
*/
|
||||
void client_map(xcb_connection_t *conn, Client *client) {
|
||||
/* Set WM_STATE_NORMAL because GTK applications don’t want to drag & drop if we don’t.
|
||||
* Also, xprop(1) needs that to work. */
|
||||
long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
|
||||
|
||||
xcb_map_window(conn, client->frame);
|
||||
}
|
||||
|
|
119
src/commands.c
119
src/commands.c
|
@ -113,7 +113,7 @@ static void focus_thing(xcb_connection_t *conn, direction_t direction, thing_t t
|
|||
}
|
||||
|
||||
LOG("Switching to ws %d\n", target->current_workspace + 1);
|
||||
show_workspace(conn, target->current_workspace + 1);
|
||||
workspace_show(conn, target->current_workspace + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -530,7 +530,7 @@ static void move_floating_window_to_workspace(xcb_connection_t *conn, Client *cl
|
|||
/* If we’re moving it to an invisible screen, we need to unmap it */
|
||||
if (!workspace_is_visible(t_ws)) {
|
||||
LOG("This workspace is not visible, unmapping\n");
|
||||
xcb_unmap_window(conn, client->frame);
|
||||
client_unmap(conn, client);
|
||||
} else {
|
||||
/* If this is not the case, we move the window to a workspace
|
||||
* which is on another screen, so we also need to adjust its
|
||||
|
@ -613,7 +613,7 @@ static void move_current_window_to_workspace(xcb_connection_t *conn, int workspa
|
|||
/* If we’re moving it to an invisible screen, we need to unmap it */
|
||||
if (!workspace_is_visible(to_container->workspace)) {
|
||||
LOG("This workspace is not visible, unmapping\n");
|
||||
xcb_unmap_window(conn, current_client->frame);
|
||||
client_unmap(conn, current_client);
|
||||
} else {
|
||||
if (current_client->fullscreen) {
|
||||
LOG("Calling client_enter_fullscreen again\n");
|
||||
|
@ -630,113 +630,6 @@ static void move_current_window_to_workspace(xcb_connection_t *conn, int workspa
|
|||
set_focus(conn, current_client, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switches to the given workspace
|
||||
*
|
||||
*/
|
||||
void show_workspace(xcb_connection_t *conn, int workspace) {
|
||||
Client *client;
|
||||
bool need_warp = false;
|
||||
xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
|
||||
/* t_ws (to workspace) is just a convenience pointer to the workspace we’re switching to */
|
||||
Workspace *t_ws = &(workspaces[workspace-1]);
|
||||
|
||||
LOG("show_workspace(%d)\n", workspace);
|
||||
|
||||
/* Store current_row/current_col */
|
||||
c_ws->current_row = current_row;
|
||||
c_ws->current_col = current_col;
|
||||
|
||||
/* Check if the workspace has not been used yet */
|
||||
if (t_ws->screen == NULL) {
|
||||
LOG("initializing new workspace, setting num to %d\n", workspace);
|
||||
t_ws->screen = c_ws->screen;
|
||||
/* Copy the dimensions from the virtual screen */
|
||||
memcpy(&(t_ws->rect), &(t_ws->screen->rect), sizeof(Rect));
|
||||
}
|
||||
|
||||
if (c_ws->screen != t_ws->screen) {
|
||||
/* We need to switch to the other screen first */
|
||||
LOG("moving over to other screen.\n");
|
||||
|
||||
/* Store the old client */
|
||||
Client *old_client = CUR_CELL->currently_focused;
|
||||
|
||||
c_ws = &(workspaces[t_ws->screen->current_workspace]);
|
||||
current_col = c_ws->current_col;
|
||||
current_row = c_ws->current_row;
|
||||
if (CUR_CELL->currently_focused != NULL)
|
||||
need_warp = true;
|
||||
else {
|
||||
Rect *dims = &(c_ws->screen->rect);
|
||||
xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0,
|
||||
dims->x + (dims->width / 2), dims->y + (dims->height / 2));
|
||||
}
|
||||
|
||||
/* Re-decorate the old client, it’s not focused anymore */
|
||||
if ((old_client != NULL) && !old_client->dock)
|
||||
redecorate_window(conn, old_client);
|
||||
else xcb_flush(conn);
|
||||
}
|
||||
|
||||
/* Check if we need to change something or if we’re already there */
|
||||
if (c_ws->screen->current_workspace == (workspace-1)) {
|
||||
Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
|
||||
if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
|
||||
set_focus(conn, last_focused, true);
|
||||
if (need_warp) {
|
||||
client_warp_pointer_into(conn, last_focused);
|
||||
xcb_flush(conn);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
t_ws->screen->current_workspace = workspace-1;
|
||||
Workspace *old_workspace = c_ws;
|
||||
c_ws = &workspaces[workspace-1];
|
||||
|
||||
/* Unmap all clients of the old workspace */
|
||||
unmap_workspace(conn, old_workspace);
|
||||
|
||||
current_row = c_ws->current_row;
|
||||
current_col = c_ws->current_col;
|
||||
LOG("new current row = %d, current col = %d\n", current_row, current_col);
|
||||
|
||||
ignore_enter_notify_forall(conn, c_ws, true);
|
||||
|
||||
/* Map all clients on the new workspace */
|
||||
FOR_TABLE(c_ws)
|
||||
CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients)
|
||||
xcb_map_window(conn, client->frame);
|
||||
|
||||
/* Map all floating clients */
|
||||
if (!c_ws->floating_hidden)
|
||||
TAILQ_FOREACH(client, &(c_ws->floating_clients), floating_clients)
|
||||
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)
|
||||
if (stack_win->container->workspace == c_ws)
|
||||
xcb_map_window(conn, stack_win->window);
|
||||
|
||||
ignore_enter_notify_forall(conn, c_ws, false);
|
||||
|
||||
/* Restore focus on the new workspace */
|
||||
Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
|
||||
if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
|
||||
set_focus(conn, last_focused, true);
|
||||
if (need_warp) {
|
||||
client_warp_pointer_into(conn, last_focused);
|
||||
xcb_flush(conn);
|
||||
}
|
||||
} else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
|
||||
|
||||
render_layout(conn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Jumps to the given window class / title.
|
||||
* Title is matched using strstr, that is, matches if it appears anywhere
|
||||
|
@ -782,7 +675,7 @@ static void jump_to_container(xcb_connection_t *conn, const char *arguments) {
|
|||
}
|
||||
|
||||
/* Move to the target workspace */
|
||||
show_workspace(conn, ws);
|
||||
workspace_show(conn, ws);
|
||||
|
||||
if (result < 3)
|
||||
return;
|
||||
|
@ -909,7 +802,7 @@ static void next_previous_workspace(xcb_connection_t *conn, int direction) {
|
|||
}
|
||||
|
||||
if (t_ws->screen != NULL)
|
||||
show_workspace(conn, i+1);
|
||||
workspace_show(conn, i+1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1086,7 +979,7 @@ void parse_command(xcb_connection_t *conn, const char *command) {
|
|||
|
||||
if (*rest == '\0') {
|
||||
/* No rest? This was a workspace number, not a times specification */
|
||||
show_workspace(conn, times);
|
||||
workspace_show(conn, times);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
137
src/config.c
137
src/config.c
|
@ -14,6 +14,11 @@
|
|||
#include <stdlib.h>
|
||||
#include <glob.h>
|
||||
|
||||
/* We need Xlib for XStringToKeysym */
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
|
||||
#include "i3.h"
|
||||
#include "util.h"
|
||||
#include "config.h"
|
||||
|
@ -59,15 +64,28 @@ static void replace_variable(char *buffer, const char *key, const char *value) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ungrab the bound keys
|
||||
/**
|
||||
* Ungrabs all keys, to be called before re-grabbing the keys because of a
|
||||
* mapping_notify event or a configuration file reload
|
||||
*
|
||||
*/
|
||||
void ungrab_all_keys(xcb_connection_t *conn) {
|
||||
Binding *bind;
|
||||
TAILQ_FOREACH(bind, &bindings, bindings) {
|
||||
LOG("Ungrabbing %d\n", bind->keycode);
|
||||
xcb_ungrab_key(conn, bind->keycode, root, bind->keycode);
|
||||
LOG("Ungrabbing all keys\n");
|
||||
xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
|
||||
}
|
||||
|
||||
static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) {
|
||||
LOG("Grabbing %d\n", keycode);
|
||||
if ((bind->mods & BIND_MODE_SWITCH) != 0)
|
||||
xcb_grab_key(conn, 0, root, 0, keycode,
|
||||
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC);
|
||||
else {
|
||||
/* Grab the key in all combinations */
|
||||
#define GRAB_KEY(modifier) xcb_grab_key(conn, 0, root, modifier, keycode, \
|
||||
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC)
|
||||
GRAB_KEY(bind->mods);
|
||||
GRAB_KEY(bind->mods | xcb_numlock_mask);
|
||||
GRAB_KEY(bind->mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,18 +96,41 @@ void ungrab_all_keys(xcb_connection_t *conn) {
|
|||
void grab_all_keys(xcb_connection_t *conn) {
|
||||
Binding *bind;
|
||||
TAILQ_FOREACH(bind, &bindings, bindings) {
|
||||
LOG("Grabbing %d\n", bind->keycode);
|
||||
if ((bind->mods & BIND_MODE_SWITCH) != 0)
|
||||
xcb_grab_key(conn, 0, root, 0, bind->keycode,
|
||||
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC);
|
||||
else {
|
||||
/* Grab the key in all combinations */
|
||||
#define GRAB_KEY(modifier) xcb_grab_key(conn, 0, root, modifier, bind->keycode, \
|
||||
XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC)
|
||||
GRAB_KEY(bind->mods);
|
||||
GRAB_KEY(bind->mods | xcb_numlock_mask);
|
||||
GRAB_KEY(bind->mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
||||
/* The easy case: the user specified a keycode directly. */
|
||||
if (bind->keycode > 0) {
|
||||
grab_keycode_for_binding(conn, bind, bind->keycode);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We need to translate the symbol to a keycode */
|
||||
LOG("Translating symbol to keycode (\"%s\")\n", bind->symbol);
|
||||
xcb_keysym_t keysym = XStringToKeysym(bind->symbol);
|
||||
if (keysym == NoSymbol) {
|
||||
LOG("Could not translate string to key symbol: \"%s\"\n", bind->symbol);
|
||||
continue;
|
||||
}
|
||||
|
||||
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, keysym);
|
||||
if (keycodes == NULL) {
|
||||
LOG("Could not translate symbol \"%s\"\n", bind->symbol);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t last_keycode;
|
||||
bind->number_keycodes = 0;
|
||||
for (xcb_keycode_t *walk = keycodes; *walk != 0; walk++) {
|
||||
/* We hope duplicate keycodes will be returned in order
|
||||
* and skip them */
|
||||
if (last_keycode == *walk)
|
||||
continue;
|
||||
grab_keycode_for_binding(conn, bind, *walk);
|
||||
last_keycode = *walk;
|
||||
bind->number_keycodes++;
|
||||
}
|
||||
LOG("Got %d different keycodes\n", bind->number_keycodes);
|
||||
bind->translated_to = smalloc(bind->number_keycodes * sizeof(xcb_keycode_t));
|
||||
memcpy(bind->translated_to, keycodes, bind->number_keycodes * sizeof(xcb_keycode_t));
|
||||
free(keycodes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,7 +275,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
}
|
||||
|
||||
/* key bindings */
|
||||
if (strcasecmp(key, "bind") == 0) {
|
||||
if (strcasecmp(key, "bind") == 0 || strcasecmp(key, "bindsym") == 0) {
|
||||
#define CHECK_MODIFIER(name) \
|
||||
if (strncasecmp(walk, #name, strlen(#name)) == 0) { \
|
||||
modifiers |= BIND_##name; \
|
||||
|
@ -259,14 +300,25 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
break;
|
||||
}
|
||||
|
||||
/* Now check for the keycode */
|
||||
int keycode = strtol(walk, &rest, 10);
|
||||
if (!rest || *rest != ' ')
|
||||
die("Invalid binding\n");
|
||||
Binding *new = scalloc(sizeof(Binding));
|
||||
|
||||
/* Now check for the keycode or copy the symbol */
|
||||
if (strcasecmp(key, "bind") == 0) {
|
||||
int keycode = strtol(walk, &rest, 10);
|
||||
if (!rest || *rest != ' ')
|
||||
die("Invalid binding (keycode)\n");
|
||||
new->keycode = keycode;
|
||||
} else {
|
||||
rest = walk;
|
||||
char *sym = rest;
|
||||
while (*rest != '\0' && *rest != ' ')
|
||||
rest++;
|
||||
if (*rest != ' ')
|
||||
die("Invalid binding (keysym)\n");
|
||||
new->symbol = strndup(sym, (rest - sym));
|
||||
}
|
||||
rest++;
|
||||
LOG("keycode = %d, modifiers = %d, command = *%s*\n", keycode, modifiers, rest);
|
||||
Binding *new = smalloc(sizeof(Binding));
|
||||
new->keycode = keycode;
|
||||
LOG("keycode = %d, symbol = %s, modifiers = %d, command = *%s*\n", new->keycode, new->symbol, modifiers, rest);
|
||||
new->mods = modifiers;
|
||||
new->command = sstrdup(rest);
|
||||
TAILQ_INSERT_TAIL(&bindings, new, bindings);
|
||||
|
@ -297,9 +349,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
continue;
|
||||
}
|
||||
|
||||
/* name "workspace number" "name of the workspace" */
|
||||
if (strcasecmp(key, "name") == 0) {
|
||||
LOG("name workspace: %s\n",value);
|
||||
/* workspace "workspace number" [screen <screen>] ["name of the workspace"]
|
||||
* with screen := <number> | <position>, e.g. screen 1280 or screen 1 */
|
||||
if (strcasecmp(key, "name") == 0 || strcasecmp(key, "workspace") == 0) {
|
||||
LOG("workspace: %s\n",value);
|
||||
char *ws_str = sstrdup(value);
|
||||
char *end = strchr(ws_str, ' ');
|
||||
if (end == NULL)
|
||||
|
@ -319,11 +372,30 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
char *name = value;
|
||||
name += strlen(ws_str) + 1;
|
||||
|
||||
if (strncasecmp(name, "screen ", strlen("screen ")) == 0) {
|
||||
char *screen = strdup(name + strlen("screen "));
|
||||
if ((end = strchr(screen, ' ')) != NULL)
|
||||
*end = '\0';
|
||||
LOG("Setting preferred screen for workspace %d to \"%s\"\n", ws_num, screen);
|
||||
workspaces[ws_num - 1].preferred_screen = screen;
|
||||
|
||||
name += strlen("screen ") + strlen(screen);
|
||||
|
||||
}
|
||||
|
||||
/* Strip leading whitespace */
|
||||
while (*name != '\0' && *name == ' ')
|
||||
name++;
|
||||
|
||||
LOG("rest to parse = %s\n", name);
|
||||
|
||||
if (name == '\0') {
|
||||
free(ws_str);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG("setting name to \"%s\"\n", name);
|
||||
|
||||
workspace_set_name(&(workspaces[ws_num - 1]), name);
|
||||
free(ws_str);
|
||||
continue;
|
||||
|
@ -433,8 +505,15 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
|
||||
/* Set an empty name for every workspace which got no name */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (workspaces[i].name != NULL)
|
||||
Workspace *ws = &(workspaces[i]);
|
||||
if (ws->name != NULL) {
|
||||
/* If the font was not specified when the workspace name
|
||||
* was loaded, we need to predict the text width now */
|
||||
if (ws->text_width == 0)
|
||||
ws->text_width = predict_text_width(global_conn,
|
||||
config.font, ws->name, ws->name_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
workspace_set_name(&(workspaces[i]), NULL);
|
||||
}
|
||||
|
|
|
@ -415,8 +415,8 @@ void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace) {
|
|||
LOG("floating_hidden is now: %d\n", workspace->floating_hidden);
|
||||
TAILQ_FOREACH(client, &(workspace->floating_clients), floating_clients) {
|
||||
if (workspace->floating_hidden)
|
||||
xcb_unmap_window(conn, client->frame);
|
||||
else xcb_map_window(conn, client->frame);
|
||||
client_unmap(conn, client);
|
||||
else client_map(conn, client);
|
||||
}
|
||||
|
||||
/* If we just unmapped all floating windows we should ensure that the focus
|
||||
|
|
102
src/handlers.c
102
src/handlers.c
|
@ -35,6 +35,7 @@
|
|||
#include "client.h"
|
||||
#include "manage.h"
|
||||
#include "floating.h"
|
||||
#include "workspace.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
|
||||
|
@ -119,9 +120,24 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_
|
|||
|
||||
/* Find the binding */
|
||||
Binding *bind;
|
||||
TAILQ_FOREACH(bind, &bindings, bindings)
|
||||
if (bind->keycode == event->detail && bind->mods == state_filtered)
|
||||
break;
|
||||
TAILQ_FOREACH(bind, &bindings, bindings) {
|
||||
/* First compare the modifiers */
|
||||
if (bind->mods != state_filtered)
|
||||
continue;
|
||||
|
||||
/* If a symbol was specified by the user, we need to look in
|
||||
* the array of translated keycodes for the event’s keycode */
|
||||
if (bind->symbol != NULL) {
|
||||
if (memmem(bind->translated_to,
|
||||
bind->number_keycodes * sizeof(xcb_keycode_t),
|
||||
&(event->detail), sizeof(xcb_keycode_t)) != NULL)
|
||||
break;
|
||||
} else {
|
||||
/* This case is easier: The user specified a keycode */
|
||||
if (bind->keycode == event->detail)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* No match? Then it was an actively grabbed key, that is with Mode_switch, and
|
||||
the user did not press Mode_switch, so just pass it… */
|
||||
|
@ -140,6 +156,29 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with coordinates of an enter_notify event or motion_notify event
|
||||
* to check if the user crossed virtual screen boundaries and adjust the
|
||||
* current workspace, if so.
|
||||
*
|
||||
*/
|
||||
static void check_crossing_screen_boundary(uint32_t x, uint32_t y) {
|
||||
i3Screen *screen;
|
||||
|
||||
if ((screen = get_screen_containing(x, y)) == NULL) {
|
||||
LOG("ERROR: No such screen\n");
|
||||
return;
|
||||
}
|
||||
if (screen == c_ws->screen)
|
||||
return;
|
||||
|
||||
c_ws->current_row = current_row;
|
||||
c_ws->current_col = current_col;
|
||||
c_ws = &workspaces[screen->current_workspace];
|
||||
current_row = c_ws->current_row;
|
||||
current_col = c_ws->current_col;
|
||||
LOG("We're now on virtual screen number %d\n", screen->num);
|
||||
}
|
||||
|
||||
/*
|
||||
* When the user moves the mouse pointer onto a window, this callback gets called.
|
||||
|
@ -176,17 +215,7 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_
|
|||
/* If not, then the user moved his cursor to the root window. In that case, we adjust c_ws */
|
||||
if (client == NULL) {
|
||||
LOG("Getting screen at %d x %d\n", event->root_x, event->root_y);
|
||||
i3Screen *screen = get_screen_containing(event->root_x, event->root_y);
|
||||
if (screen == NULL) {
|
||||
LOG("ERROR: No such screen\n");
|
||||
return 0;
|
||||
}
|
||||
c_ws->current_row = current_row;
|
||||
c_ws->current_col = current_col;
|
||||
c_ws = &workspaces[screen->current_workspace];
|
||||
current_row = c_ws->current_row;
|
||||
current_col = c_ws->current_col;
|
||||
LOG("We're now on virtual screen number %d\n", screen->num);
|
||||
check_crossing_screen_boundary(event->root_x, event->root_y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -212,6 +241,44 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* When the user moves the mouse but does not change the active window
|
||||
* (e.g. when having no windows opened but moving mouse on the root screen
|
||||
* and crossing virtual screen boundaries), this callback gets called.
|
||||
*
|
||||
*/
|
||||
int handle_motion_notify(void *ignored, xcb_connection_t *conn, xcb_motion_notify_event_t *event) {
|
||||
LOG("pointer motion notify, getting screen at %d x %d\n", event->root_x, event->root_y);
|
||||
|
||||
check_crossing_screen_boundary(event->root_x, event->root_y);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the keyboard mapping changes (for example by using Xmodmap),
|
||||
* we need to update our key bindings then (re-translate symbols).
|
||||
*
|
||||
*/
|
||||
int handle_mapping_notify(void *ignored, xcb_connection_t *conn, xcb_mapping_notify_event_t *event) {
|
||||
LOG("\n\nmapping notify\n\n");
|
||||
|
||||
if (event->request != XCB_MAPPING_KEYBOARD &&
|
||||
event->request != XCB_MAPPING_MODIFIER)
|
||||
return 0;
|
||||
|
||||
xcb_refresh_keyboard_mapping(keysyms, event);
|
||||
|
||||
xcb_get_numlock_mask(conn);
|
||||
|
||||
ungrab_all_keys(conn);
|
||||
LOG("Re-grabbing...\n");
|
||||
grab_all_keys(conn);
|
||||
LOG("Done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the button press was on a stack window, handles focus setting and returns true
|
||||
* if so, or false otherwise.
|
||||
|
@ -271,7 +338,7 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e
|
|||
int add = (event->detail == XCB_BUTTON_INDEX_4 ? -1 : 1);
|
||||
for (int i = c_ws->num + add; (i >= 0) && (i < 10); i += add)
|
||||
if (workspaces[i].screen == screen) {
|
||||
show_workspace(conn, i+1);
|
||||
workspace_show(conn, i+1);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
@ -286,7 +353,7 @@ static bool button_press_bar(xcb_connection_t *conn, xcb_button_press_event_t *e
|
|||
i, drawn, workspaces[i].text_width);
|
||||
if (event->event_x > (drawn + 1) &&
|
||||
event->event_x <= (drawn + 1 + workspaces[i].text_width + 5 + 5)) {
|
||||
show_workspace(conn, i+1);
|
||||
workspace_show(conn, i+1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -629,6 +696,9 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
|
|||
|
||||
LOG("child of 0x%08x.\n", client->frame);
|
||||
xcb_reparent_window(conn, client->child, root, 0, 0);
|
||||
|
||||
client_unmap(conn, client);
|
||||
|
||||
xcb_destroy_window(conn, client->frame);
|
||||
xcb_flush(conn);
|
||||
table_remove(&by_parent, client->frame);
|
||||
|
|
37
src/layout.c
37
src/layout.c
|
@ -118,7 +118,7 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
|
|||
} else {
|
||||
if (client->container->currently_focused == client) {
|
||||
/* Distinguish if the window is currently focused… */
|
||||
if (last_focused == client)
|
||||
if (last_focused == client && c_ws == client->workspace)
|
||||
color = &(config.client.focused);
|
||||
/* …or if it is the focused window in a not focused container */
|
||||
else color = &(config.client.focused_inactive);
|
||||
|
@ -156,8 +156,11 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
|
|||
if (client->titlebar_position != TITLEBAR_OFF) {
|
||||
/* Draw the lines */
|
||||
xcb_draw_line(conn, drawable, gc, color->border, 0, offset, client->rect.width, offset);
|
||||
xcb_draw_line(conn, drawable, gc, color->border, 2, offset + font->height + 3,
|
||||
client->rect.width - 3, offset + font->height + 3);
|
||||
if ((client->container == NULL ||
|
||||
client->container->mode != MODE_STACK ||
|
||||
CIRCLEQ_NEXT_OR_NULL(&(client->container->clients), client, clients) == NULL))
|
||||
xcb_draw_line(conn, drawable, gc, color->border, 2, offset + font->height + 3,
|
||||
client->rect.width - 3, offset + font->height + 3);
|
||||
}
|
||||
|
||||
/* If the client has a title, we draw it */
|
||||
|
@ -210,16 +213,23 @@ 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. This
|
||||
* function also updates the client’s position, so if you work on tiling clients
|
||||
* only, you can use this function instead of separate calls to reposition_client
|
||||
* and resize_client to reduce flickering.
|
||||
*
|
||||
*/
|
||||
void resize_client(xcb_connection_t *conn, Client *client) {
|
||||
i3Font *font = load_font(conn, config.font);
|
||||
|
||||
LOG("frame 0x%08x needs to be pushed to %dx%d\n", client->frame, client->rect.x, client->rect.y);
|
||||
LOG("resizing client 0x%08x to %d x %d\n", client->frame, client->rect.width, client->rect.height);
|
||||
xcb_configure_window(conn, client->frame,
|
||||
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
||||
&(client->rect.width));
|
||||
XCB_CONFIG_WINDOW_X |
|
||||
XCB_CONFIG_WINDOW_Y |
|
||||
XCB_CONFIG_WINDOW_WIDTH |
|
||||
XCB_CONFIG_WINDOW_HEIGHT,
|
||||
&(client->rect.x));
|
||||
|
||||
/* Adjust the position of the child inside its frame.
|
||||
* The coordinates of the child are relative to its frame, we
|
||||
|
@ -314,15 +324,13 @@ void render_container(xcb_connection_t *conn, Container *container) {
|
|||
if (client->force_reconfigure |
|
||||
update_if_necessary(&(client->rect.x), container->x) |
|
||||
update_if_necessary(&(client->rect.y), container->y +
|
||||
(container->height / num_clients) * current_client))
|
||||
reposition_client(conn, client);
|
||||
|
||||
/* TODO: vertical default layout */
|
||||
if (client->force_reconfigure |
|
||||
(container->height / num_clients) * current_client) |
|
||||
update_if_necessary(&(client->rect.width), container->width) |
|
||||
update_if_necessary(&(client->rect.height), container->height / num_clients))
|
||||
resize_client(conn, client);
|
||||
|
||||
/* TODO: vertical default layout */
|
||||
|
||||
client->force_reconfigure = false;
|
||||
|
||||
current_client++;
|
||||
|
@ -389,10 +397,7 @@ void render_container(xcb_connection_t *conn, Container *container) {
|
|||
* Note the bitwise OR instead of logical OR to force evaluation of both statements */
|
||||
if (client->force_reconfigure |
|
||||
update_if_necessary(&(client->rect.x), container->x) |
|
||||
update_if_necessary(&(client->rect.y), container->y + (decoration_height * num_clients)))
|
||||
reposition_client(conn, client);
|
||||
|
||||
if (client->force_reconfigure |
|
||||
update_if_necessary(&(client->rect.y), container->y + (decoration_height * num_clients)) |
|
||||
update_if_necessary(&(client->rect.width), container->width) |
|
||||
update_if_necessary(&(client->rect.height), container->height - (decoration_height * num_clients)))
|
||||
resize_client(conn, client);
|
||||
|
@ -413,7 +418,7 @@ static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int
|
|||
SLIST_FOREACH(client, &(r_ws->screen->dock_clients), dock_clients) {
|
||||
LOG("client is at %d, should be at %d\n", client->rect.y, *height);
|
||||
if (client->force_reconfigure |
|
||||
update_if_necessary(&(client->rect.x), 0) |
|
||||
update_if_necessary(&(client->rect.x), r_ws->rect.x) |
|
||||
update_if_necessary(&(client->rect.y), *height))
|
||||
reposition_client(conn, client);
|
||||
|
||||
|
|
69
src/mainx.c
69
src/mainx.c
|
@ -18,6 +18,7 @@
|
|||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/extensions/XKB.h>
|
||||
|
@ -55,6 +56,8 @@ char **start_argv;
|
|||
/* This is our connection to X11 for use with XKB */
|
||||
Display *xkbdpy;
|
||||
|
||||
xcb_key_symbols_t *keysyms;
|
||||
|
||||
/* The list of key bindings */
|
||||
struct bindings_head bindings = TAILQ_HEAD_INITIALIZER(bindings);
|
||||
|
||||
|
@ -109,6 +112,34 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When using xmodmap to change the keyboard mapping, this event
|
||||
* is only sent via XKB. Therefore, we need this special handler.
|
||||
*
|
||||
*/
|
||||
static void xkb_got_event(EV_P_ struct ev_io *w, int revents) {
|
||||
LOG("got xkb event, yay\n");
|
||||
XEvent ev;
|
||||
/* When using xmodmap, every change (!) gets an own event.
|
||||
* Therefore, we just read all events and only handle the
|
||||
* mapping_notify once (we do not receive any other XKB
|
||||
* events anyway). */
|
||||
while (XPending(xkbdpy))
|
||||
XNextEvent(xkbdpy, &ev);
|
||||
|
||||
xcb_key_symbols_free(keysyms);
|
||||
keysyms = xcb_key_symbols_alloc(global_conn);
|
||||
|
||||
xcb_get_numlock_mask(global_conn);
|
||||
|
||||
ungrab_all_keys(global_conn);
|
||||
LOG("Re-grabbing...\n");
|
||||
grab_all_keys(global_conn);
|
||||
LOG("Done\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[], char *env[]) {
|
||||
int i, screens, opt;
|
||||
char *override_configpath = NULL;
|
||||
|
@ -193,6 +224,11 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (fcntl(ConnectionNumber(xkbdpy), F_SETFD, FD_CLOEXEC) == -1) {
|
||||
fprintf(stderr, "Could not set FD_CLOEXEC on xkbdpy\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i1;
|
||||
if (!XkbQueryExtension(xkbdpy,&i1,&evBase,&errBase,&major,&minor)) {
|
||||
fprintf(stderr, "XKB not supported by X-server\n");
|
||||
|
@ -200,18 +236,30 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
}
|
||||
/* end of ugliness */
|
||||
|
||||
if (!XkbSelectEvents(xkbdpy, XkbUseCoreKbd, XkbMapNotifyMask, XkbMapNotifyMask)) {
|
||||
fprintf(stderr, "Could not set XKB event mask\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initialize event loop using libev */
|
||||
struct ev_loop *loop = ev_default_loop(0);
|
||||
if (loop == NULL)
|
||||
die("Could not initialize libev. Bad LIBEV_FLAGS?\n");
|
||||
|
||||
struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io));
|
||||
struct ev_io *xkb = scalloc(sizeof(struct ev_io));
|
||||
struct ev_check *xcb_check = scalloc(sizeof(struct ev_check));
|
||||
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
|
||||
|
||||
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
|
||||
ev_io_start(loop, xcb_watcher);
|
||||
|
||||
ev_io_init(xkb, xkb_got_event, ConnectionNumber(xkbdpy), EV_READ);
|
||||
ev_io_start(loop, xkb);
|
||||
|
||||
/* Flush the buffer so that libev can properly get new events */
|
||||
XFlush(xkbdpy);
|
||||
|
||||
ev_check_init(xcb_check, xcb_check_cb);
|
||||
ev_check_start(loop, xcb_check);
|
||||
|
||||
|
@ -257,6 +305,13 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
/* Configure request = window tried to change size on its own */
|
||||
xcb_event_set_configure_request_handler(&evenths, handle_configure_request, NULL);
|
||||
|
||||
/* Motion notify = user moved his cursor (over the root window and may
|
||||
* cross virtual screen boundaries doing that) */
|
||||
xcb_event_set_motion_notify_handler(&evenths, handle_motion_notify, NULL);
|
||||
|
||||
/* Mapping notify = keyboard mapping changed (Xmodmap), re-grab bindings */
|
||||
xcb_event_set_mapping_notify_handler(&evenths, handle_mapping_notify, NULL);
|
||||
|
||||
/* Client message are sent to the root window. The only interesting client message
|
||||
for us is _NET_WM_STATE, we honour _NET_WM_STATE_FULLSCREEN */
|
||||
xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL);
|
||||
|
@ -277,6 +332,7 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video
|
||||
projector), the root window gets a
|
||||
ConfigureNotify */
|
||||
XCB_EVENT_MASK_POINTER_MOTION |
|
||||
XCB_EVENT_MASK_PROPERTY_CHANGE |
|
||||
XCB_EVENT_MASK_ENTER_WINDOW };
|
||||
xcb_change_window_attributes(conn, root, mask, values);
|
||||
|
@ -336,6 +392,8 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root);
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME], atoms[UTF8_STRING], 8, strlen("i3"), "i3");
|
||||
|
||||
keysyms = xcb_key_symbols_alloc(conn);
|
||||
|
||||
xcb_get_numlock_mask(conn);
|
||||
|
||||
grab_all_keys(conn);
|
||||
|
@ -355,8 +413,6 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
|
||||
xcb_flush(conn);
|
||||
|
||||
manage_existing_windows(conn, &prophs, root);
|
||||
|
||||
/* Get pointer position to see on which screen we’re starting */
|
||||
xcb_query_pointer_reply_t *reply;
|
||||
if ((reply = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL)) == NULL) {
|
||||
|
@ -369,10 +425,11 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
LOG("ERROR: No screen at %d x %d\n", reply->root_x, reply->root_y);
|
||||
return 0;
|
||||
}
|
||||
if (screen->current_workspace != 0) {
|
||||
LOG("Ok, I need to go to the other workspace\n");
|
||||
c_ws = &workspaces[screen->current_workspace];
|
||||
}
|
||||
|
||||
LOG("Starting on %d\n", screen->current_workspace);
|
||||
c_ws = &workspaces[screen->current_workspace];
|
||||
|
||||
manage_existing_windows(conn, &prophs, root);
|
||||
|
||||
/* Create the UNIX domain socket for IPC */
|
||||
if (config.ipc_socket_path != NULL) {
|
||||
|
|
18
src/manage.c
18
src/manage.c
|
@ -205,11 +205,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
|
|||
/* Yo dawg, I heard you like windows, so I create a window around your window… */
|
||||
new->frame = create_window(conn, framerect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, false, mask, values);
|
||||
|
||||
/* Set WM_STATE_NORMAL because GTK applications don’t want to drag & drop if we don’t.
|
||||
* Also, xprop(1) needs that to work. */
|
||||
long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
|
||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, new->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
|
||||
|
||||
/* Put the client inside the save set. Upon termination (whether killed or normal exit
|
||||
does not matter) of the window manager, these clients will be correctly reparented
|
||||
to their most closest living ancestor (= cleanup) */
|
||||
|
@ -342,12 +337,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
|
|||
|
||||
LOG("Changing container/workspace and unmapping the client\n");
|
||||
Workspace *t_ws = &(workspaces[assign->workspace-1]);
|
||||
if (t_ws->screen == NULL) {
|
||||
LOG("initializing new workspace, setting num to %d\n", assign->workspace);
|
||||
t_ws->screen = c_ws->screen;
|
||||
/* Copy the dimensions from the virtual screen */
|
||||
memcpy(&(t_ws->rect), &(t_ws->screen->rect), sizeof(Rect));
|
||||
}
|
||||
workspace_initialize(t_ws, c_ws->screen);
|
||||
|
||||
new->container = t_ws->table[t_ws->current_col][t_ws->current_row];
|
||||
new->workspace = t_ws;
|
||||
|
@ -445,8 +435,10 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
|
|||
|
||||
/* Map the window first to avoid flickering */
|
||||
xcb_map_window(conn, child);
|
||||
if (map_frame)
|
||||
xcb_map_window(conn, new->frame);
|
||||
if (map_frame) {
|
||||
LOG("Mapping client\n");
|
||||
client_map(conn, new);
|
||||
}
|
||||
if (CUR_CELL->workspace->fullscreen_client == NULL && !new->dock) {
|
||||
/* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */
|
||||
if (new->workspace->fullscreen_client == NULL) {
|
||||
|
|
59
src/util.c
59
src/util.c
|
@ -224,65 +224,6 @@ Client *get_last_focused_client(xcb_connection_t *conn, Container *container, Cl
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmaps all clients (and stack windows) of the given workspace.
|
||||
*
|
||||
* This needs to be called separately when temporarily rendering
|
||||
* a workspace which is not the active workspace to force
|
||||
* reconfiguration of all clients, like in src/xinerama.c when
|
||||
* re-assigning a workspace to another screen.
|
||||
*
|
||||
*/
|
||||
void unmap_workspace(xcb_connection_t *conn, Workspace *u_ws) {
|
||||
Client *client;
|
||||
struct Stack_Window *stack_win;
|
||||
|
||||
/* Ignore notify events because they would cause focus to be changed */
|
||||
ignore_enter_notify_forall(conn, u_ws, true);
|
||||
|
||||
/* Unmap all clients of the given workspace */
|
||||
int unmapped_clients = 0;
|
||||
FOR_TABLE(u_ws)
|
||||
CIRCLEQ_FOREACH(client, &(u_ws->table[cols][rows]->clients), clients) {
|
||||
LOG("unmapping normal client %p / %p / %p\n", client, client->frame, client->child);
|
||||
xcb_unmap_window(conn, client->frame);
|
||||
unmapped_clients++;
|
||||
}
|
||||
|
||||
/* To find floating clients, we traverse the focus stack */
|
||||
SLIST_FOREACH(client, &(u_ws->focus_stack), focus_clients) {
|
||||
if (!client_is_floating(client))
|
||||
continue;
|
||||
|
||||
LOG("unmapping floating client %p / %p / %p\n", client, client->frame, client->child);
|
||||
|
||||
xcb_unmap_window(conn, client->frame);
|
||||
unmapped_clients++;
|
||||
}
|
||||
|
||||
/* If we did not unmap any clients, the workspace is empty and we can destroy it, at least
|
||||
* if it is not the current workspace. */
|
||||
if (unmapped_clients == 0 && u_ws != c_ws) {
|
||||
/* Re-assign the workspace of all dock clients which use this workspace */
|
||||
Client *dock;
|
||||
LOG("workspace %p is empty\n", u_ws);
|
||||
SLIST_FOREACH(dock, &(u_ws->screen->dock_clients), dock_clients) {
|
||||
if (dock->workspace != u_ws)
|
||||
continue;
|
||||
|
||||
LOG("Re-assigning dock client to c_ws (%p)\n", c_ws);
|
||||
dock->workspace = c_ws;
|
||||
}
|
||||
u_ws->screen = NULL;
|
||||
}
|
||||
|
||||
/* Unmap the stack windows on the given workspace, if any */
|
||||
SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
|
||||
if (stack_win->container->workspace == u_ws)
|
||||
xcb_unmap_window(conn, stack_win->window);
|
||||
|
||||
ignore_enter_notify_forall(conn, u_ws, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the given client as focused by updating the data structures correctly,
|
||||
|
|
298
src/workspace.c
298
src/workspace.c
|
@ -12,6 +12,8 @@
|
|||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "util.h"
|
||||
|
@ -19,6 +21,11 @@
|
|||
#include "i3.h"
|
||||
#include "config.h"
|
||||
#include "xcb.h"
|
||||
#include "table.h"
|
||||
#include "xinerama.h"
|
||||
#include "layout.h"
|
||||
#include "workspace.h"
|
||||
#include "client.h"
|
||||
|
||||
/*
|
||||
* Sets the name (or just its number) for the given workspace. This has to
|
||||
|
@ -41,7 +48,9 @@ void workspace_set_name(Workspace *ws, const char *name) {
|
|||
FREE(ws->name);
|
||||
|
||||
ws->name = convert_utf8_to_ucs2(label, &(ws->name_len));
|
||||
ws->text_width = predict_text_width(global_conn, config.font, ws->name, ws->name_len);
|
||||
if (config.font != NULL)
|
||||
ws->text_width = predict_text_width(global_conn, config.font, ws->name, ws->name_len);
|
||||
else ws->text_width = 0;
|
||||
|
||||
free(label);
|
||||
}
|
||||
|
@ -55,3 +64,290 @@ void workspace_set_name(Workspace *ws, const char *name) {
|
|||
bool workspace_is_visible(Workspace *ws) {
|
||||
return (ws->screen->current_workspace == ws->num);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switches to the given workspace
|
||||
*
|
||||
*/
|
||||
void workspace_show(xcb_connection_t *conn, int workspace) {
|
||||
bool need_warp = false;
|
||||
xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root;
|
||||
/* t_ws (to workspace) is just a convenience pointer to the workspace we’re switching to */
|
||||
Workspace *t_ws = &(workspaces[workspace-1]);
|
||||
|
||||
LOG("show_workspace(%d)\n", workspace);
|
||||
|
||||
/* Store current_row/current_col */
|
||||
c_ws->current_row = current_row;
|
||||
c_ws->current_col = current_col;
|
||||
|
||||
/* Check if the workspace has not been used yet */
|
||||
workspace_initialize(t_ws, c_ws->screen);
|
||||
|
||||
if (c_ws->screen != t_ws->screen) {
|
||||
/* We need to switch to the other screen first */
|
||||
LOG("moving over to other screen.\n");
|
||||
|
||||
/* Store the old client */
|
||||
Client *old_client = CUR_CELL->currently_focused;
|
||||
|
||||
c_ws = &(workspaces[t_ws->screen->current_workspace]);
|
||||
current_col = c_ws->current_col;
|
||||
current_row = c_ws->current_row;
|
||||
if (CUR_CELL->currently_focused != NULL)
|
||||
need_warp = true;
|
||||
else {
|
||||
Rect *dims = &(c_ws->screen->rect);
|
||||
xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0,
|
||||
dims->x + (dims->width / 2), dims->y + (dims->height / 2));
|
||||
}
|
||||
|
||||
/* Re-decorate the old client, it’s not focused anymore */
|
||||
if ((old_client != NULL) && !old_client->dock)
|
||||
redecorate_window(conn, old_client);
|
||||
else xcb_flush(conn);
|
||||
}
|
||||
|
||||
/* Check if we need to change something or if we’re already there */
|
||||
if (c_ws->screen->current_workspace == (workspace-1)) {
|
||||
Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
|
||||
if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
|
||||
set_focus(conn, last_focused, true);
|
||||
if (need_warp) {
|
||||
client_warp_pointer_into(conn, last_focused);
|
||||
xcb_flush(conn);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
t_ws->screen->current_workspace = workspace-1;
|
||||
Workspace *old_workspace = c_ws;
|
||||
c_ws = &workspaces[workspace-1];
|
||||
|
||||
/* Unmap all clients of the old workspace */
|
||||
workspace_unmap_clients(conn, old_workspace);
|
||||
|
||||
current_row = c_ws->current_row;
|
||||
current_col = c_ws->current_col;
|
||||
LOG("new current row = %d, current col = %d\n", current_row, current_col);
|
||||
|
||||
workspace_map_clients(conn, c_ws);
|
||||
|
||||
/* Restore focus on the new workspace */
|
||||
Client *last_focused = SLIST_FIRST(&(c_ws->focus_stack));
|
||||
if (last_focused != SLIST_END(&(c_ws->focus_stack))) {
|
||||
set_focus(conn, last_focused, true);
|
||||
if (need_warp) {
|
||||
client_warp_pointer_into(conn, last_focused);
|
||||
xcb_flush(conn);
|
||||
}
|
||||
} else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
|
||||
|
||||
render_layout(conn);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parses the preferred_screen property of a workspace. You can either specify
|
||||
* the screen number (it is not given that the screen numbering always stays
|
||||
* the same) or the screen coordinates (exact coordinates, e.g. 1280 will match
|
||||
* the screen starting at x=1280, but 1281 will not). For coordinates, you can
|
||||
* either specify an x coordinate ("1280") or an y coordinate ("x800") or both
|
||||
* ("1280x800").
|
||||
*
|
||||
*/
|
||||
static i3Screen *get_screen_from_preference(struct screens_head *slist, char *preference) {
|
||||
i3Screen *screen;
|
||||
char *rest;
|
||||
int preferred_screen = strtol(preference, &rest, 10);
|
||||
|
||||
LOG("Getting screen for preference \"%s\" (%d)\n", preference, preferred_screen);
|
||||
|
||||
if ((rest == preference) || (preferred_screen >= num_screens)) {
|
||||
int x = INT_MAX, y = INT_MAX;
|
||||
if (strchr(preference, 'x') != NULL) {
|
||||
/* Check if only the y coordinate was specified */
|
||||
if (*preference == 'x')
|
||||
y = atoi(preference+1);
|
||||
else {
|
||||
x = atoi(preference);
|
||||
y = atoi(strchr(preference, 'x') + 1);
|
||||
}
|
||||
} else {
|
||||
x = atoi(preference);
|
||||
}
|
||||
|
||||
LOG("Looking for screen at %d x %d\n", x, y);
|
||||
|
||||
TAILQ_FOREACH(screen, slist, screens)
|
||||
if ((x == INT_MAX || screen->rect.x == x) &&
|
||||
(y == INT_MAX || screen->rect.y == y)) {
|
||||
LOG("found %p\n", screen);
|
||||
return screen;
|
||||
}
|
||||
|
||||
LOG("none found\n");
|
||||
return NULL;
|
||||
} else {
|
||||
int c = 0;
|
||||
TAILQ_FOREACH(screen, slist, screens)
|
||||
if (c++ == preferred_screen)
|
||||
return screen;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes the given workspace if it is not already initialized. The given
|
||||
* screen is to be understood as a fallback, if the workspace itself either
|
||||
* was not assigned to a particular screen or cannot be placed there because
|
||||
* the screen is not attached at the moment.
|
||||
*
|
||||
*/
|
||||
void workspace_initialize(Workspace *ws, i3Screen *screen) {
|
||||
if (ws->screen != NULL) {
|
||||
LOG("Workspace already initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* If this workspace has no preferred screen or if the screen it wants
|
||||
* to be on is not available at the moment, we initialize it with
|
||||
* the screen which was given */
|
||||
if (ws->preferred_screen == NULL ||
|
||||
(ws->screen = get_screen_from_preference(virtual_screens, ws->preferred_screen)) == NULL)
|
||||
ws->screen = screen;
|
||||
else { LOG("yay, found assignment\n"); }
|
||||
|
||||
/* Copy the dimensions from the virtual screen */
|
||||
memcpy(&(ws->rect), &(ws->screen->rect), sizeof(Rect));
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the first unused workspace for the given screen, taking into account
|
||||
* the preferred_screen setting of every workspace (workspace assignments).
|
||||
*
|
||||
*/
|
||||
Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *screen) {
|
||||
Workspace *result = NULL;
|
||||
|
||||
for (int c = 0; c < 10; c++) {
|
||||
Workspace *ws = &(workspaces[c]);
|
||||
if (ws->preferred_screen == NULL ||
|
||||
!screens_are_equal(get_screen_from_preference(slist, ws->preferred_screen), screen))
|
||||
continue;
|
||||
|
||||
result = ws;
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == NULL) {
|
||||
/* No assignment found, returning first unused workspace */
|
||||
for (int c = 0; c < 10; c++) {
|
||||
if (workspaces[c].screen != NULL)
|
||||
continue;
|
||||
|
||||
result = &(workspaces[c]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != NULL) {
|
||||
workspace_initialize(result, screen);
|
||||
return result;
|
||||
}
|
||||
|
||||
LOG("WARNING: No free workspace found to assign!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Maps all clients (and stack windows) of the given workspace.
|
||||
*
|
||||
*/
|
||||
void workspace_map_clients(xcb_connection_t *conn, Workspace *ws) {
|
||||
Client *client;
|
||||
|
||||
ignore_enter_notify_forall(conn, ws, true);
|
||||
|
||||
/* Map all clients on the new workspace */
|
||||
FOR_TABLE(ws)
|
||||
CIRCLEQ_FOREACH(client, &(ws->table[cols][rows]->clients), clients)
|
||||
client_map(conn, client);
|
||||
|
||||
/* Map all floating clients */
|
||||
if (!ws->floating_hidden)
|
||||
TAILQ_FOREACH(client, &(ws->floating_clients), floating_clients)
|
||||
client_map(conn, client);
|
||||
|
||||
/* Map all stack windows, if any */
|
||||
struct Stack_Window *stack_win;
|
||||
SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
|
||||
if (stack_win->container->workspace == ws)
|
||||
xcb_map_window(conn, stack_win->window);
|
||||
|
||||
ignore_enter_notify_forall(conn, ws, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmaps all clients (and stack windows) of the given workspace.
|
||||
*
|
||||
* This needs to be called separately when temporarily rendering
|
||||
* a workspace which is not the active workspace to force
|
||||
* reconfiguration of all clients, like in src/xinerama.c when
|
||||
* re-assigning a workspace to another screen.
|
||||
*
|
||||
*/
|
||||
void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws) {
|
||||
Client *client;
|
||||
struct Stack_Window *stack_win;
|
||||
|
||||
/* Ignore notify events because they would cause focus to be changed */
|
||||
ignore_enter_notify_forall(conn, u_ws, true);
|
||||
|
||||
/* Unmap all clients of the given workspace */
|
||||
int unmapped_clients = 0;
|
||||
FOR_TABLE(u_ws)
|
||||
CIRCLEQ_FOREACH(client, &(u_ws->table[cols][rows]->clients), clients) {
|
||||
LOG("unmapping normal client %p / %p / %p\n", client, client->frame, client->child);
|
||||
client_unmap(conn, client);
|
||||
unmapped_clients++;
|
||||
}
|
||||
|
||||
/* To find floating clients, we traverse the focus stack */
|
||||
SLIST_FOREACH(client, &(u_ws->focus_stack), focus_clients) {
|
||||
if (!client_is_floating(client))
|
||||
continue;
|
||||
|
||||
LOG("unmapping floating client %p / %p / %p\n", client, client->frame, client->child);
|
||||
|
||||
client_unmap(conn, client);
|
||||
unmapped_clients++;
|
||||
}
|
||||
|
||||
/* If we did not unmap any clients, the workspace is empty and we can destroy it, at least
|
||||
* if it is not the current workspace. */
|
||||
if (unmapped_clients == 0 && u_ws != c_ws) {
|
||||
/* Re-assign the workspace of all dock clients which use this workspace */
|
||||
Client *dock;
|
||||
LOG("workspace %p is empty\n", u_ws);
|
||||
SLIST_FOREACH(dock, &(u_ws->screen->dock_clients), dock_clients) {
|
||||
if (dock->workspace != u_ws)
|
||||
continue;
|
||||
|
||||
LOG("Re-assigning dock client to c_ws (%p)\n", c_ws);
|
||||
dock->workspace = c_ws;
|
||||
}
|
||||
u_ws->screen = NULL;
|
||||
}
|
||||
|
||||
/* Unmap the stack windows on the given workspace, if any */
|
||||
SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
|
||||
if (stack_win->container->workspace == u_ws)
|
||||
xcb_unmap_window(conn, stack_win->window);
|
||||
|
||||
ignore_enter_notify_forall(conn, u_ws, false);
|
||||
}
|
||||
|
||||
|
|
133
src/xinerama.c
133
src/xinerama.c
|
@ -27,6 +27,7 @@
|
|||
#include "layout.h"
|
||||
#include "xcb.h"
|
||||
#include "config.h"
|
||||
#include "workspace.h"
|
||||
|
||||
/* This TAILQ of i3Screens stores the virtual screens, used for handling overlapping screens
|
||||
* (xrandr --same-as) */
|
||||
|
@ -34,6 +35,25 @@ struct screens_head *virtual_screens;
|
|||
|
||||
static bool xinerama_enabled = true;
|
||||
|
||||
/*
|
||||
* Returns true if both screen objects describe the same screen (checks their
|
||||
* size and position).
|
||||
*
|
||||
*/
|
||||
bool screens_are_equal(i3Screen *screen1, i3Screen *screen2) {
|
||||
/* If one of both objects (or both) are NULL, we cannot compare them */
|
||||
if (screen1 == NULL || screen2 == NULL)
|
||||
return false;
|
||||
|
||||
/* If the pointers are equal, take the short-circuit */
|
||||
if (screen1 == screen2)
|
||||
return true;
|
||||
|
||||
/* Compare their size - other properties are not relevant to determine
|
||||
* if a screen is equal to another one */
|
||||
return (memcmp(&(screen1->rect), &(screen2->rect), sizeof(Rect)) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Looks in virtual_screens for the i3Screen whose start coordinates are x, y
|
||||
*
|
||||
|
@ -120,8 +140,6 @@ static void initialize_screen(xcb_connection_t *conn, i3Screen *screen, Workspac
|
|||
|
||||
SLIST_INIT(&(screen->dock_clients));
|
||||
|
||||
/* Copy dimensions */
|
||||
memcpy(&(workspace->rect), &(screen->rect), sizeof(Rect));
|
||||
LOG("that is virtual screen at %d x %d with %d x %d\n",
|
||||
screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height);
|
||||
}
|
||||
|
@ -142,7 +160,6 @@ static void disable_xinerama(xcb_connection_t *conn) {
|
|||
|
||||
num_screens = 1;
|
||||
s->num = 0;
|
||||
initialize_screen(conn, s, &(workspaces[0]));
|
||||
|
||||
TAILQ_INSERT_TAIL(virtual_screens, s, screens);
|
||||
|
||||
|
@ -175,7 +192,7 @@ static void query_screens(xcb_connection_t *conn, struct screens_head *screenlis
|
|||
|
||||
for (int screen = 0; screen < screens; screen++) {
|
||||
i3Screen *s = get_screen_at(screen_info[screen].x_org, screen_info[screen].y_org, screenlist);
|
||||
if (s!= NULL) {
|
||||
if (s != NULL) {
|
||||
/* This screen already exists. We use the littlest screen so that the user
|
||||
can always see the complete workspace */
|
||||
s->rect.width = min(s->rect.width, screen_info[screen].width);
|
||||
|
@ -235,13 +252,14 @@ void initialize_xinerama(xcb_connection_t *conn) {
|
|||
|
||||
FREE(reply);
|
||||
|
||||
i3Screen *s;
|
||||
i3Screen *screen;
|
||||
num_screens = 0;
|
||||
/* Just go through each workspace and associate as many screens as we can. */
|
||||
TAILQ_FOREACH(s, virtual_screens, screens) {
|
||||
s->num = num_screens;
|
||||
initialize_screen(conn, s, &(workspaces[num_screens]));
|
||||
TAILQ_FOREACH(screen, virtual_screens, screens) {
|
||||
screen->num = num_screens;
|
||||
num_screens++;
|
||||
Workspace *ws = get_first_workspace_for_screen(virtual_screens, screen);
|
||||
initialize_screen(conn, screen, ws);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,65 +286,85 @@ void xinerama_requery_screens(xcb_connection_t *conn) {
|
|||
query_screens(conn, new_screens);
|
||||
|
||||
i3Screen *first = TAILQ_FIRST(new_screens),
|
||||
*screen;
|
||||
*screen,
|
||||
*old_screen;
|
||||
int screen_count = 0;
|
||||
/* Mark each workspace which currently is assigned to a screen, so we
|
||||
* can garbage-collect afterwards */
|
||||
for (int c = 0; c < 10; c++)
|
||||
workspaces[c].reassigned = (workspaces[c].screen == NULL);
|
||||
|
||||
TAILQ_FOREACH(screen, new_screens, screens) {
|
||||
screen->num = screen_count;
|
||||
screen->current_workspace = -1;
|
||||
for (int c = 0; c < 10; c++)
|
||||
if ((workspaces[c].screen != NULL) &&
|
||||
(workspaces[c].screen->num == screen_count)) {
|
||||
LOG("Found a matching screen\n");
|
||||
/* Try to use the same workspace, if it’s available */
|
||||
if (workspaces[c].screen->current_workspace)
|
||||
screen->current_workspace = workspaces[c].screen->current_workspace;
|
||||
|
||||
if (screen->current_workspace == -1)
|
||||
screen->current_workspace = c;
|
||||
TAILQ_FOREACH(old_screen, virtual_screens, screens) {
|
||||
if (old_screen->num != screen_count)
|
||||
continue;
|
||||
|
||||
/* Re-use the old bar window */
|
||||
screen->bar = workspaces[c].screen->bar;
|
||||
screen->bargc = workspaces[c].screen->bargc;
|
||||
LOG("Found a matching screen\n");
|
||||
/* Use the same workspace */
|
||||
screen->current_workspace = old_screen->current_workspace;
|
||||
|
||||
Rect bar_rect = {screen->rect.x,
|
||||
screen->rect.height - (font->height + 6),
|
||||
screen->rect.x + screen->rect.width,
|
||||
font->height + 6};
|
||||
/* Re-use the old bar window */
|
||||
screen->bar = old_screen->bar;
|
||||
screen->bargc = old_screen->bargc;
|
||||
LOG("old_screen->bar = %p\n", old_screen->bar);
|
||||
|
||||
xcb_configure_window(conn, screen->bar, XCB_CONFIG_WINDOW_X |
|
||||
XCB_CONFIG_WINDOW_Y |
|
||||
XCB_CONFIG_WINDOW_WIDTH |
|
||||
XCB_CONFIG_WINDOW_HEIGHT, &(bar_rect.x));
|
||||
Rect bar_rect = {screen->rect.x,
|
||||
screen->rect.height - (font->height + 6),
|
||||
screen->rect.x + screen->rect.width,
|
||||
font->height + 6};
|
||||
|
||||
/* Copy the list head for the dock clients */
|
||||
screen->dock_clients = workspaces[c].screen->dock_clients;
|
||||
LOG("configuring bar to be at %d x %d with %d x %d\n",
|
||||
bar_rect.x, bar_rect.y, bar_rect.height, bar_rect.width);
|
||||
xcb_configure_window(conn, screen->bar, XCB_CONFIG_WINDOW_X |
|
||||
XCB_CONFIG_WINDOW_Y |
|
||||
XCB_CONFIG_WINDOW_WIDTH |
|
||||
XCB_CONFIG_WINDOW_HEIGHT, &(bar_rect.x));
|
||||
|
||||
/* Update the dimensions */
|
||||
memcpy(&(workspaces[c].rect), &(screen->rect), sizeof(Rect));
|
||||
workspaces[c].screen = screen;
|
||||
/* Copy the list head for the dock clients */
|
||||
screen->dock_clients = old_screen->dock_clients;
|
||||
|
||||
/* Update the dimensions */
|
||||
for (int c = 0; c < 10; c++) {
|
||||
Workspace *ws = &(workspaces[c]);
|
||||
if (ws->screen != old_screen)
|
||||
continue;
|
||||
|
||||
LOG("re-assigning ws %d\n", ws->num);
|
||||
memcpy(&(ws->rect), &(screen->rect), sizeof(Rect));
|
||||
ws->screen = screen;
|
||||
ws->reassigned = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if (screen->current_workspace == -1) {
|
||||
/* Create a new workspace for this screen, it’s new */
|
||||
for (int c = 0; c < 10; c++)
|
||||
if (workspaces[c].screen == NULL) {
|
||||
LOG("fix: initializing new workspace, setting num to %d\n", c);
|
||||
initialize_screen(conn, screen, &(workspaces[c]));
|
||||
break;
|
||||
}
|
||||
/* Find the first unused workspace, preferring the ones
|
||||
* which are assigned to this screen and initialize
|
||||
* the screen with it. */
|
||||
LOG("getting first ws for screen %p\n", screen);
|
||||
Workspace *ws = get_first_workspace_for_screen(new_screens, screen);
|
||||
initialize_screen(conn, screen, ws);
|
||||
|
||||
/* As this workspace just got visible (we got a new screen
|
||||
* without workspace), we need to map its clients */
|
||||
workspace_map_clients(conn, ws);
|
||||
}
|
||||
screen_count++;
|
||||
}
|
||||
|
||||
/* Check for workspaces which are out of bounds */
|
||||
for (int c = 0; c < 10; c++) {
|
||||
if ((workspaces[c].screen == NULL) || (workspaces[c].screen->num < num_screens))
|
||||
if (workspaces[c].reassigned)
|
||||
continue;
|
||||
|
||||
/* f_ws is a shortcut to the workspace to fix */
|
||||
Workspace *f_ws = &(workspaces[c]);
|
||||
Client *client;
|
||||
|
||||
LOG("Closing bar window\n");
|
||||
LOG("Closing bar window (%p)\n", f_ws->screen->bar);
|
||||
xcb_destroy_window(conn, f_ws->screen->bar);
|
||||
|
||||
LOG("Workspace %d's screen out of bounds, assigning to first screen\n", c+1);
|
||||
|
@ -342,10 +380,15 @@ void xinerama_requery_screens(xcb_connection_t *conn) {
|
|||
render_workspace(conn, first, f_ws);
|
||||
|
||||
/* …unless we want to see them at the moment, we should hide that workspace */
|
||||
if (first->current_workspace == c)
|
||||
if (workspace_is_visible(f_ws))
|
||||
continue;
|
||||
|
||||
unmap_workspace(conn, f_ws);
|
||||
workspace_unmap_clients(conn, f_ws);
|
||||
|
||||
if (c_ws == f_ws) {
|
||||
LOG("Need to adjust c_ws...\n");
|
||||
c_ws = &(workspaces[first->current_workspace]);
|
||||
}
|
||||
}
|
||||
xcb_flush(conn);
|
||||
|
||||
|
|
Loading…
Reference in New Issue